/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.apache.sysds.test.applications;

import java.util.Arrays;

import org.junit.Test;
import org.apache.sysds.test.AutomatedTestBase;
import org.apache.sysds.test.TestConfiguration;

public class OrderStatisticsTest extends AutomatedTestBase 
{
	
	private static final String TEST_DIR = "applications/descriptivestats/";
	private static final String TEST_CLASS_DIR = TEST_DIR + OrderStatisticsTest.class.getSimpleName() + "/";
	
	@Override
	public void setUp() {
		addTestConfiguration("SimpleQuantileTest",
			new TestConfiguration(TEST_CLASS_DIR, "SimpleQuantileTest", new String[] { "median", "weighted_median" }));
		addTestConfiguration("QuantileTest",
			new TestConfiguration(TEST_CLASS_DIR, "QuantileTest", new String[] { "quantile", "weighted_quantile" }));
		addTestConfiguration("IQMTest",
			new TestConfiguration(TEST_CLASS_DIR, "IQMTest", new String[] { "iqm", "weighted_iqm" }));
	}
	
	public static double quantile(double[] vector, double p)
	{
		int i=(int)Math.ceil(p*vector.length);
		//System.out.println("P = "+p+", i="+i+", value: "+vector[i-1]);
		return vector[i-1];
	}
	
	public static double IQM(double[] vector) {
		int start=(int)Math.ceil(0.25*vector.length);
		int end=(int)Math.ceil(0.75*vector.length);
		double sum=0;
		for(int i=start; i<end; i++)
			sum+=vector[i];
		return sum/(end-start);
	}
	
	public static double[] sort(double[][] vector)
	{
		double[] ret=new double[vector.length];
		for(int i=0; i<ret.length; i++)
			ret[i]=vector[i][0];
		Arrays.sort(ret);
		return ret;
	}
	
	public static double[][] blowUp(double[][] vector, double[][] weight) {
		int totalNum=0;
		for(int i=0; i<weight.length; i++)		
			totalNum+=weight[i][0];
		double[][] ret=new double[totalNum][1];
		int index=0;
		for(int i=0; i<weight.length; i++)
		{	for(int j=0; j<weight[i][0]; j++)
				ret[index++][0]=vector[i][0];
		}
		return ret;
	}	
	
	public static void round(double[][] weight) {
		for(int i=0; i<weight.length; i++)
			weight[i][0]=Math.floor(weight[i][0]);
	}
	
	//@Test
	public void testSimpleQuantile()
	{
		int rows = 10;

		TestConfiguration config = getTestConfiguration("SimpleQuantileTest");
		config.addVariable("rows", rows);

		loadTestConfiguration(config);

		createHelperMatrix();
		double[][] vector = getRandomMatrix(rows, 1, 0, 1, 1, System.currentTimeMillis());
		double[][] weight = getRandomMatrix(rows, 1, 1, 10, 1, System.currentTimeMillis());
		round(weight);

		double[] sorted = sort(vector);
		double median = quantile(sorted, 0.5);

		writeInputMatrix("vector", vector);
		writeExpectedHelperMatrix("median", median);

		double[] fullsorted = sort(blowUp(vector, weight));
		double weightedmedian = quantile(fullsorted, 0.5);
		writeInputMatrix("weight", weight);
		writeExpectedHelperMatrix("weighted_median", weightedmedian);

		runTest();

		compareResults(5e-14);
	}

	@Test
	public void testQuantile() {
		int rows1 = 10;
		int rows2 = 5;

		TestConfiguration config = getTestConfiguration("QuantileTest");
		config.addVariable("rows1", rows1);
		config.addVariable("rows2", rows2);

		loadTestConfiguration(config);

		createHelperMatrix();
		double[][] vector = getRandomMatrix(rows1, 1, 0, 1, 1, System.currentTimeMillis());
		double[][] weight = getRandomMatrix(rows1, 1, 1, 10, 1, System.currentTimeMillis());
		round(weight);
		double[][] prob = getRandomMatrix(rows2, 1, 0, 1, 1, System.currentTimeMillis());

		double[][] q = new double[prob.length][1];
		double[] sorted = sort(vector);
		for(int i = 0; i < prob.length; i++)
			q[i][0] = quantile(sorted, prob[i][0]);

		writeInputMatrix("vector", vector);
		writeInputMatrix("prob", prob);
		writeExpectedMatrix("quantile", q);

		double[][] wq = new double[prob.length][1];
		double[] fullsorted = sort(blowUp(vector, weight));
		for(int i = 0; i < prob.length; i++)
			wq[i][0] = quantile(fullsorted, prob[i][0]);
		writeInputMatrix("weight", weight);
		writeExpectedMatrix("weighted_quantile", wq);

		runTest();

		compareResults(5e-14);
	}

}
