public static double Pomeranz(int n, double x)
{
// The Pomeranz algorithm to compute the KS distribution
double EPS = 1.0e-15;
int ENO = 350;
double RENO = Math.Pow(2, ENO); // for renormalization of V
int renormalizations;
double t = n * x;
double w, sum, minsum;
int k, s;
int r1, r2; // Indices i and i-1 for V[i][]
int jlow, jup, klow, kup, kup0;
double[] A = new double[2 * n + 3];
double[] floors = new double[2 * n + 3];
double[] ceilings = new double[2 * n + 3];
double[][] V = new double[2][];
for (int j = 0; j < V.Length; j++)
V[j] = new double[n + 2];
double[][] H = new double[4][]; // = pow(w, j) / Factorial(j)
for (int j = 0; j < H.Length; j++)
H[j] = new double[n + 2];
double z = computeLimits(t, floors, ceilings);
computeA(n, A, z);
computeH(n, A, H);
V[1][1] = RENO;
renormalizations = 1;
r1 = 0;
r2 = 1;
for (int i = 2; i <= 2 * n + 2; i++)
{
jlow = (int)(2 + floors[i]);
if (jlow < 1)
jlow = 1;
jup = (int)(ceilings[i]);
if (jup > n + 1)
jup = n + 1;
klow = (int)(2 + floors[i - 1]);
if (klow < 1)
klow = 1;
kup0 = (int)(ceilings[i - 1]);
// Find to which case it corresponds
w = (A[i] - A[i - 1]) / n;
s = -1;
for (int j = 0; j < 4; j++)
{
if (Math.Abs(w - H[j][1]) <= EPS)
{
s = j;
break;
}
}
minsum = RENO;
r1 = (r1 + 1) & 1; // i - 1
r2 = (r2 + 1) & 1; // i
for (int j = jlow; j <= jup; j++)
{
kup = kup0;
if (kup > j)
kup = j;
sum = 0;
for (k = kup; k >= klow; k--)
sum += V[r1][k] * H[s][j - k];
V[r2][j] = sum;
if (sum < minsum)
minsum = sum;
}
if (minsum < 1.0e-280)
{
// V is too small: renormalize to avoid underflow of probabilities
for (int j = jlow; j <= jup; j++)
V[r2][j] *= RENO;
renormalizations++; // keep track of log of RENO
}
}
sum = V[r2][n + 1];
w = Special.LogFactorial(n) - renormalizations * ENO * Constants.Log2 + Math.Log(sum);
if (w >= 0)
return 1;
return Math.Exp(w);
}