// $Id: WCMath.h,v 1.2 2011/01/08 01:13:24 samn Exp $ 
#pragma once
#include <cmath>
#include <vector>
#include "A2D.h"

using namespace std;

inline long Factorial(long n)
{
	if(n <= 1) return 1;
	return n * Factorial(n - 1);
}

inline long DFactorial(long n)
{
	if(n<=1) return 1;
	return n * DFactorial(n-2);
}

//NR version of logarithm of gamma function
inline float lngamma(float xx)
{
	double x,y,tmp,ser;
	static double cof[6]={76.18009172947146,-86.50532032941677,
		24.01409824083091,-1.231739572450155,
		0.1208650973866179e-2,-0.5395239384953e-5};
	int j;

	y=x=xx;
	tmp=x+5.5;
	tmp -= (x+0.5)*log(tmp);
	ser=1.000000000190015;
	for (j=0;j<=5;j++) ser += cof[j]/++y;
	return -tmp+log(2.5066282746310005*ser/x);
}

inline double Gamma(double R)
{
	int iR = (int) R;
	double d = iR;
	if(d == R)
	{
		return Factorial(iR-1);
	}
	else
	{
		const double PI=3.14159265358979323846;
		//R = n/2 + 1;
		//n = (R - 1)*2;		
		int n = static_cast<int>(( R - 1.0 ) * 2.0);
		return (sqrt(PI) * DFactorial(n)) / pow(2,(n+1)/2);
	}
}

//monotonically decreasing
template< class T >
inline bool MonoDec(std::vector<T>& v)
{
	int isz = v.size(), i, j;
	for(i=0;i<isz-1;i++)
	{
		for(j=i+1;j<isz;j++)
		{
			if(v[i]<v[j])
				return false;
		}
	}
	return true;
}

template< class T > 
T Sum(std::vector<T>& v)
{
	int i;
	T val = T(0);
	for(i=0;i<v.size();i++) val += v[i];
	return val;
}

template< class T > 
T Sum(std::vector<T>& v,int iStart,int iEnd)
{
	int i;
	T val = T(0);
	for(i=iStart;i<v.size() && i<iEnd;i++) val += v[i];
	return val;
}

template< class T > 
T Avg(std::vector<T>& v)
{
	if(!v.size()) return T(0);
	int i;
	T val = T(0);
	for(i=0;i<v.size();i++) val += v[i];
	return val / (T) v.size();
}

template< class T > 
T Avg(std::vector<T>& v,int iStart,int iEnd)
{
	if(!v.size()) return T(0);
	int i;
	T val = T(0);
	for(i=iStart;i<iEnd;i++) val += v[i];
	return val / (T) (iEnd-iStart);
}

template< class T >
T Variance(std::vector<T>& v,int iStart,int iEnd)
{	T avg = Avg(v,iStart,iEnd);
	int i;
	T var = T(0) , val = T(0);
	for(i=iStart;i<iEnd;i++)
	{	val = v[i]-avg;
		var += val*val;
	}
	var /= (T)(iEnd-iStart);
	return var;
}

//returns sum(1..N)
inline int IntegerSum(int N)
{
	return (N*N+N)/2;
}

using namespace std;

//#define SAMPLE_MEAN

// Variance-covariance matrix: creation
template<class REAL>
void CovarMat(const vector< vector<REAL> >& vDataIn,int iRows,int iCols,vector< vector<REAL> >& vCovarMat, vector<REAL>& vMean,bool bCorrelMat = false)
{	// Create iCols * iCols covariance matrix from given iRow * iCol data matrix. 
	int i, j, x2, x, y, j1 , j2;
	// Allocate storage for mean vector 
	vMean = vector<REAL>(iCols,0.0);

	vector< vector<REAL> > vData(vDataIn); // copy it!

#ifdef SAMPLE_MEAN
	REAL N = iRows - 1;
#else
	REAL N = iRows;
#endif

	// Determine mean of row vectors of input data matrix 
	for(x=0;x<iCols;x++)
	{	for(y=0;y<iRows;y++)
			vMean[x] += vData[y][x];
		vMean[x] /= N;
	}

	vector<REAL> vStdev(iCols);
	
	// Center the row vectors.
	for(y=0;y<iRows;y++)
    {	for(x=0;x<iCols;x++)
		{	vData[y][x] -= vMean[x];
		}
    }

	// compute std-dev
	//const bool bCorrelMat = false;
	if(bCorrelMat) for(x=0;x<iCols;x++)
	{	for(y=0;y<iRows;y++)
			vStdev[x]+=vData[y][x]*vData[y][x];
		vStdev[x] /= N;
		vStdev[x] = sqrt(vStdev[x]);
	}

	vCovarMat = vector<vector<REAL> >(iCols,vector<REAL>(iCols,0.0));

	// Calculate the iCols * iCols covariance matrix.
	for(j1=0;j1<iCols;j1++)
	{	for(j2=0;j2<=j1;j2++)
		{	vCovarMat[j1][j2]=0.0;
			for(i=0;i<iRows;i++)
				vCovarMat[j1][j2] += ( vData[i][j1] * vData[i][j2] );
			if(bCorrelMat)	//correlation matrix
				vCovarMat[j1][j2] /= (N * vStdev[j1] * vStdev[j2] );
			else	//covariance matrix
				vCovarMat[j1][j2] /= N;
			vCovarMat[j2][j1]=vCovarMat[j1][j2];
		}
	}
}

// Variance-covariance matrix: creation
template<class REALD , class REALC, class REALM>
void CovarMat(  A2D<REALD>& vData , int iRows, int iCols, A2D<REALC>& vCovarMat, vector<REALM>& vMean,bool bCorrelMat = false)
{	// Create iCols * iCols covariance matrix from given iRow * iCol data matrix. 
	int i, x, y, j1 , j2;
	// Allocate storage for mean vector 
	vMean = vector<REALM>(iCols);

#ifdef SAMPLE_MEAN
	REALM N = iRows - 1;
#else
	REALM N = iRows;
#endif

	// Determine mean of row vectors of input data matrix 
	for(x=0;x<iCols;x++)
	{	vMean[x] = 0.0;
		for(y=0;y<iRows;y++)
		{	vMean[x] += vData[y][x];
		}
		vMean[x] /= N;
	}

	vector<REALM> vStdev(iCols);
	
	// Center the row vectors.
	for(y=0;y<iRows;y++)
    {	for(x=0;x<iCols;x++)
		{	vData[y][x] -= vMean[x];
		}
    }

	// compute std-dev
	//const bool bCorrelMat = false;
	if(bCorrelMat) for(x=0;x<iCols;x++)
	{	for(y=0;y<iRows;y++)
			vStdev[x]+=vData[y][x]*vData[y][x];
		vStdev[x] /= N;
		vStdev[x] = sqrt(vStdev[x]);
	}

	vCovarMat.Init(iCols,iCols);

	if(bCorrelMat)	// Calculate the iCols * iCols correlation matrix.
	{	for(j1=0;j1<iCols;j1++)
		{	for(j2=0;j2<=j1;j2++)
			{	vCovarMat[j1][j2]=0.0;
				for(i=0;i<iRows;i++)
				{	vCovarMat[j1][j2] += (vData[i][j1] * vData[i][j2]);
				}
				vCovarMat[j1][j2] /= ((REALC) N * vStdev[j1] * vStdev[j2] );
				vCovarMat[j2][j1]=vCovarMat[j1][j2];
			}
		}
	}
	else	// Calculate the iCols * iCols covariance matrix.
	{	for(j1=0;j1<iCols;j1++)
		{	for(j2=0;j2<=j1;j2++)
			{	vCovarMat[j1][j2]=0.0;
				for(i=0;i<iRows;i++)
				{	vCovarMat[j1][j2] += (vData[i][j1] * vData[i][j2]);
				}
				vCovarMat[j1][j2] /= N;
				vCovarMat[j2][j1]=vCovarMat[j1][j2];
			}
		}
	}

	// Un-Center the row vectors.
	for(y=0;y<iRows;y++)
    {	for(x=0;x<iCols;x++)
		{	vData[y][x] += vMean[x];
		}
    }
}

// Variance-covariance matrix: creation
template<class REAL>
void CovarMat(const vector< REAL >& vDataIn,int iRows,int iCols,vector< vector<REAL> >& vCovarMat, vector<REAL>& vMean,bool bCorrelMat = false)
{	// Create iCols * iCols covariance matrix from given iRow * iCol data matrix. 
	int i, x, y, j1 , j2;
	// Allocate storage for mean vector 
	vMean = vector<REAL>(iCols);

	vector< REAL > vData(vDataIn); // copy it!

#ifdef SAMPLE_MEAN
	REAL N = iRows - 1;
#else
	REAL N = iRows;
#endif

	// Determine mean of row vectors of input data matrix 
	for(x=0;x<iCols;x++)
	{	vMean[x] = 0.0;
		for(y=0;y<iRows;y++)
		{	vMean[x] += vData[y*iCols+x];
		}
		vMean[x] /= N;
	}

	vector<REAL> vStdev(iCols);
	
	// Center the row vectors.
	for(y=0;y<iRows;y++)
    {	for(x=0;x<iCols;x++)
		{	vData[y*iCols+x] -= vMean[x];
		}
    }

	// compute std-dev
	//const bool bCorrelMat = false;
	if(bCorrelMat) for(x=0;x<iCols;x++)
	{	for(y=0;y<iRows;y++)
			vStdev[x]+=vData[y*iCols+x]*vData[y*iCols+x];
		vStdev[x] /= N;
		vStdev[x] = sqrt(vStdev[x]);
	}

	vCovarMat = vector<vector<REAL> >(iCols,vector<REAL>(iCols,0.0));

	// Calculate the iCols * iCols covariance matrix.
	for(j1=0;j1<iCols;j1++)
	{	for(j2=0;j2<=j1;j2++)
		{	vCovarMat[j1][j2]=0.0;
			for(i=0;i<iRows;i++)
			{	vCovarMat[j1][j2] += (vData[i*iCols+j1] * vData[i*iCols+j2]);
			}
			if(bCorrelMat)	//correlation matrix
				vCovarMat[j1][j2] /= ( N * vStdev[j1] * vStdev[j2] );
			else	//covariance matrix
				vCovarMat[j1][j2] /= N;
			vCovarMat[j2][j1]=vCovarMat[j1][j2];
		}
	}
}

template< class T >
int GetMaxIndex(vector<T>& v)
{
	int iSz = v.size(), i = 0, iMaxIDX = 0;
	if(iSz<1) return -1;
	T maxVal = v[0];
	for(i=1;i<iSz;i++)
	{
		if(v[i]>maxVal)
		{
			maxVal=v[i];
			iMaxIDX=i;
		}
	}
	return iMaxIDX;
}

template< class T >
int GetMinIndex(vector<T>& v)
{
	int iSz = v.size(), i = 0, iMinIDX = 0;
	if(iSz<1) return -1;
	T minVal = v[0];
	for(i=1;i<iSz;i++)
	{
		if(v[i]<minVal)
		{
			minVal=v[i];
			iMinIDX=i;
		}
	}
	return iMinIDX;
}

template< class T >
T Energy(vector<T>& v)
{
	int iSz = v.size() , i = 0;
	T sum(0);
	if(iSz<1) return sum;
	for(;i<iSz;i++) sum += v[i]*v[i];
	sum = sqrt(sum);
	return sum / (double) iSz;
}

//chi-squared cumulative distribution function
//returns probability of chi-squared with df degrees of freedom has value <= x
double chisqCDF(double x, double df);

inline bool PowOf2(int N)
{
	if(N < 0) return false;
	while(N>1)
	{
		if(N%2==1) return false;
		N /= 2;
	}
	return true;
}

inline int CeilPowOf2(int N)
{
	int T = 1;
	while(T < N) T *= 2;
	return T;
}