/// <summary> /// Compute J{v}(x) and Y{v}(x) /// </summary> /// <param name="v"></param> /// <param name="x"></param> /// <param name="needJ"></param> /// <param name="needY"></param> /// <returns></returns> public static (double J, DoubleX Y) JY(double v, double x, bool needJ, bool needY) { Debug.Assert(needJ || needY); // uses Steed's method // see Barnett et al, Computer Physics Communications, vol 8, 377 (1974) // set [out] parameters double J = double.NaN; DoubleX Y = DoubleX.NaN; // check v first so that there are no integer throws later if (Math.Abs(v) > int.MaxValue) { Policies.ReportNotImplementedError("BesselJY(v: {0}): Large |v| > int.MaxValue not yet implemented", v); return(J, Y); } if (v < 0) { v = -v; if (Math2.IsInteger(v)) { // for integer orders only, we can use the following identities: // J{-n}(x) = (-1)^n * J{n}(x) // Y{-n}(x) = (-1)^n * Y{n}(x) if (Math2.IsOdd(v)) { var(JPos, YPos) = JY(v, x, needJ, needY); if (needJ) { J = -JPos; } if (needY) { Y = -YPos; } return(J, Y); } } if (v - Math.Floor(v) == 0.5) { Debug.Assert(v >= 0); // use reflection rule: // for integer m >= 0 // J{-(m+1/2)}(x) = (-1)^(m+1) * Y{m+1/2}(x) // Y{-(m+1/2)}(x) = (-1)^m * J{m+1/2}(x) // call the general bessel functions with needJ and needY reversed var(JPos, YPos) = JY(v, x, needY, needJ); double m = v - 0.5; bool isOdd = Math2.IsOdd(m); if (needJ) { double y = (double)YPos; J = isOdd ? y : -y; } if (needY) { Y = isOdd ? -JPos : JPos; } return(J, Y); } else { // use reflection rule: // J{-v}(x) = cos(pi*v)*J{v}(x) - sin(pi*v)*Y{v}(x) // Y{-v}(x) = sin(pi*v)*J{v}(x) + cos(pi*v)*Y{v}(x) var(JPos, YPos) = JY(v, x, true, true); double cp = Math2.CosPI(v); double sp = Math2.SinPI(v); J = cp * JPos - (double)(sp * YPos); Y = sp * JPos + cp * YPos; return(J, Y); } } // both x and v are positive from here Debug.Assert(x >= 0 && v >= 0); if (x == 0) { // For v > 0 if (needJ) { J = 0; } if (needY) { Y = DoubleX.NegativeInfinity; } return(J, Y); } int n = (int)Math.Floor(v + 0.5); double u = v - n; // -1/2 <= u < 1/2 // is it an integer? if (u == 0) { if (v == 0) { if (needJ) { J = J0(x); } if (needY) { Y = Y0(x); } return(J, Y); } if (v == 1) { if (needJ) { J = J1(x); } if (needY) { Y = Y1(x); } return(J, Y); } // for integer order only if (needY && x < DoubleLimits.RootMachineEpsilon._2) { Y = YN_SmallArg(n, x); if (!needJ) { return(J, Y); } needY = !needY; } } if (needJ && ((x < 5) || (v > x * x / 4))) { // always use the J series if we can J = J_SmallArg(v, x); if (!needY) { return(J, Y); } needJ = !needJ; } if (needY && x <= 2) { // J should have already been solved above Debug.Assert(!needJ); // Evaluate using series representations. // Much quicker than Y_Temme below. // This is particularly important for x << v as in this // area Y_Temme may be slow to converge, if it converges at all. // for non-integer order only if (u != 0) { if ((x < 1) && (Math.Log(DoubleLimits.MachineEpsilon / 2) > v * Math.Log((x / 2) * (x / 2) / v))) { Y = Y_SmallArgSeries(v, x); return(J, Y); } } // Use Temme to find Yu where |u| <= 1/2, then use forward recurrence for Yv var(Yu, Yu1) = Y_Temme(u, x); var(Yvpn, Yvpnm1, YScale) = Recurrence.ForwardJY_B(u + 1, x, n, Yu1, Yu); Y = DoubleX.Ldexp(Yvpnm1, YScale); return(J, Y); } Debug.Assert(x > 2 && v >= 0); // Try asymptotics directly: if (x > v) { // x > v*v if (x >= HankelAsym.JYMinX(v)) { var result = HankelAsym.JY(v, x); if (needJ) { J = result.J; } if (needY) { Y = result.Y; } return(J, Y); } // Try Asymptotic Phase for x > 47v if (x >= MagnitudePhase.MinX(v)) { var result = MagnitudePhase.BesselJY(v, x); if (needJ) { J = result.J; } if (needY) { Y = result.Y; } return(J, Y); } } // fast and accurate within a limited range of v ~= x if (UniformAsym.IsJYPrecise(v, x)) { var(Jv, Yv) = UniformAsym.JY(v, x); if (needJ) { J = Jv; } if (needY) { Y = Yv; } return(J, Y); } // Try asymptotics with recurrence: if (x > v && x >= HankelAsym.JYMinX(v - Math.Floor(v) + 1)) { var(Jv, Yv) = JY_AsymRecurrence(v, x, needJ, needY); if (needJ) { J = Jv; } if (needY) { Y = Yv; } return(J, Y); } // Use Steed's Method var(SteedJv, SteedYv) = JY_Steed(v, x, needJ, needY); if (needJ) { J = SteedJv; } if (needY) { Y = SteedYv; } return(J, Y); }
/// <summary> /// Try to use asymptotics to generate the result /// </summary> /// <param name="v"></param> /// <param name="x"></param> /// <param name="J"></param> /// <param name="Y"></param> /// <param name="needJ"></param> /// <param name="needY"></param> /// <returns></returns> public static bool JY_TryAsymptotics(double v, double x, out double J, out DoubleX Y, bool needJ, bool needY) { J = double.NaN; Y = DoubleX.NaN; // Try asymptotics directly: if (x > v) { // x > v*v if (x >= HankelAsym.JYMinX(v)) { var result = HankelAsym.JY(v, x); if (needJ) { J = result.J; } if (needY) { Y = result.Y; } return(true); } // Try Asymptotic Phase for x > 47v if (x >= MagnitudePhase.MinX(v)) { var result = MagnitudePhase.BesselJY(v, x); if (needJ) { J = result.J; } if (needY) { Y = result.Y; } return(true); } } if (UniformAsym.IsJYPrecise(v, x)) { var(Jv, Yv) = UniformAsym.JY(v, x); if (needJ) { J = Jv; } if (needY) { Y = Yv; } return(true); } // Try asymptotics with recurrence if (x > v && x >= HankelAsym.JYMinX(v - Math.Floor(v) + 1)) { var(Jv, Yv) = JY_AsymRecurrence(v, x, needJ, needY); if (needJ) { J = Jv; } if (needY) { Y = Yv; } return(true); } return(false); }