//! \~\ingroup Math
//! \~\file pistatistic.h
//! \brief
//! \~english Calculating math statistic of values array
//! \~russian Вычисление математической статистики у массива чисел
/*
    PIP - Platform Independent Primitives
	Calculating math statistic of values array
    Andrey Bychkov work.a.b@yandex.ru

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef PISTATISTIC_H
#define PISTATISTIC_H

#include "pimathbase.h"

//! \~\ingroup Math
//! \~\brief
//! \~english Template class for calculating statistical measures of a data set
//! \~russian Шаблонный класс для вычисления статистических характеристик набора данных
//! \details
//! \~english Calculates mean, variance, skewness and kurtosis for a numeric sample.
//! \~russian Вычисляет среднее, дисперсию, асимметрию и эксцесс для числовой выборки.
template<typename T>
class PIStatistic {
	static_assert(std::is_arithmetic<T>::value, "Type must be arithmetic");

public:
	//! \~english Constructs an object with all accumulated values set to zero.
	//! \~russian Создает объект со всеми накопленными значениями, равными нулю.
	PIStatistic() { mean = variance = skewness = kurtosis = T(); }

	//! \~english Returns arithmetic mean of the sample, or zero for an empty vector.
	//! \~russian Возвращает среднее арифметическое выборки или ноль для пустого вектора.
	static T calculateMean(const PIVector<T> & val) {
		T ret = T();
		int n = val.size();
		if (n < 1) return ret;
		for (int i = 0; i < n; i++)
			ret += val[i];
		return ret / n;
	}

	//! \~english Calculates all statistics using the supplied mean value.
	//! \~russian Вычисляет всю статистику, используя переданное среднее значение.
	//! \~\details
	//! \~english Returns \c false when the sample contains fewer than two values.
	//! \~russian Возвращает \c false, если в выборке меньше двух значений.
	bool calculate(const PIVector<T> & val, const T & given_mean) {
		T v = T(), v1 = T(), v2 = T(), stddev = T(), var = T();
		int i, n = val.size();
		mean = given_mean;
		if (n < 2) return false;
		variance = skewness = kurtosis = T();
		// Variance (using corrected two-pass algorithm)
		for (i = 0; i < n; i++)
			v1 += sqr(val[i] - mean);
		for (i = 0; i < n; i++)
			v2 += val[i] - mean;
		v2       = sqr(v2) / n;
		variance = v1 / n;
		var      = (v1 / n - v2) / (n - 1);
		if (var < T()) var = T();
		stddev = sqrt(var);
		// Skewness and kurtosis
		if (stddev != T()) {
			for (i = 0; i < n; i++) {
				v        = (val[i] - mean) / stddev;
				v2       = sqr(v);
				skewness = skewness + v2 * v;
				kurtosis = kurtosis + sqr(v2);
			}
			skewness /= n;
			kurtosis = kurtosis / n - 3.;
		}
		return true;
	}

	//! \~english Calculates all statistics and derives the mean from the sample.
	//! \~russian Вычисляет всю статистику, определяя среднее по самой выборке.
	bool calculate(const PIVector<T> & val) { return calculate(val, calculateMean(val)); }

	//! \~english Sample mean.
	//! \~russian Среднее значение выборки.
	T mean;

	//! \~english Variance estimate accumulated for the sample.
	//! \~russian Оценка дисперсии, вычисленная по выборке.
	T variance;

	//! \~english Sample skewness.
	//! \~russian Асимметрия выборки.
	T skewness;

	//! \~english Excess kurtosis of the sample.
	//! \~russian Эксцесс выборки.
	T kurtosis;
};

//! \~english Integer statistics helper.
//! \~russian Вспомогательный тип статистики для целых чисел.
typedef PIStatistic<int> PIStatistici;

//! \~english Single-precision statistics helper.
//! \~russian Вспомогательный тип статистики одинарной точности.
typedef PIStatistic<float> PIStatisticf;

//! \~english Double-precision statistics helper.
//! \~russian Вспомогательный тип статистики двойной точности.
typedef PIStatistic<double> PIStatisticd;

#endif // PISTATISTIC_H
