/* ellik.c * * Incomplete elliptic integral of the first kind * * * * SYNOPSIS: * * double phi, m, y, ellik(); * * y = ellik( phi, m ); * * * * DESCRIPTION: * * Approximates the integral * * * * phi * - * | | * | dt * F(phi | m) = | ------------------ * | 2 * | | sqrt( 1 - m sin t ) * - * 0 * * of amplitude phi and modulus m, using the arithmetic - * geometric mean algorithm. * * * * * ACCURACY: * * Tested at random points with m in [0, 1] and phi as indicated. * * Relative error: * arithmetic domain # trials peak rms * IEEE -10,10 200000 7.4e-16 1.0e-16 * * */ /* * Cephes Math Library Release 2.0: April, 1987 * Copyright 1984, 1987 by Stephen L. Moshier * Direct inquiries to 30 Frost Street, Cambridge, MA 02140 */ /* Copyright 2014, Eric W. Moore */ /* Incomplete elliptic integral of first kind */ #include "mconf.h" extern double MACHEP; #include static double ellik_neg_m(double phi, double m); double ellik(double phi, double m) { double a, b, c, e, temp, t, K, denom, npio2; int d, mod, sign; if (cephes_isnan(phi) || cephes_isnan(m)) return NPY_NAN; if (m > 1.0) return NPY_NAN; if (cephes_isinf(phi) || cephes_isinf(m)) { if (cephes_isinf(m) && cephes_isfinite(phi)) return 0.0; else if (cephes_isinf(phi) && cephes_isfinite(m)) return phi; else return NPY_NAN; } if (m == 0.0) return (phi); a = 1.0 - m; if (a == 0.0) { if (fabs(phi) >= (double)NPY_PI_2) { sf_error("ellik", SF_ERROR_SINGULAR, NULL); return (NPY_INFINITY); } /* DLMF 19.6.8, and 4.23.42 */ return npy_asinh(tan(phi)); } npio2 = floor(phi / NPY_PI_2); if (fmod(fabs(npio2), 2.0) == 1.0) npio2 += 1; if (npio2 != 0.0) { K = ellpk(a); phi = phi - npio2 * NPY_PI_2; } else K = 0.0; if (phi < 0.0) { phi = -phi; sign = -1; } else sign = 0; if (a > 1.0) { temp = ellik_neg_m(phi, m); goto done; } b = sqrt(a); t = tan(phi); if (fabs(t) > 10.0) { /* Transform the amplitude */ e = 1.0 / (b * t); /* ... but avoid multiple recursions. */ if (fabs(e) < 10.0) { e = atan(e); if (npio2 == 0) K = ellpk(a); temp = K - ellik(e, m); goto done; } } a = 1.0; c = sqrt(m); d = 1; mod = 0; while (fabs(c / a) > MACHEP) { temp = b / a; phi = phi + atan(t * temp) + mod * NPY_PI; denom = 1.0 - temp * t * t; if (fabs(denom) > 10*MACHEP) { t = t * (1.0 + temp) / denom; mod = (phi + NPY_PI_2) / NPY_PI; } else { t = tan(phi); mod = (int)floor((phi - atan(t))/NPY_PI); } c = (a - b) / 2.0; temp = sqrt(a * b); a = (a + b) / 2.0; b = temp; d += d; } temp = (atan(t) + mod * NPY_PI) / (d * a); done: if (sign < 0) temp = -temp; temp += npio2 * K; return (temp); } /* N.B. This will evaluate its arguments multiple times. */ #define MAX3(a, b, c) (a > b ? (a > c ? a : c) : (b > c ? b : c)) /* To calculate legendre's incomplete elliptical integral of the first kind for * negative m, we use a power series in phi for small m*phi*phi, an asymptotic * series in m for large m*phi*phi* and the relation to Carlson's symmetric * integral of the first kind. * * F(phi, m) = sin(phi) * R_F(cos(phi)^2, 1 - m * sin(phi)^2, 1.0) * = R_F(c-1, c-m, c) * * where c = csc(phi)^2. We use the second form of this for (approximately) * phi > 1/(sqrt(DBL_MAX) ~ 1e-154, where csc(phi)^2 overflows. Elsewhere we * use the first form, accounting for the smallness of phi. * * The algorithm used is described in Carlson, B. C. Numerical computation of * real or complex elliptic integrals. (1994) https://arxiv.org/abs/math/9409227 * Most variable names reflect Carlson's usage. * * In this routine, we assume m < 0 and 0 > phi > pi/2. */ double ellik_neg_m(double phi, double m) { double x, y, z, x1, y1, z1, A0, A, Q, X, Y, Z, E2, E3, scale; int n = 0; double mpp = (m*phi)*phi; if (-mpp < 1e-6 && phi < -m) { return phi + (-mpp*phi*phi/30.0 + 3.0*mpp*mpp/40.0 + mpp/6.0)*phi; } if (-mpp > 4e7) { double sm = sqrt(-m); double sp = sin(phi); double cp = cos(phi); double a = log(4*sp*sm/(1+cp)); double b = -(1 + cp/sp/sp - a) / 4 / m; return (a + b) / sm; } if (phi > 1e-153 && m > -1e305) { double s = sin(phi); double csc2 = 1.0 / (s*s); scale = 1.0; x = 1.0 / (tan(phi) * tan(phi)); y = csc2 - m; z = csc2; } else { scale = phi; x = 1.0; y = 1 - m*scale*scale; z = 1.0; } if (x == y && x == z) { return scale / sqrt(x); } A0 = (x + y + z) / 3.0; A = A0; x1 = x; y1 = y; z1 = z; /* Carlson gives 1/pow(3*r, 1.0/6.0) for this constant. if r == eps, * it is ~338.38. */ Q = 400.0 * MAX3(fabs(A0-x), fabs(A0-y), fabs(A0-z)); while (Q > fabs(A) && n <= 100) { double sx = sqrt(x1); double sy = sqrt(y1); double sz = sqrt(z1); double lam = sx*sy + sx*sz + sy*sz; x1 = (x1 + lam) / 4.0; y1 = (y1 + lam) / 4.0; z1 = (z1 + lam) / 4.0; A = (x1 + y1 + z1) / 3.0; n += 1; Q /= 4; } X = (A0 - x) / A / (1 << 2*n); Y = (A0 - y) / A / (1 << 2*n); Z = -(X + Y); E2 = X*Y - Z*Z; E3 = X*Y*Z; return scale * (1.0 - E2/10.0 + E3/14.0 + E2*E2/24.0 - 3.0*E2*E3/44.0) / sqrt(A); }