Esempio n. 1
0
        /// <summary>
        /// Returns the ratio of gamma functions
        /// <para>TgammaRatio(x,y) = Γ(x)/Γ(y)</para>
        /// </summary>   
        /// <param name="x">The argument for the gamma function in the numerator. Requires x &gt; 0</param>
        /// <param name="y">The argument for the gamma function in the denominator. Requires y &gt; 0</param>
        public static double TgammaRatio(double x, double y)
        {
            if (!(x > 0) || !(y > 0)) {
                Policies.ReportDomainError("TgammaRatio(x: {0}, y: {1}): Requires x > 0, y > 0; negative arguments not implemented", x, y);
                return double.NaN;
            }
            if (double.IsInfinity(x)) {
                if (double.IsInfinity(y)) {
                    Policies.ReportDomainError("TgammaRatio(x: {0}, y: {1}): Infinity/Infinity", x, y);
                    return double.NaN;
                }
                return double.PositiveInfinity;
            }
            if (double.IsInfinity(y)) 
                return 0.0;


            // if we're not going to overflow, take the direct approach
            if (x <= DoubleLimits.MaxGamma && y <= DoubleLimits.MaxGamma) {
                // need to protect against denormalized numbers, so
                // use the first term of the power series: Gamma[x] ~= 1/x - Euler ...
                const double LowerLimit = DoubleLimits.MachineEpsilon / Constants.EulerMascheroni;
                if (x < LowerLimit) {
                    if (y < LowerLimit)
                        return y / x;

                    // careful: Tgamma(y) can be < 1, x can be denorm or Min Normal
                    double result = 1 / (x * Tgamma(y));
                    return result;
                }
                if (y < LowerLimit) {
                    double result = y * Tgamma(x);
                    return result;
                }

                return Tgamma(x) / Tgamma(y);
            }

            // Handle the case where we have one large argument and one small argument

            Debug.Assert(x > DoubleLimits.MaxGamma || y > DoubleLimits.MaxGamma);

            if (x <= DoubleLimits.MaxGamma) {
                // At max values
                // min denorm:  Γ(2^-1074)/Γ(170) ~= 4.7e18
                // min norm:    Γ(2^-1022)/Γ(170) ~= 1052.74
                //              Γ(1)/Γ(170)       ~= 2.3e-305

                // use duplication
                // Γ(2z) = 2^(2z-1)/Sqrt(Pi) * Γ(z) * Γ(z+1/2) 
                if ( y <= 2 * DoubleLimits.MaxGamma - 1) {
                    var t1 = Tgamma(y / 2);
                    var t2 = Math.Pow(2, y) / (2 * Constants.SqrtPI);
                    var t3 = Tgamma(y / 2 + 0.5);
                    if (x < DoubleLimits.MachineEpsilon/Constants.EulerMascheroni ) {
                        return 1.0/(x * t1 * t2 * t3);
                    } else {
                        return Tgamma(x)/ t1 / t2 / t3;
                    }
                }

                // this will underflow
                return Math.Exp(Lgamma(x) - Lgamma(y));

            }
            if (y <= DoubleLimits.MaxGamma) {
                // At max values
                // min denorm:  Γ(170)/Γ(2^-1074)    ~=    2.1e-19
                // min norm     Γ(170)/Γ(2^-1022)    ~=    9.5e-4
                //              Γ(170)/Γ(1)          ~=    4.3e304

                // use duplication
                // Γ(2z) = 2^(2z-1)/Sqrt(Pi) * Γ(z) * Γ(z+1/2) 
                if (y <= 2 * DoubleLimits.MaxGamma - 1) {
                    var t1 = Tgamma(x / 2);
                    var t2 = Math.Pow(2, x) / (2 * Constants.SqrtPI);
                    var t3 = Tgamma(x / 2 + 0.5);
                    if (y < DoubleLimits.MachineEpsilon / Constants.EulerMascheroni) {
                        return (y * t1 * t2 * t3);
                    } else {
                        return t1/Tgamma(y) * t2 * t3;
                    }
                }

                // this will overflow
                return Math.Exp(Lgamma(x) - Lgamma(y));

            }

            // Regular case, x and y both large and similar in magnitude:

            return TgammaDeltaRatio(x, y - x);

        }
Esempio n. 2
0
        /// <summary>
        /// Returns the value of the first derivative to the Airy Ai function at <paramref name="x"/>
        /// </summary>
        /// <param name="x">The function argument</param>
        /// <returns></returns>
        public static double AiryAiPrime(double x)
        {
            if (double.IsNaN(x))
            {
                Policies.ReportDomainError("AiryAiPrime(x: {0}): NaN not allowed", x);
                return(double.NaN);
            }
            if (double.IsInfinity(x))
            {
                if (x > 0)
                {
                    return(0);
                }

                Policies.ReportDomainError("AiryAiPrime(x: {0}): Requires x != -Infinity", x);
                return(double.NaN);
            }

            double absX = Math.Abs(x);

            if (x >= -3 && x <= 2)
            {
                // Use small series. See:
                // http://functions.wolfram.com/Bessel-TypeFunctions/AiryAiPrime/26/01/01/
                // http://dlmf.nist.gov/9.4

                double z  = x * x * x / 9;
                double s1 = x * x * (Constants.AiryAi0 / 2) * HypergeometricSeries.Sum0F1(5 / 3.0, z);
                double s2 = Constants.AiryAiPrime0 * HypergeometricSeries.Sum0F1(1 / 3.0, z);

                return(s1 + s2);
            }

            const double v    = 2.0 / 3;
            double       zeta = 2 * absX * Math.Sqrt(absX) / 3;

            if (x < 0)
            {
                if (x < -32)
                {
                    return(AiryAsym.AiBiPrimeNeg(x).Ai);
                }

                // The following is
                //double j1 = Math2.BesselJ(v, zeta);
                //double j2 = Math2.BesselJ(-v, zeta);
                //double aip = -x * (j1 - j2) / 3;

                var(J, Y) = _Bessel.JY(v, zeta, true, true);
                double s   = 0.5 * (J + ((double)Y) / Constants.Sqrt3);
                double aip = -x * s;
                return(aip);
            }
            else
            {
                if (x >= 16)
                {
                    return(AiryAsym.AiPrime(x));
                }

                double aip = -Math2.BesselK(v, zeta) * x / (Constants.Sqrt3 * Math.PI);
                return(aip);
            }
        }
Esempio n. 3
0
        /// <summary>
        /// Evaluates a rational polynomial with the given argument
        /// <para>(num[0] + num[1]*x + num[2]*x^2 ...)/(denom[0] + denom[1]*x + denom[2]*x^2 ...)</para>
        /// </summary>
        /// <param name="num">The numerator coefficient array</param>
        /// <param name="denom">The denominator coefficient array</param>
        /// <param name="x">The argument</param>
        /// <remarks>
        /// When numerator and denominator are equal in order there
        /// are some tricks we can use to prevent overflow that might otherwise
        /// occur in direct polynomial evaluation, for example if <paramref name="x"/> is large
        /// </remarks>
        public static double EvalRational(double[] num, double[] denom, double x)
        {
            // Uses Horner's Method
            // see http://en.wikipedia.org/wiki/Horner_scheme

            if (num == null)
            {
                Policies.ReportDomainError("Requires num != null");
                return(double.NaN);
            }
            if (denom == null)
            {
                Policies.ReportDomainError("Requires denom != null");
                return(double.NaN);
            }
            if (denom.Length == 0)
            {
                Policies.ReportDomainError("Divide by zero. Requires denom != 0");
                return(double.NaN);
            }
            if (num.Length == 0)
            {
                return(0);
            }

            if (num.Length != denom.Length)
            {
                return(Polynomial.Eval(num, x) / Polynomial.Eval(denom, x));
            }


            if (Math.Abs(x) <= 1)
            {
                double z = x;

                int    n      = num.Length - 1;
                double numX   = num[n];
                double denomX = denom[n];
                for (int i = n - 1; i >= 0; i--)
                {
                    numX   = numX * z + num[i];
                    denomX = denomX * z + denom[i];
                }
                return(numX / denomX);
            }
            else
            {
                // reverse the order
                // (1 + 1/x) / (2 + 2/x) = (x+1)/(2x+2)
                // can only do this when num.Length == denom.Length

                double z      = 1 / x;
                double numX   = num[0];
                double denomX = denom[0];
                for (int i = 1; i < num.Length; ++i)
                {
                    numX   = numX * z + num[i];
                    denomX = denomX * z + denom[i];
                }
                return(numX / denomX);
            }
        }
Esempio n. 4
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);
        }
Esempio n. 5
0
        // Carlson's elliptic integral of the second kind
        // Carlson, Numerische Mathematik, vol 33, 1 (1979)


        /// <summary>
        /// Carlson's symmetric form of elliptical integrals
        /// <para>R<sub>D</sub>(x,y,z) = R<sub>J</sub>(x,y,z,z) = (3/2) * ∫ dt/((t+z)*Sqrt((t+x)(t+y)(t+z))), t={0,∞}</para>
        /// </summary>
        /// <param name="x">Argument. Requires x >= 0</param>
        /// <param name="y">Argument. Requires y >= 0</param>
        /// <param name="z">Argument. Requires z > 0</param>
        public static double EllintRD(double x, double y, double z)
        {
            if ((!(x >= 0) || double.IsInfinity(x)) ||
                (!(y >= 0) || double.IsInfinity(y)) ||
                (!(z > 0) || double.IsInfinity(z)) ||
                (x + y == 0))
            {
                Policies.ReportDomainError("EllintRD(x: {0}, y: {1}, z: {2}) requires: finite x,y >= 0; finite z > 0; at most one argument == 0", x, y, z);
                return(double.NaN);
            }

            // Special cases from http://dlmf.nist.gov/19.20#iv

            if (x > y)
            {
                Utility.Swap(ref x, ref y);
            }
            if (x == 0)
            {
                if (y == 0)
                {
                    return(double.PositiveInfinity); // RD(0, 0, z)
                }
                if (y == z)
                {
                    return(3 * (Math.PI / 4) / (y * Math.Sqrt(y))); // RD(0, y, y)
                }
                //
                // Special handling for common case, from
                // Numerical Computation of Real or Complex Elliptic Integrals, eq.47
                //
                double xn      = Math.Sqrt(y);
                double yn      = Math.Sqrt(z);
                double x0      = xn;
                double y0      = yn;
                double sum     = 0;
                double sum_pow = 0.25;

                double c = xn - yn;
                while (Math.Abs(c) >= 2.7 * DoubleLimits.RootMachineEpsilon._2 * xn)
                {
                    double t = Math.Sqrt(xn * yn);
                    xn = (xn + yn) / 2;
                    yn = t;

                    c        = xn - yn;
                    sum_pow *= 2;
                    sum     += sum_pow * c * c;
                }
                double RF = Math.PI / (xn + yn);
                //
                // This following calculation suffers from serious cancellation when y ~ z
                // unless we combine terms.  We have:
                //
                // ( ((x0 + y0)/2)^2 - z ) / (z(y-z))
                //
                // Substituting y = x0^2 and z = y0^2 and simplifying we get the following:
                //
                double pt = (x0 + 3 * y0) / (4 * z * (x0 + y0));
                //
                // Since we've moved the demoninator from eq.47 inside the expression, we
                // need to also scale "sum" by the same value:
                //
                pt -= sum / (z * (y - z));
                return(pt * RF * 3);
            }

            if (x == y && x == z)
            {
                return(1 / (x * Math.Sqrt(x)));
            }


            // Taylor series upper limit
            const double tolerance = 0.002049052858245406612358440409548690638254492642397575328974;

            Debug.Assert(Math2.AreNearUlps(Math.Pow(DoubleLimits.MachineEpsilon / 3, 1.0 / 6.0), tolerance, 5), "Incorrect constant");

            // duplication
            double sigma  = 0;
            double factor = 1;
            int    k      = 1;

            for (; k < Policies.MaxSeriesIterations; k++)
            {
                double u = (x + y + z + z + z) / 5;
                double X = (u - x);
                double Y = (u - y);
                double Z = (u - z);

                // Termination condition:
                double utol = u * tolerance;
                if (Math.Abs(X) < utol && Math.Abs(Y) < utol && Math.Abs(Z) < utol)
                {
                    X /= u;
                    Y /= u;
                    Z /= u;

                    // Taylor series expansion to the 5th order
                    double EA    = X * Y;
                    double EB    = Z * Z;
                    double EC    = EA - EB;
                    double ED    = EA - 6 * EB;
                    double EE    = ED + EC + EC;
                    double S1    = ED * (ED * (9.0 / 88) - Z * EE * (9.0 / 52) - 3.0 / 14);
                    double S2    = Z * (EE / 6 + Z * (-EC * (9.0 / 22) + Z * EA * (3.0 / 26)));
                    double value = 3 * sigma + factor * (1 + S1 + S2) / (u * Math.Sqrt(u));

                    return(value);
                }

                double sx     = Math.Sqrt(x);
                double sy     = Math.Sqrt(y);
                double sz     = Math.Sqrt(z);
                double lambda = sy * (sx + sz) + sz * sx;
                sigma  += factor / (sz * (z + lambda));
                factor /= 4;
                x       = (x + lambda) / 4;
                y       = (y + lambda) / 4;
                z       = (z + lambda) / 4;
            }

            Policies.ReportDomainError("EllintRD(x: {0}, y: {1}, z: {2}) No convergence after {3} iterations", x, y, z, k);
            return(double.NaN);
        }
Esempio n. 6
0
        /// <summary>
        /// Carlson's symmetric form of elliptical integrals
        /// <para>R<sub>J</sub>(x,y,z,p) = (3/2) * ∫ dt/((t+p)*Sqrt((t+x)(t+y)(t+z))), t={0,∞}</para>
        /// </summary>
        /// <param name="x">Argument. Requires finite x >= 0</param>
        /// <param name="y">Argument. Requires finite y >= 0</param>
        /// <param name="z">Argument. Requires finite z >= 0</param>
        /// <param name="p">Argument. Requires finite p != 0</param>
        public static double EllintRJ(double x, double y, double z, double p)
        {
            if ((!(x >= 0) || double.IsInfinity(x)) ||
                (!(y >= 0) || double.IsInfinity(y)) ||
                (!(z >= 0) || double.IsInfinity(z)) ||
                (p == 0 || double.IsInfinity(p) || double.IsNaN(p)) ||
                (x + y == 0 || y + z == 0 || z + x == 0))
            {
                Policies.ReportDomainError("EllintRJ(x: {0}, y: {1}, z: {2}, p: {3}) requires: finite x,y,z >= 0; finite p != 0; at most one of x,y,z == 0", x, y, z, p);
                return(double.NaN);
            }

            // See Carlson, Numerische Mathematik, vol 33, 1 (1979)

            double value;

            // error scales as the 6th power of tolerance
            const double tolerance = 0.002049052858245406612358440409548690638254492642397575328974;

            Debug.Assert(Math2.AreNearUlps(Math.Pow(DoubleLimits.MachineEpsilon / 3, 1.0 / 6.0), tolerance, 5), "Incorrect constant");

            // reorder x,y,z such that x <= y <= z
            // so that 0 shows up in x first
            // and so that the p<0 case below doesn't suffer cancellation errors
            if (x > y)
            {
                Utility.Swap(ref x, ref y);
            }
            if (y > z)
            {
                Utility.Swap(ref y, ref z);
            }
            if (x > y)
            {
                Utility.Swap(ref x, ref y);
            }

            // for p < 0, the integral is singular, return Cauchy principal value
            if (p < 0)
            {
                // We must ensure that (z - y) * (y - x) is positive.
                Debug.Assert(x <= y && y <= z, "x, y, z must be in ascending order");

                double q   = -p;
                double pmy = (z - y) * (y - x) / (y + q); // p - y

                Debug.Assert(pmy >= 0);

                double pAdj = pmy + y;
                value  = pmy * Math2.EllintRJ(x, y, z, pAdj);
                value -= 3 * Math2.EllintRF(x, y, z);
                value += 3 * Math.Sqrt((x * y * z) / (x * z + pAdj * q)) * Math2.EllintRC(x * z + pAdj * q, pAdj * q);
                value /= (y + q);
                return(value);
            }

            if (x == 0)
            {
                if (y == 0) //RJ(0, 0, z, p)
                {
                    return(double.PositiveInfinity);
                }
                if (y == z) //RJ(0, y, y, p)
                {
                    return(3 * (Math.PI / 2) / (y * Math.Sqrt(p) + p * Math.Sqrt(y)));
                }
            }

            if (p == z)
            {
                if (z == y)
                {
                    if (x == y)
                    {
                        return(1 / (x * Math.Sqrt(x))); // RJ(x, x, x, x)
                    }
                    return(EllintRD(x, y, y));          // RJ(x, y, y, y)
                }
                return(EllintRD(x, y, z));              // RJ(x, y, z, z)
            }

            if (x == y && y == z)
            {
                return(EllintRD(p, p, x)); // RJ(x, x, x, p)
            }
            // duplication
            double sigma  = 0;
            double factor = 1;
            int    k      = 1;

            for (; k < Policies.MaxSeriesIterations; k++)
            {
                double u = (x + y + z + p + p) / 5;
                double X = (u - x);
                double Y = (u - y);
                double Z = (u - z);
                double P = (u - p);

                // Termination condition:
                double utol = u * tolerance;
                if (Math.Abs(X) < utol && Math.Abs(Y) < utol && Math.Abs(Z) < utol && Math.Abs(P) < utol)
                {
                    X /= u;
                    Y /= u;
                    Z /= u;
                    P /= u;

                    // Taylor series expansion to the 5th order
                    double EA = X * Y + Y * Z + Z * X;
                    double EB = X * Y * Z;
                    double EC = P * P;
                    double E2 = EA - 3 * EC;
                    double E3 = EB + 2 * P * (EA - EC);
                    double S1 = 1 + E2 * (E2 * (9.0 / 88) - E3 * (9.0 / 52) - 3.0 / 14);
                    double S2 = EB * (1.0 / 6 + P * (-6.0 / 22 + P * (3.0 / 26)));
                    double S3 = P * ((EA - EC) / 3 - P * EA * (3.0 / 22));
                    value = 3 * sigma + factor * (S1 + S2 + S3) / (u * Math.Sqrt(u));

                    return(value);
                }

                double sx = Math.Sqrt(x);
                double sy = Math.Sqrt(y);
                double sz = Math.Sqrt(z);

                double lambda = sy * (sx + sz) + sz * sx;
                double alpha  = p * (sx + sy + sz) + sx * sy * sz;
                alpha *= alpha;
                double beta = p * (p + lambda) * (p + lambda);
                sigma  += factor * Math2.EllintRC(alpha, beta);
                factor /= 4;
                x       = (x + lambda) / 4;
                y       = (y + lambda) / 4;
                z       = (z + lambda) / 4;
                p       = (p + lambda) / 4;
            }

            Policies.ReportDomainError("EllintRJ(x: {0}, y: {1}, z: {2}, p: {3}) No convergence after {4} iterations", x, y, z, p, k);
            return(double.NaN);
        }
Esempio n. 7
0
        /// <summary>
        /// Simultaneously compute I{v}(x) and K{v}(x)
        /// </summary>
        /// <param name="v"></param>
        /// <param name="x"></param>
        /// <param name="needI"></param>
        /// <param name="needK"></param>
        /// <returns>A tuple of (I, K). If needI == false or needK == false, I = NaN or K = NaN  respectively</returns>
        public static (double I, double K) IK(double v, double x, bool needI, bool needK)
        {
            Debug.Assert(needI || needK, "NeedI and NeedK cannot both be false");

            // set initial values for parameters
            double I = double.NaN;
            double K = double.NaN;

            // check v first so that there are no integer throws later
            if (Math.Abs(v) > int.MaxValue)
            {
                Policies.ReportNotImplementedError("BesselIK(v: {0}, x: {1}): Requires |v| <= {2}", v, x, int.MaxValue);
                return(I, K);
            }


            if (v < 0)
            {
                v = -v;

                // for integer orders only, we can use the following identities:
                //  I{-n}(x) = I{n}(v)
                //  K{-n}(x) = K{n}(v)
                // for any v, use reflection rule:
                //  I{-v}(x) = I{v}(x) + (2/PI) * sin(pi*v)*K{v}(x)
                //  K{-v}(x) = K{v}(x)

                if (needI && !Math2.IsInteger(v))
                {
                    var(IPos, KPos) = IK(v, x, true, true);

                    I = IPos + (2 / Math.PI) * Math2.SinPI(v) * KPos;
                    if (needK)
                    {
                        K = KPos;
                    }
                    return(I, K);
                }
            }

            if (x < 0)
            {
                Policies.ReportDomainError("BesselIK(v: {0}, x: {1}): Complex result not supported. Requires x >= 0", v, x);
                return(I, K);
            }

            // both x and v are non-negative from here
            Debug.Assert(x >= 0 && v >= 0);

            if (x == 0)
            {
                if (needI)
                {
                    I = (v == 0) ? 1.0 : 0.0;
                }
                if (needK)
                {
                    K = double.PositiveInfinity;
                }
                return(I, K);
            }

            if (needI && (x < 2 || 3 * (v + 1) > x * x))
            {
                I     = I_SmallArg(v, x);
                needI = false;
                if (!needK)
                {
                    return(I, K);
                }
            }

            // Hankel is fast, and reasonably accurate.
            // at x == 32, it will converge in about 15 iterations
            if (x >= HankelAsym.IKMinX(v))
            {
                if (needK)
                {
                    K = HankelAsym.K(v, x);
                }
                if (needI)
                {
                    I = HankelAsym.I(v, x);
                }
                return(I, K);
            }

            // the uniform expansion is here as a last resort
            // to limit the number of recurrences, but it is less accurate.
            if (UniformAsym.IsIKAvailable(v, x))
            {
                if (needK)
                {
                    K = UniformAsym.K(v, x);
                }
                if (needI)
                {
                    I = UniformAsym.I(v, x);
                }
                return(I, K);
            }


            // K{v}(x) and K{v+1}(x), binary scaled
            var(Kv, Kv1, binaryScale) = K_CF(v, x);
            if (needK)
            {
                K = Math2.Ldexp(Kv, binaryScale);
            }
            if (needI)
            {
                // use the Wronskian relationship

                // Since CF1 is O(x) for x > v, try to weed out obvious overflows
                // Note: I{v+1}(x)/I{v}(x) is in [0, 1].
                // I{0}(713. ...) == MaxDouble
                const double I0MaxX = 713;
                if (x > I0MaxX && x > v)
                {
                    I = Math2.Ldexp(1.0 / (x * (Kv + Kv1)), -binaryScale);
                    if (double.IsInfinity(I))
                    {
                        return(I, K);
                    }
                }

                double W  = 1 / x;
                double fv = I_CF1(v, x);
                I = Math2.Ldexp(W / (Kv * fv + Kv1), -binaryScale);
            }


            return(I, K);
        }
Esempio n. 8
0
        /// <summary>
        /// Returns K{0}(x), or if expScaled == true, e^x * K{0}(x)
        /// </summary>
        /// <param name="x"></param>
        /// <param name="expScaled"></param>
        /// <returns></returns>
        public static double K0(double x, bool expScaled = false)
        {
            if (!(x >= 0))
            {
                Policies.ReportDomainError("BesselK(v: 0, x: {0}) : Requires x >= 0; complex number result not supported", x);
                return(double.NaN);
            }
            if (x == 0)
            {
                return(double.PositiveInfinity);
            }

            // Modified Bessel function of the second kind of order zero
            // minimax rational approximations on intervals, see
            // Russon and Blair, Chalk River Report AECL-3461, 1969,
            // as revised by Pavel Holoborodko in "Rational Approximations
            // for the Modified Bessel Function of the Second Kind - K0(x)
            // for Computations with Double Precision", see
            // http://www.advanpix.com/2015/11/25/rational-approximations-for-the-modified-bessel-function-of-the-second-kind-k0-for-computations-with-double-precision/
            //
            // The actual coefficients used are boost's (derived by JM)
            // since we extend to both greater and lesser precision than the
            // references above.  We can also improve performance WRT to
            // Holoborodko without loss of precision.

            double value;

            if (x <= 1)
            {
                // Maximum Deviation Found:                     6.077e-17
                // Expected Error Term : -6.077e-17
                // Maximum Relative Change in Control Points : 7.797e-02
                // Max Error found at double precision = Poly : 1.003156e-16
                const double Y  = 1.137250900268554688;
                const double p0 = -1.372509002685546267e-01;
                const double p1 = 2.574916117833312855e-01;
                const double p2 = 1.395474602146869316e-02;
                const double p3 = 5.445476986653926759e-04;
                const double p4 = 7.125159422136622118e-06;

                const double q0 = 1.000000000000000000e+00;
                const double q1 = -5.458333438017788530e-02;
                const double q2 = 1.291052816975251298e-03;
                const double q3 = -1.367653946978586591e-05;

                var    a  = x * x / 4;
                double P  = p0 + a * (p1 + a * (p2 + a * (p3 + a * p4)));
                double Q  = q0 + a * (q1 + a * (q2 + a * q3));
                var    r1 = (P / Q + Y) * a + 1;

                // Maximum Deviation Found:                     3.429e-18
                // Expected Error Term : 3.392e-18
                // Maximum Relative Change in Control Points : 2.041e-02
                // Max Error found at double precision = Poly : 2.513112e-16

                const double c0 = 1.159315156584124484e-01;
                const double c1 = 2.789828789146031732e-01;
                const double c2 = 2.524892993216121934e-02;
                const double c3 = 8.460350907213637784e-04;
                const double c4 = 1.491471924309617534e-05;
                const double c5 = 1.627106892422088488e-07;
                const double c6 = 1.208266102392756055e-09;
                const double c7 = 6.611686391749704310e-12;

                a = x * x;
                var r2 = c0 + a * (c1 + a * (c2 + a * (c3 + a * (c4 + a * (c5 + a * (c6 + a * c7))))));

                value = r2 - Math.Log(x) * r1;
                if (expScaled)
                {
                    value *= Math.Exp(x);
                }
            }
            else
            {
                // Maximum Deviation Found:                     4.316e-17
                // Expected Error Term : 9.570e-18
                // Maximum Relative Change in Control Points : 2.757e-01
                // Max Error found at double precision = Poly : 1.001560e-16

                const double Y = 1;

                const double p0 = 2.533141373155002416e-01;
                const double p1 = 3.628342133984595192e+00;
                const double p2 = 1.868441889406606057e+01;
                const double p3 = 4.306243981063412784e+01;
                const double p4 = 4.424116209627428189e+01;
                const double p5 = 1.562095339356220468e+01;
                const double p6 = -1.810138978229410898e+00;
                const double p7 = -1.414237994269995877e+00;
                const double p8 = -9.369168119754924625e-02;

                const double q0 = 1.000000000000000000e+00;
                const double q1 = 1.494194694879908328e+01;
                const double q2 = 8.265296455388554217e+01;
                const double q3 = 2.162779506621866970e+02;
                const double q4 = 2.845145155184222157e+02;
                const double q5 = 1.851714491916334995e+02;
                const double q6 = 5.486540717439723515e+01;
                const double q7 = 6.118075837628957015e+00;
                const double q8 = 1.586261269326235053e-01;

                var    a = 1 / x;
                double P = p0 + a * (p1 + a * (p2 + a * (p3 + a * (p4 + a * (p5 + a * (p6 + a * (p7 + a * p8)))))));
                double Q = q0 + a * (q1 + a * (q2 + a * (q3 + a * (q4 + a * (q5 + a * (q6 + a * (q7 + a * q8)))))));
                value = (P / Q + Y);

                value /= Math.Sqrt(x);
                if (!expScaled)
                {
                    // The following is: value * Exp(-x)
                    // when x is large, don't underflow too soon
                    var ex = Math.Exp(-x / 2);
                    value = (value * ex) * ex;
                }
            }

            return(value);
        }
Esempio n. 9
0
        /// <summary>
        /// Returns K{n}(x) for integer order
        /// </summary>
        /// <param name="n"></param>
        /// <param name="x"></param>
        /// <returns></returns>
        public static double KN(int n, double x)
        {
            if (x < 0)
            {
                Policies.ReportDomainError("BesselK(v: {0}, x: {1}): Requires x >= 0 for real result", n, x);
                return(double.NaN);
            }
            if (x == 0)
            {
                return(double.PositiveInfinity);
            }


            // even function
            // K{-n}(z) = K{n}(z)
            if (n < 0)
            {
                n = -n;
            }

            if (n == 0)
            {
                return(K0(x));
            }

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

            double v = n;

            // Hankel is fast, and reasonably accurate, saving us from many recurrences.
            if (x >= HankelAsym.IKMinX(v))
            {
                return(HankelAsym.K(v, x));
            }

            // the uniform expansion is here as a last resort
            // to limit the number of recurrences, but it is less accurate.
            if (UniformAsym.IsIKAvailable(v, x))
            {
                return(UniformAsym.K(v, x));
            }

            // Since K{v}(x) has a (e^-x)/sqrt(x) multiplier
            // using recurrence can underflow too quickly for large x,
            // so, use a scaled version
            double result;

            if (x > 1)
            {
                double prev    = K0(x, true);
                double current = K1(x, true);

                // for large v and x this number can get very large
                // maximum observed K(1000,10) = 2^6211

                var(Kv, _, binaryScale) = Recurrence.ForwardK_B(1, x, n - 1, current, prev);


                // Compute: value * 2^(binaryScale) * e^-x

                if (x < -DoubleX.MinLogValue)
                {
                    DoubleX exs = DoubleX.Ldexp(DoubleX.Exp(-x), binaryScale);
                    result = Math2.Ldexp(Kv * exs.Mantissa, exs.Exponent);
                }
                else
                {
                    result = Math.Exp(-x + Math.Log(Kv) + binaryScale * Constants.Ln2);
                }
            }
            else
            {
                double prev    = K0(x);
                double current = K1(x);

                result = Recurrence.ForwardK(1, x, n - 1, current, prev).Kvpn;
            }


            return(result);
        }
Esempio n. 10
0
        /// <summary>
        /// Carlson's symmetric form of elliptical integrals
        /// <para>RG = 1/4 ∫ sqrt((t+x)(t+y)(t+z))*(x/(t+x)+y/(t+y)+z/(t+z))*t*dt t={0,∞}</para>
        /// <para>RG = 1/(4π) ∫∫ sqrt(x*sin^2(θ)*cos^2(φ)+y*sin^2(θ)*sin^2(φ)+z*cos^2(θ))*sin(θ) dθdφ θ={0,π} φ={0,2π}</para>
        /// </summary>
        /// <param name="x">Argument. Requires x >= 0</param>
        /// <param name="y">Argument. Requires y >= 0</param>
        /// <param name="z">Argument. Requires z >= 0</param>
        /// <returns></returns>
        /// <remarks>
        /// Numerical Computation of Real or Complex Elliptic Integrals, B.C. Carlson, 6 Sep 1994
        /// </remarks>
        /// <see href="http://arxiv.org/abs/math/9409227v1"/>
        public static double EllintRG(double x, double y, double z)
        {
            if (!(x >= 0) && !(y >= 0) && !(z >= 0))
            {
                Policies.ReportDomainError("EllintRG(x: {0}, y: {1}, z: {2}) requires finite x >= 0, y >= 0, z >= 0", x, y, z);
                return(double.NaN);
            }

            // reorder x,y,z such that x <= y <= z
            if (x > y)
            {
                Utility.Swap(ref x, ref y);
            }
            if (y > z)
            {
                Utility.Swap(ref y, ref z);
            }
            if (x > y)
            {
                Utility.Swap(ref x, ref y);
            }

            if (x == 0)
            {
                if (y == 0)
                {
                    return(Math.Sqrt(z) / 2); // RG(0, 0, z)
                }
                if (y == z)
                {
                    return((Math.PI / 4) * Math.Sqrt(y)); // RG(0, y, y)
                }
                double xn      = Math.Sqrt(z);
                double yn      = Math.Sqrt(y);
                double a       = (xn + yn) / 2;
                double sum     = 0;
                double sum_pow = 0.25;

                double c = xn - yn;
                while (Math.Abs(c) >= 2.7 * DoubleLimits.RootMachineEpsilon._2 * xn)
                {
                    double t = Math.Sqrt(xn * yn);
                    xn = (xn + yn) / 2;
                    yn = t;

                    c        = xn - yn;
                    sum_pow *= 2;
                    sum     += sum_pow * (c * c);
                }
                return((a * a - sum) * ((Math.PI / 2) / (xn + yn)));
            }

            if (x == y)
            {
                if (x == z)
                {
                    return(Math.Sqrt(x));                        // RG(x, x, x)
                }
                return((x * EllintRC(z, x) + Math.Sqrt(z)) / 2); //RG(x, x, z)
            }

            if (y == z)
            {
                return((y * EllintRC(x, y) + Math.Sqrt(x)) / 2); //RG(x, y, y)
            }
            // Swap y and z to avoid cancellation error in the equation:
            // We want the multiplier (x - z)(y - z) < 0 so that the term is additive.
            // So let x < z and let y > z

            Utility.Swap(ref y, ref z);
            return((z * EllintRF(x, y, z) - (x - z) * (y - z) * EllintRD(x, y, z) / 3 + Math.Sqrt(x * y / z)) / 2);
        }
Esempio n. 11
0
        /// <summary>
        /// Returns K{1}(x), or if expScaled == true, e^x * K{1}(x)
        /// </summary>
        /// <param name="x"></param>
        /// <param name="expScaled"></param>
        /// <returns></returns>
        public static double K1(double x, bool expScaled = false)
        {
            if (x < 0)
            {
                Policies.ReportDomainError("BesselK(v: 1, x: {0}): Requires x >= 0; complex number result not supported", x);
                return(double.NaN);
            }
            if (x == 0)
            {
                return(double.PositiveInfinity);
            }

            // Modified Bessel function of the second kind of order zero
            // minimax rational approximations on intervals, see
            // Russon and Blair, Chalk River Report AECL-3461, 1969,
            // as revised by Pavel Holoborodko in "Rational Approximations
            // for the Modified Bessel Function of the Second Kind - K0(x)
            // for Computations with Double Precision", see
            // http://www.advanpix.com/2016/01/05/rational-approximations-for-the-modified-bessel-function-of-the-second-kind-k1-for-computations-with-double-precision/
            //
            // The actual coefficients used are boost's (derived by JM)
            // since we extend to both greater and lesser precision than the
            // references above.  We can also improve performance WRT to
            // Holoborodko without loss of precision.

            double value;

            if (x <= 1)
            {
                double r1, r2;
                {
                    // Maximum Deviation Found:                     1.922e-17
                    // Expected Error Term : 1.921e-17
                    // Maximum Relative Change in Control Points : 5.287e-03
                    // Max Error found at double precision = Poly : 2.004747e-17
                    const double Y  = 8.69547128677368164e-02;
                    const double p0 = -3.62137953440350228e-03;
                    const double p1 = 7.11842087490330300e-03;
                    const double p2 = 1.00302560256614306e-05;
                    const double p3 = 1.77231085381040811e-06;


                    const double q0 = 1.00000000000000000e+00;
                    const double q1 = -4.80414794429043831e-02;
                    const double q2 = 9.85972641934416525e-04;
                    const double q3 = -8.91196859397070326e-06;

                    var    a = x * x / 4;
                    double P = p0 + a * (p1 + a * (p2 + a * p3));
                    double Q = q0 + a * (q1 + a * (q2 + a * q3));

                    r1 = (P / Q + Y);
                    r1 = (1 + a * (0.5 + r1 * a)) * (0.5 * x);
                }
                {
                    // Maximum Deviation Found:                     4.053e-17
                    // Expected Error Term : -4.053e-17
                    // Maximum Relative Change in Control Points : 3.103e-04
                    // Max Error found at double precision = Poly : 1.246698e-16

                    const double p0 = -3.07965757829206184e-01;
                    const double p1 = -7.80929703673074907e-02;
                    const double p2 = -2.70619343754051620e-03;
                    const double p3 = -2.49549522229072008e-05;

                    const double q0 = 1.00000000000000000e+00;
                    const double q1 = -2.36316836412163098e-02;
                    const double q2 = 2.64524577525962719e-04;
                    const double q3 = -1.49749618004162787e-06;

                    var    a = x * x;
                    double P = p0 + a * (p1 + a * (p2 + a * p3));
                    double Q = q0 + a * (q1 + a * (q2 + a * q3));
                    r2 = P / Q;
                }

                value = r2 * x + 1 / x + Math.Log(x) * r1;
                if (expScaled)
                {
                    value *= Math.Exp(x);
                }
            }
            else
            {
                // Maximum Deviation Found:                     8.883e-17
                // Expected Error Term : -1.641e-17
                // Maximum Relative Change in Control Points : 2.786e-01
                // Max Error found at double precision = Poly : 1.258798e-16

                const double Y = 1.45034217834472656;

                const double p0 = -1.97028041029226295e-01;
                const double p1 = -2.32408961548087617e+00;
                const double p2 = -7.98269784507699938e+00;
                const double p3 = -2.39968410774221632e+00;
                const double p4 = 3.28314043780858713e+01;
                const double p5 = 5.67713761158496058e+01;
                const double p6 = 3.30907788466509823e+01;
                const double p7 = 6.62582288933739787e+00;
                const double p8 = 3.08851840645286691e-01;


                const double q0 = 1.00000000000000000e+00;
                const double q1 = 1.41811409298826118e+01;
                const double q2 = 7.35979466317556420e+01;
                const double q3 = 1.77821793937080859e+02;
                const double q4 = 2.11014501598705982e+02;
                const double q5 = 1.19425262951064454e+02;
                const double q6 = 2.88448064302447607e+01;
                const double q7 = 2.27912927104139732e+00;
                const double q8 = 2.50358186953478678e-02;

                var    a = 1 / x;
                double P = p0 + a * (p1 + a * (p2 + a * (p3 + a * (p4 + a * (p5 + a * (p6 + a * (p7 + a * p8)))))));
                double Q = q0 + a * (q1 + a * (q2 + a * (q3 + a * (q4 + a * (q5 + a * (q6 + a * (q7 + a * q8)))))));
                value  = (P / Q + Y);
                value /= Math.Sqrt(x);

                if (!expScaled)
                {
                    // The following is: value * Exp(-x)
                    // when x is large, don't underflow too soon
                    var ex = Math.Exp(-x / 2);
                    value = (value * ex) * ex;
                }
            }

            return(value);
        }
Esempio n. 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);
        }
Esempio n. 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);
        }
Esempio n. 14
0
        /// <summary>
        /// Returns the ratio of gamma functions
        /// <para>TgammaDeltaRatio(x,delta) = Γ(x)/Γ(x+delta)</para>
        /// </summary>
        /// <param name="x">The argument for the gamma function in the numerator. Requires x &gt; 0</param>
        /// <param name="delta">The offset for the denominator. Requires x + delta &gt; 0</param>
        public static double TgammaDeltaRatio(double x, double delta)
        {
            double xpd = x + delta;
            if (double.IsNaN(x) || double.IsNaN(delta)) {
                Policies.ReportDomainError("TgammaDeltaRatio(x: {0}, delta: {1}): Requires x > 0; x + delta > 0", x, delta);
                return double.NaN;
            }

            // Γ(x)/Γ(x) == 1
            // this needs to be here in case x = 0
            if (delta == 0)
                return 1;

            if (!(x > 0)) {
                Policies.ReportDomainError("TgammaDeltaRatio(x: {0}, delta: {1}): Requires x > 0; negative x not implemented", x, delta);
                return double.NaN;
            }
            if (!(xpd > 0)) {
                Policies.ReportDomainError("TgammaDeltaRatio(x: {0}, delta: {1}): Requires x+delta > 0", x, delta);
                return double.NaN;
            }
            if (double.IsInfinity(x)) {
                if (double.IsInfinity(delta)) {
                    Policies.ReportDomainError("TgammaDeltaRatio(x: {0}, delta: {1}): Infinity/Infinity", x, delta);
                    return double.NaN;
                }
                return double.PositiveInfinity;
            }

            if (double.IsInfinity(delta))
                return 0.0;

            if (IsInteger(delta)) {
                if (IsInteger(x)) {
                    // Both x and delta are integers, see if we can just use table lookup
                    // of the factorials to get the result:
                    if ((x <= Math2.FactorialTable.Length) && (x + delta <= Math2.FactorialTable.Length))
                        return Math2.FactorialTable[(int)x - 1] / Math2.FactorialTable[(int)(x + delta) - 1];
                }


                // if delta is a small integer, we can use a finite product
                if (Math.Abs(delta) < 20) {
                    if (delta == 0)
                        return 1;
                    if (delta < 0) {
                        x -= 1;
                        double result = x;
                        while ( ++delta < 0 ) {
                            x -= 1;
                            result *= x;
                        }
                        return result;
                    } else {
                        double result = x;
                        while ( --delta > 0 ) {
                            x += 1;
                            result *= x;
                        }
                        return 1/result;
                    }
                }
            }

            // our Tgamma ratio already handles cases where 
            // one or both arguments are small 

            const double LowerLimit = DoubleLimits.MachineEpsilon / Constants.EulerMascheroni; // 1.73eps
            if (x < LowerLimit || xpd < LowerLimit)
                return Math2.TgammaRatio(x, xpd);

            if ((x < 1 && delta > Math2.FactorialTable.Length) || (xpd < 1 && x > Math2.FactorialTable.Length))
                return Math2.TgammaRatio(x, xpd);

            // Use the Lanczos approximation to compute the delta ratio 
            return Lanczos.TgammaDeltaRatio(x, delta);
        }
Esempio n. 15
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);
        }
Esempio n. 16
0
        /// <summary>
        /// Returns the Arithmetic-Geometric Mean of a and b
        /// </summary>
        /// <param name="a">First Agm argument</param>
        /// <param name="b">Second Agm argument</param>
        /// <returns></returns>
        public static double Agm(double a, double b)
        {
            if (double.IsNaN(a) || double.IsNaN(b))
            {
                Policies.ReportDomainError("Agm(a: {0}, b: {1}): NaNs not allowed", a, b);
                return(double.NaN);
            }

            // Agm(x, 0) = Agm(0, x) = 0
            if (a == 0 || b == 0)
            {
                return(0);
            }

            // We don't handle complex results
            if (Math.Sign(a) != Math.Sign(b))
            {
                Policies.ReportDomainError("Agm(a: {0}, b: {1}): Requires a, b have the same sign", a, b);
                return(double.NaN);
            }

            // Agm(± a, ± ∞) = ± ∞
            if (double.IsInfinity(a))
            {
                return(a);
            }
            if (double.IsInfinity(b))
            {
                return(b);
            }

            if (a == b)
            {
                return(a); //or b
            }
            // save a, b for error messages
            double aOriginal = a;
            double bOriginal = b;

            // Agm(-a, -b) = -Agm(a, b)
            double mult = 1;

            if (a < 0)
            {
                a    = -a;
                b    = -b;
                mult = -1;
            }

            // Set a > b: Agm(a, b) == Agm(b, a)
            if (a < b)
            {
                Utility.Swap(ref a, ref b);
            }


            // If b/a < eps^(1/4), we can transform Agm(a, b) to a * Agm(1, b/a)
            // and use the Taylor series Agm(1, y) at y = 0
            // See: http://functions.wolfram.com/EllipticFunctions/ArithmeticGeometricMean/06/01/01/

            double y = b / a;

            if (y <= DoubleLimits.RootMachineEpsilon._4)
            {
                // Compute log((b/a)/4) being careful not to underflow on b/a
                double logyDiv4;
                if (y < 4 * DoubleLimits.MinNormalValue)
                {
                    logyDiv4  = (y < DoubleLimits.MinNormalValue) ? Math.Log(b) - Math.Log(a) : Math.Log(y);
                    logyDiv4 -= 2 * Constants.Ln2;
                }
                else
                {
                    logyDiv4 = Math.Log(y / 4);
                }

                double r      = (Math.PI / 8) * (-4.0 + (1 + 1 / logyDiv4) * y * y) / logyDiv4;
                double result = mult * a * r;
                return(result);
            }

            // scale by a to guard against overflows/underflows in a*b
            mult *= a;
            b     = y;
            a     = 1;

            // Use a series of arithmetic and geometric means.
            int n = 0;

            for (; n < Policies.MaxSeriesIterations; n++)
            {
#if !USE_GENERAL
                // If |b/a-1| < eps^(1/9), we can transform Agm(a, b) to a*Agm(1, b/a)
                // and use the Taylor series Agm(1, z) at z = 1:
                // Agm(1, z) = 1 + (z-1)/2 - (z-1)^2/16 + ....
                // http://functions.wolfram.com/EllipticFunctions/ArithmeticGeometricMean/06/01/02/0001/

                double z = (b - a);
                if (Math.Abs(z) < a * DoubleLimits.RootMachineEpsilon._9)
                {
                    const double c0 = 1;
                    const double c1 = 1.0 / 2;
                    const double c2 = -1.0 / 16;
                    const double c3 = 1.0 / 32;
                    const double c4 = -21.0 / 1024;
                    const double c5 = 31.0 / 2048;
                    const double c6 = -195.0 / 16384;
                    const double c7 = 319.0 / 32768;
                    const double c8 = -34325.0 / 4194304;

                    z /= a; // z=(b-a)/a = b/a-1
                    double r      = c0 + z * (c1 + z * (c2 + z * (c3 + z * (c4 + z * (c5 + z * (c6 + z * (c7 + z * c8)))))));
                    double result = mult * a * r;
                    return(result);
                }

                // take a step

                double nextA = 0.5 * (a + b);
                b = Math.Sqrt(a * b);
                a = nextA;
#else
                // the double specialized series above is a little faster,
                // so this more general approach is disabled

                // tol = Sqrt(8 * eps)
                const double tol   = 2 * Constants.Sqrt2 * DoubleLimits.RootMachineEpsilon._2;
                double       nextA = 0.5 * (a + b);
                if (Math.Abs(b - a) < a * tol)
                {
                    return(mult * nextA);
                }

                // take a step

                b = Math.Sqrt(a * b);
                a = nextA;
#endif
            }

            Policies.ReportConvergenceError("Agm(a: {0}, b: {1}): Did not converge after {2} iterations", aOriginal, bOriginal, n);
            return(double.NaN);
        }
Esempio n. 17
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);
        }
Esempio n. 18
0
        /// <summary>
        /// Returns the Jacobi elliptic functions: sn, cn, dn
        /// </summary>
        /// <param name="k">The modulus</param>
        /// <param name="u">The argument</param>
        /// <returns></returns>
        static JacobiSnCnDn JacobiElliptic(double k, double u)
        {
            if (double.IsNaN(k) || double.IsInfinity(k) || double.IsNaN(u) || double.IsInfinity(u))
            {
                Policies.ReportDomainError("Jacobi(k: {0}, u: {1}): Requires finite u, k", k, u);
                return(new JacobiSnCnDn {
                    Sn = double.NaN, Cn = double.NaN, Dn = double.NaN
                });
            }

            // For k < 0 or k > 1, see: http://dlmf.nist.gov/22.17#i

            if (k < 0)
            {
                k = -k;
            }

            if (k > 1)
            {
                var results = JacobiElliptic(1 / k, u * k);
                return(new JacobiSnCnDn {
                    Sn = results.Sn / k, Cn = results.Dn, Dn = results.Cn
                });
            }

            if (u == 0)
            {
                return new JacobiSnCnDn {
                           Sn = 0, Cn = 1, Dn = 1
                }
            }
            ;

            if (k == 0)
            {
                return new JacobiSnCnDn {
                           Sn = Math.Sin(u), Cn = Math.Cos(u), Dn = 1
                }
            }
            ;

            if (k == 1)
            {
                double sech = 1 / Math.Cosh(u);

                return(new JacobiSnCnDn {
                    Sn = Math.Tanh(u), Cn = sech, Dn = sech
                });
            }

            double sn, cn, dn;

            if (k < DoubleLimits.RootMachineEpsilon._4)
            {
                // Asymptotic form from A&S 16.13:
                // and http://dlmf.nist.gov/22.10#ii

                double tu = Math.Tanh(u);
                double su = Math.Sin(u);
                double cu = Math.Cos(u);
                double m  = k * k;
                sn = su - m * (u - cu * su) * cu / 4;
                cn = cu + m * (u - cu * su) * su / 4;
                dn = 1 - m * su * su / 2;
            }
            else if (k * k > 1 - DoubleLimits.RootMachineEpsilon._2)
            {
                // Asymptotic form from A&S 16.15:
                // and http://dlmf.nist.gov/22.10#ii
                // O(k'^4) = O((1-k^2)^2)

                double tu   = Math.Tanh(u);
                double cu   = Math.Cosh(u);
                double su   = Math.Sinh(u);
                double sech = 1 / Math.Cosh(u);

                double kc  = 1 - k;
                double kp2 = kc * (2 - kc);     // k'^2 = 1-k^2

                dn = (sech + kp2 * (u * sech + su) * tu) / 4;
                cn = (sech + kp2 * (u * sech - su) * tu) / 4;
                sn = (tu + kp2 * (tu - u / (cu * cu))) / 4;
                //sn -= (72 * u * cu + 4 * (8 * u * u - 5) * su - 19 * Math.Sinh(3 * u) + Math.Sinh(5 * u)) * sec * sec * sec * kp2 * kp2 / 512;
            }
            else
            {
                double kc      = 1 - k;
                double k_prime = k < 0.5 ? Math.Sqrt((1 - k) * (1 + k)) : Math.Sqrt(kc * (2 - kc));

                double T1;
                double T0 = Jacobi_Recurse(k, u, 1, k_prime, 0, out T1);

                sn = Math.Sin(T0);
                cn = Math.Cos(T0);
                dn = Math.Cos(T0) / Math.Cos(T1 - T0);
            }


            return(new JacobiSnCnDn {
                Sn = sn, Cn = cn, Dn = dn
            });
        }
Esempio n. 19
0
        /// <summary>
        /// Calculate (K{v}(x), K{v+1}(x)) by evaluating continued fraction
        /// z1 / z0 = U(v+1.5, 2v+1, 2x) / U(v+0.5, 2v+1, 2x)
        /// <para>|x| &gt;= |v|, CF2_ik converges rapidly</para>
        /// <para>|x| -&gt; 0, CF2_ik fails to converge</para>
        /// </summary>
        /// <param name="v"></param>
        /// <param name="x"></param>
        /// <param name="expScale">if true, exponentially scale the results</param>
        /// <returns>
        /// If <paramref name="expScale"/> is false K{v}(x) and K{v+1}(x);
        /// otherwise e^x * K{v}(x) and e^x * K{v+1}(x)
        /// </returns>
        static (double Kv, double Kvp1) K_CF2(double v, double x, bool expScale)
        {
            // See Thompson and Barnett, Computer Physics Communications, vol 47, 245 (1987)

            const double tolerance = DoubleLimits.MachineEpsilon;
            double       C, f, q, delta;
            int          k;

            Debug.Assert(Math.Abs(x) > 1);

            // Steed's algorithm, see Thompson and Barnett,
            // Journal of Computational Physics, vol 64, 490 (1986)
            double a = v * v - 0.25;
            double b = 2 * (x + 1);
            double D = 1 / b;

            f = delta = D;                                // f1 = delta1 = D1, coincidence
            double prev    = 0;
            double current = 1;
            double Q = C = -a;
            double S = 1 + Q * delta;

            // starting from 2
            for (k = 2; k < Policies.MaxSeriesIterations; k++)
            {
                // continued fraction f = z1 / z0
                a     -= 2 * (k - 1);
                b     += 2;
                D      = 1 / (b + a * D);
                delta *= b * D - 1;
                f     += delta;

                q       = (prev - (b - 2) * current) / a;
                prev    = current;
                current = q;                        // forward recurrence for q
                C      *= -a / k;
                Q      += C * q;
                S      += Q * delta;

                // S converges slower than f

                if (Math.Abs(Q * delta) < Math.Abs(S) * tolerance)
                {
                    break;
                }
            }

            if (k >= Policies.MaxSeriesIterations)
            {
                Policies.ReportConvergenceError("K_CF2(v:{0}, x:{1}) did not converge after {2} iterations", v, x, Policies.MaxSeriesIterations);
                return(double.NaN, double.NaN);
            }

            double Kv, Kv1;

            if (expScale)
            {
                Kv = (Constants.SqrtHalfPI / Math.Sqrt(x)) / S;
            }
            else
            {
                Kv = (Constants.SqrtHalfPI / Math.Sqrt(x)) * Math.Exp(-x) / S;
            }

            Kv1 = Kv * (0.5 + v + x + (v * v - 0.25) * f) / x;

            return(Kv, Kv1);
        }
Esempio n. 20
0
        public static double Imp(double k, double n, double nc, double phi)
        {
            // Note arg nc = 1-n, possibly without cancellation errors

            Debug.Assert(k >= -1 && k <= 1, "Requires |k| <= 1: k = " + k);

            // See: http://dlmf.nist.gov/19.6#iv
            if (phi == 0)
            {
                return(0);
            }
            if (phi == Math.PI / 2)
            {
                return(Math2.EllintPi(k, n));
            }

            if (n == 0)
            {
                if (k == 0)
                {
                    return(phi);
                }
                return(Math2.EllintF(k, phi));
            }


            // Carlson's algorithm works only for |phi| <= π/2,
            // use the integrand's periodicity to normalize phi
            // Π(k, n, phi + π*mult) = Π(k, n, phi) + 2*mult*Π(k,n)


            double result = 0;
            double rphi   = Math.Abs(phi);

            if (rphi > Math.PI / 2)
            {
                // Normalize periodicity so that |rphi| <= π/2
                var(angleMultiple, angleRemainder) = Trig.RangeReducePI(rphi);
                double mult = 2 * angleMultiple;
                rphi = angleRemainder;
                if ((mult > 0) && (nc > 0))
                {
                    result = mult * _EllintPi.Imp(k, n, nc);
                }
            }


            if (k == 0)
            {
                double ncr;

                // A&S 17.7.20:
                if (n < 1)
                {
                    if (nc == 1)
                    {
                        return(phi);
                    }
                    ncr     = Math.Sqrt(nc);
                    result += Math.Atan(ncr * Math.Tan(rphi)) / ncr;
                }
                else if (n == 1)
                {
                    result += Math.Tan(rphi);
                }
                else
                {
                    // n > 1:
                    ncr     = Math.Sqrt(-nc);
                    result += Math2.Atanh(ncr * Math.Tan(rphi)) / ncr;
                }

                return((phi < 0) ? -result : result);
            }


            double sphi = Math.Sin(Math.Abs(phi));

            if (n > 1 / (sphi * sphi))
            {
                // Complex result is a domain error:
                Policies.ReportDomainError("EllintPi(k: {0}, nu: {1}, phi: {2}): Complex results not supported. Requires n > 1 / sin^2(phi)", k, n, phi);
                return(double.NaN);
            }


            // Special cases first:

            if (n < 0)
            {
                //
                // If we don't shift to 0 <= n <= 1 we get
                // cancellation errors later on.  Use
                // A&S 17.7.15/16 to shift to n > 0:
                //
                double k2 = k * k;
                double N  = (k2 - n) / nc;
                double Nc = (1 - k2) / nc; // Nc = 1-N = (1-k^2)/nc

                // check to see if a rounding error occurred
                //  k^2 <= N <= 1
                if (N < k2)
                {
#if EXTRA_DEBUG
                    Debug.WriteLine("Rounding error: EllintPi(k: {0} , nu: {1} , phi: {2})", k, n, phi);
#endif
                    N  = k2;
                    Nc = 1 - N;
                }

                double p2    = Math.Sqrt(-n * N);
                double delta = Math.Sqrt(1 - k2 * sphi * sphi);


                // Reduce A&S 17.7.15/16
                // Mathematica eqns below
                // N is protected in Mathematica, so use V
                // V = (k2 - n)/(1 - n)
                // Assuming[(k2 >= 0 && k2 <= 1) && n < 0,  FullSimplify[Sqrt[(1 - V)*(1 - k2/V)]/Sqrt[((1 - n)*(1 - k2/n))]]]
                // Result: ((-1 + k2) n)/((-1 + n) (-k2 + n))

                // Assuming[(k2 >= 0 && k2 <= 1) && n < 0,  FullSimplify[k2/(Sqrt[-n*(k2 - n)/(1 - n)]*Sqrt[(1 - n)*(1 - k2/n)])]]
                // Result: k2/(k2 - n)

                // Assuming[(k2 >= 0 && k2 <= 1) && n < 0,  FullSimplify[Sqrt[1/((1 - n)*(1 - k2/n))]]]
                // Result: Sqrt[n/((k2 - n) (-1 + n))]

                double nResult = -(n / nc) * ((1 - k2) / (k2 - n)) * _EllintPi.Imp(k, N, Nc, rphi);
                nResult += k2 / (k2 - n) * Math2.EllintF(k, rphi);
                nResult += Math.Sqrt((n / nc) / (n - k2)) * Math.Atan((p2 / (2 * delta)) * Math.Sin(2 * rphi));

                result += nResult;

                return((phi < 0) ? -result : result);
            }


            double sinp = Math.Sin(rphi);
            double cosp = Math.Cos(rphi);
            double x    = cosp * cosp;
            double t    = sinp * sinp;
            double y    = 1 - k * k * t;
            double z    = 1;
            double p    = (n * t < 0.5) ? 1 - n * t : x + nc * t;

            result += sinp * (Math2.EllintRF(x, y, z) + n * t * Math2.EllintRJ(x, y, z, p) / 3);

            return((phi < 0) ? -result : result);
        }
Esempio n. 21
0
        /// <summary>
        /// Returns Y{1}(x)
        /// </summary>
        /// <param name="x"></param>
        /// <returns></returns>
        public static double Y1(double x)
        {
            if (x <= 0)
            {
                Policies.ReportDomainError("BesselY(v: 1, x: {0}): Requires x >= 0; complex number result not supported", x);
                return(double.NaN);
            }

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


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

            double value, factor;

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

            if (x <= 4)                        // x in (0, 4]
            {
                const double Zero1  = 2.1971413260310170351e+00;
                const double Zero1a = 2359162535.0 / 1073741824;
                const double Zero1b = -1.56191001087854667603372694698166849966580723533404956e-12;

                const double p0 = 4.0535726612579544093e+13;
                const double p1 = 5.4708611716525426053e+12;
                const double p2 = -3.7595974497819597599e+11;
                const double p3 = 7.2144548214502560419e+09;
                const double p4 = -5.9157479997408395984e+07;
                const double p5 = 2.2157953222280260820e+05;
                const double p6 = -3.1714424660046133456e+02;

                const double q0 = 3.0737873921079286084e+14;
                const double q1 = 4.1272286200406461981e+12;
                const double q2 = 2.7800352738690585613e+10;
                const double q3 = 1.2250435122182963220e+08;
                const double q4 = 3.8136470753052572164e+05;
                const double q5 = 8.2079908168393867438e+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 / Zero1) * _Bessel.J1(x);
                factor = (x + Zero1) * ((x - Zero1a) - Zero1b) / x;
                value  = y + factor * (P / Q);
            }
            else if (x <= 8)                     // x in (4, 8]

            {
                const double Zero2  = 5.4296810407941351328e+00;
                const double Zero2a = 11660151249.0 / 2147483648;
                const double Zero2b = -1.81486215767496919599158034162425239708541634963014629e-11;

                const double p0 = 1.1514276357909013326e+19;
                const double p1 = -5.6808094574724204577e+18;
                const double p2 = -2.3638408497043134724e+16;
                const double p3 = 4.0686275289804744814e+15;
                const double p4 = -5.9530713129741981618e+13;
                const double p5 = 3.7453673962438488783e+11;
                const double p6 = -1.1957961912070617006e+09;
                const double p7 = 1.9153806858264202986e+06;
                const double p8 = -1.2337180442012953128e+03;

                const double q0 = 5.3321844313316185697e+20;
                const double q1 = 5.6968198822857178911e+18;
                const double q2 = 3.0837179548112881950e+16;
                const double q3 = 1.1187010065856971027e+14;
                const double q4 = 3.0221766852960403645e+11;
                const double q5 = 6.3550318087088919566e+08;
                const double q6 = 1.0453748201934079734e+06;
                const double q7 = 1.2855164849321609336e+03;
                const double q8 = 1.0;


                double z = x * x;
                double P = p0 + z * (p1 + z * (p2 + z * (p3 + z * (p4 + z * (p5 + z * (p6 + z * (p7 + z * p8)))))));
                double Q = q0 + z * (q1 + z * (q2 + z * (q3 + z * (q4 + z * (q5 + z * (q6 + z * (q7 + z * q8)))))));

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

            {
                double y = 8 / x;
                double z = y * y;
                double rc, rs;
                {
                    const double p0 = -4.4357578167941278571e+06;
                    const double p1 = -9.9422465050776411957e+06;
                    const double p2 = -6.6033732483649391093e+06;
                    const double p3 = -1.5235293511811373833e+06;
                    const double p4 = -1.0982405543459346727e+05;
                    const double p5 = -1.6116166443246101165e+03;
                    const double p6 = 0.0;

                    const double q0 = -4.4357578167941278568e+06;
                    const double q1 = -9.9341243899345856590e+06;
                    const double q2 = -6.5853394797230870728e+06;
                    const double q3 = -1.5118095066341608816e+06;
                    const double q4 = -1.0726385991103820119e+05;
                    const double q5 = -1.4550094401904961825e+03;
                    const double q6 = 1.0;

                    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)))));
                    rc = P / Q;
                }
                {
                    const double p0 = 3.3220913409857223519e+04;
                    const double p1 = 8.5145160675335701966e+04;
                    const double p2 = 6.6178836581270835179e+04;
                    const double p3 = 1.8494262873223866797e+04;
                    const double p4 = 1.7063754290207680021e+03;
                    const double p5 = 3.5265133846636032186e+01;
                    const double p6 = 0.0;

                    const double q0 = 7.0871281941028743574e+05;
                    const double q1 = 1.8194580422439972989e+06;
                    const double q2 = 1.4194606696037208929e+06;
                    const double q3 = 4.0029443582266975117e+05;
                    const double q4 = 3.7890229745772202641e+04;
                    const double q5 = 8.6383677696049909675e+02;
                    const double q6 = 1.0;

                    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)))));
                    rs = P / Q;
                }

                // The following code is:
                // double z = x - 0.75f * 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.75);
                //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);
        }
Esempio n. 22
0
        /// <summary>
        /// Returns the value of the Exponential Integral
        /// <para>Ei(x) = ∫ e^t/t dt, t={-∞,x}</para>
        /// </summary>
        public static double Expint(double x)
        {
            if (x < 0)
            {
                return(-E1(-x));
            }

            if (double.IsNaN(x))
            {
                Policies.ReportDomainError("Expint(x: {0}) NaN not allowed", x);
                return(double.NaN);
            }

            if (x == 0)
            {
                return(double.NegativeInfinity);
            }

            double result;

            if (x <= 6)
            {
                // Maximum Deviation Found:                     2.852e-18
                // Expected Error Term:                         2.852e-18
                // Max Error found at double precision =        Poly: 2.636335e-16   Cheb: 4.187027e-16

                const double R  = 0.372507410781366634461991866580119133535689497771654051555657435242200120636201854384926049951548942392;
                const double R1 = 1677624236387711.0 / 4503599627370496.0;
                const double R2 = 0.131401834143860282009280387409357165515556574352422001206362e-16;

                const double p0 = 2.98677224343598593013;
                const double p1 = 0.356343618769377415068;
                const double p2 = 0.780836076283730801839;
                const double p3 = 0.114670926327032002811;
                const double p4 = 0.0499434773576515260534;
                const double p5 = 0.00726224593341228159561;
                const double p6 = 0.00115478237227804306827;
                const double p7 = 0.000116419523609765200999;
                const double p8 = 0.798296365679269702435e-5;
                const double p9 = 0.2777056254402008721e-6;

                const double q0 = 1;
                const double q1 = -1.17090412365413911947;
                const double q2 = 0.62215109846016746276;
                const double q3 = -0.195114782069495403315;
                const double q4 = 0.0391523431392967238166;
                const double q5 = -0.00504800158663705747345;
                const double q6 = 0.000389034007436065401822;
                const double q7 = -0.138972589601781706598e-4;

                double z = (x / 3) - 1;
                double P = p0 + z * (p1 + z * (p2 + z * (p3 + z * (p4 + z * (p5 + z * (p6 + z * (p7 + z * (p8 + z * p9))))))));
                double Q = q0 + z * (q1 + z * (q2 + z * (q3 + z * (q4 + z * (q5 + z * (q6 + z * q7))))));

                double xmr = (x - R1) - R2;
                result  = (P / Q) * xmr;
                result += (Math.Abs(xmr) < 0.1) ? Math2.Log1p(xmr / R) : Math.Log(x / R);
            }
            else if (x <= 10)
            {
                // Maximum Deviation Found:                     6.546e-17
                // Expected Error Term:                         6.546e-17
                // Max Error found at double precision =        Poly: 6.890169e-17   Cheb: 6.772128e-17

                const double Y = 1.158985137939453125;

                const double p0 = 0.00139324086199402804173;
                const double p1 = -0.0349921221823888744966;
                const double p2 = -0.0264095520754134848538;
                const double p3 = -0.00761224003005476438412;
                const double p4 = -0.00247496209592143627977;
                const double p5 = -0.000374885917942100256775;
                const double p6 = -0.554086272024881826253e-4;
                const double p7 = -0.396487648924804510056e-5;

                const double q0 = 1;
                const double q1 = 0.744625566823272107711;
                const double q2 = 0.329061095011767059236;
                const double q3 = 0.100128624977313872323;
                const double q4 = 0.0223851099128506347278;
                const double q5 = 0.00365334190742316650106;
                const double q6 = 0.000402453408512476836472;
                const double q7 = 0.263649630720255691787e-4;

                double z = x / 2 - 4;
                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 = Math.Exp(x) / x * (Y + P / Q) + x;
            }
            else if (x <= 20)
            {
                // Maximum Deviation Found:                     1.843e-17
                // Expected Error Term:                         -1.842e-17
                // Max Error found at double precision =        Poly: 4.375868e-17   Cheb: 5.860967e-17

                const double Y = 1.0869731903076171875;

                const double p0 = -0.00893891094356945667451;
                const double p1 = -0.0484607730127134045806;
                const double p2 = -0.0652810444222236895772;
                const double p3 = -0.0478447572647309671455;
                const double p4 = -0.0226059218923777094596;
                const double p5 = -0.00720603636917482065907;
                const double p6 = -0.00155941947035972031334;
                const double p7 = -0.000209750022660200888349;
                const double p8 = -0.138652200349182596186e-4;

                const double q0 = 1;
                const double q1 = 1.97017214039061194971;
                const double q2 = 1.86232465043073157508;
                const double q3 = 1.09601437090337519977;
                const double q4 = 0.438873285773088870812;
                const double q5 = 0.122537731979686102756;
                const double q6 = 0.0233458478275769288159;
                const double q7 = 0.00278170769163303669021;
                const double q8 = 0.000159150281166108755531;


                double z = x / 5 - 3;
                double P = p0 + z * (p1 + z * (p2 + z * (p3 + z * (p4 + z * (p5 + z * (p6 + z * (p7 + z * p8)))))));
                double Q = q0 + z * (q1 + z * (q2 + z * (q3 + z * (q4 + z * (q5 + z * (q6 + z * (q7 + z * q8)))))));

                result = Math.Exp(x) / x * (Y + P / Q) + x;
            }
            else if (x <= 40)
            {
                // Maximum Deviation Found:                     5.102e-18
                // Expected Error Term:                         5.101e-18
                // Max Error found at double precision =        Poly: 1.441088e-16   Cheb: 1.864792e-16

                const double Y = 1.03937530517578125;

                const double p0 = -0.00356165148914447597995;
                const double p1 = -0.0229930320357982333406;
                const double p2 = -0.0449814350482277917716;
                const double p3 = -0.0453759383048193402336;
                const double p4 = -0.0272050837209380717069;
                const double p5 = -0.00994403059883350813295;
                const double p6 = -0.00207592267812291726961;
                const double p7 = -0.000192178045857733706044;
                const double p8 = -0.113161784705911400295e-9;

                const double q0 = 1;
                const double q1 = 2.84354408840148561131;
                const double q2 = 3.6599610090072393012;
                const double q3 = 2.75088464344293083595;
                const double q4 = 1.2985244073998398643;
                const double q5 = 0.383213198510794507409;
                const double q6 = 0.0651165455496281337831;
                const double q7 = 0.00488071077519227853585;

                double z = x / 10 - 3;
                double P = p0 + z * (p1 + z * (p2 + z * (p3 + z * (p4 + z * (p5 + z * (p6 + z * (p7 + z * p8)))))));
                double Q = q0 + z * (q1 + z * (q2 + z * (q3 + z * (q4 + z * (q5 + z * (q6 + z * q7))))));

                result = Math.Exp(x) / x * (Y + P / Q) + x;
            }
            else
            {
                // Max Error found at double precision =        3.381886e-17

                const double Y = 1.013065338134765625;

                const double p0 = -0.0130653381347656243849;
                const double p1 = 0.19029710559486576682;
                const double p2 = 94.7365094537197236011;
                const double p3 = -2516.35323679844256203;
                const double p4 = 18932.0850014925993025;
                const double p5 = -38703.1431362056714134;

                const double q0 = 1;
                const double q1 = 61.9733592849439884145;
                const double q2 = -2354.56211323420194283;
                const double q3 = 22329.1459489893079041;
                const double q4 = -70126.245140396567133;
                const double q5 = 54738.2833147775537106;
                const double q6 = 8297.16296356518409347;


                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 + z * q6)))));

                result = Y + P / Q;
                if (x < 41)
                {
                    result *= Math.Exp(x) / x;
                }
                else
                {
                    // Avoid premature overflow if we can:
                    const double exp40 = 2.35385266837019985407899910749034804508871617254555467236651e17;
                    result *= (Math.Exp(x - 40) / x);
                    result *= exp40;
                }
                result += x;
            }

            return(result);
        }
Esempio n. 23
0
        /// <summary>
        /// Computes Y{v}(x), where v is an integer
        /// </summary>
        /// <param name="v">Integer order</param>
        /// <param name="x">Argument. Requires x &gt; 0</param>
        /// <returns></returns>
        public static DoubleX YN(double v, double x)
        {
            if ((x == 0) && (v == 0))
            {
                return(double.NegativeInfinity);
            }
            if (x <= 0)
            {
                Policies.ReportDomainError("BesselY(v: {0}, x: {1}): Complex number result not supported. Requires x >= 0", v, x);
                return(double.NaN);
            }


            //
            // Reflection comes first:
            //

            double sign = 1;

            if (v < 0)
            {
                // Y_{-n}(z) = (-1)^n Y_n(z)
                if (Math2.IsOdd(v))
                {
                    sign = -1;
                }
                v = -v;
            }

            Debug.Assert(v >= 0 && x >= 0);

            if (v > int.MaxValue)
            {
                Policies.ReportNotImplementedError("BesselY(v: {0}, x: {1}): Large integer values not yet implemented", v, x);
                return(double.NaN);
            }

            int n = (int)v;

            if (n == 0)
            {
                return(Y0(x));
            }

            if (n == 1)
            {
                return(sign * Y1(x));
            }

            if (x < DoubleLimits.RootMachineEpsilon._2)
            {
                DoubleX smallArgValue = YN_SmallArg(n, x);
                return(smallArgValue * sign);
            }

            // if v > MinAsymptoticV, use the asymptotics
            const int MinAsymptoticV = 7; // arbitrary constant to reduce iterations

            if (v > MinAsymptoticV)
            {
                double  Jv;
                DoubleX Yv;
                if (JY_TryAsymptotics(v, x, out Jv, out Yv, false, true))
                {
                    return(sign * Yv);
                }
            }


            // forward recurrence always OK (though unstable)
            double prev    = Y0(x);
            double current = Y1(x);

            var(Yvpn, Yvpnm1, YScale) = Recurrence.ForwardJY_B(1.0, x, n - 1, current, prev);

            return(DoubleX.Ldexp(sign * Yvpn, YScale));
        }
Esempio n. 24
0
        /// <summary>
        /// Returns J{v}(x) for integer v
        /// </summary>
        /// <param name="v"></param>
        /// <param name="x"></param>
        /// <returns></returns>
        public static double JN(double v, double x)
        {
            Debug.Assert(Math2.IsInteger(v));


            // For integer orders only, we can use two identities:
            // #1: J{-n}(x) = (-1)^n * J{n}(x)
            // #2: J{n}(-x) = (-1)^n * J{n}(x)

            double sign = 1;

            if (v < 0)
            {
                v = -v;
                if (Math2.IsOdd(v))
                {
                    sign = -sign;
                }
            }

            if (x < 0)
            {
                x = -x;
                if (Math2.IsOdd(v))
                {
                    sign = -sign;
                }
            }

            Debug.Assert(v >= 0 && x >= 0);

            if (v > int.MaxValue)
            {
                Policies.ReportNotImplementedError("BesselJ(v: {0}): Large integer values not yet implemented", v);
                return(double.NaN);
            }

            int n = (int)v;

            //
            // Special cases:
            //
            if (n == 0)
            {
                return(sign * J0(x));
            }

            if (n == 1)
            {
                return(sign * J1(x));
            }

            // n >= 2
            Debug.Assert(n >= 2);

            if (x == 0)
            {
                return(0);
            }

            if (x < 5 || (v > x * x / 4))
            {
                return(sign * J_SmallArg(v, x));
            }


            // if v > MinAsymptoticV, use the asymptotics
            const int MinAsymptoticV = 7; // arbitrary constant to reduce iterations

            if (v > MinAsymptoticV)
            {
                double  Jv;
                DoubleX Yv;
                if (JY_TryAsymptotics(v, x, out Jv, out Yv, true, false))
                {
                    return(sign * Jv);
                }
            }


            // v < abs(x), forward recurrence stable and usable
            // v >= abs(x), forward recurrence unstable, use Miller's algorithm

            if (v < x)
            {
                // use forward recurrence
                double prev    = J0(x);
                double current = J1(x);
                return(sign * Recurrence.ForwardJY(1.0, x, n - 1, current, prev).JYvpn);
            }
            else
            {
                // Steed is somewhat more accurate when n gets large
                if (v >= 200)
                {
                    var(J, Y) = JY_Steed(n, x, true, false);
                    return(J);
                }

                // J{n+1}(x) / J{n}(x)
                var(fv, s) = J_CF1(n, x);
                var(Jvmn, Jvmnp1, scale) = Recurrence.BackwardJY_B(n, x, n, s, fv * s);

                // scale the result
                double Jv = (J0(x) / Jvmn);
                Jv = Math2.Ldexp(Jv, -scale);

                return((s * sign) * Jv);      // normalization
            }
        }
Esempio n. 25
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);
        }
Esempio n. 26
0
        /// <summary>
        /// Returns the falling factorial:
        /// <para>(x)_{_n} = x*(x-1)*(x-2)...*(x-(n-1)) = Γ(x+1)/Γ(x-n+1)</para>
        /// </summary>
        /// <param name="x"></param>
        /// <param name="n">Requires n ≥ 0</param>
        /// <returns>
        /// <para>if n > 0, x*(x-1)*(x-2)...*(x-(n-1))</para>
        /// <para>if n == 0, 1</para>
        /// </returns>
        public static double FactorialFalling(double x, int n)
        {
            // This section uses the following notation:
            // falling factorial: (x)_{_n}
            // rising factorial: (x)_{n}

            // Standard eqn for the falling factorial:
            // (x)_{_n} = Γ(x+1)/Γ(x+1-n)

            double result = double.NaN;

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

            if ((double.IsNaN(x)) ||
                (n < 0))
            {
                Policies.ReportDomainError("FactorialFalling(x: {0}, n: {1}): Requires finite x; n >= 0", x, n);
                return(double.NaN);
            }

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

            if (x == 0)
            {
                return(0);
            }
            if (x < 0)
            {
                //
                // For x < 0 we really have a rising factorial
                // modulo a possible change of sign:
                // (x)_{_n} = (-1)^n * (-x)_{n}
                result = FactorialRising(-x, n);
                return(IsOdd(n) ? -result : result);
            }

            // standard case: Γ(x+1)/Γ(x+1-n)
            if (x > n - 1)
            {
                // (n)_{_n} = n!
                if (x == n)
                {
                    return(Math2.Factorial(n));
                }

                // (n)_{_n+1} = n!, n>1
                if (x == n + 1)
                {
                    Debug.Assert(n > 1);
                    return(Math2.Factorial(n + 1));
                }

                return(Math2.TgammaDeltaRatio(x + 1, -n));
            }


            Debug.Assert(x <= n - 1);


            if (x < 1)
            {
                int m = n - 1;

                double MaxFactorial = Math2.MaxFactorialIndex;
                if (m < MaxFactorial)
                {
                    result = x / TgammaDeltaRatio(1 - x, m);
                }
                else
                {
                    // try not to overflow/underflow too soon

                    double m2 = m - MaxFactorial;
                    result = (x / TgammaDeltaRatio(1 - x, MaxFactorial)) / TgammaDeltaRatio(1 - x + MaxFactorial, m2);
                }


                return(IsOdd(m) ? -result : result);
            }

            //
            // x+1-n will be negative and TgammaDeltaRatio won't
            // handle it, split the product up into three parts:
            //
            double xp1 = x + 1;
            int    n2  = (int)xp1;

            if (n2 == xp1)
            {
                return(0);
            }
            result  = Math2.TgammaDeltaRatio(xp1, -n2);
            x      -= n2;
            result *= x;
            ++n2;
            if (n2 < n)
            {
                result *= FactorialFalling(x - 1, n - n2);
            }
            return(result);
        }
Esempio n. 27
0
        /// <summary>
        /// Returns the value of first derivative to the Airy Bi function at <paramref name="x"/>
        /// </summary>
        /// <param name="x">AiryBiPrime function argument</param>
        /// <returns></returns>
        public static double AiryBiPrime(double x)
        {
            if (double.IsNaN(x))
            {
                Policies.ReportDomainError("AiryBiPrime(x: {0}): NaN not allowed", x);
                return(double.NaN);
            }
            if (double.IsInfinity(x))
            {
                if (x < 0)
                {
                    return(0);
                }
                return(double.PositiveInfinity);
            }

            double absX = Math.Abs(x);

            if (x >= -3 && x <= 3)
            {
                // Use small series. See:
                // http://functions.wolfram.com/Bessel-TypeFunctions/AiryBiPrime/26/01/01/
                // http://dlmf.nist.gov/9.4

                double z  = x * x * x / 9;
                double s1 = x * x * (Constants.AiryBi0 / 2) * HypergeometricSeries.Sum0F1(5 / 3.0, z);
                double s2 = Constants.AiryBiPrime0 * HypergeometricSeries.Sum0F1(1 / 3.0, z);

                return(s1 + s2);
            }


            const double v    = 2.0 / 3;
            double       zeta = 2 * absX * Math.Sqrt(absX) / 3;

            if (x < 0)
            {
                if (x < -32)
                {
                    return(AiryAsym.AiBiPrimeNeg(x).Bi);
                }

                // The following is
                //double j1 = Math2.BesselJ(v, zeta);
                //double j2 = Math2.BesselJ(-v, zeta);
                //double bip = -x * (j1 + j2) / Constants.Sqrt3;
                //return bip;

                var(J, Y) = _Bessel.JY(v, zeta, true, true);
                double s   = 0.5 * (J / Constants.Sqrt3 - ((double)Y));
                double bip = -x * s;
                return(bip);
            }
            else
            {
                if (x >= 16)
                {
                    return(AiryAsym.BiPrime(x));
                }

                // The following is
                //double j1 = Math2.BesselI(v, zeta);
                //double j2 = Math2.BesselI(-v, zeta);
                //double bip = (x / Constants.Sqrt3) * (j1 + j2);

                var(I, K) = _Bessel.IK(v, zeta, true, true);
                double s   = (2 / Constants.Sqrt3 * I + K / Math.PI);
                var    bip = x * s;
                return(bip);
            }
        }
Esempio n. 28
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);
        }
Esempio n. 29
0
        // Elliptic integrals (complete and incomplete) of the second kind
        // Carlson, Numerische Mathematik, vol 33, 1 (1979)


        /// <summary>
        /// Returns the incomplete elliptic integral of the second kind E(φ, k)
        /// <para>E(φ, k) =  ∫ sqrt(1-k^2*sin^2(θ)) dθ, θ={0,φ}</para>
        /// </summary>
        /// <param name="k">The modulus. Requires |k| ≤ 1</param>
        /// <param name="phi">The amplitude</param>
        public static double EllintE(double k, double phi)
        {
            if ((!(k >= -1 && k <= 1)) || (double.IsNaN(phi)))
            {
                Policies.ReportDomainError("EllintE(k: {0}, phi: {1}): Requires |k| <= 1; phi not NaN", k, phi);
                return(double.NaN);
            }
            if (double.IsInfinity(phi))
            {
                return(phi);
            }

            if (Math.Abs(phi) > Trig.PiReductionLimit)
            {
                Policies.ReportNotImplementedError("EllintE(k: {0}, phi: {1}): |phi| > {2} not implemented", k, phi, Trig.PiReductionLimit);
                return(double.NaN);
            }

            // special values
            if (k == 0)
            {
                return(phi);
            }
            if (phi == 0)
            {
                return(0);
            }
            if (phi == Math.PI / 2)
            {
                return(Math2.EllintE(k));
            }

            // Carlson's algorithm works only for |phi| <= π/2,
            // use the integrand's periodicity to normalize phi
            // E(k, phi + π*mult) = E(k, phi) + 2*mult*E(k)

            double result = 0;
            double rphi   = Math.Abs(phi);

            if (rphi > Math.PI / 2)
            {
                // Normalize periodicity so that |rphi| <= π/2
                var(angleMultiple, angleRemainder) = Trig.RangeReducePI(rphi);
                double mult = 2 * angleMultiple;
                rphi = angleRemainder;
                if (mult != 0)
                {
                    result += mult * EllintE(k);
                }
            }

            if (k == 1)
            {
                result += Math.Sin(rphi);
            }
            else
            {
                double k2   = k * k;
                double sinp = Math.Sin(rphi);
                double cosp = Math.Cos(rphi);
                double x    = cosp * cosp;
                double t    = k2 * sinp * sinp;
                double y    = (t < 0.875) ? 1 - t : (1 - k2) + k2 * x;
                double z    = 1;

                result += sinp * (Math2.EllintRF(x, y, z) - t * Math2.EllintRD(x, y, z) / 3);
            }

            return((phi < 0) ? -result : result);
        }
Esempio n. 30
0
        // Elliptic integrals (complete and incomplete) of the second kind
        // Carlson, Numerische Mathematik, vol 33, 1 (1979)


        /// <summary>
        /// Returns the incomplete elliptic integral of the second kind D(φ, k)
        /// <para>D(φ, k) =  ∫ sin^2(θ)/sqrt(1-k^2*sin^2(θ)) dθ, θ={0,φ}</para>
        /// </summary>
        /// <param name="k">The modulus. Requires |k| ≤ 1</param>
        /// <param name="phi">The amplitude</param>
        public static double EllintD(double k, double phi)
        {
            if ((!(k >= -1 && k <= 1)) || (double.IsNaN(phi)))
            {
                Policies.ReportDomainError("EllintD(k: {0}, phi: {1}): Requires |k| <= 1; phi not NaN", k, phi);
                return(double.NaN);
            }
            if (double.IsInfinity(phi))
            {
                return(phi); // Note: result != phi, only +/- infinity
            }
            if (Math.Abs(phi) > Trig.PiReductionLimit)
            {
                Policies.ReportNotImplementedError("EllintD(k: {0}, phi: {1}): |phi| > {2} not implemented", k, phi, Trig.PiReductionLimit);
                return(double.NaN);
            }

            // special values
            //if (k == 0) // too much cancellation error near phi=0, general case works fine
            //    return (phi - Cos(phi)*Sin(phi))/2
            if (phi == 0)
            {
                return(0);
            }
            if (phi == Math.PI / 2)
            {
                return(Math2.EllintD(k));
            }

            // Carlson's algorithm works only for |phi| <= π/2,
            // use the integrand's periodicity to normalize phi
            // D(k, phi + π*mult) = D(k, phi) + 2*mult*D(k)

            double result = 0;
            double rphi   = Math.Abs(phi);

            if (rphi > Math.PI / 2)
            {
                // Normalize periodicity so that |rphi| <= π/2
                var(angleMultiple, angleRemainder) = Trig.RangeReducePI(rphi);
                double mult = 2 * angleMultiple;
                rphi = angleRemainder;
                if (mult != 0)
                {
                    result += mult * EllintD(k);
                }
            }

            double k2   = k * k;
            double sinp = Math.Sin(rphi);
            double cosp = Math.Cos(rphi);
            double x    = cosp * cosp;
            double t    = k2 * sinp * sinp;
            double y    = (t < 0.875) ? 1 - t : (1 - k2) + k2 * x;
            double z    = 1;

            // http://dlmf.nist.gov/19.25#E13
            // and RD(lambda*x, lambda*y, lambda*z) = lambda^(-3/2) * RD(x, y, z)
            result += EllintRD(x, y, z) * sinp * sinp * sinp / 3;

            return((phi < 0) ? -result : result);
        }