/***************************************************************************
**
**  This file is part of QGpCoreMath.
**
**  This library 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 2.1 of the License, or (at your option) any later version.
**
**  This file 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 library; if not, write to the Free Software
**  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
**
**  See http://www.geopsy.org for more information.
**
**  Created: 2003-12-15
**  Copyright: 2003-2019
**    Marc Wathelet
**    Marc Wathelet (ULg, Liège, Belgium)
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include <math.h>
#include <stdio.h>

#include "StatComparator.h"

namespace QGpCoreMath {

/**
 The Student function calculate the probability for a certain degree of
 freedom and a certain threshold value of the random variable.
 This function inverse the relation, to obtain the threshold value as
 a function of the probability and the degree of freedom (as presented
 in usual tables)
 */
double StatComparator::invStudent(double p, double nu)
{
  TRACE;
//	if(p<0.001 || p>0.9) {
//		printf("Probability out of range in StatComparator::invStudent\n");
//		return 0;
//	}
	double t1, t2, t3, a3;
	t1=0.125;
	t2=700.0;
	t3=350.0625;
	a3=student(t3,nu);
	int nit=0;
	do {
	    nit++;
	    if(a3<p) t2=t3; else t1=t3;
	    t3=0.5*(t1+t2);
			a3=student(t3,nu);
			//printf("t1=%lf t2=%lf t3=%lf a3=%lf\n",t1,t2,t3,a3);
	} while(a3!=p && t2-t1>1e-10 && nit<100);
	if(nit>=100)
	    printf("*** WARNING ***: cannot achieve a good precision, err=%lf\n",
						 fabs(a3-p));
	return t3;
}

/**
	The Fisher function calculate the probability for a certain degree of
	freedom and a certain threshold value of the random variable.
	This function inverse the relation, to obtain the threshold value as
	a function of the probability and the degree of freedom (as presented
	in usual tables)
	*/
double StatComparator::invFisher(double p, double nu1, double nu2)
{
  TRACE;
//	if(p<0.001 || p>0.9) {
//		printf("Probability out of range in StatComparator::invFisher\n");
//		return 0;
//	}
	double f1, f2, f3, a3;
	f1=0;
	f2=10000.0;
	f3=5000.0;
	a3=fisher(f3,nu1,nu2);
	int nit=0;
	do {
	    nit++;
	    if(a3<p) f2=f3; else f1=f3;
	    f3=0.5*(f1+f2);
			a3=fisher(f3,nu1,nu2);
			//printf("t1=%lf t2=%lf t3=%lf a3=%lf\n",t1,t2,t3,a3);
	} while(a3!=p && f2-f1>1e-10 && nit<100);
	if(nit>=100)
	    printf("*** WARNING ***: cannot achieve a good precision, err=%lf\n",
						 fabs(a3-p));
	return f3;
}

/**
	Incomplete Beta function
	(Numerical Recipes chapter 6.4)
	Is(a,b)=Bx(a,b)/B(a,b)=1/B(a,b,) Integral(0->x)[t^(a-1)*(1-t)^(b-1)*dt]
	for(a,b >0)
	
	The Student and Fisher functions are calculated from this integral
	*/
double StatComparator::betai(double a, double b, double x)
{
  TRACE;
	//printf("betai: a=%lf, b=%lf, x=%lf\n",a,b,x);
	double bt;
	if(x<0.0 || x> 1.0) {
		printf("Bad x in routine StatComparator::betai\n");
		return 0;
	}
	if(x==0.0 || x== 1.0) bt=0.0;
	else bt=exp(gammln(a+b)-gammln(a)-gammln(b)+a*log(x)+b*log(1.0-x));
	if(x< (a+1.0)/(a+b+2.0))
		return bt*betacf(a,b,x)/a; // Use continued fraction directly
	else
		return 1.0-bt*betacf(b,a,1.0-x)/b; /* Use continued fraction after
																					making the symetry transf. */
}

/**
	The gamma function
	(Numerical Recipes chapter 6.1)
	Returns the value ln(gamma(x)) for x>0
	*/
double StatComparator::gammln(double x)
{
  TRACE;
	double 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;
	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);
}

#define MAXIT 100
#define EPS 3.0e-7
#define FPMIN 1.0e-30
double StatComparator::betacf(double a, double b, double x)
{
  TRACE;
	int m, m2;
	double aa,c,d,del,h,qab,qam,qap;
	
	qab=a+b;
	qap=a+1.0;
	qam=a-1.0;
	c=1.0;
	d=1.0-qab*x/qap;
	if(fabs(d) < FPMIN) d=FPMIN;
	d=1.0/d;
	h=d;
	for(m=1;m<=MAXIT;m++) {
		m2=2*m;
		aa=m*(b-m)*x/ ((qam+m2)*(a+m2));
		d=1.0+aa*d;
		if(fabs(d) < FPMIN) d=FPMIN;
		c=1.0+aa/c;
		if(fabs(c) < FPMIN) c=FPMIN;
		d=1.0/d;
		h*=d*c;
		aa= -(a+m)*(qab+m)*x/ ((a+m2)*(qap+m2));
		d=1.0+aa*d;
		if(fabs(d) < FPMIN) d=FPMIN;
		c=1.0+aa/c;
		if(fabs(c) < FPMIN) c=FPMIN;
		d=1.0/d;
		del=d*c;
		h*=del;
		if(fabs(del-1.0) < EPS) break;
	}
	if(m > MAXIT) printf("a or b too big, or MAXIT too small in StatComparator::betacf\n");
	return h;
}

} // namespace QGpCoreMath
