/// <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); }
/// <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); }
/// <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); } }
/// <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); }
/// <summary> /// Returns the Spherical Bessel function of the first kind: j<sub>n</sub>(x) /// </summary> /// <param name="n">Order. Requires n >= 0</param> /// <param name="x">Argument. Requires x >= 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)); }
/// <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); }
/// <summary> /// Simple, stable, but not very accurate LogGammaP for small z < 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); }
/// <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; }
// 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); }
/// <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; }
/// <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 }
/// <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); }
// // 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 -> 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); }
/// <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); }
/// <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); } }
/// <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); } }
/// <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)); }
/// <summary> /// Asymptotic series for Γ(a, z) with z > a /// <para>Currently set to z >= 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 < eps, so z = 50 /// Targeting n=16: As a-> inf, (a/z)^n < 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)); }