namespace VisualMath.Accord.Math
{
using System;
///
/// Set of special mathematic functions.
///
///
/// References:
///
/// -
/// Numerical Recipes in C, 2nd Edition (1992)
///
/// -
/// Cephes Math Library, http://www.netlib.org/cephes/
///
///
///
public static class Special
{
#region Constants
/// Double-precision machine roundoff error.
/// This value is actually different from Double.Epsilon.
public const double DoubleEpsilon = 1.11022302462515654042E-16;
/// Single-precision machine roundoff error.
/// This value is actually different from Single.Epsilon.
public const float SingleEpsilon = 1.1920929E-07f;
/// Maximum log on the machine.
public const double LogMax = 7.09782712893383996732E2;
/// Minimum log on the machine.
public const double LogMin = -7.451332191019412076235E2;
/// Maximum gamma on the machine.
public const double GammaMax = 171.624376956302725;
/// Log of number PI.
public const double LogPI = 1.14472988584940017414;
/// Square root of number PI.
public const double SqrtPI = 2.50662827463100050242E0;
/// Square root of 2.
public const double Sqrt2 = 1.4142135623730950488016887;
/// Half square root of 2.
public const double SqrtH = 7.07106781186547524401E-1;
#endregion
///
/// Gamma function of the specified value.
///
public static double Gamma(double x)
{
double[] P = {
1.60119522476751861407E-4,
1.19135147006586384913E-3,
1.04213797561761569935E-2,
4.76367800457137231464E-2,
2.07448227648435975150E-1,
4.94214826801497100753E-1,
9.99999999999999996796E-1
};
double[] Q = {
-2.31581873324120129819E-5,
5.39605580493303397842E-4,
-4.45641913851797240494E-3,
1.18139785222060435552E-2,
3.58236398605498653373E-2,
-2.34591795718243348568E-1,
7.14304917030273074085E-2,
1.00000000000000000320E0
};
double p, z;
double q = System.Math.Abs(x);
if (q > 33.0)
{
if (x < 0.0)
{
p = System.Math.Floor(q);
if (p == q)
throw new OverflowException();
z = q - p;
if (z > 0.5)
{
p += 1.0;
z = q - p;
}
z = q * System.Math.Sin(System.Math.PI * z);
if (z == 0.0)
throw new OverflowException();
z = System.Math.Abs(z);
z = System.Math.PI / (z * Stirf(q));
return -z;
}
else
{
return Stirf(x);
}
}
z = 1.0;
while (x >= 3.0)
{
x -= 1.0;
z *= x;
}
while (x < 0.0)
{
if (x == 0.0)
{
throw new ArithmeticException();
}
else if (x > -1.0E-9)
{
return (z / ((1.0 + 0.5772156649015329 * x) * x));
}
z /= x;
x += 1.0;
}
while (x < 2.0)
{
if (x == 0.0)
{
throw new ArithmeticException();
}
else if (x < 1.0E-9)
{
return (z / ((1.0 + 0.5772156649015329 * x) * x));
}
z /= x;
x += 1.0;
}
if ((x == 2.0) || (x == 3.0)) return z;
x -= 2.0;
p = Polevl(x, P, 6);
q = Polevl(x, Q, 7);
return z * p / q;
}
///
/// Regularized Gamma function (P)
///
public static double Rgamma(double a, double z)
{
return Igam(a, z) / Gamma(a);
}
///
/// Digamma function.
///
public static double Digamma(double x)
{
double s = 0;
double w = 0;
double y = 0;
double z = 0;
double nz = 0;
bool negative = false;
if (x <= 0.0)
{
negative = true;
double q = x;
double p = (int)System.Math.Floor(q);
if (p == q)
throw new OverflowException("Function computation resulted in arithmetic overflow.");
nz = q - p;
if (nz != 0.5)
{
if (nz > 0.5)
{
p = p + 1.0;
nz = q - p;
}
nz = System.Math.PI / System.Math.Tan(System.Math.PI * nz);
}
else
{
nz = 0.0;
}
x = 1.0 - x;
}
if (x <= 10.0 & x == System.Math.Floor(x))
{
y = 0.0;
int n = (int)System.Math.Floor(x);
for (int i = 1; i <= n - 1; i++)
{
w = i;
y = y + 1.0 / w;
}
y = y - 0.57721566490153286061;
}
else
{
s = x;
w = 0.0;
while (s < 10.0)
{
w = w + 1.0 / s;
s = s + 1.0;
}
if (s < 1.0E17)
{
z = 1.0 / (s * s);
double polv = 8.33333333333333333333E-2;
polv = polv * z - 2.10927960927960927961E-2;
polv = polv * z + 7.57575757575757575758E-3;
polv = polv * z - 4.16666666666666666667E-3;
polv = polv * z + 3.96825396825396825397E-3;
polv = polv * z - 8.33333333333333333333E-3;
polv = polv * z + 8.33333333333333333333E-2;
y = z * polv;
}
else
{
y = 0.0;
}
y = System.Math.Log(s) - 0.5 / s - y - w;
}
if (negative == true)
{
y = y - nz;
}
return y;
}
///
/// Gamma function as computed by Stirling's formula.
///
public static double Stirf(double x)
{
double[] STIR = {
7.87311395793093628397E-4,
-2.29549961613378126380E-4,
-2.68132617805781232825E-3,
3.47222221605458667310E-3,
8.33333333333482257126E-2,
};
double MAXSTIR = 143.01608;
double w = 1.0 / x;
double y = System.Math.Exp(x);
w = 1.0 + w * Polevl(w, STIR, 4);
if (x > MAXSTIR)
{
double v = System.Math.Pow(x, 0.5 * x - 0.25);
y = v * (v / y);
}
else
{
y = System.Math.Pow(x, x - 0.5) / y;
}
y = SqrtPI * y * w;
return y;
}
///
/// Complemented incomplete gamma function.
///
public static double Igamc(double a, double x)
{
double big = 4.503599627370496e15;
double biginv = 2.22044604925031308085e-16;
double ans, ax, c, yc, r, t, y, z;
double pk, pkm1, pkm2, qk, qkm1, qkm2;
if (x <= 0 || a <= 0) return 1.0;
if (x < 1.0 || x < a) return 1.0 - Igam(a, x);
ax = a * System.Math.Log(x) - x - Lgamma(a);
if (ax < -LogMax) return 0.0;
ax = System.Math.Exp(ax);
/* continued fraction */
y = 1.0 - a;
z = x + y + 1.0;
c = 0.0;
pkm2 = 1.0;
qkm2 = x;
pkm1 = x + 1.0;
qkm1 = z * x;
ans = pkm1 / qkm1;
do
{
c += 1.0;
y += 1.0;
z += 2.0;
yc = y * c;
pk = pkm1 * z - pkm2 * yc;
qk = qkm1 * z - qkm2 * yc;
if (qk != 0)
{
r = pk / qk;
t = System.Math.Abs((ans - r) / r);
ans = r;
}
else
t = 1.0;
pkm2 = pkm1;
pkm1 = pk;
qkm2 = qkm1;
qkm1 = qk;
if (System.Math.Abs(pk) > big)
{
pkm2 *= biginv;
pkm1 *= biginv;
qkm2 *= biginv;
qkm1 *= biginv;
}
} while (t > DoubleEpsilon);
return ans * ax;
}
///
/// Incomplete gamma function.
///
public static double Igam(double a, double x)
{
double ans, ax, c, r;
if (x <= 0 || a <= 0) return 0.0;
if (x > 1.0 && x > a) return 1.0 - Igamc(a, x);
ax = a * System.Math.Log(x) - x - Lgamma(a);
if (ax < -LogMax) return (0.0);
ax = System.Math.Exp(ax);
r = a;
c = 1.0;
ans = 1.0;
do
{
r += 1.0;
c *= x / r;
ans += c;
} while (c / ans > DoubleEpsilon);
return (ans * ax / a);
}
///
/// Chi-square function (left hand tail).
///
///
/// Returns the area under the left hand tail (from 0 to x)
/// of the Chi square probability density function with
/// df degrees of freedom.
///
/// degrees of freedom
/// double value
///
public static double ChiSq(double df, double x)
{
if (x < 0.0 || df < 1.0) return 0.0;
return Igam(df / 2.0, x / 2.0);
}
///
/// Chi-square function (right hand tail).
///
///
/// Returns the area under the right hand tail (from x to
/// infinity) of the Chi square probability density function
/// with df degrees of freedom:
///
/// degrees of freedom
/// double value
///
public static double ChiSqc(double df, double x)
{
if (x < 0.0 || df < 1.0) return 0.0;
return Igamc(df / 2.0, x / 2.0);
}
///
/// Sum of the first k terms of the Poisson distribution.
///
/// number of terms
/// double value
///
public static double Poisson(int k, double x)
{
if (k < 0 || x < 0) return 0.0;
return Igamc((double)(k + 1), x);
}
///
/// Sum of the terms k+1 to infinity of the Poisson distribution.
///
/// start
/// double value
///
public static double Poissonc(int k, double x)
{
if (k < 0 || x < 0) return 0.0;
return Igam((double)(k + 1), x);
}
///
/// Area under the Gaussian probability density function,
/// integrated from minus infinity to the given value.
///
///
/// The area under the Gaussian p.d.f. integrated
/// from minus infinity to the given value.
///
public static double Normal(double value)
{
double x, y, z;
x = value * SqrtH;
z = System.Math.Abs(x);
if (z < SqrtH) y = 0.5 + 0.5 * Erf(x);
else
{
y = 0.5 * Erfc(z);
if (x > 0) y = 1.0 - y;
}
return y;
}
///
/// Complementary error function of the specified value.
///
///
/// http://mathworld.wolfram.com/Erfc.html
///
public static double Erfc(double value)
{
double x, y, z, p, q;
double[] P = {
2.46196981473530512524E-10,
5.64189564831068821977E-1,
7.46321056442269912687E0,
4.86371970985681366614E1,
1.96520832956077098242E2,
5.26445194995477358631E2,
9.34528527171957607540E2,
1.02755188689515710272E3,
5.57535335369399327526E2
};
double[] Q = {
//1.0
1.32281951154744992508E1,
8.67072140885989742329E1,
3.54937778887819891062E2,
9.75708501743205489753E2,
1.82390916687909736289E3,
2.24633760818710981792E3,
1.65666309194161350182E3,
5.57535340817727675546E2
};
double[] R = {
5.64189583547755073984E-1,
1.27536670759978104416E0,
5.01905042251180477414E0,
6.16021097993053585195E0,
7.40974269950448939160E0,
2.97886665372100240670E0
};
double[] S = {
//1.00000000000000000000E0,
2.26052863220117276590E0,
9.39603524938001434673E0,
1.20489539808096656605E1,
1.70814450747565897222E1,
9.60896809063285878198E0,
3.36907645100081516050E0
};
if (value < 0.0) x = -value;
else x = value;
if (x < 1.0) return 1.0 - Erf(value);
z = -value * value;
if (z < -LogMax)
{
if (value < 0) return (2.0);
else return (0.0);
}
z = System.Math.Exp(z);
if (x < 8.0)
{
p = Polevl(x, P, 8);
q = P1evl(x, Q, 8);
}
else
{
p = Polevl(x, R, 5);
q = P1evl(x, S, 6);
}
y = (z * p) / q;
if (value < 0) y = 2.0 - y;
if (y == 0.0)
{
if (value < 0) return 2.0;
else return (0.0);
}
return y;
}
///
/// Error function of the specified value.
///
public static double Erf(double x)
{
double y, z;
double[] T = {
9.60497373987051638749E0,
9.00260197203842689217E1,
2.23200534594684319226E3,
7.00332514112805075473E3,
5.55923013010394962768E4
};
double[] U = {
//1.00000000000000000000E0,
3.35617141647503099647E1,
5.21357949780152679795E2,
4.59432382970980127987E3,
2.26290000613890934246E4,
4.92673942608635921086E4
};
if (System.Math.Abs(x) > 1.0)
return (1.0 - Erfc(x));
z = x * x;
y = x * Polevl(z, T, 4) / P1evl(z, U, 5);
return y;
}
///
/// Natural logarithm of gamma function.
///
///
///
public static double Lgamma(double x)
{
double p, q, w, z;
double[] A = {
8.11614167470508450300E-4,
-5.95061904284301438324E-4,
7.93650340457716943945E-4,
-2.77777777730099687205E-3,
8.33333333333331927722E-2
};
double[] B = {
-1.37825152569120859100E3,
-3.88016315134637840924E4,
-3.31612992738871184744E5,
-1.16237097492762307383E6,
-1.72173700820839662146E6,
-8.53555664245765465627E5
};
double[] C = {
/* 1.00000000000000000000E0, */
-3.51815701436523470549E2,
-1.70642106651881159223E4,
-2.20528590553854454839E5,
-1.13933444367982507207E6,
-2.53252307177582951285E6,
-2.01889141433532773231E6
};
if (x < -34.0)
{
q = -x;
w = Lgamma(q);
p = System.Math.Floor(q);
if (p == q)
throw new OverflowException("lgamma");
z = q - p;
if (z > 0.5)
{
p += 1.0;
z = p - q;
}
z = q * System.Math.Sin(System.Math.PI * z);
if (z == 0.0)
throw new OverflowException("lgamma");
z = LogPI - System.Math.Log(z) - w;
return z;
}
if (x < 13.0)
{
z = 1.0;
while (x >= 3.0)
{
x -= 1.0;
z *= x;
}
while (x < 2.0)
{
if (x == 0.0)
throw new OverflowException("lgamma");
z /= x;
x += 1.0;
}
if (z < 0.0) z = -z;
if (x == 2.0) return System.Math.Log(z);
x -= 2.0;
p = x * Polevl(x, B, 5) / P1evl(x, C, 6);
return (System.Math.Log(z) + p);
}
if (x > 2.556348e305)
throw new OverflowException("lgamma");
q = (x - 0.5) * System.Math.Log(x) - x + 0.91893853320467274178;
if (x > 1.0e8) return (q);
p = 1.0 / (x * x);
if (x >= 1000.0)
q += ((7.9365079365079365079365e-4 * p
- 2.7777777777777777777778e-3) * p
+ 0.0833333333333333333333) / x;
else
q += Polevl(p, A, 4) / x;
return q;
}
///
/// Incomplete beta function evaluated from zero to xx.
///
public static double Ibeta(double aa, double bb, double xx)
{
double a, b, t, x, xc, w, y;
bool flag;
if (aa <= 0.0)
throw new ArgumentOutOfRangeException("aa", "domain error");
if (bb <= 0.0)
throw new ArgumentOutOfRangeException("bb", "domain error");
if ((xx <= 0.0) || (xx >= 1.0))
{
if (xx == 0.0) return 0.0;
if (xx == 1.0) return 1.0;
throw new ArgumentOutOfRangeException("xx", "domain error");
}
flag = false;
if ((bb * xx) <= 1.0 && xx <= 0.95)
{
t = PowerSeries(aa, bb, xx);
return t;
}
w = 1.0 - xx;
if (xx > (aa / (aa + bb)))
{
flag = true;
a = bb;
b = aa;
xc = xx;
x = w;
}
else
{
a = aa;
b = bb;
xc = w;
x = xx;
}
if (flag && (b * x) <= 1.0 && x <= 0.95)
{
t = PowerSeries(a, b, x);
if (t <= DoubleEpsilon) t = 1.0 - DoubleEpsilon;
else t = 1.0 - t;
return t;
}
y = x * (a + b - 2.0) - (a - 1.0);
if (y < 0.0)
w = Incbcf(a, b, x);
else
w = Incbd(a, b, x) / xc;
y = a * System.Math.Log(x);
t = b * System.Math.Log(xc);
if ((a + b) < GammaMax && System.Math.Abs(y) < LogMax && System.Math.Abs(t) < LogMax)
{
t = System.Math.Pow(xc, b);
t *= System.Math.Pow(x, a);
t /= a;
t *= w;
t *= Gamma(a + b) / (Gamma(a) * Gamma(b));
if (flag)
{
if (t <= DoubleEpsilon) t = 1.0 - DoubleEpsilon;
else t = 1.0 - t;
}
return t;
}
y += t + Lgamma(a + b) - Lgamma(a) - Lgamma(b);
y += System.Math.Log(w / a);
if (y < LogMin)
t = 0.0;
else
t = System.Math.Exp(y);
if (flag)
{
if (t <= DoubleEpsilon) t = 1.0 - DoubleEpsilon;
else t = 1.0 - t;
}
return t;
}
///
/// Continued fraction expansion #1 for incomplete beta integral.
///
public static double Incbcf(double a, double b, double x)
{
double xk, pk, pkm1, pkm2, qk, qkm1, qkm2;
double k1, k2, k3, k4, k5, k6, k7, k8;
double r, t, ans, thresh;
int n;
double big = 4.503599627370496e15;
double biginv = 2.22044604925031308085e-16;
k1 = a;
k2 = a + b;
k3 = a;
k4 = a + 1.0;
k5 = 1.0;
k6 = b - 1.0;
k7 = k4;
k8 = a + 2.0;
pkm2 = 0.0;
qkm2 = 1.0;
pkm1 = 1.0;
qkm1 = 1.0;
ans = 1.0;
r = 1.0;
n = 0;
thresh = 3.0 * DoubleEpsilon;
do
{
xk = -(x * k1 * k2) / (k3 * k4);
pk = pkm1 + pkm2 * xk;
qk = qkm1 + qkm2 * xk;
pkm2 = pkm1;
pkm1 = pk;
qkm2 = qkm1;
qkm1 = qk;
xk = (x * k5 * k6) / (k7 * k8);
pk = pkm1 + pkm2 * xk;
qk = qkm1 + qkm2 * xk;
pkm2 = pkm1;
pkm1 = pk;
qkm2 = qkm1;
qkm1 = qk;
if (qk != 0) r = pk / qk;
if (r != 0)
{
t = System.Math.Abs((ans - r) / r);
ans = r;
}
else
t = 1.0;
if (t < thresh) return ans;
k1 += 1.0;
k2 += 1.0;
k3 += 2.0;
k4 += 2.0;
k5 += 1.0;
k6 -= 1.0;
k7 += 2.0;
k8 += 2.0;
if ((System.Math.Abs(qk) + System.Math.Abs(pk)) > big)
{
pkm2 *= biginv;
pkm1 *= biginv;
qkm2 *= biginv;
qkm1 *= biginv;
}
if ((System.Math.Abs(qk) < biginv) || (System.Math.Abs(pk) < biginv))
{
pkm2 *= big;
pkm1 *= big;
qkm2 *= big;
qkm1 *= big;
}
} while (++n < 300);
return ans;
}
///
/// Continued fraction expansion #2 for incomplete beta integral.
///
public static double Incbd(double a, double b, double x)
{
double xk, pk, pkm1, pkm2, qk, qkm1, qkm2;
double k1, k2, k3, k4, k5, k6, k7, k8;
double r, t, ans, z, thresh;
int n;
double big = 4.503599627370496e15;
double biginv = 2.22044604925031308085e-16;
k1 = a;
k2 = b - 1.0;
k3 = a;
k4 = a + 1.0;
k5 = 1.0;
k6 = a + b;
k7 = a + 1.0;
;
k8 = a + 2.0;
pkm2 = 0.0;
qkm2 = 1.0;
pkm1 = 1.0;
qkm1 = 1.0;
z = x / (1.0 - x);
ans = 1.0;
r = 1.0;
n = 0;
thresh = 3.0 * DoubleEpsilon;
do
{
xk = -(z * k1 * k2) / (k3 * k4);
pk = pkm1 + pkm2 * xk;
qk = qkm1 + qkm2 * xk;
pkm2 = pkm1;
pkm1 = pk;
qkm2 = qkm1;
qkm1 = qk;
xk = (z * k5 * k6) / (k7 * k8);
pk = pkm1 + pkm2 * xk;
qk = qkm1 + qkm2 * xk;
pkm2 = pkm1;
pkm1 = pk;
qkm2 = qkm1;
qkm1 = qk;
if (qk != 0) r = pk / qk;
if (r != 0)
{
t = System.Math.Abs((ans - r) / r);
ans = r;
}
else
t = 1.0;
if (t < thresh) return ans;
k1 += 1.0;
k2 -= 1.0;
k3 += 2.0;
k4 += 2.0;
k5 += 1.0;
k6 += 1.0;
k7 += 2.0;
k8 += 2.0;
if ((System.Math.Abs(qk) + System.Math.Abs(pk)) > big)
{
pkm2 *= biginv;
pkm1 *= biginv;
qkm2 *= biginv;
qkm1 *= biginv;
}
if ((System.Math.Abs(qk) < biginv) || (System.Math.Abs(pk) < biginv))
{
pkm2 *= big;
pkm1 *= big;
qkm2 *= big;
qkm1 *= big;
}
} while (++n < 300);
return ans;
}
///
/// Power series for incomplete beta integral. Use when b*x
/// is small and x not too close to 1.
///
public static double PowerSeries(double a, double b, double x)
{
double s, t, u, v, n, t1, z, ai;
ai = 1.0 / a;
u = (1.0 - b) * x;
v = u / (a + 1.0);
t1 = v;
t = u;
n = 2.0;
s = 0.0;
z = DoubleEpsilon * ai;
while (System.Math.Abs(v) > z)
{
u = (n - b) * x / n;
t *= u;
v = t / (a + n);
s += v;
n += 1.0;
}
s += t1;
s += ai;
u = a * System.Math.Log(x);
if ((a + b) < GammaMax && System.Math.Abs(u) < LogMax)
{
t = Gamma(a + b) / (Gamma(a) * Gamma(b));
s = s * t * System.Math.Pow(x, a);
}
else
{
t = Lgamma(a + b) - Lgamma(a) - Lgamma(b) + u + System.Math.Log(s);
if (t < LogMin) s = 0.0;
else s = System.Math.Exp(t);
}
return s;
}
///
/// Evaluates polynomial of degree N
///
public static double Polevl(double x, double[] coef, int n)
{
double ans;
ans = coef[0];
for (int i = 1; i <= n; i++)
ans = ans * x + coef[i];
return ans;
}
///
/// Evaluates polynomial of degree N with assumption that coef[N] = 1.0
///
public static double P1evl(double x, double[] coef, int n)
{
double ans;
ans = x + coef[0];
for (int i = 1; i < n; i++)
ans = ans * x + coef[i];
return ans;
}
///
/// Returns the Bessel function of order 0 of the specified number.
///
public static double BesselJ0(double x)
{
double ax;
if ((ax = System.Math.Abs(x)) < 8.0)
{
double y = x * x;
double ans1 = 57568490574.0 + y * (-13362590354.0 + y * (651619640.7
+ y * (-11214424.18 + y * (77392.33017 + y * (-184.9052456)))));
double ans2 = 57568490411.0 + y * (1029532985.0 + y * (9494680.718
+ y * (59272.64853 + y * (267.8532712 + y * 1.0))));
return ans1 / ans2;
}
else
{
double z = 8.0 / ax;
double y = z * z;
double xx = ax - 0.785398164;
double ans1 = 1.0 + y * (-0.1098628627e-2 + y * (0.2734510407e-4
+ y * (-0.2073370639e-5 + y * 0.2093887211e-6)));
double ans2 = -0.1562499995e-1 + y * (0.1430488765e-3
+ y * (-0.6911147651e-5 + y * (0.7621095161e-6
- y * 0.934935152e-7)));
return System.Math.Sqrt(0.636619772 / ax) *
(System.Math.Cos(xx) * ans1 - z * System.Math.Sin(xx) * ans2);
}
}
///
/// Returns the Bessel function of order 1 of the specified number.
///
public static double BesselJ(double x)
{
double ax;
double y;
double ans1, ans2;
if ((ax = System.Math.Abs(x)) < 8.0)
{
y = x * x;
ans1 = x * (72362614232.0 + y * (-7895059235.0 + y * (242396853.1
+ y * (-2972611.439 + y * (15704.48260 + y * (-30.16036606))))));
ans2 = 144725228442.0 + y * (2300535178.0 + y * (18583304.74
+ y * (99447.43394 + y * (376.9991397 + y * 1.0))));
return ans1 / ans2;
}
else
{
double z = 8.0 / ax;
double xx = ax - 2.356194491;
y = z * z;
ans1 = 1.0 + y * (0.183105e-2 + y * (-0.3516396496e-4
+ y * (0.2457520174e-5 + y * (-0.240337019e-6))));
ans2 = 0.04687499995 + y * (-0.2002690873e-3
+ y * (0.8449199096e-5 + y * (-0.88228987e-6
+ y * 0.105787412e-6)));
double ans = System.Math.Sqrt(0.636619772 / ax) *
(System.Math.Cos(xx) * ans1 - z * System.Math.Sin(xx) * ans2);
if (x < 0.0) ans = -ans;
return ans;
}
}
///
/// Returns the Bessel function of order n of the specified number.
///
public static double BesselJ(int n, double x)
{
int j, m;
double ax, bj, bjm, bjp, sum, tox, ans;
bool jsum;
double ACC = 40.0;
double BIGNO = 1.0e+10;
double BIGNI = 1.0e-10;
if (n == 0) return BesselJ0(x);
if (n == 1) return BesselJ(x);
ax = System.Math.Abs(x);
if (ax == 0.0) return 0.0;
else if (ax > (double)n)
{
tox = 2.0 / ax;
bjm = BesselJ0(ax);
bj = BesselJ(ax);
for (j = 1; j < n; j++)
{
bjp = j * tox * bj - bjm;
bjm = bj;
bj = bjp;
}
ans = bj;
}
else
{
tox = 2.0 / ax;
m = 2 * ((n + (int)System.Math.Sqrt(ACC * n)) / 2);
jsum = false;
bjp = ans = sum = 0.0;
bj = 1.0;
for (j = m; j > 0; j--)
{
bjm = j * tox * bj - bjp;
bjp = bj;
bj = bjm;
if (System.Math.Abs(bj) > BIGNO)
{
bj *= BIGNI;
bjp *= BIGNI;
ans *= BIGNI;
sum *= BIGNI;
}
if (jsum) sum += bj;
jsum = !jsum;
if (j == n) ans = bjp;
}
sum = 2.0 * sum - bj;
ans /= sum;
}
return x < 0.0 && n % 2 == 1 ? -ans : ans;
}
///
/// Returns the Bessel function of the second kind, of order 0 of the specified number.
///
public static double BesselY0(double x)
{
if (x < 8.0)
{
double y = x * x;
double ans1 = -2957821389.0 + y * (7062834065.0 + y * (-512359803.6
+ y * (10879881.29 + y * (-86327.92757 + y * 228.4622733))));
double ans2 = 40076544269.0 + y * (745249964.8 + y * (7189466.438
+ y * (47447.26470 + y * (226.1030244 + y * 1.0))));
return (ans1 / ans2) + 0.636619772 * BesselJ0(x) * System.Math.Log(x);
}
else
{
double z = 8.0 / x;
double y = z * z;
double xx = x - 0.785398164;
double ans1 = 1.0 + y * (-0.1098628627e-2 + y * (0.2734510407e-4
+ y * (-0.2073370639e-5 + y * 0.2093887211e-6)));
double ans2 = -0.1562499995e-1 + y * (0.1430488765e-3
+ y * (-0.6911147651e-5 + y * (0.7621095161e-6
+ y * (-0.934945152e-7))));
return System.Math.Sqrt(0.636619772 / x) *
(System.Math.Sin(xx) * ans1 + z * System.Math.Cos(xx) * ans2);
}
}
///
/// Returns the Bessel function of the second kind, of order 1 of the specified number.
///
public static double BesselY(double x)
{
if (x < 8.0)
{
double y = x * x;
double ans1 = x * (-0.4900604943e13 + y * (0.1275274390e13
+ y * (-0.5153438139e11 + y * (0.7349264551e9
+ y * (-0.4237922726e7 + y * 0.8511937935e4)))));
double ans2 = 0.2499580570e14 + y * (0.4244419664e12
+ y * (0.3733650367e10 + y * (0.2245904002e8
+ y * (0.1020426050e6 + y * (0.3549632885e3 + y)))));
return (ans1 / ans2) + 0.636619772 * (BesselJ(x) * System.Math.Log(x) - 1.0 / x);
}
else
{
double z = 8.0 / x;
double y = z * z;
double xx = x - 2.356194491;
double ans1 = 1.0 + y * (0.183105e-2 + y * (-0.3516396496e-4
+ y * (0.2457520174e-5 + y * (-0.240337019e-6))));
double ans2 = 0.04687499995 + y * (-0.2002690873e-3
+ y * (0.8449199096e-5 + y * (-0.88228987e-6
+ y * 0.105787412e-6)));
return System.Math.Sqrt(0.636619772 / x) *
(System.Math.Sin(xx) * ans1 + z * System.Math.Cos(xx) * ans2);
}
}
///
/// Returns the Bessel function of the second kind, of order n of the specified number.
///
public static double BesselY(int n, double x)
{
double by, bym, byp, tox;
if (n == 0) return BesselY0(x);
if (n == 1) return BesselY(x);
tox = 2.0 / x;
by = BesselY(x);
bym = BesselY0(x);
for (int j = 1; j < n; j++)
{
byp = j * tox * by - bym;
bym = by;
by = byp;
}
return by;
}
///
/// Computes the Basic Spline of order n
///
public static double BSpline(int n, double x)
{
// ftp://ftp.esat.kuleuven.ac.be/pub/SISTA/hamers/PhD_bhamers.pdf
// http://sepwww.stanford.edu/public/docs/sep105/sergey2/paper_html/node5.html
double a = 1.0 / Special.Factorial(n);
double c;
bool positive = true;
for (int k = 0; k <= n + 1; k++)
{
c = Binomial(n + 1, k) * Tools.TruncatedPower(x + (n + 1.0) / 2.0 - k, n);
a += positive ? c : -c;
positive = !positive;
}
return a;
}
///
/// Computes the Binomial Coefficients C(n,k).
///
public static double Binomial(int n, int k)
{
return Math.Floor(0.5 + Math.Exp(Lfactorial(n) - Lfactorial(k) - Lfactorial(n - k)));
}
///
/// Returns the log factorial of a number (ln(n!))
///
public static double Lfactorial(int n)
{
if (lnfcache == null)
lnfcache = new double[101];
if (n < 0)
{
// Factorial is not defined for negative numbers.
throw new ArgumentException("Argument cannot be negative.", "n");
}
if (n <= 1)
{
// Factorial for n between 0 and 1 is 1, so log(factorial(n)) is 0.
return 0.0;
}
if (n <= 100)
{
// Compute the factorial using ln(gamma(n)) approximation, using the cache
// if the value has been previously computed.
return (lnfcache[n] > 0) ? lnfcache[n] : (lnfcache[n] = Lgamma(n + 1.0));
}
else
{
// Just compute the factorial using ln(gamma(n)) approximation.
return Lgamma(n + 1.0);
}
}
///
/// Computes log(1+x) without losing precision for small values of x.
///
///
/// References:
/// - http://www.johndcook.com/csharp_log_one_plus_x.html
///
public static double Log1p(double x)
{
if (x <= -1.0)
return Double.NaN;
if (Math.Abs(x) > 1e-4)
return Math.Log(1.0 + x);
// Use Taylor approx. log(1 + x) = x - x^2/2 with error roughly x^3/3
// Since |x| < 10^-4, |x|^3 < 10^-12, relative error less than 10^-8
return (-0.5 * x + 1.0) * x;
}
///
/// Compute exp(x) - 1 without loss of precision for small values of x.
///
///
/// References:
/// - http://www.johndcook.com/cpp_expm1.html
///
public static double Expm1(double x)
{
if (Math.Abs(x) < 1e-5)
return x + 0.5 * x * x;
else
return Math.Exp(x) - 1.0;
}
///
/// Computes the factorial of a number (n!)
///
public static double Factorial(int n)
{
if (fcache == null)
{
// Initialize factorial cache
fcache = new double[33];
fcache[0] = 1; fcache[1] = 1;
fcache[2] = 2; fcache[3] = 6;
fcache[4] = 24; ftop = 4;
}
if (n < 0)
{
// Factorial is not defined for negative numbers
throw new ArgumentException("Argument can not be negative", "n");
}
if (n > 32)
{
// Return Gamma approximation using exp(gammaln(n+1)),
// which for some reason is faster than gamma(n+1).
return Math.Exp(Lgamma(n + 1.0));
}
else
{
// Compute in the standard way, but use the
// factorial cache to speed up computations.
while (ftop < n)
{
int j = ftop++;
fcache[ftop] = fcache[j] * ftop;
}
return fcache[n];
}
}
///
/// Estimates unit roundoff in quantities of size x.
///
///
/// This is a port of the epslon function from EISPACK.
///
public static double Epslon(double x)
{
double a, b, c, eps;
a = 1.3333333333333333;
L10:
b = a - 1.0;
c = b + b + b;
eps = System.Math.Abs(c - 1.0);
if (eps == 0.0) goto L10;
return eps * System.Math.Abs(x);
}
///
/// Returns A with the sign of B.
///
///
/// This is a port of the sign transfer function from EISPACK.
///
/// If B > 0 then the result is ABS(A), else it is -ABS(A).
public static double Sign(double a, double b)
{
double x;
x = (a >= 0 ? a : -a);
return (b >= 0 ? x : -x);
}
// factorial function caches
private static int ftop;
private static double[] fcache;
private static double[] lnfcache;
}
}