// /// <summary> /// Calculates Ix(a,b) for large a and b (i.e >= 15) using an asymptotic expansion /// </summary> /// <param name="a"></param> /// <param name="b"></param> /// <param name="lambda"></param> /// <returns></returns> static double LargeABAsymptotic(double a, double b, double lambda) { const double eps = 10 * DoubleLimits.MachineEpsilon; // relative tolerance Debug.Assert(a >= 15 && b >= 15, "Requires a >= 15 && b >= 15"); Debug.Assert(lambda >= 0); // lambda = (a + b)*(1-x) - b // This is BASYM from TOMS708. // See: Armido Didonato, Alfred Morris, Algorithm 708: // Significant Digit Computation of the Incomplete Beta Function Ratios, // ACM Transactions on Mathematical Software, // Volume 18, Number 3, 1992, pages 360-373. // // num is the maximum value that n can take in the do loop // ending at statement 50. it is required that num be even. // the arrays a0, b0, c, d have dimension num + 2. // const double e0 = 2.0 * Constants.RecipSqrtPI; // 2/Sqrt(PI) const double e1 = 0.5 * Constants.RecipSqrt2; // 0.5/Sqrt(2) const int num = 20; double[] a0 = new double[num + 2]; double[] b0 = new double[num + 2]; double[] c = new double[num + 2]; double[] d = new double[num + 2]; double h, r0, r1, w0; if (a < b) { h = a / b; r0 = 1.0 / (1.0 + h); r1 = (b - a) / b; w0 = 1.0 / Math.Sqrt(a * (1.0 + h)); } else { h = b / a; r0 = 1.0 / (1.0 + h); r1 = (b - a) / a; w0 = 1.0 / Math.Sqrt(b * (1.0 + h)); } //double f = -(a * Math2.Log1pmx( -lambda/a ) + b * Math2.Log1pmx(lambda/b)); double f = -(a * Math2.Log1p(-lambda / a) + b * Math2.Log1p(lambda / b)); double t = Math.Exp(-f); if (t == 0) { return(0); } double z0 = Math.Sqrt(f); double z = 0.5 * (z0 / e1); double z2 = f + f; a0[1] = (2.0 / 3.0) * r1; c[1] = -0.5 * a0[1]; d[1] = -c[1]; // double j0 = ( 0.5 / e0 ) * Math.Exp(z0*z0)*Math2.Erfc(z0); double j0 = (0.5 / e0) * Math2.Erfcx(z0); double j1 = e1; double sum2 = j0 + d[1] * w0 * j1; double s = 1.0; double h2 = h * h; double hn = 1.0; double w = w0; double znm1 = z; double zn = z2; for (int n = 2; n <= num; n += 2) { hn = h2 * hn; a0[n] = 2.0 * r0 * (1.0 + h * hn) / (n + 2.0); int np1 = n + 1; s = s + hn; a0[np1] = 2.0 * r1 * s / (n + 3.0); for (int i = n; i <= np1; i++) { double r = -0.5 * (i + 1.0); b0[1] = r * a0[1]; for (int m = 2; m <= i; m++) { double bsum = 0.0; int mm1 = m - 1; for (int j = 1; j <= mm1; j++) { int mmj = m - j; bsum += (j * r - mmj) * a0[j] * b0[mmj]; } b0[m] = r * a0[m] + bsum / m; } c[i] = b0[i] / (i + 1.0); double dsum = 0.0; int im1 = i - 1; for (int j = 1; j <= im1; j++) { dsum += d[i - j] * c[j]; } d[i] = -(dsum + c[i]); } j0 = e1 * znm1 + (n - 1.0) * j0; j1 = e1 * zn + n * j1; znm1 = z2 * znm1; zn = z2 * zn; w = w0 * w; double t0 = d[n] * w * j0; w = w0 * w; double t1 = d[np1] * w * j1; sum2 += (t0 + t1); if ((Math.Abs(t0) + Math.Abs(t1)) <= eps * sum2) { break; } } double u = StirlingGamma.GammaSeries(a + b) / (StirlingGamma.GammaSeries(a) * StirlingGamma.GammaSeries(b)); //Math.Exp(-Bcorr(a, b)); // below is the equivalent: // double p = a/(a+b); // double q = b/(a+b); // return Math.Sqrt(2*Math.PI*(a+b)/(a*b))*_Beta.PowerTerms(a, b, p, q, true); return(e0 * t * u * sum2); }
// sc = 1-s public static double Imp(double s, double sc) { Debug.Assert(s != 1); // Use integer routines if we can. if (Math.Floor(s) == s && s > int.MinValue && s <= int.MaxValue) { return(Imp((int)s)); } // Zeta(x) approx = -0.5 - 1/2 * Log(2*PI) * x // Expected error should be approx 3 ulps const double LowerLimit = DoubleLimits.RootMachineEpsilon._2; if (Math.Abs(s) < LowerLimit) { return(-0.5 - (Constants.Ln2PI / 2) * s); } if (s < 0) { // The zeta function = 0 for -2, -4, -6...-2n if (Math.Floor(s / 2) == s / 2) { return(0); } // http://functions.wolfram.com/ZetaFunctionsandPolylogarithms/Zeta/17/01/01/0001/ // Use reflection: ζ(s) = Γ(1-s) * (2^s) * π^(s-1) * Sin(sπ/2) * ζ(1-s) if (sc <= DoubleLimits.MaxGamma) { return(2 * Math.Pow(2 * Math.PI, -sc) * Math2.Tgamma(sc) * Math2.SinPI(0.5 * s) * Imp(sc, s)); } // use Stirling double sin = Math2.SinPI(0.5 * s); double sign = Math.Sign(sin); sin = Math.Abs(sin); double value; var factor = StirlingGamma.GammaSeries(sc) * Imp(sc, s); var log = (sc - 0.5) * Math.Log(sc / (2 * Math.PI)); if (log < DoubleLimits.MaxLogValue) { value = factor * Math.Pow(sc / (2 * Math.PI), sc - 0.5) * Math.Exp(-sc) * (2 * sin); } else if (log < 2 * DoubleLimits.MaxLogValue) { double e = Math.Pow(sc / (2 * Math.PI), sc / 2 - 0.25) * Math.Exp(-sc / 2); value = factor * e * (sin * 2) * e; } else { value = Math.Exp(log - sc + Math.Log(2 * sin * factor)); } return(sign * value); } double result; if (s < 1) { // Rational Approximation // Maximum Deviation Found: 2.020e-18 // Expected Error Term: -2.020e-18 // Max error found at double precision: 3.994987e-17 const double Y = -1.2433929443359375; const double p0 = 0.24339294433593750202; const double p1 = -0.49092470516353571651; const double p2 = 0.0557616214776046784287; const double p3 = -0.00320912498879085894856; const double p4 = 0.000451534528645796438704; const double p5 = -0.933241270357061460782e-5; const double q0 = 1; const double q1 = -0.279960334310344432495; const double q2 = 0.0419676223309986037706; const double q3 = -0.00413421406552171059003; const double q4 = 0.00024978985622317935355; const double q5 = -0.101855788418564031874e-4; double z = sc; double P = p0 + z * (p1 + z * (p2 + z * (p3 + z * (p4 + z * p5)))); double Q = q0 + z * (q1 + z * (q2 + z * (q3 + z * (q4 + z * q5)))); result = ((P / Q) + Y + sc) / sc; } else if (s <= 2) { // Maximum Deviation Found: 9.007e-20 // Expected Error Term: 9.007e-20 const double p0 = 0.577215664901532860516; const double p1 = 0.243210646940107164097; const double p2 = 0.0417364673988216497593; const double p3 = 0.00390252087072843288378; const double p4 = 0.000249606367151877175456; const double p5 = 0.110108440976732897969e-4; const double q0 = 1; const double q1 = 0.295201277126631761737; const double q2 = 0.043460910607305495864; const double q3 = 0.00434930582085826330659; const double q4 = 0.000255784226140488490982; const double q5 = 0.10991819782396112081e-4; double z = -sc; double P = p0 + z * (p1 + z * (p2 + z * (p3 + z * (p4 + z * p5)))); double Q = q0 + z * (q1 + z * (q2 + z * (q3 + z * (q4 + z * q5)))); result = P / Q + 1 / (-sc); } else if (s <= 4) { // Maximum Deviation Found: 5.946e-22 // Expected Error Term: -5.946e-22 const double _Y3 = 0.6986598968505859375; const double p0 = -0.0537258300023595030676; const double p1 = 0.0445163473292365591906; const double p2 = 0.0128677673534519952905; const double p3 = 0.00097541770457391752726; const double p4 = 0.769875101573654070925e-4; const double p5 = 0.328032510000383084155e-5; const double q0 = 1; const double q1 = 0.33383194553034051422; const double q2 = 0.0487798431291407621462; const double q3 = 0.00479039708573558490716; const double q4 = 0.000270776703956336357707; const double q5 = 0.106951867532057341359e-4; const double q6 = 0.236276623974978646399e-7; double z = s - 2; double P = p0 + z * (p1 + z * (p2 + z * (p3 + z * (p4 + z * p5)))); double Q = q0 + z * (q1 + z * (q2 + z * (q3 + z * (q4 + z * (q5 + z * q6))))); result = P / Q + _Y3 + 1 / (-sc); } else if (s <= 7) { // Maximum Deviation Found: 2.955e-17 // Expected Error Term: 2.955e-17 // Max error found at double precision: 2.009135e-16 const double p0 = -2.49710190602259410021; const double p1 = -2.60013301809475665334; const double p2 = -0.939260435377109939261; const double p3 = -0.138448617995741530935; const double p4 = -0.00701721240549802377623; const double p5 = -0.229257310594893932383e-4; const double q0 = 1; const double q1 = 0.706039025937745133628; const double q2 = 0.15739599649558626358; const double q3 = 0.0106117950976845084417; const double q4 = -0.36910273311764618902e-4; const double q5 = 0.493409563927590008943e-5; const double q6 = -0.234055487025287216506e-6; const double q7 = 0.718833729365459760664e-8; const double q8 = -0.1129200113474947419e-9; double z = s - 4; double P = p0 + z * (p1 + z * (p2 + z * (p3 + z * (p4 + z * p5)))); double Q = q0 + z * (q1 + z * (q2 + z * (q3 + z * (q4 + z * (q5 + z * (q6 + z * (q7 + z * q8))))))); result = 1 + Math.Exp(P / Q); } else if (s < 15) { // Maximum Deviation Found: 7.117e-16 // Expected Error Term: 7.117e-16 // Max error found at double precision: 9.387771e-16 const double p0 = -4.78558028495135619286; const double p1 = -1.89197364881972536382; const double p2 = -0.211407134874412820099; const double p3 = -0.000189204758260076688518; const double p4 = 0.00115140923889178742086; const double p5 = 0.639949204213164496988e-4; const double p6 = 0.139348932445324888343e-5; const double q0 = 1; const double q1 = 0.244345337378188557777; const double q2 = 0.00873370754492288653669; const double q3 = -0.00117592765334434471562; const double q4 = -0.743743682899933180415e-4; const double q5 = -0.21750464515767984778e-5; const double q6 = 0.471001264003076486547e-8; const double q7 = -0.833378440625385520576e-10; const double q8 = 0.699841545204845636531e-12; double z = s - 7; double P = p0 + z * (p1 + z * (p2 + z * (p3 + z * (p4 + z * (p5 + z * p6))))); double Q = q0 + z * (q1 + z * (q2 + z * (q3 + z * (q4 + z * (q5 + z * (q6 + z * (q7 + z * q8))))))); result = 1 + Math.Exp(P / Q); } else if (s < 36) { // Max error in interpolated form: 1.668e-17 // Max error found at long double precision: 1.669714e-17 const double p0 = -10.3948950573308896825; const double p1 = -2.85827219671106697179; const double p2 = -0.347728266539245787271; const double p3 = -0.0251156064655346341766; const double p4 = -0.00119459173416968685689; const double p5 = -0.382529323507967522614e-4; const double p6 = -0.785523633796723466968e-6; const double p7 = -0.821465709095465524192e-8; const double q0 = 1; const double q1 = 0.208196333572671890965; const double q2 = 0.0195687657317205033485; const double q3 = 0.00111079638102485921877; const double q4 = 0.408507746266039256231e-4; const double q5 = 0.955561123065693483991e-6; const double q6 = 0.118507153474022900583e-7; const double q7 = 0.222609483627352615142e-14; double z = s - 15; double P = p0 + z * (p1 + z * (p2 + z * (p3 + z * (p4 + z * (p5 + z * (p6 + z * p7)))))); double Q = q0 + z * (q1 + z * (q2 + z * (q3 + z * (q4 + z * (q5 + z * (q6 + z * q7)))))); result = 1 + Math.Exp(P / Q); } else if (s < 53) { result = 1 + Math.Pow(2.0, -s); } else { result = 1; } return(result); }