/*
Copyright (C) 2011  Diego Darriba, David Posada

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
package es.uvigo.darwin.jmodeltest.statistics;


public class Statistics 
	{

	public Statistics() 
		{
		}

 /*************************** chiSquareProbability *************************
 *																		 	*
 *	Returns the pvalue corresponding to a given chi square value.		 	*
 * 																		 	*
 *	Adapted from: 	Hill, I. D. and Pike, M. C.  Algorithm 299 Collected 	*
 *	Algorithms 	for the CACM 1967 p. 243. Updated for rounding errors 	 	*
 *	based on remark inACM TOMS 											 	*
 *	June 1985, page 185. Found in Perlman.lib								*
 *																		 	*
 *	x: obtained chi-square value, df: degrees of freedom					*																			*
 *																		 	*
 ***************************************************************************/

	static private final double	BIGX =			20.0;							/* max value to represent exp (x) */
	static private final double	LOG_SQRT_PI =	0.5723649429247000870717135;	/* log (sqrt (pi)) */
	static private final double	I_SQRT_PI =		0.5641895835477562869480795;	/* 1 / sqrt (pi) */
	static private final double	Z_MAX =			6.0;							/* maximum meaningful z value */ 
		
	static public double chiSquareProbability (double x, int df)  
		{
		double	a, y, s;
		double	e, c, z;
		boolean	even;         /* true if df is an even number */
		
		if (x <= 0.0 || df < 1)
			return (1.0);
		
		y= 1;
		
		a = 0.5 * x;
		even = (2*(df/2)) == df;
		if (df > 1)
			y = ex (-a);
		s = (even ? y : (2.0 * normalProbability (-Math.sqrt(x))));
		if (df > 2)
			{
			x = 0.5 * (df - 1.0);
			z = (even ? 1.0 : 0.5);
			if (a > BIGX)
				{
	   		e = (even ? 0.0 : LOG_SQRT_PI);
				c = Math.log (a);
				while (z <= x)
					{
					e = Math.log (z) + e;
					s += ex (c*z-a-e);
					z += 1.0;
					}
				return (s);
				}
			else
				{
				e = (even ? 1.0 : (I_SQRT_PI / Math.sqrt (a)));
				c = 0.0;
				while (z <= x)
					{
					e = e * (a / z);
					c = c + e;
					z += 1.0;
					}
				return (c * y + s);
				}
			}
		else
			return (s);
		}

		


 /*************************** normalProbability *************************
 *																		 	*
 *	Returns the probability that a standard normal is less thana given	 	*
 * 		z standard normal value											 	*
 *	
 *   Cumulative distribution function										*
 *																		 	*
 *	Adapted from a polynomial approximation in: Ibbetson D, Algorithm 209	*	
 *	Collected Algorithms of the CACM 1963 p. 616							*
 *	This routine has six digit accuracy, so it is only useful for absolute	*
 *	z values < 6.  For z values >= to 6.0, normalProbability() returns 1.0.	*
 *																		 	*
 *																		 	*
 ***************************************************************************/
		
	static private double normalProbability (double z)        /* VAR returns cumulative probability from -oo to z VAR normal z value */
		{	
		double 	y, x, w;
		
		if (z == 0.0)
			x = 0.0;
		else
			{
			y = 0.5 * Math.abs(z);
			if (y >= (Z_MAX * 0.5))
				x = 1.0;
			else if (y < 1.0)
				{
				w = y*y;
				x = ((((((((0.000124818987 * w
					-0.001075204047) * w +0.005198775019) * w
					-0.019198292004) * w +0.059054035642) * w
					-0.151968751364) * w +0.319152932694) * w
					-0.531923007300) * w +0.797884560593) * y * 2.0;
				}
			else
				{
				y -= 2.0;
				x = (((((((((((((-0.000045255659 * y
					+0.000152529290) * y -0.000019538132) * y
					-0.000676904986) * y +0.001390604284) * y
					-0.000794620820) * y -0.002034254874) * y
					+0.006549791214) * y -0.010557625006) * y
					+0.011630447319) * y -0.009279453341) * y
					+0.005353579108) * y -0.002141268741) * y
						+0.000535310849) * y +0.999936657524;
					}
				}
		return (z > 0.0 ? ((x + 1.0) * 0.5) : ((1.0 - x) * 0.5));	
		}




/**************************** ex ***************************************
 *																		*
 *	Resturns exp(X) or 0 if x is too small								*					
 *																		*
 ************************************************************************/

	static private final double	ex (double x)
		{
		return (((x) < -BIGX) ? 0.0 : Math.exp (x));   
		}


} // class Statistics