public static double OneSideUpperTail(double n, double x)
{
if (n > 200000)
{
// Use an asymptotic formula for n too high
double t = (6 * n * x + 1.0);
double z = t * t / (18 * n);
double v = (1.0 - (2 * z * z - 4 * z - 1.0) / (18 * n)) * Math.Exp(-z);
if (v <= 0.0) return 0.0;
if (v >= 1.0) return 1.0;
else return 1.0 * v;
}
else
{
// Use Smirnov's stable formula for computing Pn+, the upper tail of
// the one-sided Kolmogorov's statistic Dn+. This upper tail of the
// one-sided statistic can then be used to approximate the upper tail
// Pn of the Kolmogorov statistic Dn with Pn ~ 2*Pn+.
int jmax = (int)(n * (1.0 - x));
if ((1.0 - x - (double)jmax / n) <= 0.0)
jmax--;
// Initialize
int jdiv = (n > 3000) ? 2 : 3;
int jstart = jmax / jdiv + 1;
double logBinomial = Special.LogBinomial(n, jstart);
double LOGJMAX = logBinomial;
double EPSILON = 1.0E-12;
// Start computing the series
double sum = 0;
for (int j = jstart; j <= jmax; j++)
{
double q = (double)j / n + x;
double term = logBinomial + (j - 1) * Math.Log(q) + (n - j) * Special.Log1p(-q);
double t = Math.Exp(term);
sum += t;
logBinomial += Math.Log((double)(n - j) / (j + 1));
if (t <= sum * EPSILON)
break;
}
jstart = jmax / jdiv;
logBinomial = LOGJMAX + Math.Log((double)(jstart + 1) / (n - jstart));
for (int j = jstart; j > 0; j--)
{
double q = (double)j / n + x;
double term = logBinomial + (j - 1) * Math.Log(q) + (n - j) * Special.Log1p(-q);
double t = Math.Exp(term);
sum += t;
logBinomial += Math.Log((double)j / (n - j + 1));
if (t <= sum * EPSILON)
break;
}
return sum * x + Math.Exp(n * Special.Log1p(-x));
}
}