public static double CumulativeFunction(double n, double x)
{
if (Double.IsNaN(x))
throw new ArgumentOutOfRangeException("x");
double nxx = n * x * x; // nx²
int nn = (int)Math.Ceiling(n);
// First of all, check if the given values do not represent
// a special case. There are some combination of values for
// which the distribution has a known, exact solution.
// Ruben-Gambino
if (x >= 1.0 || nxx >= 18.0)
return 1.0;
if (x <= 0.5 / n)
return 0.0;
if (n == 1)
return 2.0 * x - 1.0;
if (x <= 1.0 / n)
return (n <= 20) ? Special.Factorial(n) * Math.Pow(2.0 * x - 1.0 / n, n)
: Math.Exp(Special.LogFactorial(n) + n * Math.Log(2.0 * x - 1.0 / n));
if (x >= 1.0 - 1.0 / n)
return 1.0 - 2.0 * Math.Pow(1.0 - x, n);
// This is not a special case. Continue processing to
// select the most adequate method for the given inputs
if (n <= 140)
{
// This is the first case (i) as referred in Simard's
// paper. Use exact algorithms depending on nx² with
// at least 13 to 15 decimal digits of precision.
// Durbin
if (nxx < 0.754693)
return Durbin(nn, x);
// Pomeranz
if (nxx < 4.0)
return Pomeranz(nn, x);
// Complementary CDF
return 1.0 - ComplementaryDistributionFunction(n, x);
}
else
{
if (n <= 100000)
{
// This is the second case (ii) referred in Simard's
// paper. Use either the Durbin approximation or the
// Pelz-Good asymptotic series depending on nx^(3/2).
// Obs:
//
// x^(3/2) = x^(1 + 1/2) = x*x^(1/2) = x*sqrt(x)
//
// (n*x) * sqrt(x) <= 1.40
// sqrt((n*x)*(n*x)) * sqrt(x) <= 1.40
// sqrt((n*x)*(n*x) * x) <= 1.40
// (n*x)*(n*x) * x <= 1.96
//
// n*n*x*x*x <= 1.96
//
if (n * nxx * x <= 1.96)
return Durbin(nn, x);
else return PelzGood(n, x);
}
else
{
// This is the third case (iii) as referred in Simard's
// paper. Use only the Pelz-Good asymptotic series.
return PelzGood(n, x);
}
}
}