// Carlson's degenerate elliptic integral // Carlson, Numerische Mathematik, vol 33, 1 (1979) /// <summary> /// Carlson's symmetric form of elliptical integrals /// <para>R<sub>C</sub>(x,y) = R<sub>F</sub>(x,y,y) = (1/2) * ∫ dt/((t+y)*Sqrt(t+x)), t={0,∞}</para> /// </summary> public static double EllintRC(double x, double y) { if ((!(x >= 0) || double.IsInfinity(x)) || (y == 0 || double.IsInfinity(y) || double.IsNaN(y))) { Policies.ReportDomainError("EllintRC(x: {0}, y: {1}): Requires finite x >= 0; finite y != 0", x, y); return(double.NaN); } // Taylor series: Max Value const double tolerance = 0.003100392679625389599124425076703727071077135406054400409781; Debug.Assert(Math2.AreNearUlps(tolerance, Math.Pow(4 * DoubleLimits.MachineEpsilon, 1.0 / 6), 5), "Incorrect constant"); // for y < 0, the integral is singular, return Cauchy principal value double prefix = 1; if (y < 0) { prefix = Math.Sqrt(x / (x - y)); x -= y; y = -y; } if (x == 0) { return(prefix * ((Math.PI / 2) / Math.Sqrt(y))); } if (x == y) { return(prefix / Math.Sqrt(x)); } int k = 1; for (; k < Policies.MaxSeriesIterations; k++) { double u = (x + y + y) / 3; double S = y / u - 1; // 1 - x / u = 2 * S if (2 * Math.Abs(S) < tolerance) { // Taylor series expansion to the 5th order double value = (1 + S * S * (3.0 / 10 + S * (1.0 / 7 + S * (3.0 / 8 + S * (9.0 / 22))))) / Math.Sqrt(u); return(value * prefix); } double sx = Math.Sqrt(x); double sy = Math.Sqrt(y); double lambda = 2 * sx * sy + y; x = (x + lambda) / 4; y = (y + lambda) / 4; } Policies.ReportDomainError("EllintRC(x: {0}, y: {1}): No convergence after {2} iterations", x, y, k); return(double.NaN); }
// Carlson's elliptic integral of the second kind // Carlson, Numerische Mathematik, vol 33, 1 (1979) /// <summary> /// Carlson's symmetric form of elliptical integrals /// <para>R<sub>D</sub>(x,y,z) = R<sub>J</sub>(x,y,z,z) = (3/2) * ∫ dt/((t+z)*Sqrt((t+x)(t+y)(t+z))), t={0,∞}</para> /// </summary> /// <param name="x">Argument. Requires x >= 0</param> /// <param name="y">Argument. Requires y >= 0</param> /// <param name="z">Argument. Requires z > 0</param> public static double EllintRD(double x, double y, double z) { if ((!(x >= 0) || double.IsInfinity(x)) || (!(y >= 0) || double.IsInfinity(y)) || (!(z > 0) || double.IsInfinity(z)) || (x + y == 0)) { Policies.ReportDomainError("EllintRD(x: {0}, y: {1}, z: {2}) requires: finite x,y >= 0; finite z > 0; at most one argument == 0", x, y, z); return(double.NaN); } // Special cases from http://dlmf.nist.gov/19.20#iv if (x > y) { Utility.Swap(ref x, ref y); } if (x == 0) { if (y == 0) { return(double.PositiveInfinity); // RD(0, 0, z) } if (y == z) { return(3 * (Math.PI / 4) / (y * Math.Sqrt(y))); // RD(0, y, y) } // // Special handling for common case, from // Numerical Computation of Real or Complex Elliptic Integrals, eq.47 // double xn = Math.Sqrt(y); double yn = Math.Sqrt(z); double x0 = xn; double y0 = yn; double sum = 0; double sum_pow = 0.25; double c = xn - yn; while (Math.Abs(c) >= 2.7 * DoubleLimits.RootMachineEpsilon._2 * xn) { double t = Math.Sqrt(xn * yn); xn = (xn + yn) / 2; yn = t; c = xn - yn; sum_pow *= 2; sum += sum_pow * c * c; } double RF = Math.PI / (xn + yn); // // This following calculation suffers from serious cancellation when y ~ z // unless we combine terms. We have: // // ( ((x0 + y0)/2)^2 - z ) / (z(y-z)) // // Substituting y = x0^2 and z = y0^2 and simplifying we get the following: // double pt = (x0 + 3 * y0) / (4 * z * (x0 + y0)); // // Since we've moved the demoninator from eq.47 inside the expression, we // need to also scale "sum" by the same value: // pt -= sum / (z * (y - z)); return(pt * RF * 3); } if (x == y && x == z) { return(1 / (x * Math.Sqrt(x))); } // Taylor series upper limit const double tolerance = 0.002049052858245406612358440409548690638254492642397575328974; Debug.Assert(Math2.AreNearUlps(Math.Pow(DoubleLimits.MachineEpsilon / 3, 1.0 / 6.0), tolerance, 5), "Incorrect constant"); // duplication double sigma = 0; double factor = 1; int k = 1; for (; k < Policies.MaxSeriesIterations; k++) { double u = (x + y + z + z + z) / 5; double X = (u - x); double Y = (u - y); double Z = (u - z); // Termination condition: double utol = u * tolerance; if (Math.Abs(X) < utol && Math.Abs(Y) < utol && Math.Abs(Z) < utol) { X /= u; Y /= u; Z /= u; // Taylor series expansion to the 5th order double EA = X * Y; double EB = Z * Z; double EC = EA - EB; double ED = EA - 6 * EB; double EE = ED + EC + EC; double S1 = ED * (ED * (9.0 / 88) - Z * EE * (9.0 / 52) - 3.0 / 14); double S2 = Z * (EE / 6 + Z * (-EC * (9.0 / 22) + Z * EA * (3.0 / 26))); double value = 3 * sigma + factor * (1 + S1 + S2) / (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; sigma += factor / (sz * (z + lambda)); factor /= 4; x = (x + lambda) / 4; y = (y + lambda) / 4; z = (z + lambda) / 4; } Policies.ReportDomainError("EllintRD(x: {0}, y: {1}, z: {2}) No convergence after {3} iterations", x, y, z, k); return(double.NaN); }
/// <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); }
// Carlson's elliptic integral of the first kind // Carlson, Numerische Mathematik, vol 33, 1 (1979) /// <summary> /// Carlson's symmetric form of elliptical integrals /// <para>R<sub>F</sub>(x,y,z) = (1/2) * ∫ dt/((t+x)(t+y)(t+z)), t={0,∞}</para> /// </summary> /// <param name="x">Argument. Requires x >= 0</param> /// <param name="y">Argument. Requires y >= 0</param> /// <param name="z">Argument. Requires z >= 0</param> public static double EllintRF(double x, double y, double z) { if ((!(x >= 0) || double.IsInfinity(x)) || (!(y >= 0) || double.IsInfinity(y)) || (!(z >= 0) || double.IsInfinity(z)) || (x + y == 0 || y + z == 0 || z + x == 0)) { Policies.ReportDomainError("EllintRF(x: {0}, y: {1}, z: {2}) requires: finite x, y, z >= 0; at most one of x,y,z == 0", x, y, z); return(double.NaN); } double xOrig = x; double yOrig = y; double zOrig = z; // reorder x,y,z such that x <= y <= z 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); } // Special cases from http://dlmf.nist.gov/19.20#i if (x == 0) { //Debug.Assert(y != 0 || z != 0, "Expecting only one zero"); if (y == z) { return((Math.PI / 2) / Math.Sqrt(y)); } // http://dlmf.nist.gov/19.22#E8 return((Math.PI / 2) / Agm(Math.Sqrt(y), Math.Sqrt(z))); } if (x == y) { if (x == z) { return(1 / Math.Sqrt(x)); // EllintRF(x, x, x); } return(EllintRC(z, x)); // EllintRF(x, x, z); } Debug.Assert(x != z, "Parameters are not ordered properly"); if (y == z) { return(EllintRC(x, y)); // EllintRF(x, y, y) } // Carlson scales error as the 6th power of tolerance, // but this seems not to work for types larger than // 80-bit reals, this heuristic seems to work OK: const double tolerance = 0.003100392679625389599124425076703727071077135406054400409781; Debug.Assert(Math2.AreNearUlps(tolerance, Math.Pow(4 * DoubleLimits.MachineEpsilon, 1.0 / 6), 5), "Incorrect constant"); // duplication int k = 1; for (; k < Policies.MaxSeriesIterations; k++) { double u = (x + y + z) / 3; double X = (u - x); double Y = (u - y); double Z = (u - z); // Termination condition: double utol = u * tolerance; if (Math.Abs(X) < utol && Math.Abs(Y) < utol && Math.Abs(Z) < utol) { // Taylor series expansion to the 5th order X /= u; Y /= u; Z /= u; double E2 = X * Y - Z * Z; double E3 = X * Y * Z; double value = (1 + E2 * (E2 / 24 - E3 * (3.0 / 44) - 0.1) + E3 / 14) / 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; x = (x + lambda) / 4; y = (y + lambda) / 4; z = (z + lambda) / 4; } Policies.ReportDomainError("EllintRF(x: {0}, y: {1}, z: {2}) No convergence after {3} iterations", xOrig, yOrig, zOrig, k); return(double.NaN); }