Пример #1
0
            /// <summary>
            /// Returns (J{v+n}(x), J{v+n-1}(x)) OR (Y{v+n}(x), Y{v+n-1}(x)) after n forward recurrences
            /// </summary>
            /// <param name="v">Order of the current parameter</param>
            /// <param name="x">Argument</param>
            /// <param name="n">Number of forward recurrence steps. 0 returns (current,prev)</param>
            /// <param name="current">J{v}(x) OR Y{v}(x)</param>
            /// <param name="prev">J{v-1}(x) OR Y{v-1}(x)</param>
            /// <returns></returns>
            public static (double JYvpn, double JYvpnm1) ForwardJY(double v, double x, int n, double current, double prev)
            {
                int scale = 0;

                for (int k = 0; k < n; k++)
                {
                    double fact  = 2 * (v + k) / x;
                    double value = fact * current - prev;

                    if (double.IsInfinity(value))
                    {
                        int exp = 0;
                        current = Math2.Frexp(current, out exp);
                        prev    = Math2.Ldexp(prev, -exp);
                        scale  += exp;
                        value   = fact * current - prev;
                    }
                    prev    = current;
                    current = value;
                }

                if (scale != 0)
                {
                    current = Math2.Ldexp(current, scale);
                    prev    = Math2.Ldexp(prev, scale);
                }

                return(current, prev);
            }
Пример #2
0
            /// <summary>
            /// Returns (K{v+n}, K{v+n-1}) after n forward recurrences
            /// </summary>
            /// <param name="v">Order of the current parameter</param>
            /// <param name="x">Argument</param>
            /// <param name="n">Number of forward recurrence steps. 0 returns (current,prev)</param>
            /// <param name="current">K{v}(x)</param>
            /// <param name="prev">K{v-1}(x)</param>
            /// <returns></returns>
            public static (double Kvpn, double Kvpnm1) ForwardK(double v, double x, int n, double current, double prev)
            {
                Debug.Assert(n >= 0);

                // binary scale
                int binaryScale = 0;

                for (int k = 0; k < n; k++)
                {
                    double factor = 2 * (v + k) / x;
                    double value  = factor * current + prev;

                    if (double.IsInfinity(value))
                    {
                        int exp = 0;
                        current      = Math2.Frexp(current, out exp);
                        prev         = Math2.Ldexp(prev, -exp);
                        binaryScale += exp;
                        value        = factor * current + prev;
                    }

                    prev    = current;
                    current = value;
                }

                if (binaryScale != 0)
                {
                    current = Math2.Ldexp(current, binaryScale);
                    prev    = Math2.Ldexp(prev, binaryScale);
                }

                return(current, prev);
            }
Пример #3
0
            /// <summary>
            /// Returns a tuple (J{v+n}(x), J{v+n-1}(x), BScale) OR (Y{v+n}(x), Y{v+n-1}(x), BScale) after n forward recurrences.
            /// Note: to get actual values multiply by 2^BScale.
            /// </summary>
            /// <param name="v">Order of the current parameter</param>
            /// <param name="x">Argument</param>
            /// <param name="n">Number of forward recurrence steps. 0 returns (current,prev)</param>
            /// <param name="current">J{v}(x) OR Y{v}(x)</param>
            /// <param name="prev">J{v-1}(x) OR Y{v-1}(x)</param>
            /// <returns></returns>
            public static (double JYvpn, double JYvpnm1, int BScale) ForwardJY_B(double v, double x, int n, double current, double prev)
            {
                Debug.Assert(n >= 0);
                Debug.Assert(!double.IsInfinity(2 * (v + n) / x));

                int binaryScale = 0;

                for (int k = 0; k < n; k++)
                {
                    double fact  = 2 * (v + k) / x;
                    double value = fact * current - prev;

                    if (double.IsInfinity(value))
                    {
                        int exp = 0;
                        current      = Math2.Frexp(current, out exp);
                        prev         = Math2.Ldexp(prev, -exp);
                        binaryScale += exp;
                        value        = fact * current - prev;
                    }
                    prev    = current;
                    current = value;
                }

                {
                    int exp = 0;
                    current      = Math2.Frexp(current, out exp);
                    prev         = Math2.Ldexp(prev, -exp);
                    binaryScale += exp;
                }

                return(current, prev, binaryScale);
            }
Пример #4
0
            /// <summary>
            /// Returns (J{v-n}(x), J{v-n+1}(x)) OR (Y{v-n}(x), Y{v-n+1}(x)) after n backward recurrences
            /// </summary>
            /// <param name="v">Order of the current parameter</param>
            /// <param name="x">Argument</param>
            /// <param name="n">Number of backward recurrence steps. 0 returns (current,prev)</param>
            /// <param name="current">J{v}(x) OR Y{v}(x)</param>
            /// <param name="prev">J{v+1}(x) OR Y{v+1}(x)</param>
            /// <returns></returns>
            public static (double JYvmn, double JYvmnp1) BackwardJY(double v, double x, int n, double current, double prev)
            {
                Debug.Assert(n >= 0);
                Debug.Assert(!double.IsInfinity(2 * v / x));

                int scale = 0;

                for (int k = 0; k < n; k++)
                {
                    double fact = 2 * (v - k) / x;
                    double next = fact * current - prev;

                    if (double.IsInfinity(next))
                    {
                        int exp = 0;
                        current = Math2.Frexp(current, out exp);
                        prev    = Math2.Ldexp(prev, -exp);
                        scale  += exp;

                        next = fact * current - prev;
                    }

                    prev    = current;
                    current = next;
                }

                if (scale != 0)
                {
                    current = Math2.Ldexp(current, scale);
                    prev    = Math2.Ldexp(prev, scale);
                }

                return(current, prev);
            }
Пример #5
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);
        }
Пример #6
0
        /// <summary>
        /// Computes the Jacobi elliptic functions using the arithmetic-geometric mean
        /// </summary>
        /// <param name="k">The modulus</param>
        /// <param name="u">The argument</param>
        /// <param name="anm1"></param>
        /// <param name="bnm1"></param>
        /// <param name="N"></param>
        /// <param name="pTn"></param>
        /// <returns></returns>
        /// <see href="http://dlmf.nist.gov/22.20#ii"/>
        private static double Jacobi_Recurse(double k, double u, double anm1, double bnm1, int N, out double pTn)
        {
            ++N;
            double Tn;
            double cn = (anm1 - bnm1) / 2;
            double an = (anm1 + bnm1) / 2;

            if (cn < DoubleLimits.MachineEpsilon)
            {
                Tn = Math2.Ldexp(1, N) * u * an;
            }
            else
            {
                Tn = Jacobi_Recurse(k, u, an, Math.Sqrt(anm1 * bnm1), N, out double unused);
            }

            pTn = Tn;
            return((Tn + Math.Asin((cn / an) * Math.Sin(Tn))) / 2);
        }
Пример #7
0
        /// <summary>
        /// Returns the double factorial = n!!
        /// <para>If n is odd, returns: n * (n-2) * ... * 5 * 3 * 1</para>
        /// <para>If n is even, returns: n * (n-2) * ... * 6 * 4 * 2</para>
        /// <para>If n = 0, -1, returns 1</para>
        /// </summary>
        /// <param name="n">Requires n ≥ -1</param>
        public static double Factorial2(int n)
        {
            if (n < -1)
            {
                Policies.ReportDomainError("Factorial2(n: {0}): Requires n >= -1", n);
                return(double.NaN);
            }

            // common values
            if (n == -1 || n == 0)
            {
                return(1);
            }

            double result;

            if (IsOdd(n))
            {
                // odd n:
                if (n < FactorialTable.Length)
                {
                    int k = (n - 1) / 2;
                    return(Math.Ceiling(Math2.Ldexp(FactorialTable[n] / FactorialTable[k], -k) - 0.5));
                }
                //
                // Fallthrough: n is too large to use table lookup, try the
                // gamma function instead.
                //
                result = Constants.RecipSqrtPI * Math2.Tgamma(0.5 * n + 1.0);
                result = Math.Ceiling(Math2.Ldexp(result, (n + 1) / 2) - 0.5);
            }
            else
            {
                // even n:
                int k = n / 2;
                result = Math2.Ldexp(Factorial(k), k);
            }

            return(result);
        }
Пример #8
0
        //
        // Spherical
        //


        /// <summary>
        /// Returns the associated legendre polynomial with angle parameterization P{l,m}( cos(theta) )
        /// </summary>
        /// <param name="n"></param>
        /// <param name="m"></param>
        /// <param name="theta"></param>
        /// <returns></returns>
        static double LegendrePAngle(int n, int m, double theta)
        {
            if (n < 0)
            {
                n = -(n + 1); // P{n,m}(x) = P{-n-1,m}(x)
            }
            if (m == 0)
            {
                return(Math2.LegendreP(n, Math.Cos(theta)));
            }

            // if ( abs(m) < n ) return 0 by definition; handling int.MinValue too
            if (m < -n || m > n)
            {
                return(0);
            }

            // P{n,-m} = (-1)^m * (n-m)!/(n+m)! * P{n,m}
            if (m < 0)
            {
                double legP = LegendrePAngle(n, -m, theta);
                if (legP == 0)
                {
                    return(legP);
                }
                if (IsOdd(m))
                {
                    legP = -legP;
                }

                int MaxFactorial = Math2.FactorialTable.Length - 1;
                if (n - m <= MaxFactorial)
                {
                    double ratio = Math2.FactorialTable[n + m] / Math2.FactorialTable[n - m];
                    return(ratio * legP);
                }

                if (n - m <= 300)
                {
                    // break up the Tgamma ratio into two separate factors
                    // to prevent an underflow: a!/b! = (a!/170!) * (170!/b!)
                    // 170!/300! ~= 2.37e-308 > double.MinNormalizedValue

                    return((legP * (Math2.FactorialTable[n + m] / Math2.FactorialTable[MaxFactorial])) * Math2.TgammaRatio(MaxFactorial + 1, n + 1 - m));
                }


#if false
                // since we are setting Lmax = 128, so Mmax = 128, and Lmax+Mmax = 256
                // so this should never be called, but left here for completeness
                // 170!/300! ~= 2.37e-308 > double.MinNormalizedValue

                double ratio = Math2.TgammaRatio(l + m + 1, l + 1 - m);
                if (ratio >= DoubleLimits.MinNormalizedValue)
                {
                    return(ratio * legP);
                }

                double value = Math.Exp(Math2.Lgamma(l + m + 1) - Math2.Lgamma(l + 1 - m) + Math.Log(Math.Abs(legP)));
                return(Math.Sign(legP) * value);
#else
                Policies.ReportNotImplementedError("LegendrePAngle(l: {0}, m: {1}, theta: {2}): Not implemented for |l| >= 128", n, m, theta);
                return(double.NaN);
#endif
            }

            // at this point n >= 0 && 0 <= m <= n
            Debug.Assert(n >= 0 && (m >= 0 && m <= n));

            // use the recurrence relations
            // P{n, n} = (-1)^n * (2*n - 1)!! * (1 - x^2)^(n/2)
            // P{n+1, n} = (2*n + 1) * x * P{n, n}
            // (n-m+1)*P{n+1,m} = (2*n + 1)* x * P{n,m} - (n+m)*P{n-1, m}
            // see: http://en.wikipedia.org/wiki/Associated_Legendre_polynomials

            double x         = Math.Cos(theta);
            double sin_theta = Math.Sin(theta);

            // the following computes (2m-1)!! * sin_theta^m
            // taking special care not to underflow too soon
            // double pmm = Math.Pow(sin_theta, m) * Math2.Factorial2(2 * m - 1)

            int    exp       = 0;
            double mant      = Math2.Frexp(sin_theta, out exp);
            double pmmScaled = Math.Pow(mant, m) * Math2.Factorial2(2 * m - 1);
            double pmm       = Math2.Ldexp(pmmScaled, m * exp);

            if (IsOdd(m))
            {
                pmm = -pmm;
            }

            if (n == m)
            {
                return(pmm);
            }

            // p0 - current
            // p1 - next

            double p0 = pmm;
            double p1 = (2.0 * m + 1.0) * x * p0; //P{m+1,m}(x)

            for (uint k = (uint)m + 1; k < (uint)n; k++)
            {
                double next = ((2 * k + 1) * x * p1 - (k + (uint)m) * p0) / (k - (uint)m + 1);

                p0 = p1;
                p1 = next;
            }

            return(p1);
        }
Пример #9
0
        /// <summary>
        /// Returns the Associated Legendre Polynomial of the first kind
        /// </summary>
        /// <param name="n">Polynomial degree. Limited to |<paramref name="n"/>| ≤ 127</param>
        /// <param name="m">Polynomial order</param>
        /// <param name="x">Requires |<paramref name="x"/>| ≤ 1</param>
        public static double LegendreP(int n, int m, double x)
        {
            if (!(x >= -1 && x <= 1))
            {
                Policies.ReportDomainError("LegendreP(n: {0}, m: {1}, x: {2}): Requires |x| <= 1", n, m, x);
                return(double.NaN);
            }
            if (n <= -128 || n >= 128)
            {
                Policies.ReportNotImplementedError("LegendreP(n: {0}, m: {1}, x: {2}): Not implemented for |n| >= 128", n, m, x);
                return(double.NaN);
            }

            // P{l,m}(x) == P{-l-1,m}(x); watch overflow
            if (n < 0)
            {
                n = -(n + 1);
            }

            if (m == 0)
            {
                return(Math2.LegendreP(n, x));
            }

            // by definition P{n,m}(x) == 0 when |m| > l
            if (m < -n || m > n)
            {
                return(0);
            }

            // P{n,-m} = (-1)^m * (n-m)!/(n+m)! * P{n,m}
            if (m < 0)
            {
                double legP = LegendreP(n, -m, x);
                if (legP == 0)
                {
                    return(legP);
                }
                if (IsOdd(m))
                {
                    legP = -legP;
                }

                int MaxFactorial = Math2.FactorialTable.Length - 1;
                if (n - m <= MaxFactorial)
                {
                    double ratio = Math2.FactorialTable[n + m] / Math2.FactorialTable[n - m];
                    return(ratio * legP);
                }

                if (n - m <= 300)
                {
                    // break up the Tgamma ratio into two separate factors
                    // to prevent an underflow: a!/b! = (a!/170!) * (170!/b!)
                    // 170!/300! ~= 2.37e-308 > double.MinNormalizedValue

                    return((legP * (Math2.FactorialTable[n + m] / Math2.FactorialTable[MaxFactorial])) * Math2.TgammaRatio(MaxFactorial + 1, n + 1 - m));
                }

#if false
                // since we are setting Nmax = 128, so Mmax = 128, and Nmax+Mmax = 256
                // so this should never be called, but left here for completeness
                // 170!/300! ~= 2.37e-308 > double.MinNormalizedValue

                double ratioL = Math2.TgammaRatio(n + m + 1, n + 1 - m);
                if (ratioL >= DoubleLimits.MinNormalizedValue)
                {
                    return(ratioL * legP);
                }

                double value = Math.Exp(Math2.Lgamma(n + m + 1) - Math2.Lgamma(n + 1 - m) + Math.Log(Math.Abs(legP)));
                return(Math.Sign(legP) * value);
#else
                Policies.ReportNotImplementedError("LegendreP(n: {0}, m: {1}, x: {2}): Not implemented for |n| >= 128", n, m, x);
                return(double.NaN);
#endif
            }

            // at this point n >= 0 && 0 <= m <= n
            Debug.Assert(n >= 0 && (m >= 0 && m <= n));

            // use the following recurrence relations:
            // P{n, n} = (-1)^n * (2*n - 1)!! * (1 - x^2)^(n/2)
            // P{n+1, n} = (2*n + 1) * x * P{n, n}
            // (n-m+1)*P{n+1,m} = (2*n + 1)* x * P{n,m} - (n+m)*P{n-1, m}
            // see: http://en.wikipedia.org/wiki/Associated_Legendre_polynomials


            // the following is:
            // pmm = Math2.Factorial2(2 * m - 1) * Math.Pow(1.0 - x*x, m/2.0);
            // being careful not to underflow too soon

            double pmm = 0;
            if (Math.Abs(x) > Constants.RecipSqrt2)
            {
                double inner = (1 - x) * (1 + x);
                int    exp   = 0;
                double mant  = Math2.Frexp(inner, out exp);
                // careful: use integer division
                double pmm_scaled = Math.Pow(mant, m / 2) * Math2.Factorial2(2 * m - 1);
                pmm = Math2.Ldexp(pmm_scaled, (m / 2) * exp);
                if (IsOdd(m))
                {
                    pmm *= Math.Sqrt(inner);
                }
            }
            else
            {
                double p_sin_theta = Math.Exp(m / 2.0 * Math2.Log1p(-x * x)); //Math.Pow(1.0 - x*x, m/2.0);
                pmm = Math2.Factorial2(2 * m - 1) * p_sin_theta;
            }

            if (IsOdd(m))
            {
                pmm = -pmm;
            }

            if (n == m)
            {
                return(pmm);
            }

            // p0 - current
            // p1 - next

            double p0 = pmm;
            double p1 = (2.0 * m + 1.0) * x * p0; //P{m+1,m}(x)

            for (uint k = (uint)m + 1; k < (uint)n; k++)
            {
                double next = ((2 * k + 1) * x * p1 - (k + (uint)m) * p0) / (k - (uint)m + 1);
                p0 = p1;
                p1 = next;
            }


            return(p1);
        }
Пример #10
0
        /// <summary>
        /// Simultaneously compute I{v}(x) and K{v}(x)
        /// </summary>
        /// <param name="v"></param>
        /// <param name="x"></param>
        /// <param name="needI"></param>
        /// <param name="needK"></param>
        /// <returns>A tuple of (I, K). If needI == false or needK == false, I = NaN or K = NaN  respectively</returns>
        public static (double I, double K) IK(double v, double x, bool needI, bool needK)
        {
            Debug.Assert(needI || needK, "NeedI and NeedK cannot both be false");

            // set initial values for parameters
            double I = double.NaN;
            double K = double.NaN;

            // check v first so that there are no integer throws later
            if (Math.Abs(v) > int.MaxValue)
            {
                Policies.ReportNotImplementedError("BesselIK(v: {0}, x: {1}): Requires |v| <= {2}", v, x, int.MaxValue);
                return(I, K);
            }


            if (v < 0)
            {
                v = -v;

                // for integer orders only, we can use the following identities:
                //  I{-n}(x) = I{n}(v)
                //  K{-n}(x) = K{n}(v)
                // for any v, use reflection rule:
                //  I{-v}(x) = I{v}(x) + (2/PI) * sin(pi*v)*K{v}(x)
                //  K{-v}(x) = K{v}(x)

                if (needI && !Math2.IsInteger(v))
                {
                    var(IPos, KPos) = IK(v, x, true, true);

                    I = IPos + (2 / Math.PI) * Math2.SinPI(v) * KPos;
                    if (needK)
                    {
                        K = KPos;
                    }
                    return(I, K);
                }
            }

            if (x < 0)
            {
                Policies.ReportDomainError("BesselIK(v: {0}, x: {1}): Complex result not supported. Requires x >= 0", v, x);
                return(I, K);
            }

            // both x and v are non-negative from here
            Debug.Assert(x >= 0 && v >= 0);

            if (x == 0)
            {
                if (needI)
                {
                    I = (v == 0) ? 1.0 : 0.0;
                }
                if (needK)
                {
                    K = double.PositiveInfinity;
                }
                return(I, K);
            }

            if (needI && (x < 2 || 3 * (v + 1) > x * x))
            {
                I     = I_SmallArg(v, x);
                needI = false;
                if (!needK)
                {
                    return(I, K);
                }
            }

            // Hankel is fast, and reasonably accurate.
            // at x == 32, it will converge in about 15 iterations
            if (x >= HankelAsym.IKMinX(v))
            {
                if (needK)
                {
                    K = HankelAsym.K(v, x);
                }
                if (needI)
                {
                    I = HankelAsym.I(v, x);
                }
                return(I, K);
            }

            // the uniform expansion is here as a last resort
            // to limit the number of recurrences, but it is less accurate.
            if (UniformAsym.IsIKAvailable(v, x))
            {
                if (needK)
                {
                    K = UniformAsym.K(v, x);
                }
                if (needI)
                {
                    I = UniformAsym.I(v, x);
                }
                return(I, K);
            }


            // K{v}(x) and K{v+1}(x), binary scaled
            var(Kv, Kv1, binaryScale) = K_CF(v, x);
            if (needK)
            {
                K = Math2.Ldexp(Kv, binaryScale);
            }
            if (needI)
            {
                // use the Wronskian relationship

                // Since CF1 is O(x) for x > v, try to weed out obvious overflows
                // Note: I{v+1}(x)/I{v}(x) is in [0, 1].
                // I{0}(713. ...) == MaxDouble
                const double I0MaxX = 713;
                if (x > I0MaxX && x > v)
                {
                    I = Math2.Ldexp(1.0 / (x * (Kv + Kv1)), -binaryScale);
                    if (double.IsInfinity(I))
                    {
                        return(I, K);
                    }
                }

                double W  = 1 / x;
                double fv = I_CF1(v, x);
                I = Math2.Ldexp(W / (Kv * fv + Kv1), -binaryScale);
            }


            return(I, K);
        }
Пример #11
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
            }
        }
Пример #12
0
        /// <summary>
        /// Returns K{n}(x) for integer order
        /// </summary>
        /// <param name="n"></param>
        /// <param name="x"></param>
        /// <returns></returns>
        public static double KN(int n, double x)
        {
            if (x < 0)
            {
                Policies.ReportDomainError("BesselK(v: {0}, x: {1}): Requires x >= 0 for real result", n, x);
                return(double.NaN);
            }
            if (x == 0)
            {
                return(double.PositiveInfinity);
            }


            // even function
            // K{-n}(z) = K{n}(z)
            if (n < 0)
            {
                n = -n;
            }

            if (n == 0)
            {
                return(K0(x));
            }

            if (n == 1)
            {
                return(K1(x));
            }

            double v = n;

            // Hankel is fast, and reasonably accurate, saving us from many recurrences.
            if (x >= HankelAsym.IKMinX(v))
            {
                return(HankelAsym.K(v, x));
            }

            // the uniform expansion is here as a last resort
            // to limit the number of recurrences, but it is less accurate.
            if (UniformAsym.IsIKAvailable(v, x))
            {
                return(UniformAsym.K(v, x));
            }

            // Since K{v}(x) has a (e^-x)/sqrt(x) multiplier
            // using recurrence can underflow too quickly for large x,
            // so, use a scaled version
            double result;

            if (x > 1)
            {
                double prev    = K0(x, true);
                double current = K1(x, true);

                // for large v and x this number can get very large
                // maximum observed K(1000,10) = 2^6211

                var(Kv, _, binaryScale) = Recurrence.ForwardK_B(1, x, n - 1, current, prev);


                // Compute: value * 2^(binaryScale) * e^-x

                if (x < -DoubleX.MinLogValue)
                {
                    DoubleX exs = DoubleX.Ldexp(DoubleX.Exp(-x), binaryScale);
                    result = Math2.Ldexp(Kv * exs.Mantissa, exs.Exponent);
                }
                else
                {
                    result = Math.Exp(-x + Math.Log(Kv) + binaryScale * Constants.Ln2);
                }
            }
            else
            {
                double prev    = K0(x);
                double current = K1(x);

                result = Recurrence.ForwardK(1, x, n - 1, current, prev).Kvpn;
            }


            return(result);
        }
Пример #13
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);
        }