Beispiel #1
0
        /// <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);
        }
Beispiel #2
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
            }
        }
Beispiel #3
0
        /// <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);
        }