public static double Imp(double k, double n, double nc, double phi) { // Note arg nc = 1-n, possibly without cancellation errors Debug.Assert(k >= -1 && k <= 1, "Requires |k| <= 1: k = " + k); // See: http://dlmf.nist.gov/19.6#iv if (phi == 0) { return(0); } if (phi == Math.PI / 2) { return(Math2.EllintPi(k, n)); } if (n == 0) { if (k == 0) { return(phi); } return(Math2.EllintF(k, phi)); } // Carlson's algorithm works only for |phi| <= π/2, // use the integrand's periodicity to normalize phi // Π(k, n, phi + π*mult) = Π(k, n, phi) + 2*mult*Π(k,n) 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) && (nc > 0)) { result = mult * _EllintPi.Imp(k, n, nc); } } if (k == 0) { double ncr; // A&S 17.7.20: if (n < 1) { if (nc == 1) { return(phi); } ncr = Math.Sqrt(nc); result += Math.Atan(ncr * Math.Tan(rphi)) / ncr; } else if (n == 1) { result += Math.Tan(rphi); } else { // n > 1: ncr = Math.Sqrt(-nc); result += Math2.Atanh(ncr * Math.Tan(rphi)) / ncr; } return((phi < 0) ? -result : result); } double sphi = Math.Sin(Math.Abs(phi)); if (n > 1 / (sphi * sphi)) { // Complex result is a domain error: Policies.ReportDomainError("EllintPi(k: {0}, nu: {1}, phi: {2}): Complex results not supported. Requires n > 1 / sin^2(phi)", k, n, phi); return(double.NaN); } // Special cases first: if (n < 0) { // // If we don't shift to 0 <= n <= 1 we get // cancellation errors later on. Use // A&S 17.7.15/16 to shift to n > 0: // double k2 = k * k; double N = (k2 - n) / nc; double Nc = (1 - k2) / nc; // Nc = 1-N = (1-k^2)/nc // check to see if a rounding error occurred // k^2 <= N <= 1 if (N < k2) { #if EXTRA_DEBUG Debug.WriteLine("Rounding error: EllintPi(k: {0} , nu: {1} , phi: {2})", k, n, phi); #endif N = k2; Nc = 1 - N; } double p2 = Math.Sqrt(-n * N); double delta = Math.Sqrt(1 - k2 * sphi * sphi); // Reduce A&S 17.7.15/16 // Mathematica eqns below // N is protected in Mathematica, so use V // V = (k2 - n)/(1 - n) // Assuming[(k2 >= 0 && k2 <= 1) && n < 0, FullSimplify[Sqrt[(1 - V)*(1 - k2/V)]/Sqrt[((1 - n)*(1 - k2/n))]]] // Result: ((-1 + k2) n)/((-1 + n) (-k2 + n)) // Assuming[(k2 >= 0 && k2 <= 1) && n < 0, FullSimplify[k2/(Sqrt[-n*(k2 - n)/(1 - n)]*Sqrt[(1 - n)*(1 - k2/n)])]] // Result: k2/(k2 - n) // Assuming[(k2 >= 0 && k2 <= 1) && n < 0, FullSimplify[Sqrt[1/((1 - n)*(1 - k2/n))]]] // Result: Sqrt[n/((k2 - n) (-1 + n))] double nResult = -(n / nc) * ((1 - k2) / (k2 - n)) * _EllintPi.Imp(k, N, Nc, rphi); nResult += k2 / (k2 - n) * Math2.EllintF(k, rphi); nResult += Math.Sqrt((n / nc) / (n - k2)) * Math.Atan((p2 / (2 * delta)) * Math.Sin(2 * rphi)); result += nResult; return((phi < 0) ? -result : result); } double sinp = Math.Sin(rphi); double cosp = Math.Cos(rphi); double x = cosp * cosp; double t = sinp * sinp; double y = 1 - k * k * t; double z = 1; double p = (n * t < 0.5) ? 1 - n * t : x + nc * t; result += sinp * (Math2.EllintRF(x, y, z) + n * t * Math2.EllintRJ(x, y, z, p) / 3); return((phi < 0) ? -result : result); }