/** * Regularized lower gamma function 'P' * @param s * @param x * @return Value of the regularized lower gamma function 'P'. */ public static double regularizedGammaLowerP(double s, double x) { if (Double.IsNaN(x)) { return(Double.NaN); } if (Double.IsNaN(s)) { return(Double.NaN); } if (MathFunctions.almostEqual(x, 0)) { return(0); } if (MathFunctions.almostEqual(s, 0)) { return(1 + SpecialFunctions.exponentialIntegralEi(-x) / MathConstants.EULER_MASCHERONI); } if (MathFunctions.almostEqual(s, 1)) { return(1 - Math.Exp(-x)); } if (x < 0) { return(Double.NaN); } if (s < 0) { return(regularizedGammaLowerP(s + 1, x) + (Math.Pow(x, s) * Math.Exp(-x)) / (s * gamma(s))); } const double epsilon = 0.000000000000001; const double bigNumber = 4503599627370496.0; const double bigNumberInverse = 2.22044604925031308085e-16; double ax = (s * Math.Log(x)) - x - logGamma(s); if (ax < -709.78271289338399) { return(1); } if (x <= 1 || x <= s) { double r2 = s; double c2 = 1; double ans2 = 1; do { r2 = r2 + 1; c2 = c2 * x / r2; ans2 += c2; } while ((c2 / ans2) > epsilon); return(Math.Exp(ax) * ans2 / s); } int c = 0; double y = 1 - s; double z = x + y + 1; double p3 = 1; double q3 = x; double p2 = x + 1; double q2 = z * x; double ans = p2 / q2; double error; do { c++; y += 1; z += 2; double yc = y * c; double p = (p2 * z) - (p3 * yc); double q = (q2 * z) - (q3 * yc); if (q != 0) { double nextans = p / q; error = Math.Abs((ans - nextans) / nextans); ans = nextans; } else { // zero div, skip error = 1; } // shift p3 = p2; p2 = p; q3 = q2; q2 = q; // normalize fraction when the numerator becomes large if (Math.Abs(p) > bigNumber) { p3 *= bigNumberInverse; p2 *= bigNumberInverse; q3 *= bigNumberInverse; q2 *= bigNumberInverse; } } while (error > epsilon); return(1 - (Math.Exp(ax) * ans)); }
/** * Regularized upper gamma function 'Q' * @param s * @param x * @return Value of the regularized upper gamma function 'Q'. */ public static double regularizedGammaUpperQ(double s, double x) { if (Double.IsNaN(x)) { return(Double.NaN); } if (Double.IsNaN(s)) { return(Double.NaN); } if (MathFunctions.almostEqual(x, 0)) { return(1); } if (MathFunctions.almostEqual(s, 0)) { return(-SpecialFunctions.exponentialIntegralEi(-x) / MathConstants.EULER_MASCHERONI); } if (MathFunctions.almostEqual(s, 1)) { return(Math.Exp(-x)); } if (x < 0) { return(Double.NaN); } if (s < 0) { return(regularizedGammaUpperQ(s + 1, x) - (Math.Pow(x, s) * Math.Exp(-x)) / (s * gamma(s))); } double ax = s * Math.Log(x) - x - logGamma(s); if (ax < -709.78271289338399) { return(0); } double t; const double igammaepsilon = 0.000000000000001; const double igammabignumber = 4503599627370496.0; const double igammabignumberinv = 2.22044604925031308085 * 0.0000000000000001; ax = Math.Exp(ax); double y = 1 - s; double z = x + y + 1; double c = 0; double pkm2 = 1; double qkm2 = x; double pkm1 = x + 1; double qkm1 = z * x; double ans = pkm1 / qkm1; do { c = c + 1; y = y + 1; z = z + 2; double yc = y * c; double pk = pkm1 * z - pkm2 * yc; double qk = qkm1 * z - qkm2 * yc; if (qk != 0) { double r = pk / qk; t = Math.Abs((ans - r) / r); ans = r; } else { t = 1; } pkm2 = pkm1; pkm1 = pk; qkm2 = qkm1; qkm1 = qk; if (Math.Abs(pk) > igammabignumber) { pkm2 = pkm2 * igammabignumberinv; pkm1 = pkm1 * igammabignumberinv; qkm2 = qkm2 * igammabignumberinv; qkm1 = qkm1 * igammabignumberinv; } } while (t > igammaepsilon); return(ans * ax); }