// 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); }
// Elliptic integrals (complete and incomplete) of the third kind // Carlson, Numerische Mathematik, vol 33, 1 (1979) public static double Imp(double k, double n, double nc) { // Note arg nc = 1-n, possibly without cancellation errors Debug.Assert(k >= -1 && k <= 1, "Requires |k| <= 1: k = " + k); // special values // See: http://dlmf.nist.gov/19.6.E3 if (n == 0) { if (k == 0) { return(Math.PI / 2); } return(Math2.EllintK(k)); } // special values // See: http://functions.wolfram.com/EllipticIntegrals/EllipticPi/03/01/01/ if (k == 0) { return((Math.PI / 2) / Math.Sqrt(nc)); } if (k == 1) { return(double.NegativeInfinity / Math.Sign(n - 1)); } if (n < 0) { // when the magnitude of n gets very large, N below becomes ~= 1, so // there can be some error, so use the series at n=Infinity // http://functions.wolfram.com/EllipticIntegrals/EllipticPi/06/01/05/ if (n < -1 / DoubleLimits.MachineEpsilon) { return((Math.PI / 2) / Math.Sqrt(-n) + (Math2.EllintE(k) - Math2.EllintK(k)) / n); } // A&S 17.7.17: // shift to k^2 < N < 1 double k2 = k * k; double N = (k2 - n) / nc; double Nc = (1 - k2) / nc; // Nc = 1-N double result = k2 / (k2 - n) * Math2.EllintK(k); result -= (n / nc) * ((1 - k2) / (k2 - n)) * _EllintPi.Imp(k, N, Nc); return(result); } double x = 0; double y = 1 - k * k; double z = 1; double p = nc; double value = Math2.EllintK(k) + n * Math2.EllintRJ(x, y, z, p) / 3; return(value); }