Example #1
0
        /// <summary>
        /// Returns Iv(x) for small <paramref name="x"/>
        /// </summary>
        /// <param name="v"></param>
        /// <param name="x"></param>
        /// <returns></returns>
        public static double I_SmallArg(double v, double x)
        {
            double prefix;
            if (v <= DoubleLimits.MaxGamma - 1) {
                prefix = Math.Pow(x / 2, v) / Math2.Tgamma(v + 1);
            } else {
                prefix = StirlingGamma.PowDivGamma(x / 2, v) / v;
            }
            if (prefix == 0)
                return prefix;

            double series = HypergeometricSeries.Sum0F1(v + 1, x * x / 4);
            return prefix * series;
        }
Example #2
0
        /// <summary>
        /// Compute (z^a)(e^-z)/Γ(a) used in the regularized incomplete gammas
        /// </summary>
        /// <param name="a"></param>
        /// <param name="z"></param>
        /// <returns></returns>
        /// <remarks>Note: most of the error occurs in this function</remarks>
        public static double PrefixRegularized(double a, double z)
        {
            Debug.Assert(a > 0);
            Debug.Assert(z >= 0);

            // if we can't compute Tgamma directly, use Stirling
            if (a > DoubleLimits.MaxGamma || (a >= StirlingGamma.LowerLimit && z > -DoubleLimits.MinLogValue))
            {
                return(StirlingGamma.PowExpDivGamma(a, z));
            }

            // avoid overflows from dividing small Gamma(a)
            // for z > 1, e^(eps*ln(z)-z) ~= e^-z, so underflow is ok
            if (a < DoubleLimits.MachineEpsilon / Constants.EulerMascheroni)
            {
                return(a * Math.Pow(z, a) * Math.Exp(-z));
            }

            double alz = a * Math.Log(z);
            double g   = Math2.Tgamma(a);

            double prefix;

            if ((alz > DoubleLimits.MinLogValue && alz < DoubleLimits.MaxLogValue) && (z < -DoubleLimits.MinLogValue))
            {
                prefix = Math.Pow(z, a) * Math.Exp(-z) / g;
            }
            else if ((alz > 2 * DoubleLimits.MinLogValue && alz < 2 * DoubleLimits.MaxLogValue) && (z < -2 * DoubleLimits.MinLogValue))
            {
                double rp = Math.Pow(z, a / 2) * Math.Exp(-z / 2);
                prefix = (rp / g) * rp;
            }
            else if ((alz > 4 * DoubleLimits.MinLogValue && alz < 4 * DoubleLimits.MaxLogValue) && (z < -4 * DoubleLimits.MinLogValue))
            {
                double rp = Math.Pow(z, a / 4) * Math.Exp(-z / 4);
                prefix = (rp / g) * rp * rp * rp;
            }
            else
            {
                prefix = Math.Exp(alz - z - Math.Log(g));
            }

            return(prefix);
        }
Example #3
0
        /// <summary>
        /// Series evaluation for Jv(x), for small x
        /// </summary>
        /// <param name="v"></param>
        /// <param name="x"></param>
        /// <returns></returns>
        public static double J_SmallArg(double v, double x)
        {
            Debug.Assert(v >= 0);

            // See http://functions.wolfram.com/Bessel-TypeFunctions/BesselJ/06/01/04/01/01/0003/
            // Converges rapidly for all x << v.
            // Most of the error occurs calculating the prefix

            double prefix;
            if (v <= DoubleLimits.MaxGamma - 1)
                prefix = Math.Pow(x / 2, v) / Math2.Tgamma(v + 1);
            else
                prefix = StirlingGamma.PowDivGamma(x / 2, v)/v;

            if (prefix == 0)
                return prefix;

            double series = HypergeometricSeries.Sum0F1(v + 1, -x * x / 4);

            return prefix * series;
        }
Example #4
0
        //
        /// <summary>
        /// Calculates Ix(a,b) for large a and b (i.e &gt;= 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);
        }
Example #5
0
        // 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);
        }
Example #6
0
        /// <summary>
        /// Returns Log(Beta(a,b))
        /// </summary>
        /// <param name="a">Requires finite a > 0</param>
        /// <param name="b">Requires finite b > 0</param>
        public static double LogBeta(double a, double b)
        {
            double c = a + b;

            if ((!(a > 0) || double.IsInfinity(a))
                || (!(b > 0) || double.IsInfinity(b))) {
                Policies.ReportDomainError("Beta(a: {0}, b: {1}): Requires finite a,b > 0", a, b);
                return double.NaN;
            }
            if (double.IsInfinity(c)) {
                Policies.ReportDomainError("Beta(a: {0}, b: {1}): Requires finite c == a+b: c = {2}", a, b, c);
                return double.NaN;
            }

            // Special cases:
            if ((c == a) && (b < DoubleLimits.MachineEpsilon))
                return Math2.Lgamma(b);
            if ((c == b) && (a < DoubleLimits.MachineEpsilon))
                return Math2.Lgamma(a);
            if (b == 1)
                return -Math.Log(a);
            if (a == 1)
                return -Math.Log(b);

            // B(a,b) == B(b, a)
            if (a < b)
                Utility.Swap(ref a, ref b);

            // from this point a >= b

            if (a < 1) {
                // When x < TinyX, Γ(x) ~ 1/x 
                const double SmallX = DoubleLimits.MachineEpsilon / Constants.EulerMascheroni;
                if (a < SmallX) {
                    if (c < SmallX) {
                        // In this range, Beta(a, b) ~= 1/a + 1/b 
                        // so, we won't have an argument overflow as long as 
                        // the min(a, b) >= 2*DoubleLimits.MinNormalValue

                        if (b < 2 * DoubleLimits.MinNormalValue)
                            return Math.Log(c / a) - Math.Log(b);

                        return Math.Log((c / a) / b);
                    }
                    Debug.Assert(c <= GammaSmall.UpperLimit);
                    return -Math.Log((GammaSmall.Tgamma(c) * a * b));
                }
                if (b < SmallX)
                    return Math.Log(Tgamma(a) / (b * Tgamma(c)));

                return Math.Log((Tgamma(a) / Tgamma(c)) * Tgamma(b));

            } else if (a >= StirlingGamma.LowerLimit) {

                if ( b >= StirlingGamma.LowerLimit )
                    return StirlingGamma.LogBeta(a, b);

                return StirlingGamma.LgammaDelta(a, b) + Lgamma(b);
            }

            return Lanczos.LogBeta(a, b);
        }
Example #7
0
        /// <summary>
        /// Returns the Beta function
        /// <para>B(a,b) = Γ(a)*Γ(b)/Γ(a+b)</para>
        /// </summary>
        /// <param name="a">Requires finite a > 0</param>
        /// <param name="b">Requires finite b > 0</param>
        public static double Beta(double a, double b)
        {
            double c = a + b;

            if ((!(a > 0) || double.IsInfinity(a))
                || (!(b > 0) || double.IsInfinity(b))) {
                Policies.ReportDomainError("Beta(a: {0}, b: {1}): Requires finite a,b > 0", a, b);
                return double.NaN;
            }
            if (double.IsInfinity(c)) {
                Policies.ReportDomainError("Beta(a: {0}, b: {1}): Requires finite c == a+b: c = {2}", a, b, c);
                return double.NaN;
            }


            // Special cases:
            if ((c == a) && (b < DoubleLimits.MachineEpsilon))
                return Math2.Tgamma(b);
            if ((c == b) && (a < DoubleLimits.MachineEpsilon))
                return Math2.Tgamma(a);
            if (b == 1)
                return 1 / a;
            if (a == 1)
                return 1 / b;

            // B(a,b) == B(b, a)
            if (a < b)
                Utility.Swap(ref a, ref b);

            // from this point a >= b

            if (a < 1) {
                // When x < TinyX, Γ(x) ~ 1/x 
                const double SmallX = DoubleLimits.MachineEpsilon / Constants.EulerMascheroni;
                if ( a < SmallX ) {
                    if (c < SmallX)
                        return (c / a) / b;
                    Debug.Assert(c <= GammaSmall.UpperLimit);
                    return 1/(GammaSmall.Tgamma(c) * a * b);
                }
                if ( b < SmallX ) 
                    return Tgamma(a) / (b * Tgamma(c));
                return Tgamma(a) * ( Tgamma(b)/Tgamma(c) );
            }

            // Our Stirling series is more accurate than Lanczos
            if (a >= StirlingGamma.LowerLimit ) {
                if (b >= StirlingGamma.LowerLimit)
                    return StirlingGamma.Beta(a, b);

                // for large a, Γ(a)/Γ(a + b) ~ a^-b, so don't underflow too soon
                if (b < 1 || b * Math.Log(a) <= -DoubleLimits.MinLogValue)
                    return Tgamma(b) * StirlingGamma.TgammaDeltaRatio(a, b);

                // fall through for very large a small 1 < b < LowerLimit

            }


            return Lanczos.Beta(a, b);
        }
Example #8
0
        /// <summary>
        /// Returns the natural log of the Gamma Function = ln(|Γ(x)|).
        /// Sets the sign = Sign(Γ(x)); 0 for poles or indeterminate.
        /// </summary>
        /// <param name="x">Lgamma function argument</param>
        /// <param name="sign">Lgamma output value = Sign(Γ(x))</param>
        public static double Lgamma(double x, out int sign)
        {
            sign = 0;
            if (double.IsNaN(x))
            {
                Policies.ReportDomainError("Lgamma(x: {0}): NaN not allowed", x);
                return(double.NaN);
            }
            if (double.IsInfinity(x))
            {
                if (x < 0)
                {
                    Policies.ReportDomainError("Lgamma(x: {0}): Requires x != -Infinity", x);
                    return(double.PositiveInfinity);
                }
                sign = 1;
                return(double.PositiveInfinity);
            }


            double result = 0;

            sign = 1;
            if (x <= 0)
            {
                if (IsInteger(x))
                {
                    sign = 0;
                    Policies.ReportPoleError("Lgamma(x: {0}): Evaluation at zero, or a negative integer", x);
                    return(double.PositiveInfinity);
                }

                if (x > -GammaSmall.UpperLimit)
                {
                    return(GammaSmall.Lgamma(x, out sign));
                }

                if (x <= -StirlingGamma.LowerLimit)
                {
                    // Our Stirling routine does the reflection
                    // So no need to reflect here
                    result = StirlingGamma.Lgamma(x, out sign);
                    return(result);
                }
                else
                {
                    double product = 1;
                    double xm2     = x - 2;
                    double xm1     = x - 1;
                    while (x < 1)
                    {
                        product *= x;
                        xm2      = xm1;
                        xm1      = x;
                        x       += 1;
                    }

                    if (product < 0)
                    {
                        sign    = -1;
                        product = -product;
                    }
                    result = -Math.Log(product) + _Lgamma.Rational_1_3(x, xm1, xm2);
                    return(result);
                }
            }
            else if (x < 1)
            {
                if (x < GammaSmall.UpperLimit)
                {
                    return(GammaSmall.Lgamma(x, out sign));
                }

                // Log(Γ(x)) = Log(Γ(x+1)/x)
                result = -Math.Log(x) + _Lgamma.Rational_1_3(x + 1, x, x - 1);
            }
            else if (x < 3)
            {
                result = _Lgamma.Rational_1_3(x, x - 1, x - 2);
            }
            else if (x < 8)
            {
                // use recurrence to shift x to [2, 3)

                double product = 1;
                while (x >= 3)
                {
                    product *= --x;
                }

                result = Math.Log(product) + _Lgamma.Rational_1_3(x, x - 1, x - 2);
            }
            else
            {
                // regular evaluation:
                result = StirlingGamma.Lgamma(x);
            }

            return(result);
        }
Example #9
0
        /// <summary>
        /// Returns the Gamma Function = Γ(x)
        /// </summary>
        /// <param name="x">Tgamma function argument</param>
        public static double Tgamma(double x)
        {
            if (double.IsNaN(x))
            {
                Policies.ReportDomainError("Tgamma(x: {0}): NaN not allowed", x);
                return(double.NaN);
            }
            if (double.IsInfinity(x))
            {
                if (x < 0)
                {
                    Policies.ReportDomainError("Tgamma(x: {0}): Requires x is not negative infinity", x);
                    return(double.NaN);
                }

                return(double.PositiveInfinity);
            }

            double result = 1;

            if (x <= 0)
            {
                if (IsInteger(x))
                {
                    Policies.ReportPoleError("Tgamma(x: {0}): Requires x != 0 and x be a non-negative integer", x);
                    return(double.NaN);
                }

                if (x >= -GammaSmall.UpperLimit)
                {
                    return(GammaSmall.Tgamma(x));
                }

                if (x <= -StirlingGamma.LowerLimit)
                {
                    const double MinX = -185;
                    if (x < MinX)
                    {
                        return(0);
                    }


                    // If we use the reflection formula directly for x < -NegMaxGamma, Tgamma will overflow;
                    // so, use recurrence to get x > -NegMaxGamma.
                    // The result is likely to be subnormal or zero.
                    double shiftedX       = x;
                    double recurrenceMult = 1.0;
                    double NegMaxGamma    = -(Math2.MaxFactorialIndex + 1);
                    while (shiftedX < NegMaxGamma)
                    {
                        recurrenceMult *= shiftedX;
                        shiftedX       += 1;
                    }

                    result = -(Math.PI / StirlingGamma.Tgamma(-shiftedX)) / (shiftedX * recurrenceMult * Math2.SinPI(shiftedX));
                    return(result);
                }

                // shift x to > 0:
                double product = 1;
                while (x < 0)
                {
                    product *= x++;
                }

                result /= product;

                // fall through
            }

            if (x < 1)
            {
                if (x <= GammaSmall.UpperLimit)
                {
                    result *= GammaSmall.Tgamma(x);
                }
                else
                {
                    // Γ(x) = Exp(LogΓ(x+1))/x
                    result *= Math.Exp(_Lgamma.Rational_1_3(x + 1, x, x - 1)) / x;
                }
            }
            else if (IsInteger(x) && (x <= Math2.FactorialTable.Length))
            {
                // Γ(n) = (n-1)!
                result *= Math2.FactorialTable[(int)x - 1];
            }
            else if (x < StirlingGamma.LowerLimit)
            {
                // ensure x is in [1, 3)
                // if x >= 3, use recurrence to shift x to [2, 3)

                while (x >= 3)
                {
                    result *= --x;
                }

                result *= Math.Exp(_Lgamma.Rational_1_3(x, x - 1, x - 2));
            }
            else
            {
                result *= StirlingGamma.Tgamma(x);
            }

            return(result);
        }