Example #1
0
        /// <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);
        }