/// <summary> /// BASYM method described in Didonato and Morris. /// </summary> /// <param name="x">Value.</param> /// <param name="a">True count.</param> /// <param name="b">False count.</param> /// <param name="epsilon">Tolerance.</param> /// <returns></returns> private static double BAsym(double x, double a, double b, double epsilon) { double p = a / (a + b); double q = 1.0 - p; double y = 1.0 - x; double lambda = (a + b) * (p - x); double zsq = -(a * Math.Log(x / p) + b * Math.Log(y / q)); // should be >= 0 double z = Math.Sqrt(zsq); double twoPowMinus15 = Math.Pow(2, -1.5); double zsqrt2 = z * Math.Sqrt(2.0); double LPrev = 0.25 * Math.Sqrt(Math.PI) * Math.Exp(zsq) * MMath.Erfc(z); double LCurr = twoPowMinus15; double U = Math.Exp(GammaLnSeries(a + b) - GammaLnSeries(a) - GammaLnSeries(b)); double mult = 2 * U * Math.Exp(-zsq) / Math.Sqrt(Math.PI); double betaGamma = (a < b) ? Math.Sqrt(q / a) : Math.Sqrt(p / b); List <double> aTerm = new List <double>(); List <double> cTerm = new List <double>(); List <double> eTerm = new List <double>(); double aFunc(int n) { double neg = (n % 2) == 0 ? 1.0 : -1.0; return((a <= b) ? 2.0 * q * (1.0 + neg * Math.Pow(a / b, n + 1)) / (2.0 + n) : 2.0 * p * (neg + Math.Pow(b / a, n + 1)) / (2.0 + n)); } // Assumes aTerm has been populated up to n double bFunc(int n, double r) { if (n == 0) { return(1.0); } else if (n == 1) { return(r * aTerm[1]); } else { List <double> bTerm = new List <double>(); bTerm.Add(1.0); bTerm.Add(r * aTerm[1]); for (int j = 2; j <= n; j++) { bTerm.Add(r * aTerm[j] + Enumerable.Range(1, j - 1).Sum(i => ((j - i) * r - i) * bTerm[i] * aTerm[j - i]) / j); } return(bTerm[n]); } } // Assumes aTerm has been populated up to n double cFunc(int n) => bFunc(n - 1, -n / 2.0) / n; aTerm.Add(aFunc(0)); aTerm.Add(aFunc(1)); cTerm.Add(cFunc(1)); cTerm.Add(cFunc(2)); eTerm.Add(1.0); eTerm.Add(-eTerm[0] * cTerm[1]); double result = LPrev + eTerm[1] * LCurr * betaGamma; double powBetaGamma = betaGamma; double powZSqrt2 = 1.0; for (int n = 2; ; n++) { powZSqrt2 *= zsqrt2; double L = (twoPowMinus15 * powZSqrt2) + (n - 1) * LPrev; LPrev = LCurr; LCurr = L; powBetaGamma *= betaGamma; aTerm.Add(aFunc(n)); cTerm.Add(cFunc(n + 1)); eTerm.Add(-Enumerable.Range(0, n).Sum(i => eTerm[i] * cTerm[n - i])); double term = eTerm[n] * LCurr * powBetaGamma; result += term; if (aTerm.Last() != 0 && Math.Abs(term) <= epsilon * Math.Abs(result)) { break; } if (n > 100) { throw new Exception("BASym series not converging"); } } return(mult * result); }