/*
	Copyright (C) 2003, 2004 by J. David Sexton.
	Some rights NOT reserved!

	This source code should compile with any C compiler that meets the
	ISO 9899:1990 ("C89") standard.

	This source code is not public domain or Open Source, but I hereby license
	you to copy it and give it to others so long as you don't profit by doing
	so. In addition, you may include these functions in your own software, so
	long as that software is not allowed to be sold or used for profit.
	If I sold this, I would guarantee it; but as I don't, I won't.
*/

#include 
#include 
#include 

long double HexTimesX (char *theString, long double theMultiplier);
long double LDFract (long double theX);
long double ATimesLog2OfX (long double theA, long double theX,
			   long double *theInt);
long double Log2OfX (long double theX, long double *theInt);
long double TwoToTheX (long double theX);
long double Log2OfGamma (long double theX, long double *theInt);
long double IncompleteGammaFactor (long double theA, long double theX);
double IncompleteGammaBySeries (double theA, double theX);
double IncompleteGammaByFraction (double theA, double theX);
double IncompleteGamma (double theA, double theX);
double ChiSquaredPValue (double degOfFreedom, double theChiSquared);

/*
	Here begins a small library of math functions. These functions
	don't call any functions in , so we have to make sure
	the following macros are appropriately defined. HUGE_VAL is part
	of the C89 standard. HUGE_VALL and NAN are not part of the C89
	standard, but they are in C99. These macros, and other macro
	definitions below, depend on definitions in . The
	functions also require  and .
*/

#ifndef HUGE_VAL
#define HUGE_VAL (DBL_MAX / DBL_MIN)
#endif
#ifndef HUGE_VALL
#define HUGE_VALL (LDBL_MAX / LDBL_MIN)
#endif
#ifndef NAN
#define NAN (0.0L / 0.0L)
#endif

/*
	HexTimesX multiplies the hexadecimal floating-point
	number specified in theString by theMultiplier.
	This is a rather computationally expensive way to
	insure maximum accuracy when multiplying by floating-
	point literals. It's probably only worth the trouble
	for literals that can't be precisely represented by
	the long double type.
*/

long double
HexTimesX (char *theString, long double theMultiplier)
{
  char *strPtr, theChar;
  long double theProduct, placeValue;
  size_t numOfDigits, theDigit;

  strPtr = theString;
  placeValue = theMultiplier;
  if (*strPtr == '-')
    {
      ++strPtr;
      placeValue = -placeValue;
    }
  if (placeValue == HUGE_VALL || placeValue == -HUGE_VALL)
    return (errno = ERANGE, placeValue);
  theProduct = 0.0L;
  numOfDigits = theDigit = 0;
  while ((theChar = *(strPtr++)))
    {
      if (theChar == '.')
	break;
      if ((theChar >= 'a' && theChar <= 'f') ||
	  (theChar >= 'A' && theChar <= 'F') ||
	  (theChar >= '0' && theChar <= '9'))
	++numOfDigits;
      else
	break;
    }
  if (theChar == '.')
    {
      while ((theChar = *(strPtr++)))
	{
	  if ((theChar >= 'a' && theChar <= 'f') ||
	      (theChar >= 'A' && theChar <= 'F') ||
	      (theChar >= '0' && theChar <= '9'))
	    {
	      ++numOfDigits;
	      placeValue /= 16.0L;
	    }
	  else
	    break;
	}
    }
  --strPtr;
  while (theDigit < numOfDigits)
    {
      theChar = *(--strPtr);
      if (theChar == '.')
	continue;
      if (theChar >= 'a' && theChar <= 'f')
	theChar -= 'a' - 10;
      else if (theChar >= 'A' && theChar <= 'F')
	theChar -= 'A' - 10;
      else
	theChar -= '0';
      theProduct += placeValue * ((long double) theChar);
      placeValue *= 16.0L;
      ++theDigit;
    }
  if (theProduct == HUGE_VALL || theProduct == -HUGE_VALL)
    errno = ERANGE;
  return (theProduct);
}

/*
	LDFract returns theX minus the largest
	integer not greater than theX.
	E.g., LDFract (2.1L) returns 0.1L,
	and LDFract (-2.1L) returns 0.9L.
*/

long double
LDFract (long double theX)
{
  long double ldX, theIndex;

  if (theX == HUGE_VALL || theX == -HUGE_VALL)
    return (errno = ERANGE, 0.0L);
  ldX = theX;
  while (ldX < 0.0L)
    {
      theIndex = 2.0L;
      do
	{
	  ldX += theIndex;
	  theIndex *= theIndex;
	}
      while (-ldX >= theIndex);
    }
  while (ldX >= 2.0L)
    {
      theIndex = 2.0L;
      do
	{
	  ldX -= theIndex;
	  theIndex *= theIndex;
	}
      while (ldX >= theIndex);
    }
  if (ldX >= 1.0L)
    --ldX;
  return (ldX);
}

/*
	ATimesLog2OfX calculates theA times the base 2 logarithm of
	theX and returns the fractional part of this product. The
	integer part of the product is returned in the variable
	pointed to by theInt; this will be the largest integer not
	greater than the product. E.g., if the product is 2.1,
	ATimesLog2OfX will return 0.1 and 2.0; if the product is -2.1,
	it will return 0.9 and -3.0.
*/

long double
ATimesLog2OfX (long double theA, long double theX, long double *theInt)
{
  long double fractA, fractB, intA, intB;

  fractA = Log2OfX (theX, &intA);
  fractA *= theA;
  intA *= theA;
  fractB = LDFract (fractA);
  intB = fractA - fractB;
  fractA = LDFract (intA);
  intA -= fractA;
  if (fractA + fractB >= 1.0L)
    {
      --fractB;
      ++intB;
    }
  fractA += fractB;
  intA += intB;
  *theInt = intA;
  return (fractA);
}

/*
	Log2OfX returns the mantissa of the base 2 logarithm of theX.
	The integer part of the logarithm is returned in the variable
	pointed to by theInt; this will be the largest integer not
	greater than the logarithm. E.g., if the logarithm is 2.1,
	Log2OfX will return 0.1 and 2.0; if the logarithm is -2.1,
	it will return 0.9 and -3.0.
	If FLT_RADIX is 2, the mantissa returned should be accurate to
	LDBL_MANT_DIG bits. The value returned in the variable pointed
	to by theInt should be precise.
*/

long double
Log2OfX (long double theX, long double *theInt)
{
  long double theSum, lastSum, pSum, qSum;
  long double ldX, ldY, ldZ, theIndex;

  if (theX < 0.0L)
    {
      errno = EDOM;
      *theInt = NAN;
      return (0.0L);
    }
  if (theX == 0.0L)
    {
      errno = ERANGE;
      *theInt = -HUGE_VALL;
      return (0.0L);
    }
  if (theX == HUGE_VALL)
    {
      errno = ERANGE;
      *theInt = HUGE_VALL;
      return (0.0L);
    }
  ldX = theX;
  theSum = pSum = 0.0L;
  ldZ = 1.0L;
  while (ldX / ldZ < 1.0L)
    {
      theIndex = 1.0L;
      ldY = 2.0L;
      do
	{
	  theSum -= theIndex;
	  ldZ /= ldY;
	  theIndex += theIndex;
	  ldY *= ldY;
	}
      while (ldZ / ldX >= ldY);
    }
  while (ldX / ldZ >= 2.0L)
    {
      theIndex = 1.0L;
      ldY = 2.0L;
      do
	{
	  theSum += theIndex;
	  ldZ *= ldY;
	  theIndex += theIndex;
	  ldY *= ldY;
	}
      while (ldX / ldZ >= ldY);
    }
  ldX = (ldX - ldZ) / ldZ;
  if (ldX > 0.0L)
    {
      theIndex = 1.0L;
      do
	{
	  theIndex /= 2.0L;
	  ldY = ldX * ldX;
	  ldZ = ldX + ldX;
	  ldX = ldY + ldZ;
	}
      while (ldX < 1.0L);
      ldX = (ldY + --ldZ) / 2.0L;
      qSum = theIndex;
      if (ldX > 0.0L)
	{
	  do
	    {
	      lastSum = pSum;
	      do
		{
		  theIndex /= 2.0L;
		  ldY = ldX * ldX;
		  ldZ = ldX + ldX;
		  ldX = ldY + ldZ;
		}
	      while (ldX < 1.0L);
	      ldX = (ldY + --ldZ) / 2.0L;
	      pSum += theIndex;
	    }
	  while (ldX > 0.0L && pSum != lastSum);
	}
      pSum += qSum;
    }
  *theInt = theSum;
  return (pSum);
}

/*
	TwoToTheX returns 2 to the power of a long double argument.
	If FLT_RADIX is 2, the value returned should be accurate to
	LDBL_MANT_DIG bits.
*/

long double
TwoToTheX (long double theX)
{
  long double ldX, theIndex, theProd, pProd;
  long double thePower, prevPower, lastApprox;

  if (theX == -HUGE_VALL)
    return (errno = ERANGE, 0.0L);
  if (theX == HUGE_VALL)
    return (errno = ERANGE, HUGE_VALL);
  ldX = theX;
  theProd = 1.0L;
  while (ldX < 0.0L)
    {
      theIndex = 1.0L;
      thePower = 2.0L;
      do
	{
	  theProd /= thePower;
	  ldX += theIndex;
	  if (theProd == 0.0L)
	    return (errno = ERANGE, theProd);
	  theIndex += theIndex;
	  thePower *= thePower;
	}
      while (-ldX >= theIndex);
    }
  while (ldX >= 1.0L)
    {
      theIndex = 1.0L;
      thePower = 2.0L;
      do
	{
	  theProd *= thePower;
	  ldX -= theIndex;
	  if (theProd == HUGE_VALL)
	    return (errno = ERANGE, theProd);
	  theIndex += theIndex;
	  thePower *= thePower;
	}
      while (ldX >= theIndex);
    }
  if (ldX > 0.0L)
    {
      pProd = 0.0L;
      thePower = theIndex = 1.0L;
      do
	{
	  prevPower = thePower;
	  thePower = 0.0L;
	  do
	    {
	      lastApprox = thePower;
	      thePower += (prevPower - thePower) / (thePower + 1.0L);
	      thePower /= 2.0L;
	    }
	  while (thePower != lastApprox);
	  theIndex /= 2.0L;
	  if (ldX >= theIndex)
	    {
	      pProd += (pProd * thePower) + thePower;
	      ldX -= theIndex;
	    }
	}
      while (ldX > 0.0L && (thePower + 1.0L) > 1.0L);
      theProd += theProd * pProd;
      if (theProd == HUGE_VALL)
	errno = ERANGE;
    }
  return (theProd);
}

/*
	These macros are used in calls to HexTimesX.
	Cantrell's coefficients are rational numbers, but can't be precisely
	represented as long doubles. The other constants are irrational.
	The "S" at the beginning of S_SQUARE_ROOT_OF_PI and S_BASE_2_LOG_OF_E
	is meant to prevent possible conflicts. MANT_OF_BASE_2_LOG_SQRT_2PI
	is the mantissa of the base 2 log of the square root of 2pi.
*/

#if LDBL_DIG < 19
#define MANT_OF_BASE_2_LOG_SQRT_2PI "0.536439A4C6EFBAD86E"
#define S_SQUARE_ROOT_OF_PI         "1.C5BF891B4EF6AA79C"
#define S_BASE_2_LOG_OF_E           "1.71547652B82FE1778"
#define CANTRELL_COEFFICIENT_EIGHT  "1.8F27478402B14D893"
#define CANTRELL_COEFFICIENT_SEVEN  "0.28E791360B93BC5F5D"
#define CANTRELL_COEFFICIENT_SIX    "2.0D5E32BC6D9F5D78E"
#define CANTRELL_COEFFICIENT_FIVE   "0.372642BE5EE71DC552"
#define CANTRELL_COEFFICIENT_FOUR   "2.FDCDC5670AA34302A"
#define CANTRELL_COEFFICIENT_THREE  "0.54C0965CAE8514023D"
#define CANTRELL_COEFFICIENT_TWO    "5.734CE827B8D8E9839"
#define CANTRELL_COEFFICIENT_ONE    "0.B6679DB440592CF7C4"
#else
#define MANT_OF_BASE_2_LOG_SQRT_2PI "0.536439A4C6EFBAD86D9727840544713A5C"
#define S_SQUARE_ROOT_OF_PI         "1.C5BF891B4EF6AA79C3B0520D5DB9383FE"
#define S_BASE_2_LOG_OF_E           "1.71547652B82FE1777D0FFDA0D23A7D11D"
#define CANTRELL_COEFFICIENT_EIGHT  "1.8F27478402B14D89312C473E2C34ACB43"
#define CANTRELL_COEFFICIENT_SEVEN  "0.28E791360B93BC5F5C9D6B186484A0EBEA"
#define CANTRELL_COEFFICIENT_SIX    "2.0D5E32BC6D9F5D78DF50633852B8005E1"
#define CANTRELL_COEFFICIENT_FIVE   "0.372642BE5EE71DC5524A4434F49AE415FB"
#define CANTRELL_COEFFICIENT_FOUR   "2.FDCDC5670AA343029D7681AE3E727CB38"
#define CANTRELL_COEFFICIENT_THREE  "0.54C0965CAE8514023D78641D8EE67C0D33"
#define CANTRELL_COEFFICIENT_TWO    "5.734CE827B8D8E983955383CB54E551F73"
#define CANTRELL_COEFFICIENT_ONE    "0.B6679DB440592CF7C43636F98A326A2641"
#endif

/*
	Log2OfGamma returns the mantissa of the base 2 logarithm of the
	(complete) Gamma function of theX. The integer part of the logarithm
	is returned in the variable pointed to by theInt; this will be the
	largest integer not greater than the logarithm. E.g., if the logarithm
	is 2.1, Log2OfGamma will return 0.1 and 2.0; if the logarithm is -0.1,
	it will return 0.9 and -1.0.
	Log2OfGamma uses an approximation developed by David W. Cantrell;
	thanks to Hugo Pfoertner for pointing me to it. See:
	http://mathforum.org/epigone/sci.math.num-analysis/zonchahee/
*/

long double
Log2OfGamma (long double theX, long double *theInt)
{
  long double theValue, ldX, thePochhammer;
  long double fractA, intA, fractB, intB;

  if (theX < 0.0L)
    {
      errno = EDOM;
      *theInt = NAN;
      return (0.0L);
    }
  if (theX == 0.0L || theX == HUGE_VALL)
    {
      errno = ERANGE;
      *theInt = HUGE_VALL;
      return (0.0L);
    }
  ldX = theX;
  /*      If it's easy, calculate Gamma the obvious way.  */
  if (ldX <= 9.0L && LDFract (ldX * 2.0L) == 0.0L)
    {
      theValue = 1.0L;
      while (--ldX > 0.0L)
	theValue *= ldX;
      if (ldX < 0.0L)
	theValue = HexTimesX (S_SQUARE_ROOT_OF_PI, theValue);
      return (Log2OfX (theValue, theInt));
    }
  /*      Otherwise, use Cantrell's approximation.        */
  thePochhammer = 1.0L;
  while (ldX <= 9.0L)
    thePochhammer *= ldX++;
  ldX -= 0.5L;
  theValue = HexTimesX (CANTRELL_COEFFICIENT_EIGHT, ldX);
  theValue = HexTimesX (CANTRELL_COEFFICIENT_SEVEN, ldX) + (1.0L / theValue);
  theValue = HexTimesX (CANTRELL_COEFFICIENT_SIX, ldX) + (1.0L / theValue);
  theValue = HexTimesX (CANTRELL_COEFFICIENT_FIVE, ldX) + (1.0L / theValue);
  theValue = HexTimesX (CANTRELL_COEFFICIENT_FOUR, ldX) + (1.0L / theValue);
  theValue = HexTimesX (CANTRELL_COEFFICIENT_THREE, ldX) + (1.0L / theValue);
  theValue = HexTimesX (CANTRELL_COEFFICIENT_TWO, ldX) + (1.0L / theValue);
  theValue = HexTimesX (CANTRELL_COEFFICIENT_ONE, ldX) + (1.0L / theValue);
  theValue = (24.0L * ldX) + (1.0L / theValue) - 0.5L;
  theValue = (1.0L / (1.0L + (1.0L / theValue))) / thePochhammer;
  fractA = Log2OfX (theValue, &intA);
  fractB = HexTimesX (MANT_OF_BASE_2_LOG_SQRT_2PI, 1.0L);
  intB = 1.0L;
  if (fractA + fractB >= 1.0L)
    {
      --fractB;
      ++intB;
    }
  fractA += fractB;
  intA += intB;
  fractB = ATimesLog2OfX (ldX, ldX, &intB);
  if (fractA + fractB >= 1.0L)
    {
      --fractB;
      ++intB;
    }
  fractA += fractB;
  intA += intB;
  intB = HexTimesX (S_BASE_2_LOG_OF_E, ldX);
  fractB = LDFract (intB);
  intB -= fractB;
  if (fractA - fractB < 0.0L)
    {
      --fractB;
      ++intB;
    }
  fractA -= fractB;
  intA -= intB;
  theValue = fractA;
  *theInt = intA;
  if (intA == HUGE_VALL)
    errno = ERANGE;
  return (theValue);
}

/*
	IncompleteGammaFactor is called by the incomplete gamma functions.
	Where "^" means "to the power of," and "Gamma" stands for an
	upper-case Greek letter gamma, this function calculates
	(x^a)/(Gamma(a)(e^x)).
*/

long double
IncompleteGammaFactor (long double theA, long double theX)
{
  long double fractA, fractB, intA, intB;

  fractA = ATimesLog2OfX (theA, theX, &intA);
  fractB = Log2OfGamma (theA, &intB);
  if (fractA - fractB < 0.0L)
    {
      --fractB;
      ++intB;
    }
  fractA -= fractB;
  intA -= intB;
  intB = HexTimesX (S_BASE_2_LOG_OF_E, theX);
  fractB = LDFract (intB);
  intB -= fractB;
  if (fractA - fractB < 0.0L)
    {
      --fractB;
      ++intB;
    }
  fractA -= fractB;
  intA -= intB;
  return (TwoToTheX (fractA) * TwoToTheX (intA));
}

/*	Don't call IncompleteGammaBySeries directly; call IncompleteGamma.	*/

double
IncompleteGammaBySeries (double theA, double theX)
{
  long double theSum, lastSum, aValue, theProd;
  long double ldA, ldX;

  ldA = theA;
  ldX = theX;
  aValue = ldA;
  theSum = theProd = 1.0L / aValue;
  do
    {
      lastSum = theSum;
      theProd /= ++aValue;
      theProd *= ldX;
      theSum += theProd;
    }
  while (theSum != lastSum);
  return (1.0L - (theSum * IncompleteGammaFactor (ldA, ldX)));
}

/*	Don't call IncompleteGammaByFraction directly; call IncompleteGamma.	*/

double
IncompleteGammaByFraction (double theA, double theX)
{
  long double aValue, theB, theC, theD, ldA, ldX;
  long double theIndex, theProd, lastProd;

  ldA = theA;
  ldX = theX;
  theB = 1.0L + (ldX - ldA);
  theD = theProd = 1.0L / theB;
  aValue = ldA - 1.0L;
  theB += 2.0L;
  theC = theB;
  theD = 1.0L / (theB + (aValue * theD));
  theProd *= theC * theD;
  theIndex = 1.0L;
  do
    {
      lastProd = theProd;
      ++theIndex;
      aValue = theIndex * (ldA - theIndex);
      theB += 2.0L;
      theC = theB + (aValue / theC);
      theD = 1.0L / (theB + (aValue * theD));
      theProd *= theC * theD;
    }
  while (theProd != lastProd);
  return (theProd * IncompleteGammaFactor (ldA, ldX));
}

/*
	IncompleteGamma calculates the regularized incomplete gamma function.
	Where "Gamma" stands for an upper-case Greek letter gamma, IncompleteGamma
	calculates Gamma(a,x)/Gamma(a). W. H. Press, et al (in Numerical Recipes
	in C) write this function as Q(a,x). It is the complement of P(a,x); i.e.
	Q(a,x) = 1 - P(a,x). Though the functions here are not from Numerical
	Recipes in C, they do implement formulae (6.2.5) and (6.2.7) from that
	book. Cf. formulae (2) and (4) in "Computing the incomplete Gamma function
	to arbitrary precision" (2003: LNCS 2667) by Serge Winitzki. The continued
	fraction expansion [Press, et al (6.2.6) and Winitzki (4)] is due to Legendre.
*/

double
IncompleteGamma (double theA, double theX)
{
  if (theA <= 0.0 || theX < 0.0)
    return (errno = EDOM, 0.0);
  if (theA == HUGE_VAL)
    return (errno = ERANGE, theX / HUGE_VAL);
  if (theX == HUGE_VAL)
    return (0.0);
  if (theX == 0.0)
    return (1.0);
  if (theX < theA)
    return (IncompleteGammaBySeries (theA, theX));
  return (IncompleteGammaByFraction (theA, theX));
}

/*
	ChiSquaredPValue returns the probability of a chi-squared statistic
	greater than or equal to theChiSquared.
*/

double
ChiSquaredPValue (double degOfFreedom, double theChiSquared)
{
  return (IncompleteGamma (degOfFreedom / 2.0, theChiSquared / 2.0));
}

/*	Here ends this small library of math functions.	*/

    Source: geocities.com/da5id65536