示例#1
0
        /// <summary>
        /// Asymptotic AiryAiPrime and AiryBiPrime series for large -x
        /// </summary>
        /// <param name="x"></param>
        /// <returns></returns>
        /// <seealso href="http://functions.wolfram.com/Bessel-TypeFunctions/AiryAiPrime/06/02/01/02/"/>
        /// <seealso href="http://functions.wolfram.com/Bessel-TypeFunctions/AiryBiPrime/06/02/01/02/"/>
        public static (double Ai, double Bi) AiBiPrimeNeg(double x)
        {
            Debug.Assert(x < 0);

            double z       = -x;
            double sqrtz   = Math.Sqrt(z);
            double z1_5    = z * sqrtz;
            double hyp_arg = (-9.0 / 4) / (z * z * z);
            double s1      = HypergeometricSeries.Sum4F1(-1.0 / 12, 5.0 / 12, 7.0 / 12, 13.0 / 12, 1.0 / 2, hyp_arg);
            double s2      = HypergeometricSeries.Sum4F1(5.0 / 12, 11.0 / 12, 13.0 / 12, 19.0 / 12, 3.0 / 2, hyp_arg);

            double zeta = (2 * z1_5) / 3;
            double p    = (7.0 / (48 * z1_5));

            // The following is:
            // ai = -(1/(sqrt(pi) * z^(1/4))) * (cos(zeta + pi/4) * s1 - p * sin(zeta + pi/4) * s2)
            // bi = (1/(sqrt(pi) * z^(1/4))) * (sin(zeta + pi/4) * s1 + p * cos(zeta + pi/4) * s2)
            // Using trig expansion.

            double sin = Math.Sin(zeta);
            double cos = Math.Cos(zeta);

            double prefix = Constants.RecipSqrt2PI * Math.Sqrt(sqrtz);
            double ai     = -prefix * ((s1 - p * s2) * cos - (s1 + p * s2) * sin);
            double bi     = prefix * ((s1 + p * s2) * cos + (s1 - p * s2) * sin);

            return(ai, bi);
        }
示例#2
0
        /// <summary>
        /// Returns the complete elliptic integral of the second kind D(k) = D(π/2,k)
        /// <para>D(k) = ∫ sin^2(θ)/sqrt(1-k^2*sin^2(θ)) dθ, θ={0,π/2}</para>
        /// </summary>
        /// <param name="k">The modulus. Requires |k| ≤ 1</param>
        public static double EllintD(double k)
        {
            if (!(k >= -1 && k <= 1))
            {
                Policies.ReportDomainError("EllintD(k: {0}): Requires |k| <= 1", k);
                return(double.NaN);
            }

            // special values
            if (k == 0)
            {
                return(Math.PI / 4);
            }

            if (Math.Abs(k) == 1)
            {
                return(Double.PositiveInfinity);
            }

            // http://dlmf.nist.gov/19.5#E3
            if (k * k < DoubleLimits.RootMachineEpsilon._13)
            {
                return((Math.PI / 4) * HypergeometricSeries.Sum2F1(1.5, 0.5, 2, k * k));
            }

            double x = 0;
            double y = 1 - k * k;
            double z = 1;

            double value = Math2.EllintRD(x, y, z) / 3;

            return(value);
        }
示例#3
0
        /// <summary>
        /// Returns the value of the Airy Ai function at <paramref name="x"/>
        /// </summary>
        /// <param name="x">The function argument</param>
        /// <returns></returns>
        public static double AiryAi(double x)
        {
            if (double.IsNaN(x))
            {
                Policies.ReportDomainError("AiryAi(x: {0}): NaN not allowed", x);
                return(double.NaN);
            }
            if (double.IsInfinity(x))
            {
                return(0);
            }

            double absX = Math.Abs(x);

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

                double z  = x * x * x / 9;
                double s1 = Constants.AiryAi0 * HypergeometricSeries.Sum0F1(2 / 3.0, z);
                double s2 = x * Constants.AiryAiPrime0 * HypergeometricSeries.Sum0F1(4 / 3.0, z);
                return(s1 + s2);
            }

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

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

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

                var(J, Y) = _Bessel.JY(v, zeta, true, true);
                double s  = 0.5 * (J - ((double)Y) / Constants.Sqrt3);
                double ai = Math.Sqrt(-x) * s;

                return(ai);
            }
            else
            {
                // Use the K relationship: http://dlmf.nist.gov/9.6

                if (x >= 16)
                {
                    return(AiryAsym.Ai(x));
                }

                double ai = Math2.BesselK(v, zeta) * Math.Sqrt(x / 3) / Math.PI;
                return(ai);
            }
        }
示例#4
0
        /// <summary>
        /// Returns the AiryAiPrime for positive x using the asymptotic series
        /// </summary>
        /// <param name="x"></param>
        /// <returns></returns>
        /// <seealso href="http://functions.wolfram.com/Bessel-TypeFunctions/AiryAiPrime/06/02/01/01/0005/"/>
        public static double AiPrime(double x)
        {
            Debug.Assert(x > 0);

            double z     = x;
            double sqrtz = Math.Sqrt(z);
            double z1_5  = z * sqrtz;

            double hyp_arg = (-3.0 / 4) / z1_5;
            double s1      = HypergeometricSeries.Sum2F0(-1.0 / 6, 7.0 / 6, hyp_arg);

            double aip;
            double prefix = ((Constants.RecipSqrtPI / 2) * Math.Sqrt(sqrtz));
            double expArg = -2 * z1_5 / 3;

            if (expArg > DoubleLimits.MinLogValue)
            {
                aip = prefix * s1 * Math.Exp(expArg);
            }
            else if (expArg > 2 * DoubleLimits.MinLogValue)
            {
                double e = Math.Exp(expArg / 2);
                aip = (prefix * s1 * e) * e;
            }
            else
            {
                aip = Math.Exp(expArg - Math.Log(prefix * s1));
            }

            return(-aip);
        }
示例#5
0
        /// <summary>
        /// Returns the Spherical Bessel function of the first kind: j<sub>n</sub>(x)
        /// </summary>
        /// <param name="n">Order. Requires n &gt;= 0</param>
        /// <param name="x">Argument. Requires x &gt;= 0</param>
        /// <returns></returns>
        public static double SphBesselJ(int n, double x)
        {
            if (!(n >= 0) || !(x >= 0))
            {
                Policies.ReportDomainError("SphBesselJ(n: {0}, x: {1}): Requires n >= 0, x >= 0", n, x);
                return(double.NaN);
            }
            if (double.IsInfinity(x))
            {
                return(0);
            }

            //
            // Special case, n == 0 resolves down to the sinus cardinal of x:
            //
            if (n == 0)
            {
                return(Math2.Sinc(x));
            }
            if (x < 1)
            {
                // the straightforward evaluation below can underflow too quickly when x is small
                double result = (0.5 * Constants.SqrtPI);
                result *= (Math.Pow(x / 2, n) / Math2.Tgamma(n + 1.5));
                if (result != 0)
                {
                    result *= HypergeometricSeries.Sum0F1(n + 1.5, -x * x / 4);
                }
                return(result);
            }

            // Default case is just a straightforward evaluation of the definition:
            return(Constants.SqrtHalfPI * BesselJ(n + 0.5, x) / Math.Sqrt(x));
        }
示例#6
0
        /// <summary>
        /// Returns the AiryAi for positive x using the asymptotic series
        /// </summary>
        /// <param name="x"></param>
        /// <returns></returns>
        /// <seealso href="http://functions.wolfram.com/Bessel-TypeFunctions/AiryBi/06/02/01/01/0005/"/>
        public static double Bi(double x)
        {
            Debug.Assert(x > 0);

            double z     = x;
            double sqrtz = Math.Sqrt(z);
            double z1_5  = z * sqrtz;

            double hyp_arg = (3.0 / 4) / z1_5;
            double s1      = HypergeometricSeries.Sum2F0(1.0 / 6, 5.0 / 6, hyp_arg);

            // watch for overflows
            double bi;
            double prefix = (Constants.RecipSqrtPI / Math.Sqrt(sqrtz));
            double expArg = 2 * z1_5 / 3;

            if (expArg < DoubleLimits.MaxLogValue)
            {
                bi = prefix * s1 * Math.Exp(expArg);
            }
            else if (expArg < 2 * DoubleLimits.MaxLogValue)
            {
                double e = Math.Exp(expArg / 2);
                bi = (prefix * s1 * e) * e;
            }
            else
            {
                bi = Math.Exp(expArg - Math.Log(prefix * s1));
            }

            return(bi);
        }
示例#7
0
        /// <summary>
        /// Simple, stable, but not very accurate LogGammaP for small z &lt; 0.7*a
        /// <para>Log((z^a)(e^-z)/(a*Γ(a)) * 1F1(1, a+1, z))</para>
        /// </summary>
        /// <param name="a"></param>
        /// <param name="z"></param>
        /// <returns></returns>
        static double LogGammaPSmallZ(double a, double z)
        {
            // See: http://dlmf.nist.gov/8.5#E1
            double t      = HypergeometricSeries.Sum1F1(1, a + 1, z, -1);
            double result = a * Math.Log(z) - z - Math2.Lgamma(1 + a) + Math2.Log1p(t);

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

            double series = HypergeometricSeries.Sum0F1(v + 1, x * x / 4);
            return prefix * series;
        }
示例#9
0
        // Asymptotic series for positive arguments >=16
        // Mathematica eqns to check "convergence" of asymptotic series:
        // AiTerm[z_, k_] :=  Pochhammer[1/6, k]*Pochhammer[5/6, k]/k! * (-3/(4*z^(3/2)))^k
        // BiTerm[z_, k_] :=  Pochhammer[1/6, k]*Pochhammer[5/6, k]/k! * (3/(4*z^(3/2)))^k
        // Table[N[AiTerm[16, n], 30], {n, 1, 30}]
        // Table[N[BiTerm[16, n], 30], {n, 1, 30}]


        /// <summary>
        /// Returns the AiryAi for positive x using the asymptotic series
        /// </summary>
        /// <param name="x"></param>
        /// <returns></returns>
        /// <seealso href="http://functions.wolfram.com/Bessel-TypeFunctions/AiryAi/06/02/01/01/"/>
        public static double Ai(double x)
        {
            Debug.Assert(x > 0);

            double z     = x;
            double sqrtz = Math.Sqrt(z);
            double z1_5  = z * Math.Sqrt(z);

            double hyp_arg = (-3.0 / 4) / z1_5;
            double s1      = HypergeometricSeries.Sum2F0(1.0 / 6, 5.0 / 6, hyp_arg);

            double prefix = ((Constants.RecipSqrtPI / 2) / Math.Sqrt(sqrtz)) * Math.Exp(-2 * z1_5 / 3);
            double ai     = prefix * s1;

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

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

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

            if (prefix == 0)
                return prefix;

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

            return prefix * series;
        }
示例#11
0
        /// <summary>
        /// Returns the complete elliptic integral of the first kind K(k) = F(π/2,k)
        /// <para>K(k) = ∫ dθ/sqrt(1-k^2*sin^2(θ)), θ={0,π/2}</para>
        /// <para>K(k) = ∫ dt/sqrt((1-t^2)*(1-k^2*t^2)), t={0,1}</para>
        /// </summary>
        /// <param name="k">The modulus. Requires |k| ≤ 1</param>
        public static double EllintK(double k)
        {
            if (!(k >= -1 && k <= 1))
            {
                Policies.ReportDomainError("EllintK(k: {0}): Requires |k| <= 1", k);
                return(double.NaN);
            }

            // See: http://functions.wolfram.com/EllipticIntegrals/EllipticK/06/01/03/01/0004/
            // K(k) = 2F1(1/2, 1/2; 1; k^2)
            // Be careful of notational differences
            double k2 = k * k;

            if (k2 < DoubleLimits.RootMachineEpsilon._13)
            {
                if (k2 < 4 * DoubleLimits.MachineEpsilon)
                {
                    return(Math.PI / 2);
                }
                return((Math.PI / 2) * HypergeometricSeries.Sum2F1(0.5, 0.5, 1, k2));
            }

            if (Math.Abs(k) == 1)
            {
                return(double.PositiveInfinity);
            }

#if false
            // Save this for future reference
            double x     = 0;
            double y     = (1 - k) * (1 + k); // 1-k^2
            double z     = 1;
            double value = EllintRF(x, y, z);
            return(value);
#else
            return((Math.PI / 2) / Agm(1 - k, 1 + k));
#endif
        }
示例#12
0
        /// <summary>
        /// Returns the complete elliptic integral of the second kind E(k) = E(π/2,k)
        /// <para>E(k) = ∫ sqrt(1-k^2*sin^2(θ)) dθ, θ={0,π/2}</para>
        /// </summary>
        /// <param name="k">The modulus. Requires |k| ≤ 1</param>
        public static double EllintE(double k)
        {
            if (!(k >= -1 && k <= 1))
            {
                Policies.ReportDomainError("EllintE(k: {0}): Requires |k| <= 1", k);
                return(double.NaN);
            }

            // Small series at k == 0
            // Note that z = k^2 in the following link
            // http://functions.wolfram.com/EllipticIntegrals/EllipticE/06/01/03/01/0004/

            double k2 = k * k;

            if (k2 < DoubleLimits.RootMachineEpsilon._13)
            {
                if (k2 < 4 * DoubleLimits.MachineEpsilon)
                {
                    return(Math.PI / 2); // E(0) = Pi/2
                }
                return((Math.PI / 2) * HypergeometricSeries.Sum2F1(-0.5, 0.5, 1, k2));
            }

            if (Math.Abs(k) == 1)
            {
                return(1);
            }

            double x = 0;
            double y = 1 - k2;
            double z = 1;

            double value = 2 * Math2.EllintRG(x, y, z);

            return(value);
        }
示例#13
0
        //
        // Series form for BesselY as z -> 0, 
        // see: http://functions.wolfram.com/Bessel-TypeFunctions/BesselY/06/01/04/01/01/0003/
        // This series is only useful when the second term is small compared to the first
        // otherwise we get catestrophic cancellation errors.
        //
        // Approximating tgamma(v) by v^v, and assuming |tgamma(-z)| < eps we end up requiring:
        // eps/2 * v^v(x/2)^-v > (x/2)^v or Math.Log(eps/2) > v Math.Log((x/2)^2/v)
        //


        /// <summary>
        /// Series form for Y{v}(x) as z -&gt; 0 and v is not an integer
        /// <para>Y{v}(z) = (-((z/2)^v Cos(πv) Γ(-v))/π)) * 0F1(; 1+v; -z^2/4) - ((z/2)^-v Γ(v))/π) * 0F1(; 1-v; -z^2/4)</para>
        /// </summary>
        /// <param name="v"></param>
        /// <param name="x"></param>
        /// <returns></returns>
        /// <seealso href="http://functions.wolfram.com/Bessel-TypeFunctions/BesselY/26/01/02/0001/"/>
        public static DoubleX Y_SmallArgSeries(double v, double x)
        {
            Debug.Assert(v >= 0 && x >= 0);
            Debug.Assert( !Math2.IsInteger(v), "v cannot be an integer");


            DoubleX powDivGamma;
            double lnp = v * Math.Log(x / 2);
            if (v > DoubleLimits.MaxGamma || lnp < DoubleLimits.MinLogValue || lnp > DoubleLimits.MaxLogValue) {
                powDivGamma = DoubleX.Exp(lnp - Math2.Lgamma(v));
            } else {
                DoubleX p = Math.Pow(x / 2, v);
                DoubleX gam = Math2.Tgamma(v);
                powDivGamma = p / gam;
            }


            // Series 1: -((z/2)^-v Γ(v))/π) * 0F1(; 1-v; -z^2/4)

            double s1 = HypergeometricSeries.Sum0F1(1 - v, -x * x / 4);
            DoubleX result1 = -(s1/Math.PI)/ powDivGamma;

            // Series2: -((z/2)^v Cos(πv) Γ(-v))/π)) * 0F1(; 1+v; -z^2/4)
            // using the reflection formula: Γ(-v) = -π/(Γ(v) * v * Sin(π * v)) 
            // prefix = (z/2)^v * Cos(π * v)/(Γ(v) * v * Sin(π * v)) 
            // prefix = (z/2)^v * Cot(π * v) / (Γ(v) * v) 

            DoubleX result2 = DoubleX.Zero;
            double cot = Math2.CotPI(v);
            if (cot != 0) {
                double s2 = HypergeometricSeries.Sum0F1(v + 1, -x * x / 4);
                result2 = powDivGamma * cot * s2 / v; // Do this all in DoubleX
            }

            return (result1 - result2);
        }
示例#14
0
        /// <summary>
        /// Computes Erf, Erfc, or Erfcx
        /// </summary>
        /// <param name="x"></param>
        /// <param name="complement">Compute 1-Erf</param>
        /// <param name="scaled">Compute Erfcx</param>
        /// <returns></returns>
        private static double Erf_Imp(double x, bool complement, bool scaled)
        {
            const double MaxErfArg  = 5.81;
            const double MaxErfcArg = 27.25;

            if (x < 0)
            {
                // Transformations:
                // Erf(-x) = -Erf(x);
                // Erfc(-x) = 1+Erf(x)
                // Erfcx(-x) = Math.Exp(x * x) * (1 + Erf(-x))

                if (!complement)
                {
                    return(-Erf_Imp(-x, false, false));
                }
                if (!scaled)
                {
                    return(1 + Erf_Imp(-x, false, false));
                }
                return(Math.Exp(x * x) * (1 + Erf_Imp(-x, false, false)));
            }

            double result = 0;

            if (x <= 0.5)
            {
                // In this region compute Erf

                if (x < DoubleLimits.RootMachineEpsilon._2)
                {
                    // _Y0 = 2/Sqrt(PI)
                    const double Y = 1.128379167095512573896158903121545171688101258657997713688171;

                    result = x * Y;
                }
                else
                {
                    // Maximum Deviation Found:                     1.561e-17
                    // Expected Error Term:                         1.561e-17
                    // Maximum Relative Change in Control Points:   1.155e-04
                    // Max Error found at double precision =        2.961182e-17

                    const double Y = 1.044948577880859375;

                    const double p0 = 0.0834305892146531832907;
                    const double p1 = -0.338165134459360935041;
                    const double p2 = -0.0509990735146777432841;
                    const double p3 = -0.00772758345802133288487;
                    const double p4 = -0.000322780120964605683831;

                    const double q0 = 1;
                    const double q1 = 0.455004033050794024546;
                    const double q2 = 0.0875222600142252549554;
                    const double q3 = 0.00858571925074406212772;
                    const double q4 = 0.000370900071787748000569;

                    double z = x * x;
                    double P = p0 + z * (p1 + z * (p2 + z * (p3 + z * p4)));
                    double Q = q0 + z * (q1 + z * (q2 + z * (q3 + z * q4)));

                    result = x * (Y + P / Q);
                }

                // Erfc = 1-Erf
                // Erfcx = exp(z*z)*(1-Erf)
                if (complement)
                {
                    result = 1 - result;
                }
                if (scaled)
                {
                    result *= Math.Exp(x * x);
                }

                return(result);
            }

            // For double precision,
            // Erf(z) ~= 1, when z >= 5.81
            // Erfc(x) ~= 0, when x >= 27.25
            if (!complement)
            {
                if (x >= MaxErfArg)
                {
                    return(1);
                }
            }
            else if (!scaled)
            {
                if (x >= MaxErfcArg)
                {
                    return(0);
                }
            }

            // compute Erfcx = exp(z*z) * erfc(z)

            if (x < 1.5)
            {
                // Maximum Deviation Found:                     3.702e-17
                // Expected Error Term:                         3.702e-17
                // Maximum Relative Change in Control Points:   2.845e-04
                // Max Error found at double precision =        4.841816e-17
                const double Y = 0.405935764312744140625;

                const double p0 = -0.098090592216281240205;
                const double p1 = 0.178114665841120341155;
                const double p2 = 0.191003695796775433986;
                const double p3 = 0.0888900368967884466578;
                const double p4 = 0.0195049001251218801359;
                const double p5 = 0.00180424538297014223957;

                const double q0 = 1;
                const double q1 = 1.84759070983002217845;
                const double q2 = 1.42628004845511324508;
                const double q3 = 0.578052804889902404909;
                const double q4 = 0.12385097467900864233;
                const double q5 = 0.0113385233577001411017;
                const double q6 = 0.337511472483094676155e-5;

                double z = x - 0.5;

                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) / x;
            }
            else if (x < 2.5)
            {
                // Max Error found at double precision =        6.599585e-18
                // Maximum Deviation Found:                     3.909e-18
                // Expected Error Term:                         3.909e-18
                // Maximum Relative Change in Control Points:   9.886e-05
                const double Y = 0.50672817230224609375;

                const double p0 = -0.0243500476207698441272;
                const double p1 = 0.0386540375035707201728;
                const double p2 = 0.04394818964209516296;
                const double p3 = 0.0175679436311802092299;
                const double p4 = 0.00323962406290842133584;
                const double p5 = 0.000235839115596880717416;

                const double q0 = 1;
                const double q1 = 1.53991494948552447182;
                const double q2 = 0.982403709157920235114;
                const double q3 = 0.325732924782444448493;
                const double q4 = 0.0563921837420478160373;
                const double q5 = 0.00410369723978904575884;

                double z = x - 1.5;
                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 + P / Q) / x;
            }
            else if (x < 4.5)
            {
                // Maximum Deviation Found:                     1.512e-17
                // Expected Error Term:                         1.512e-17
                // Maximum Relative Change in Control Points:   2.222e-04
                // Max Error found at double precision =        2.062515e-17
                const double Y = 0.5405750274658203125;

                const double p0 = 0.00295276716530971662634;
                const double p1 = 0.0137384425896355332126;
                const double p2 = 0.00840807615555585383007;
                const double p3 = 0.00212825620914618649141;
                const double p4 = 0.000250269961544794627958;
                const double p5 = 0.113212406648847561139e-4;

                const double q0 = 1;
                const double q1 = 1.04217814166938418171;
                const double q2 = 0.442597659481563127003;
                const double q3 = 0.0958492726301061423444;
                const double q4 = 0.0105982906484876531489;
                const double q5 = 0.000479411269521714493907;


                double z = x - 3.5;
                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 + P / Q) / x;
            }
            else if (x < 28)
            {
                // Max Error found at double precision =        2.997958e-17
                // Maximum Deviation Found:                     2.860e-17
                // Expected Error Term:                         2.859e-17
                // Maximum Relative Change in Control Points:   1.357e-05
                const double Y = 0.5579090118408203125;

                const double p0 = 0.00628057170626964891937;
                const double p1 = 0.0175389834052493308818;
                const double p2 = -0.212652252872804219852;
                const double p3 = -0.687717681153649930619;
                const double p4 = -2.5518551727311523996;
                const double p5 = -3.22729451764143718517;
                const double p6 = -2.8175401114513378771;

                const double q0 = 1;
                const double q1 = 2.79257750980575282228;
                const double q2 = 11.0567237927800161565;
                const double q3 = 15.930646027911794143;
                const double q4 = 22.9367376522880577224;
                const double q5 = 13.5064170191802889145;
                const double q6 = 5.48409182238641741584;


                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 = (Y + P / Q) / x;
            }
            else
            {
                // otherwise use the asymptotic series
                result = Constants.RecipSqrtPI / x;
                if (x < Constants.RecipSqrt2 / DoubleLimits.RootMachineEpsilon._2)
                {
                    result *= HypergeometricSeries.Sum2F0(1, 0.5, -1 / (x * x));
                }
            }

            // Erfc = exp(-z*z) * Erfcx
            // Erf = 1-Erfc
            if (!scaled)
            {
                result *= Math.Exp(-x * x);
            }
            if (!complement)
            {
                result = 1 - result;
            }

            return(result);
        }
示例#15
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);
            }
        }
示例#16
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);
            }
        }
示例#17
0
 /// <summary>
 /// Lower gamma series sum: Σ( z^k/(a+1)_k) k={0, inf}
 /// <para>γ(a,z) = ((z^a) * (e^-z) / a) * LowerSeriesSum(a,z)</para>
 /// </summary>
 /// <param name="a"></param>
 /// <param name="z"></param>
 /// <returns></returns>
 /// <see href="http://dlmf.nist.gov/8.5#E1"/>
 public static double LowerSeries(double a, double z)
 {
     // Sum1F1 is optimized for a1 == 1
     return(HypergeometricSeries.Sum1F1(1, 1 + a, z));
 }
示例#18
0
 /// <summary>
 /// Asymptotic series for Γ(a, z) with z &gt; a
 /// <para>Currently set to z &gt;= Max(50, a * 10)</para>
 /// <para>Σ( (-1)^k * (1-a)_{k} * z^-k, k={0, inf})</para>
 /// <para>Γ(a, z) = e^-z * z^(a-1) * TgammaAsymSeries(a,z)</para>
 /// </summary>
 /// <param name="a"></param>
 /// <param name="z"></param>
 /// <returns></returns>
 /// <seealso href="http://functions.wolfram.com/GammaBetaErf/Gamma2/06/02/02/"/>
 /// <remarks>
 /// Targeting n=20: As a-> 0, n!/z^n &lt; eps, so z = 50
 /// Targeting n=16: As a-> inf, (a/z)^n &lt; eps, z ~= a*2^(52/16)
 /// Result ~= 1
 /// </remarks>
 public static double Asym_SeriesLargeZ(double a, double z)
 {
     //Sum2F0 is optimized for a0=1
     return(HypergeometricSeries.Sum2F0(1, 1 - a, -1 / z));
 }