/// <summary> /// Compute (J, Y) using Steeds method /// </summary> /// <param name="v"></param> /// <param name="x"></param> /// <param name="needJ"></param> /// <param name="needY"></param> /// <returns></returns> static (double J, DoubleX Y) JY_Steed(double v, double x, bool needJ, bool needY) { Debug.Assert(x > 0 && v >= 0); Debug.Assert(needJ || needY); double J = double.NaN; DoubleX Y = DoubleX.NaN; int n = (int)Math.Floor(v + 0.5); double u = v - n; Debug.Assert(u >= -0.5 && u < 0.5); // Ensure u in [-1/2, 1/2) // compute J{v+1}(x) / J{v}(x) var(fv, s) = J_CF1(v, x); var(Jvmn, Jvmnp1, scale) = Recurrence.BackwardJY_B(v, x, n, s, fv * s); double ratio = s / Jvmn; // normalization double fu = Jvmnp1 / Jvmn; // prev/current, can also call CF1_jy() to get fu, not much difference in precision //double fuCF; //int sfu; //Bessel.CF1_jy(u,x,fuCF,sfu); //fu = fuCF; var(p, q) = JY_CF2(u, x); // continued fraction JY_CF2 double t = u / x - fu; // t = J'/J double gamma = (p - t) / q; // // We can't allow gamma to cancel out to zero competely as it messes up // the subsequent logic. So pretend that one bit didn't cancel out // and set to a suitably small value. The only test case we've been able to // find for this, is when v = 8.5 and x = 4*PI. // if (gamma == 0) { gamma = u * DoubleLimits.MachineEpsilon / x; } double W = (2 / Math.PI) / x; // Wronskian double Ju = Math.Sign(Jvmn) * Math.Sqrt(W / (q + gamma * (p - t))); double Jv = Ju * ratio; // normalization J = Math2.Ldexp(Jv, -scale); if (needY) { double Yu = gamma * Ju; double Yu1 = Yu * (u / x - p - q / gamma); var(JYvpn, JYvpnm1, YScale) = Recurrence.ForwardJY_B(u + 1, x, n, Yu1, Yu); Y = DoubleX.Ldexp(JYvpnm1, YScale); } return(J, Y); }
/// <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 } }
/// <summary> /// Computes J{v}(x), Y{v}(x) using a combination of asymptotic approximation and recurrence /// </summary> /// <param name="v"></param> /// <param name="x"></param> /// <param name="needJ"></param> /// <param name="needY"></param> /// <returns></returns> static (double J, DoubleX Y) JY_AsymRecurrence(double v, double x, bool needJ, bool needY) { Debug.Assert(v >= 0 && x >= 1, "Requires positive values for v,x"); Debug.Assert(v < int.MaxValue, "v too large: v = " + v); var J = double.NaN; var Y = DoubleX.NaN; int nPos = (int)Math.Floor(v); double uPos = v - nPos; // Using Hankel, find: // J{v-Floor(v)}(x) and J{v-Floor(v)+1}(x) // Y{v-Floor(v)}(x) and Y{v-Floor(v)+1}(x) // then use recurrence to find J{v}(x), Y{v}(x) double u0, u1;; int n; if (x >= 9) { // set the start of the recurrence near sqrt(x) double maxV = Math.Floor(Math.Sqrt(x)); u1 = (maxV - 1) + uPos; u0 = u1 - 1; n = (int)Math.Floor(v - u1 + 0.5); Debug.Assert(n >= 0); } else { u0 = uPos; u1 = uPos + 1; n = nPos - 1; } Debug.Assert(x >= HankelAsym.JYMinX(u1), "x is too small for HankelAsym"); var(Ju, Yu) = HankelAsym.JY(u0, x); var(Jup1, Yup1) = HankelAsym.JY(u1, x); if (needJ) { if (v < x) { J = Recurrence.ForwardJY(u1, x, n, Jup1, Ju).JYvpn; } else { // Use fv = J{v+1}(x) / J{v}(x) // and backward recurrence to find (J{v-n+1}/J{v-n}) var(fv, s) = J_CF1(v, x); var(Jvmn, Jvmnp1, scale) = Recurrence.BackwardJY_B(v, x, n, s, fv * s); var Jv = Math2.Ldexp(Jup1 / Jvmn, -scale); J = s * Jv; // normalization } } if (needY) { var(Yv, Yvm1, YScale) = Recurrence.ForwardJY_B(u1, x, n, Yup1, Yu); Y = DoubleX.Ldexp(Yv, YScale); } return(J, Y); }