예제 #1
0
        /// <summary>
        /// Returns Γ(x + 1) - 1 with accuracy improvements in [-0.5, 2]
        /// </summary>
        /// <param name="x">Tgamma1pm1 function argument</param>
        public static double Tgamma1pm1(double x)
        {
            if (double.IsNaN(x))
            {
                Policies.ReportDomainError("Tgamma1pm1(x: {0}): NaN not allowed", x);
                return(double.NaN);
            }
            if (double.IsInfinity(x))
            {
                if (x < 0)
                {
                    Policies.ReportDomainError("Tgamma1pm1(x: {0}): Requires x != -Infinity", x);
                    return(double.NaN);
                }
                return(double.PositiveInfinity);
            }

            double result;

            if (x < -0.5 || x >= 2)
            {
                return(Math2.Tgamma(1 + x) - 1); // Best method is simply to subtract 1 from tgamma:
            }
            if (Math.Abs(x) < GammaSmall.UpperLimit)
            {
                return(GammaSmall.Tgamma1pm1(x));
            }

            if (x < 0)
            {
                // compute exp( log( Γ(x+2)/(1+x) ) ) - 1
                result = Math2.Expm1(-Math2.Log1p(x) + _Lgamma.Rational_1_3(x + 2, x + 1, x));
            }
            else
            {
                // compute exp( log(Γ(x+1)) ) - 1
                result = Math2.Expm1(_Lgamma.Rational_1_3(x + 1, x, x - 1));
            }

            return(result);
        }
예제 #2
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);
        }
예제 #3
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);
        }
예제 #4
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);
        }
예제 #5
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);
        }