/*******************************************************************************
 * Caleydo - Visualization for Molecular Biology - http://caleydo.org
 * Copyright (c) The Caleydo Team. All rights reserved.
 * Licensed under the new BSD license, available at http://caleydo.org/license
 ******************************************************************************/
package org.caleydo.core.util.function;

import java.util.Arrays;

/**
 * advanced version of {@link DoubleStatistics} including median, and quartiles but requires the full data so no line
 * version
 *
 * @author Samuel Gratzl
 *
 */
public class AdvancedDoubleStatistics extends DoubleStatistics {
	private final double median, quartile25, quartile75, medianAbsoluteDeviation;

	// http://stat.ethz.ch/R-manual/R-patched/library/stats/html/mad.html
	public static final double MEDIAN_ABSOLUTE_DEVIATION_CONSTANT = 1.4826f;

	public AdvancedDoubleStatistics(double median, double quartile25, double quartile75, double medianAbsoluteDeviation) {
		this.median = median;
		this.quartile25 = quartile25;
		this.quartile75 = quartile75;
		this.medianAbsoluteDeviation = medianAbsoluteDeviation;
	}

	/**
	 * @return the median, see {@link #median}
	 */
	public double getMedian() {
		return median;
	}

	/**
	 * @return the median absolute deviation as implemented in R:
	 *         http://stat.ethz.ch/R-manual/R-patched/library/stats/html/mad.html, see {@link #medianAbsoluteDeviation}
	 */
	public double getMedianAbsoluteDeviation() {
		return medianAbsoluteDeviation;
	}

	/**
	 * @return the quartile25, see {@link #quartile25}
	 */
	public double getQuartile25() {
		return quartile25;
	}

	/**
	 * @return the quartile75, see {@link #quartile75}
	 */
	public double getQuartile75() {
		return quartile75;
	}

	public double getIQR() {
		return quartile75 - quartile25;
	}

	public static AdvancedDoubleStatistics of(IDoubleList list) {
		return ofImpl(list.toPrimitiveArray());
	}

	public static AdvancedDoubleStatistics of(double... arr) {
		return ofImpl(Arrays.copyOf(arr, arr.length));
	}

	private static AdvancedDoubleStatistics ofImpl(double[] data) {
		Arrays.sort(data);

		double median = median(data);

		double quartile25 = percentile(data, 0.25f);
		double quartile75 = percentile(data, 0.75f);

		double[] medianDeltas = new double[data.length];
		for (int i = 0; i < data.length; i++) {
			medianDeltas[i] = Math.abs(data[i] - median);
		}
		Arrays.sort(medianDeltas);

		double medianAbsoluteDeviation = MEDIAN_ABSOLUTE_DEVIATION_CONSTANT * median(medianDeltas);

		AdvancedDoubleStatistics result = new AdvancedDoubleStatistics(median, quartile25, quartile75,
				medianAbsoluteDeviation);
		result.add(data);
		return result;
	}

	/*
	 * assumes sorted data
	 */
	private static double median(double[] data) {
		final int n = data.length;

		int middle = n / 2;

		double median;
		if (n % 2 == 1) {
			median = data[middle];
		} else {
			median = (data[middle - 1] + data[middle]) * 0.5f;
		}

		return median;
	}

	/*
	 * assumes sorted data
	 */
	private static double percentile(double[] data, double percentile) {
		final int n = data.length;

		double k = (n - 1) * percentile;
		int f = (int) Math.floor(k);
		int c = (int) Math.ceil(k);
		if (f == c) {
			return data[(int) k];
		} else {
			double d0 = data[f] * (c - k);
			double d1 = data[c] * (k - f);
			return d0 + d1;
		}
	}

}

@@ -2138,32 +2138,153 @@
 ays.sort(data);%0A
+%09%09int nans = countNans(data);%0A%09%09// observation nans are always as the right side%0A%09%09final int length = data.length - nans;
 %0A%09%09double median
@@ -2297,16 +2297,24 @@
 ian(data
+, length
 );%0A%0A%09%09do
@@ -2347,16 +2347,24 @@
 le(data,
+ length,
  0.25f);
@@ -2402,16 +2402,24 @@
 le(data,
+ length,
  0.75f);
@@ -2457,21 +2457,16 @@
  double%5B
-data.
 length%5D;
@@ -2488,21 +2488,16 @@
  0; i %3C 
-data.
 length; 
@@ -2673,24 +2673,32 @@
 medianDeltas
+, length
 );%0A%0A%09%09Advanc
@@ -2864,32 +2864,37 @@
 %0A%09/*
+*
 %0A%09 * 
-assumes sorted data
+@param data%0A%09 * @return
 %0A%09 *
@@ -2911,29 +2911,29 @@
  static 
-double medi
+int countN
 an
+s
 (double%5B
@@ -2948,35 +2948,277 @@
 %7B%0A%09%09
-final int n = data.length;%0A
+// observation nans are always as the right side%0A%09%09for (int i = data.length - 1; i %3E= 0; --i)%0A%09%09%09if (!Double.isNaN(data%5Bi%5D))%0A%09%09%09%09return data.length - i - 1;%0A%09%09return data.length;%0A%09%7D%0A%0A%09/*%0A%09 * assumes sorted data%0A%09 */%0A%09private static double median(double%5B%5D data, int n) %7B
 %0A%09%09i
@@ -3472,16 +3472,23 @@
 %5B%5D data,
+ int n,
  double 
@@ -3505,38 +3505,8 @@
 ) %7B%0A
-%09%09final int n = data.length;%0A%0A
 %09%09do

fix AdvandedDoubleStatistics to correctly consider NaNs