示例#1
0
        /// <summary>
        /// Returns the value of the Legendre Polynomial of the first kind
        /// </summary>
        /// <param name="n">Polynomial degree. Limited to |n| ≤ 127</param>
        /// <param name="x">Requires |x| ≤ 1</param>
        public static double LegendreP(int n, double x)
        {
            if (!(x >= -1 && x <= 1))
            {
                Policies.ReportDomainError("LegendreP(n: {0}, x: {1}): Requires |x| <= 1", n, x);
                return(double.NaN);
            }
            if (n <= -128 || n >= 128)
            {
                Policies.ReportNotImplementedError("LegendreP(n: {0}, x: {1}): Not implemented for |n| >= 128", n, x);
                return(double.NaN);
            }

            // LegendreP(n,x) == LegendreP(-n-1,x)
            if (n < 0)
            {
                n = -(n + 1);
            }


            // Special values
            if (n == 0)
            {
                return(1);
            }
            if (x == 1)
            {
                return(1);
            }
            if (x == -1)
            {
                return((IsOdd(n)) ? -1 : 1);
            }

            // Implement Legendre P polynomials via recurrance
            // p0 - current
            // p1 - next

            double p0 = 1;
            double p1 = x;

            for (uint k = 1; k < (uint)n; k++)
            {
                double next = ((2 * k + 1) * x * p1 - k * p0) / (k + 1);
                p0 = p1;
                p1 = next;
            }
            return(p1);
        }
示例#2
0
        /// <summary>
        /// Returns the value of the Associated Laguerre polynomial of degree <paramref name="n"/> and order <paramref name="m"/> at point <paramref name="x"/>
        /// </summary>
        /// <param name="n">Polynomial degree. Requires <paramref name="n"/> ≥ 0. Limited to <paramref name="n"/> ≤ 127</param>
        /// <param name="m">Polynomial order. Requires <paramref name="m"/> ≥ 0</param>
        /// <param name="x">Polynomial argument</param>
        public static double LaguerreL(int n, int m, double x)
        {
            if ((n < 0) || (m < 0))
            {
                Policies.ReportDomainError("LaguerreL(n: {0}, m: {1}, x: {2}): Requires n,m >= 0", n, m, x);
                return(double.NaN);
            }
            if (n >= 128)
            {
                Policies.ReportNotImplementedError("LaguerreL(n: {0}, m: {1}, x: {2}): Not implemented for n >= 128", n, m, x);
                return(double.NaN);
            }

            // Special cases:
            if (m == 0)
            {
                return(Math2.LaguerreL(n, x));
            }
            if (n == 0)
            {
                return(1);
            }

            if (double.IsNaN(x))
            {
                Policies.ReportDomainError("LaguerreL(n: {0}, m: {1}, x: {2}): NaN not allowed", n, m, x);
                return(double.NaN);
            }
            if (double.IsInfinity(x))
            {
                return((x > 0 && IsOdd(n)) ? double.NegativeInfinity : double.PositiveInfinity);
            }

            // use recurrence
            // p0 - current
            // p1 - next

            double p0 = 1;
            double p1 = m + 1 - x;

            for (uint k = 1; k < (uint)n; k++)
            {
                double next = ((2 * k + (uint)m + 1 - x) * p1 - (k + (uint)m) * p0) / (k + 1);
                p0 = p1;
                p1 = next;
            }
            return(p1);
        }
示例#3
0
        /// <summary>
        /// Returns a pair representing x/(π/2) ensuring that |remainder| ≤ π/4
        /// <para>Multiple = Sign(x) * Floor(|x|/(π/2) + 1/2)</para>
        /// <para>Remainder = Sign(x) * (|x|-(π/2)*Floor(|x|/(π/2) + 1/2))</para>
        /// </summary>
        public static (double Multiple, double Remainder) RangeReduceHalfPI(double x)
        {
            if (double.IsNaN(x) || double.IsInfinity(x))
            {
                Policies.ReportDomainError("RangeReduceHalfPI(x: {0}) Requires finite argument", x);
                return(double.NaN, double.NaN);
            }

            double absX = Math.Abs(x);

            if (absX > HalfPi.MaxLimit)
            {
                Policies.ReportNotImplementedError("RangeReduceHalfPI(x: {0}) |x| > {1} is not implemented", x, HalfPi.MaxLimit);
                return(double.NaN, double.NaN);
            }

            if (absX < HalfPi.Value / 2)
            {
                return(0, x);
            }

            double k = Math.Floor(absX * HalfPi.Reciprocal + 0.5);
            double rem;

            for (; ;)
            {
                rem = ((absX - (k * HalfPi.A)) - k * HalfPi.B) - k * HalfPi.C;

                if (rem >= -HalfPi.Value / 2)
                {
                    break;
                }

                k--;
            }

            if (x < 0)
            {
                k   = -k;
                rem = -rem;
            }

            Debug.Assert(Math.Abs(rem) <= HalfPi.Value / 2);


            return(k, rem);
        }
示例#4
0
        /// <summary>
        /// Returns the value of the Legendre polynomial that is the second solution to the Legendre differential equation
        /// </summary>
        /// <param name="n">Polynomial degree. Requires n ≥ 0. Limited to n ≤ 127</param>
        /// <param name="x">Requires |x| ≤ 1</param>
        public static double LegendreQ(int n, double x)
        {
            if ((n < 0) ||
                (!(x >= -1 && x <= 1)))
            {
                Policies.ReportDomainError("LegendreQ(n: {0}, x: {1}): Requires n >= 0; |x| <= 1", n, x);
                return(double.NaN);
            }

            if (x == 1)
            {
                return(double.PositiveInfinity);
            }

            if (x == -1)
            {
                return(IsOdd(n) ? double.PositiveInfinity : double.NegativeInfinity);
            }

            if (n >= 128)
            {
                Policies.ReportNotImplementedError("LegendreQ(n: {0}, x: {1}): Not implemented for n >= 128", n, x);
                return(double.NaN);
            }

            // Implement Legendre Q polynomials via recurrance
            // p0 - current
            // p1 - next

            double p0 = Math2.Atanh(x);
            double p1 = x * p0 - 1;

            if (n == 0)
            {
                return(p0);
            }

            for (uint k = 1; k < (uint)n; k++)
            {
                double next = ((2 * k + 1) * x * p1 - k * p0) / (k + 1);
                p0 = p1;
                p1 = next;
            }
            return(p1);
        }
示例#5
0
        /// <summary>
        /// Returns the incomplete elliptic integral of the third kind Π(n, φ, k)
        /// <para>Π(n, φ, k) = ∫ dθ/((1-n^2*sin^2(θ)) * sqrt(1-k^2*sin^2(θ))), θ={0,φ}</para>
        /// </summary>
        /// <param name="k">The modulus. Requires |k| ≤ 1</param>
        /// <param name="nu">The characteristic. Requires nu &lt; 1/sin^2(φ)</param>
        /// <param name="phi">The amplitude</param>
        /// <remarks>
        /// Perhaps more than any other special functions the elliptic integrals are expressed in a variety of different ways.
        /// In particular, the final parameter k (the modulus) may be expressed using a modular angle α, or a parameter m.
        /// These are related by:
        ///    <para>k = sin α</para>
        ///    <para>m = k^2 = (sin α)^2</para>
        /// So that the incomplete integral of the third kind may be expressed as either:
        /// <para>Π(n, φ, k)</para>
        /// <para>Π(n, φ \ α)</para>
        /// <para>Π(n, φ| m)</para>
        /// To further complicate matters, some texts refer to the complement of the parameter m, or 1 - m, where:
        /// 1 - m = 1 - k^2 = (cos α)^2
        /// </remarks>
        public static double EllintPi(double k, double nu, double phi)
        {
            if ((!(k >= -1 && k <= 1)) || (double.IsNaN(nu) || double.IsNaN(phi)))
            {
                Policies.ReportDomainError("EllintPi(k: {0}, nu: {1}, phi: {2}): Requires |k| <= 1; No NaNs", k, nu, phi);
                return(double.NaN);
            }
            if (double.IsInfinity(phi))
            {
                return(phi);
            }

            if (Math.Abs(phi) > Trig.PiReductionLimit)
            {
                Policies.ReportNotImplementedError("EllintPi(k: {0}, nu: {1}, phi: {2}): |phi| > {3} not implemented", k, nu, phi, Trig.PiReductionLimit);
                return(double.NaN);
            }

            return(_EllintPi.Imp(k, nu, 1 - nu, phi));
        }
示例#6
0
        /// <summary>
        /// Returns the Spherical Harmonic Y{n,m}(theta,phi = 0)
        /// <para>Y{n,m}(theta,0) = Sqrt((2*n+1)/(4*π) * (n-m)!/(n+m)!)* P{n,m}(cos(theta))</para>
        /// </summary>
        /// <param name="n">Polynomial degree. Requires <paramref name="n"/> ≥ 0. Limited to <paramref name="n"/> ≤ 127</param>
        /// <param name="m">Polynomial order</param>
        /// <param name="theta">Colatitude, or polar angle, [0,π] where 0=North Pole, π=South Pole, π/2=Equator</param>
        public static double SphLegendre(int n, int m, double theta)
        {
            if ((n < 0) ||
                (double.IsNaN(theta) || double.IsInfinity(theta)))
            {
                Policies.ReportDomainError("SphLegendre(n: {0}, m: {1}, theta: {2}): Requires n >= 0; finite theta", n, m, theta);
                return(double.NaN);
            }

            // by definition: if ( abs(m) < n ) return 0
            if (m < -n || m > n)
            {
                return(0);
            }

            if (n >= 128)
            {
                Policies.ReportNotImplementedError("SphLegendre(n: {0}, m: {1}, theta: {2}): Not implemented for n >= 128", n, m, theta);
                return(double.NaN);
            }

            bool sign = false;

            if (m < 0)
            {
                sign = IsOdd(m);
                m    = Math.Abs(m);
            }

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

            double result;
            double legP = LegendrePAngle(n, m, theta);

            int MaxFactorial = Math2.FactorialTable.Length - 1;

            if (n + m <= MaxFactorial)
            {
                double ratio = Math2.FactorialTable[n - m] / Math2.FactorialTable[n + m];
                result = 0.5 * Constants.RecipSqrtPI * Math.Sqrt((2 * n + 1) * ratio) * legP;
            }
            else 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

                double ratio1 = Math2.FactorialTable[n - m] / Math2.FactorialTable[MaxFactorial];
                double ratio2 = Math2.TgammaRatio(MaxFactorial + 1, n + m + 1);
                result = 0.5 * Constants.RecipSqrtPI * Math.Sqrt(ratio1) * Math.Sqrt((2 * n + 1) * ratio2) * legP;
            }
            else
            {
                // use logs
                // note: with a limit of n=128, we should never reach here, but it is left for completeness
                double ratio = Math2.TgammaRatio(n - m + 1, n + m + 1);
                if (ratio < DoubleLimits.MinNormalValue)
                {
                    double mult = (2 * n + 1) / (4 * Math.PI);
                    result  = Math.Exp((Math2.Lgamma(n - m + 1) - Math2.Lgamma(n + m + 1) + Math.Log(mult)) / 2 + Math.Log(Math.Abs(legP)));
                    result *= Math.Sign(legP);
                }
                else
                {
                    result = 0.5 * Constants.RecipSqrtPI * Math.Sqrt((2 * n + 1) * ratio) * legP;
                }
            }

            return(sign ? -result : result);
        }
示例#7
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);
        }
示例#8
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);
        }
示例#9
0
        // Elliptic integrals (complete and incomplete) of the first kind
        // Carlson, Numerische Mathematik, vol 33, 1 (1979)


        /// <summary>
        /// Returns the incomplete elliptic integral of the first kind
        /// <para>F(φ, k) = F(φ | k^2) = F(sin(φ); k)</para>
        /// <para>F(φ, k) = ∫ dθ/sqrt(1-k^2*sin^2(θ)), θ={0,φ}</para>
        /// <para>F(x; k) = ∫ dt/sqrt((1-t^2)*(1-k^2*t^2)), t={0,x}</para>
        /// </summary>
        /// <param name="k">The modulus. Requires |k| ≤ 1</param>
        /// <param name="phi">The amplitude</param>
        public static double EllintF(double k, double phi)
        {
            if ((!(k >= -1 && k <= 1)) || (double.IsNaN(phi)))
            {
                Policies.ReportDomainError("EllintF(k: {0}, phi: {1}): Requires |k| <= 1; phi not NaN", k, phi);
                return(double.NaN);
            }
            if (double.IsInfinity(phi))
            {
                return(phi);
            }

            if (Math.Abs(phi) > Trig.PiReductionLimit)
            {
                Policies.ReportNotImplementedError("EllintF(k: {0}, phi: {1}): |phi| > {2} not implemented", k, phi, Trig.PiReductionLimit);
                return(double.NaN);
            }

#if EXTRA_DEBUG
            Debug.WriteLine("EllintF(phi = {0}; k = {1})", phi, k);
#endif

            // special values
            if (k == 0)
            {
                return(phi);
            }
            if (phi == 0)
            {
                return(0);
            }
            if (phi == Math.PI / 2)
            {
                return(Math2.EllintK(k));
            }


            // Carlson's algorithm works only for |phi| <= π/2,
            // use the integrand's periodicity to normalize phi
            // F(k, phi + π*mult) = F(k, phi) + 2*mult*K(k)

            double result = 0;
            double rphi   = Math.Abs(phi);
            if (rphi > Math.PI / 2)
            {
                // Normalize periodicity so that |rphi| <= π/2
                var(angleMultiple, angleRemainder) = Trig.RangeReducePI(rphi);
                double mult = 2 * angleMultiple;
                rphi = angleRemainder;
                if (mult != 0)
                {
                    result += mult * EllintK(k);
                }
            }


            double sinp = Math.Sin(rphi);
            double cosp = Math.Cos(rphi);

            double k2 = k * k;
            double x  = cosp * cosp;
            double y  = 1 - k2 * sinp * sinp;
            if (y > 0.125)   // use a more accurate form with less cancellation
            {
                y = (1 - k2) + k2 * x;
            }
            double z = 1;

            result += sinp * EllintRF(x, y, z);

            return((phi < 0) ? -result : result);
        }
示例#10
0
        // Elliptic integrals (complete and incomplete) of the second kind
        // Carlson, Numerische Mathematik, vol 33, 1 (1979)


        /// <summary>
        /// Returns the incomplete elliptic integral of the second kind E(φ, k)
        /// <para>E(φ, k) =  ∫ sqrt(1-k^2*sin^2(θ)) dθ, θ={0,φ}</para>
        /// </summary>
        /// <param name="k">The modulus. Requires |k| ≤ 1</param>
        /// <param name="phi">The amplitude</param>
        public static double EllintE(double k, double phi)
        {
            if ((!(k >= -1 && k <= 1)) || (double.IsNaN(phi)))
            {
                Policies.ReportDomainError("EllintE(k: {0}, phi: {1}): Requires |k| <= 1; phi not NaN", k, phi);
                return(double.NaN);
            }
            if (double.IsInfinity(phi))
            {
                return(phi);
            }

            if (Math.Abs(phi) > Trig.PiReductionLimit)
            {
                Policies.ReportNotImplementedError("EllintE(k: {0}, phi: {1}): |phi| > {2} not implemented", k, phi, Trig.PiReductionLimit);
                return(double.NaN);
            }

            // special values
            if (k == 0)
            {
                return(phi);
            }
            if (phi == 0)
            {
                return(0);
            }
            if (phi == Math.PI / 2)
            {
                return(Math2.EllintE(k));
            }

            // Carlson's algorithm works only for |phi| <= π/2,
            // use the integrand's periodicity to normalize phi
            // E(k, phi + π*mult) = E(k, phi) + 2*mult*E(k)

            double result = 0;
            double rphi   = Math.Abs(phi);

            if (rphi > Math.PI / 2)
            {
                // Normalize periodicity so that |rphi| <= π/2
                var(angleMultiple, angleRemainder) = Trig.RangeReducePI(rphi);
                double mult = 2 * angleMultiple;
                rphi = angleRemainder;
                if (mult != 0)
                {
                    result += mult * EllintE(k);
                }
            }

            if (k == 1)
            {
                result += Math.Sin(rphi);
            }
            else
            {
                double k2   = k * k;
                double sinp = Math.Sin(rphi);
                double cosp = Math.Cos(rphi);
                double x    = cosp * cosp;
                double t    = k2 * sinp * sinp;
                double y    = (t < 0.875) ? 1 - t : (1 - k2) + k2 * x;
                double z    = 1;

                result += sinp * (Math2.EllintRF(x, y, z) - t * Math2.EllintRD(x, y, z) / 3);
            }

            return((phi < 0) ? -result : result);
        }
示例#11
0
        /// <summary>
        /// Computes Y{v}(x), where v is an integer
        /// </summary>
        /// <param name="v">Integer order</param>
        /// <param name="x">Argument. Requires x &gt; 0</param>
        /// <returns></returns>
        public static DoubleX YN(double v, double x)
        {
            if ((x == 0) && (v == 0))
            {
                return(double.NegativeInfinity);
            }
            if (x <= 0)
            {
                Policies.ReportDomainError("BesselY(v: {0}, x: {1}): Complex number result not supported. Requires x >= 0", v, x);
                return(double.NaN);
            }


            //
            // Reflection comes first:
            //

            double sign = 1;

            if (v < 0)
            {
                // Y_{-n}(z) = (-1)^n Y_n(z)
                if (Math2.IsOdd(v))
                {
                    sign = -1;
                }
                v = -v;
            }

            Debug.Assert(v >= 0 && x >= 0);

            if (v > int.MaxValue)
            {
                Policies.ReportNotImplementedError("BesselY(v: {0}, x: {1}): Large integer values not yet implemented", v, x);
                return(double.NaN);
            }

            int n = (int)v;

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

            if (n == 1)
            {
                return(sign * Y1(x));
            }

            if (x < DoubleLimits.RootMachineEpsilon._2)
            {
                DoubleX smallArgValue = YN_SmallArg(n, x);
                return(smallArgValue * sign);
            }

            // 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, false, true))
                {
                    return(sign * Yv);
                }
            }


            // forward recurrence always OK (though unstable)
            double prev    = Y0(x);
            double current = Y1(x);

            var(Yvpn, Yvpnm1, YScale) = Recurrence.ForwardJY_B(1.0, x, n - 1, current, prev);

            return(DoubleX.Ldexp(sign * Yvpn, YScale));
        }
示例#12
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);
        }
示例#13
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
            }
        }
示例#14
0
        // Elliptic integrals (complete and incomplete) of the second kind
        // Carlson, Numerische Mathematik, vol 33, 1 (1979)


        /// <summary>
        /// Returns the incomplete elliptic integral of the second kind D(φ, k)
        /// <para>D(φ, k) =  ∫ sin^2(θ)/sqrt(1-k^2*sin^2(θ)) dθ, θ={0,φ}</para>
        /// </summary>
        /// <param name="k">The modulus. Requires |k| ≤ 1</param>
        /// <param name="phi">The amplitude</param>
        public static double EllintD(double k, double phi)
        {
            if ((!(k >= -1 && k <= 1)) || (double.IsNaN(phi)))
            {
                Policies.ReportDomainError("EllintD(k: {0}, phi: {1}): Requires |k| <= 1; phi not NaN", k, phi);
                return(double.NaN);
            }
            if (double.IsInfinity(phi))
            {
                return(phi); // Note: result != phi, only +/- infinity
            }
            if (Math.Abs(phi) > Trig.PiReductionLimit)
            {
                Policies.ReportNotImplementedError("EllintD(k: {0}, phi: {1}): |phi| > {2} not implemented", k, phi, Trig.PiReductionLimit);
                return(double.NaN);
            }

            // special values
            //if (k == 0) // too much cancellation error near phi=0, general case works fine
            //    return (phi - Cos(phi)*Sin(phi))/2
            if (phi == 0)
            {
                return(0);
            }
            if (phi == Math.PI / 2)
            {
                return(Math2.EllintD(k));
            }

            // Carlson's algorithm works only for |phi| <= π/2,
            // use the integrand's periodicity to normalize phi
            // D(k, phi + π*mult) = D(k, phi) + 2*mult*D(k)

            double result = 0;
            double rphi   = Math.Abs(phi);

            if (rphi > Math.PI / 2)
            {
                // Normalize periodicity so that |rphi| <= π/2
                var(angleMultiple, angleRemainder) = Trig.RangeReducePI(rphi);
                double mult = 2 * angleMultiple;
                rphi = angleRemainder;
                if (mult != 0)
                {
                    result += mult * EllintD(k);
                }
            }

            double k2   = k * k;
            double sinp = Math.Sin(rphi);
            double cosp = Math.Cos(rphi);
            double x    = cosp * cosp;
            double t    = k2 * sinp * sinp;
            double y    = (t < 0.875) ? 1 - t : (1 - k2) + k2 * x;
            double z    = 1;

            // http://dlmf.nist.gov/19.25#E13
            // and RD(lambda*x, lambda*y, lambda*z) = lambda^(-3/2) * RD(x, y, z)
            result += EllintRD(x, y, z) * sinp * sinp * sinp / 3;

            return((phi < 0) ? -result : result);
        }
示例#15
0
        /// <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);
        }