Exemplo n.º 1
0
        /// <summary>
        /// Carlson's symmetric form of elliptical integrals
        /// <para>R<sub>J</sub>(x,y,z,p) = (3/2) * ∫ dt/((t+p)*Sqrt((t+x)(t+y)(t+z))), t={0,∞}</para>
        /// </summary>
        /// <param name="x">Argument. Requires finite x >= 0</param>
        /// <param name="y">Argument. Requires finite y >= 0</param>
        /// <param name="z">Argument. Requires finite z >= 0</param>
        /// <param name="p">Argument. Requires finite p != 0</param>
        public static double EllintRJ(double x, double y, double z, double p)
        {
            if ((!(x >= 0) || double.IsInfinity(x)) ||
                (!(y >= 0) || double.IsInfinity(y)) ||
                (!(z >= 0) || double.IsInfinity(z)) ||
                (p == 0 || double.IsInfinity(p) || double.IsNaN(p)) ||
                (x + y == 0 || y + z == 0 || z + x == 0))
            {
                Policies.ReportDomainError("EllintRJ(x: {0}, y: {1}, z: {2}, p: {3}) requires: finite x,y,z >= 0; finite p != 0; at most one of x,y,z == 0", x, y, z, p);
                return(double.NaN);
            }

            // See Carlson, Numerische Mathematik, vol 33, 1 (1979)

            double value;

            // error scales as the 6th power of tolerance
            const double tolerance = 0.002049052858245406612358440409548690638254492642397575328974;

            Debug.Assert(Math2.AreNearUlps(Math.Pow(DoubleLimits.MachineEpsilon / 3, 1.0 / 6.0), tolerance, 5), "Incorrect constant");

            // reorder x,y,z such that x <= y <= z
            // so that 0 shows up in x first
            // and so that the p<0 case below doesn't suffer cancellation errors
            if (x > y)
            {
                Utility.Swap(ref x, ref y);
            }
            if (y > z)
            {
                Utility.Swap(ref y, ref z);
            }
            if (x > y)
            {
                Utility.Swap(ref x, ref y);
            }

            // for p < 0, the integral is singular, return Cauchy principal value
            if (p < 0)
            {
                // We must ensure that (z - y) * (y - x) is positive.
                Debug.Assert(x <= y && y <= z, "x, y, z must be in ascending order");

                double q   = -p;
                double pmy = (z - y) * (y - x) / (y + q); // p - y

                Debug.Assert(pmy >= 0);

                double pAdj = pmy + y;
                value  = pmy * Math2.EllintRJ(x, y, z, pAdj);
                value -= 3 * Math2.EllintRF(x, y, z);
                value += 3 * Math.Sqrt((x * y * z) / (x * z + pAdj * q)) * Math2.EllintRC(x * z + pAdj * q, pAdj * q);
                value /= (y + q);
                return(value);
            }

            if (x == 0)
            {
                if (y == 0) //RJ(0, 0, z, p)
                {
                    return(double.PositiveInfinity);
                }
                if (y == z) //RJ(0, y, y, p)
                {
                    return(3 * (Math.PI / 2) / (y * Math.Sqrt(p) + p * Math.Sqrt(y)));
                }
            }

            if (p == z)
            {
                if (z == y)
                {
                    if (x == y)
                    {
                        return(1 / (x * Math.Sqrt(x))); // RJ(x, x, x, x)
                    }
                    return(EllintRD(x, y, y));          // RJ(x, y, y, y)
                }
                return(EllintRD(x, y, z));              // RJ(x, y, z, z)
            }

            if (x == y && y == z)
            {
                return(EllintRD(p, p, x)); // RJ(x, x, x, p)
            }
            // duplication
            double sigma  = 0;
            double factor = 1;
            int    k      = 1;

            for (; k < Policies.MaxSeriesIterations; k++)
            {
                double u = (x + y + z + p + p) / 5;
                double X = (u - x);
                double Y = (u - y);
                double Z = (u - z);
                double P = (u - p);

                // Termination condition:
                double utol = u * tolerance;
                if (Math.Abs(X) < utol && Math.Abs(Y) < utol && Math.Abs(Z) < utol && Math.Abs(P) < utol)
                {
                    X /= u;
                    Y /= u;
                    Z /= u;
                    P /= u;

                    // Taylor series expansion to the 5th order
                    double EA = X * Y + Y * Z + Z * X;
                    double EB = X * Y * Z;
                    double EC = P * P;
                    double E2 = EA - 3 * EC;
                    double E3 = EB + 2 * P * (EA - EC);
                    double S1 = 1 + E2 * (E2 * (9.0 / 88) - E3 * (9.0 / 52) - 3.0 / 14);
                    double S2 = EB * (1.0 / 6 + P * (-6.0 / 22 + P * (3.0 / 26)));
                    double S3 = P * ((EA - EC) / 3 - P * EA * (3.0 / 22));
                    value = 3 * sigma + factor * (S1 + S2 + S3) / (u * Math.Sqrt(u));

                    return(value);
                }

                double sx = Math.Sqrt(x);
                double sy = Math.Sqrt(y);
                double sz = Math.Sqrt(z);

                double lambda = sy * (sx + sz) + sz * sx;
                double alpha  = p * (sx + sy + sz) + sx * sy * sz;
                alpha *= alpha;
                double beta = p * (p + lambda) * (p + lambda);
                sigma  += factor * Math2.EllintRC(alpha, beta);
                factor /= 4;
                x       = (x + lambda) / 4;
                y       = (y + lambda) / 4;
                z       = (z + lambda) / 4;
                p       = (p + lambda) / 4;
            }

            Policies.ReportDomainError("EllintRJ(x: {0}, y: {1}, z: {2}, p: {3}) No convergence after {4} iterations", x, y, z, p, k);
            return(double.NaN);
        }