Пример #1
0
        /// <summary>
        /// Returns the value of the Riemann Zeta function at <paramref name="x"/>
        /// <para>ζ(x) = Σ n^-x, n={1, ∞}</para>
        /// </summary>
        /// <param name="x">The function argument</param>
        public static double Zeta(double x)
        {
            if (double.IsNaN(x))
            {
                Policies.ReportDomainError("Zeta(x: {0}): NaN not allowed", x);
                return(double.NaN);
            }
            if (double.IsInfinity(x))
            {
                if (x > 0)
                {
                    return(1);
                }

                Policies.ReportDomainError("Zeta(x: {0}): -Infinity not allowed", x);
                return(double.NaN);
            }
            if (x == 1)
            {
                Policies.ReportPoleError("Zeta(x: {0}): Evaluation at pole", x);
                return(double.NaN);
            }

            return(_Zeta.Imp(x, 1 - x));
        }
Пример #2
0
        /// <summary>
        /// Returns Floor(Log(2,|<paramref name="x"/>|)).
        /// Note: this is the exponent field of a double precision number.
        /// <para>If x is NaN then x is returned</para>
        /// <para>If x == ±∞ then +∞ is returned</para>
        /// <para>If x == ±0 then -∞ is returned</para>
        /// </summary>
        /// <param name="x">Argument</param>
        public static double Logb(double x)
        {
            IEEEDouble rep = new IEEEDouble(x);

            // Check for +/- Inf or NaN
            if (!rep.IsFinite) {
                if ( rep.IsInfinity )
                    return double.PositiveInfinity;
                Policies.ReportDomainError("Logb(x: {0}): NaNs not allowed", x);
                return x;
            }

            if ( x == 0 ) {
                Policies.ReportPoleError("Logb(x: {0}): Logb(0) == -∞", x);
                return double.NegativeInfinity;

            }

            int exponent = 0;
            if (rep.IsSubnormal) {
                // Multiply by 2^53 to normalize the number
                const double normalMult = (1L << IEEEDouble.MantissaBits);
                rep = new IEEEDouble(x * normalMult);
                exponent = -IEEEDouble.MantissaBits;

                Debug.Assert(!rep.IsSubnormal);
            }

            exponent += rep.ExponentValue; 
            return exponent;
        }
Пример #3
0
        /// <summary>
        /// Returns the value of the Riemann Zeta function at <paramref name="n"/>
        /// <para>ζ(n) = Σ k^-n, k={1, ∞}</para>
        /// </summary>
        /// <param name="n">The function argument</param>
        public static double Zeta(int n)
        {
            if (n == 1)
            {
                Policies.ReportPoleError("Zeta(n: {0}): Evaluation at pole", n);
                return(double.NaN);
            }

            return(_Zeta.Imp(n));
        }
Пример #4
0
        /// <summary>
        /// Returns the Exponential Integral
        /// <para>E<sub>n</sub>(x) = ∫ e^(-x*t)/t^n dt, t={1,∞}</para>
        /// </summary>
        /// <param name="n">Requires n ≥ 0</param>
        /// <param name="x">Requires x ≥ 0</param>
        public static double Expint(int n, double x)
        {
            if (!(n >= 0) || !(x >= 0))
            {
                Policies.ReportDomainError("Expint(n: {0}, x: {1}): Requires n,x >= 0", n, x);
                return(double.NaN);
            }

            // E_{n}(∞) = 0
            // see: http://functions.wolfram.com/06.34.03.0014.01
            if (double.IsInfinity(x))
            {
                return(0);
            }

            if (x == 0)
            {
                if (n <= 1)
                {
                    Policies.ReportPoleError("Expint(n: {0}, x: {1}): Requires x > 0 when n <= 1", n, x);
                    return(double.NaN);
                }
                return(1.0 / (n - 1));
            }

            if (n == 0)
            {
                return(Math.Exp(-x) / x);
            }

            if (n == 1)
            {
                return(E1(x));
            }

            if (n < 3)
            {
                if (x < 0.5)
                {
                    return(_Expint.En_Series(n, x));
                }

                return(_Expint.En_Fraction(n, x));
            }

            if (x < (n - 2.0) / (n - 1.0))
            {
                return(_Expint.En_Series(n, x));
            }

            return(_Expint.En_Fraction(n, x));
        }
Пример #5
0
 /// <summary>
 /// Returns Γ(n)
 /// </summary>
 /// <param name="n">Argument</param>
 public static double Tgamma(int n)
 {
     if (n <= 0)
     {
         Policies.ReportPoleError("Tgamma(n: {0}): Requires n > 0", n);
         return(double.NaN);
     }
     if (n >= FactorialTable.Length + 1)
     {
         return(double.PositiveInfinity);
     }
     return(FactorialTable[n - 1]);
 }
Пример #6
0
        /// <summary>
        /// Returns Tan(π * x)
        /// </summary>
        /// <param name="x">Argument</param>
        public static double TanPI(double x)
        {
            if (double.IsNaN(x) || double.IsInfinity(x))
            {
                Policies.ReportDomainError("TanPI(x: {0}): Requires finite x", x);
                return(double.NaN);
            }

            // save x for error messages
            double originalX = x;

            // tan(-x) == -tan(x)
            bool neg = false;

            if (x < 0)
            {
                x   = -x;
                neg = !neg;
            }

            // the period for tan(x) is π
            if (x >= 1)
            {
                x -= Math.Floor(x);
            }

            Debug.Assert(x >= 0 && x < 1);

            // shift x to [-0.5,0.5]*π
            if (x > 0.5)
            {
                x -= 1.0;
            }


            if (x == 0.5)
            {
                Policies.ReportPoleError("TanPI(x: {0}): Requires x not a half integer", originalX);
                return(double.NaN);
            }

            double result = Math.Tan(Math.PI * x);

            return((neg) ? -result : result);
        }
Пример #7
0
        /// <summary>
        /// Returns Cot(π * x)
        /// </summary>
        /// <param name="x">Argument</param>
        public static double CotPI(double x)
        {
            if (double.IsNaN(x) || double.IsInfinity(x))
            {
                Policies.ReportDomainError("CotPI(x: {0}): Requires finite x", x);
                return(double.NaN);
            }

            double absX = Math.Abs(x);

            // the period for cot(x) is π
            if (absX >= 1)
            {
                absX -= Math.Floor(absX);
            }

            if (absX == 0)
            {
                Policies.ReportPoleError("CotPI(x: {0}): Requires non-integer value for x", x);
                return(double.NaN);
            }

            Debug.Assert(absX > 0 && absX < 1);

            // cot(x) == tan(π/2 - x)
            double result;

            if (absX >= 0.5)
            {
                result = -Math.Tan((absX - 0.5) * Math.PI);
            }
            else
            {
                result = 1.0 / Math.Tan(absX * Math.PI);
            }

            // cot(-x) == -cot(x)
            return((x < 0) ? -result : result);
        }
Пример #8
0
        /// <summary>
        /// Returns the Digamma function
        /// <para>ψ(x) = d/dx(ln(Γ(x))) = Γ'(x)/Γ(x)</para>
        /// </summary>
        /// <param name="x">Digamma function argument</param>
        public static double Digamma(double x)
        {
            if (double.IsNaN(x))
            {
                Policies.ReportDomainError("Digamma(x: {0}): NaN not allowed", x);
                return(double.NaN);
            }
            if (double.IsInfinity(x))
            {
                if (x < 0)
                {
                    Policies.ReportDomainError("Digamma(x: {0}): Requires x != -Infinity", x);
                    return(double.NaN);
                }
                return(double.PositiveInfinity);
            }


            if (x <= 0)
            {
                if (IsInteger(x))
                {
                    Policies.ReportPoleError("Digamma(x: {0}) Requires x is not an integer when x <= 0", x);
                    return(double.NaN);
                }


                // use the following equations to reflect
                // ψ(1-x) - ψ(x) = π * cot(π*x)
                // ψ(-x) = 1/x + ψ(x) +  π * cot(π*x)

                if (x < -1)
                {
                    return(Digamma(1 - x) - Math.PI * Math2.CotPI(x));
                }

                // The following is the forward recurrence:
                //      -(1/x + 1/(x+1)) + Digamma(x + 2);
                // with a little less cancellation error near Digamma root: x = -0.50408...
                return(_Digamma.Rational_1_2(x + 2) - (2 * x + 1) / (x * (x + 1)));
            }

            // If we're above the lower-limit for the asymptotic expansion then use it:
            if (x >= 10)
            {
                const double p0 = 0.083333333333333333333333333333333333333333333333333;
                const double p1 = -0.0083333333333333333333333333333333333333333333333333;
                const double p2 = 0.003968253968253968253968253968253968253968253968254;
                const double p3 = -0.0041666666666666666666666666666666666666666666666667;
                const double p4 = 0.0075757575757575757575757575757575757575757575757576;
                const double p5 = -0.021092796092796092796092796092796092796092796092796;
                const double p6 = 0.083333333333333333333333333333333333333333333333333;
                const double p7 = -0.44325980392156862745098039215686274509803921568627;

                double xm1 = x - 1;
                double z   = 1 / (xm1 * xm1);
                double P   = p0 + z * (p1 + z * (p2 + z * (p3 + z * (p4 + z * (p5 + z * (p6 + z * p7))))));
                return(Math.Log(xm1) + 1.0 / (2 * xm1) - z * P);
            }


            double result = 0;

            //
            // If x > 2 reduce to the interval [1,2]:
            //
            while (x > 2)
            {
                x      -= 1;
                result += 1 / x;
            }
            //
            // If x < 1 use recurrence to shift to > 1:
            //
            if (x < 1)
            {
                result = -1 / x;
                x     += 1;
            }
            result += _Digamma.Rational_1_2(x);

            return(result);
        }
Пример #9
0
        /// <summary>
        /// Returns the Trigamma function
        /// <para>ψ'(x) = d/dx(ψ(x)) = d''/dx(ln(Γ(x)))</para>
        /// </summary>
        /// <param name="x">Trigamma function argument</param>
        public static double Trigamma(double x)
        {
            if (double.IsNaN(x))
            {
                Policies.ReportDomainError("Trigamma(x: {0}): NaN not allowed", x);
                return(double.NaN);
            }
            if (double.IsInfinity(x))
            {
                if (x < 0)
                {
                    Policies.ReportDomainError("Trigamma(x: {0}): Requires x != -Infinity", x);
                    return(double.NaN);
                }
                return(0);
            }

            double result = 0;

            //
            // Check for negative arguments and use reflection:
            //
            if (x <= 0)
            {
                if (IsInteger(x))
                {
                    Policies.ReportPoleError("Trigamma(x: {0}) Requires x is not an integer when x <= 0", x);
                    return(double.NaN);
                }

                // Reflect:
                double z = 1 - x;
                double s = Math.Abs(x) < Math.Abs(z) ? Math2.SinPI(x) : Math2.SinPI(z);
                return(-Trigamma(z) + (Math.PI * Math.PI) / (s * s));
            }

            if (x < 1)
            {
                result = 1 / (x * x);
                x     += 1;
            }

            if (x <= 2)
            {
                // Max error in interpolated form: 3.736e-017
                const double Y_1_2 = 2.1093254089355469;

                const double p0 = -1.1093280605946045;
                const double p1 = -3.8310674472619321;
                const double p2 = -3.3703848401898283;
                const double p3 = 0.28080574467981213;
                const double p4 = 1.6638069578676164;
                const double p5 = 0.64468386819102836;

                const double q0 = 1.0;
                const double q1 = 3.4535389668541151;
                const double q2 = 4.5208926987851437;
                const double q3 = 2.7012734178351534;
                const double q4 = 0.64468798399785611;
                const double q5 = -0.20314516859987728e-6;

                double z = x;
                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 += (Y_1_2 + P / Q) / (x * x);
            }
            else if (x <= 4)
            {
                // Max error in interpolated form: 1.159e-017
                const double p0 = -0.13803835004508849e-7;
                const double p1 = 0.50000049158540261;
                const double p2 = 1.6077979838469348;
                const double p3 = 2.5645435828098254;
                const double p4 = 2.0534873203680393;
                const double p5 = 0.74566981111565923;

                const double q0 = 1.0;
                const double q1 = 2.8822787662376169;
                const double q2 = 4.1681660554090917;
                const double q3 = 2.7853527819234466;
                const double q4 = 0.74967671848044792;
                const double q5 = -0.00057069112416246805;

                double z = 1 / x;
                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 += (1 + P / Q) / x;
            }
            else
            {
                // Maximum Deviation Found:                     6.896e-018
                // Expected Error Term :                       -6.895e-018
                // Maximum Relative Change in Control Points :  8.497e-004
                const double p0 = 0.68947581948701249e-17;
                const double p1 = 0.49999999999998975;
                const double p2 = 1.0177274392923795;
                const double p3 = 2.498208511343429;
                const double p4 = 2.1921221359427595;
                const double p5 = 1.5897035272532764;
                const double p6 = 0.40154388356961734;

                const double q0 = 1.0;
                const double q1 = 1.7021215452463932;
                const double q2 = 4.4290431747556469;
                const double q3 = 2.9745631894384922;
                const double q4 = 2.3013614809773616;
                const double q5 = 0.28360399799075752;
                const double q6 = 0.022892987908906897;

                double z = 1 / x;
                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)))));

                result += (1 + P / Q) / x;
            }


            return(result);
        }
Пример #10
0
        /// <summary>
        /// Returns Y{0}(x)
        /// </summary>
        /// <param name="x"></param>
        /// <returns></returns>
        public static double Y0(double x)
        {
            if (x < 0)
            {
                Policies.ReportDomainError("BesselY(v: 0, x: {0}): Requires x >= 0; complex number result not supported", x);
                return(double.NaN);
            }
            if (x == 0)
            {
                Policies.ReportPoleError("BesselY(v: 0, x: {0}): Overflow", x);
                return(double.NegativeInfinity);
            }

            if (double.IsInfinity(x))
            {
                return(0);
            }


            double value, factor;

            if (x < DoubleLimits.MachineEpsilon)
            {
                return((2 / Math.PI) * (Math.Log(x / 2) + Constants.EulerMascheroni));
            }

            // Bessel function of the second kind of order zero
            // x <= 8, minimax rational approximations on root-bracketing intervals
            // x > 8, Hankel asymptotic expansion in Hart, Computer Approximations, 1968


            if (x <= 3)                        // x in [eps, 3]

            {
                const double Zero1  = 8.9357696627916752158e-01;
                const double Zero1a = 3837883847.0 / 4294967296;
                const double Zero1b = -8.66317862382940502854117587748531380699855129307710548e-11;

                const double p0 = 1.0723538782003176831e+11;
                const double p1 = -8.3716255451260504098e+09;
                const double p2 = 2.0422274357376619816e+08;
                const double p3 = -2.1287548474401797963e+06;
                const double p4 = 1.0102532948020907590e+04;
                const double p5 = -1.8402381979244993524e+01;

                const double q0 = 5.8873865738997033405e+11;
                const double q1 = 8.1617187777290363573e+09;
                const double q2 = 5.5662956624278251596e+07;
                const double q3 = 2.3889393209447253406e+05;
                const double q4 = 6.6475986689240190091e+02;
                const double q5 = 1.0;


                double z = x * x;
                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))));

                double y = (2 / Math.PI) * Math.Log(x / Zero1) * _Bessel.J0(x);
                factor = (x + Zero1) * ((x - Zero1a) - Zero1b);
                value  = y + factor * (P / Q);
            }
            else if (x <= 5.5)                      // x in (3, 5.5]

            {
                const double Zero2  = 3.9576784193148578684e+00;
                const double Zero2a = 16998099379.0 / 4294967296;
                const double Zero2b = 9.846238317388612698651281418603765563630625507511794841e-12;

                const double p0 = -2.2213976967566192242e+13;
                const double p1 = -5.5107435206722644429e+11;
                const double p2 = 4.3600098638603061642e+10;
                const double p3 = -6.9590439394619619534e+08;
                const double p4 = 4.6905288611678631510e+06;
                const double p5 = -1.4566865832663635920e+04;
                const double p6 = 1.7427031242901594547e+01;

                const double q0 = 4.3386146580707264428e+14;
                const double q1 = 5.4266824419412347550e+12;
                const double q2 = 3.4015103849971240096e+10;
                const double q3 = 1.3960202770986831075e+08;
                const double q4 = 4.0669982352539552018e+05;
                const double q5 = 8.3030857612070288823e+02;
                const double q6 = 1.0;

                double z = x * x;
                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)))));

                double y = (2 / Math.PI) * Math.Log(x / Zero2) * _Bessel.J0(x);
                factor = (x + Zero2) * ((x - Zero2a) - Zero2b);
                value  = y + factor * (P / Q);
            }
            else if (x <= 8)                    // x in (5.5, 8]

            {
                const double Zero3  = 7.0860510603017726976e+00;
                const double Zero3a = 15217178781.0 / 2147483648;
                const double Zero3b = -5.07017534414388797421475310284896188222355301448323476e-11;

                const double p0 = -8.0728726905150210443e+15;
                const double p1 = 6.7016641869173237784e+14;
                const double p2 = -1.2829912364088687306e+11;
                const double p3 = -1.9363051266772083678e+11;
                const double p4 = 2.1958827170518100757e+09;
                const double p5 = -1.0085539923498211426e+07;
                const double p6 = 2.1363534169313901632e+04;
                const double p7 = -1.7439661319197499338e+01;

                const double q0 = 3.4563724628846457519e+17;
                const double q1 = 3.9272425569640309819e+15;
                const double q2 = 2.2598377924042897629e+13;
                const double q3 = 8.6926121104209825246e+10;
                const double q4 = 2.4727219475672302327e+08;
                const double q5 = 5.3924739209768057030e+05;
                const double q6 = 8.7903362168128450017e+02;
                const double q7 = 1.0;

                double z = x * x;
                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))))));

                double y = (2 / Math.PI) * Math.Log(x / Zero3) * _Bessel.J0(x);
                factor = (x + Zero3) * ((x - Zero3a) - Zero3b);
                value  = y + factor * (P / Q);
            }
            else                                    // x in (8, infinity)

            {
                double y = 8 / x;
                double z = y * y;

                double rc, rs;
                {
                    const double p0 = 2.2779090197304684302e+04;
                    const double p1 = 4.1345386639580765797e+04;
                    const double p2 = 2.1170523380864944322e+04;
                    const double p3 = 3.4806486443249270347e+03;
                    const double p4 = 1.5376201909008354296e+02;
                    const double p5 = 8.8961548424210455236e-01;

                    const double q0 = 2.2779090197304684318e+04;
                    const double q1 = 4.1370412495510416640e+04;
                    const double q2 = 2.1215350561880115730e+04;
                    const double q3 = 3.5028735138235608207e+03;
                    const double q4 = 1.5711159858080893649e+02;
                    const double q5 = 1.0;


                    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))));

                    rc = P / Q;
                }

                {
                    const double p0 = -8.9226600200800094098e+01;
                    const double p1 = -1.8591953644342993800e+02;
                    const double p2 = -1.1183429920482737611e+02;
                    const double p3 = -2.2300261666214198472e+01;
                    const double p4 = -1.2441026745835638459e+00;
                    const double p5 = -8.8033303048680751817e-03;

                    const double q0 = 5.7105024128512061905e+03;
                    const double q1 = 1.1951131543434613647e+04;
                    const double q2 = 7.2642780169211018836e+03;
                    const double q3 = 1.4887231232283756582e+03;
                    const double q4 = 9.0593769594993125859e+01;
                    const double q5 = 1.0;

                    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))));

                    rs = P / Q;
                }


                // The following code is:
                //double z = x - 0.25f * Math.PI;
                //value = (Constants.RecipSqrtHalfPI / Math.Sqrt(x)) * (rc * Math.Sin(z) + y * rs * Math.Cos(z));

                // Which can be written as:
                // (double, double) sincos = Math2.SinCos(x,-0.25);
                // value = (Constants.RecipSqrtHalfPI / Math.Sqrt(x)) * (rc * sincos.Item1 + y * rs * sincos.Item2);

                // Or, using trig addition rules, simplified to:
                value = (Constants.RecipSqrtPI / Math.Sqrt(x)) * ((rc + y * rs) * Math.Sin(x) + (-rc + y * rs) * Math.Cos(x));
            }

            return(value);
        }
Пример #11
0
        /// <summary>
        /// Returns the rising factorial:
        /// <para>(x)_{n} = Γ(x+n)/Γ(x)</para>
        /// </summary>
        /// <returns>
        /// <para> if n > 0, x*(x+1)*(x+2)...*(x+(n-1))</para>
        /// <para> if n == 0, 1</para>
        /// <para> if n &lt; 0, 1/((x-1)*(x-2)*...*(x-n))</para>
        /// </returns>
        public static double FactorialRising(double x, int n)
        {
            // This section uses the following notation:
            // falling factorial: (x)_{_n}
            // rising factorial: (x)_{n}

            // Standard eqn for the rising factorial:
            // (x)_{n} = Γ(x+n)/Γ(x)

            double result = double.NaN;

            // (x)_{0} = 1
            if (n == 0)
            {
                return(1);
            }

            if (double.IsNaN(x))
            {
                Policies.ReportDomainError("FactorialRising(x: {0}, n: {1}): Requires x not NaN", x, n);
                return(double.NaN);
            }


            if (n == int.MinValue)
            {
                // avoid -int.MinValue error
                // this will overflow/underflow, but here it is
                if (x > 0 && IsInteger(x) && (x + n) <= 0)
                {
                    Policies.ReportPoleError("FactorialRising(x: {0}, n: {1}): Divide by Zero", x, n);
                    return(double.NaN);
                }

                // (x)_{n} = (x)_{n+1}/(x+n)
                return(FactorialRising(x, n + 1) / (x + n));
            }


            if (x == 0)
            {
                // (0)_{n} = 0, n > 0
                if (n > 0)
                {
                    return(0);
                }

                // (0)_{-n} = (-1)^n / n!
                result = Math2.Factorial(-n);
                if (IsOdd(n))
                {
                    result = -result;
                }
                return(1 / result);
            }

            if (x < 0)
            {
                // For x less than zero, we really have a falling
                // factorial, modulo a possible change of sign.
                // Note: the falling factorial isn't defined for negative n
                // so cover that first

                if (n < -x)
                {
                    // handle (n < 0 || ( n > 0 && n < -x)) directly
                    // (x)_{n} = Γ(1-x)/Γ(1-x-n)
                    result = Math2.TgammaDeltaRatio(1 - x, -n);
                }
                else
                {
                    Debug.Assert(n > 0);
                    result = FactorialFalling(-x, n);
                }

                if (IsOdd(n))
                {
                    result = -result;
                }

                return(result);
            }


            // x > 0

            // standard case: (n > 0 || (n < 0 && x > -n))
            // (x)_{n} = Γ(x+n)/Γ(x)
            if (-x < n)
            {
                return(1 / Math2.TgammaDeltaRatio(x, n));
            }

            Debug.Assert((x + n) <= 0);

            // (x)_{n} = (-1)^n *  Γ(1-x)/Γ(1-x-n)
            if (x < 1)
            {
                result = TgammaDeltaRatio(1 - x, -n);
                if (IsOdd(n))
                {
                    result = -result;
                }
                return(result);
            }

            // x >= 1
            result = FactorialFalling(x - 1, -n);
            if (result == 0)
            {
                if (IsInteger(x))
                {
                    Policies.ReportPoleError("FactorialRising(x: {0}, n: {1}): Divide by Zero", x, n);
                    return(double.NaN);
                }

                return(double.PositiveInfinity);
            }
            return(1 / result);
        }
Пример #12
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);
        }
Пример #13
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);
        }