EllipticFunction.cpp
Go to the documentation of this file.
00001 /**
00002  * \file EllipticFunction.cpp
00003  * \brief Implementation for GeographicLib::EllipticFunction class
00004  *
00005  * Copyright (c) Charles Karney (2008) <charles@karney.com>
00006  * http://charles.karney.info/geographic
00007  * and licensed under the LGPL.
00008  **********************************************************************/
00009 
00010 #include "GeographicLib/EllipticFunction.hpp"
00011 #include "GeographicLib/Constants.hpp"
00012 #include <limits>
00013 #include <cmath>
00014 #include <algorithm>
00015 
00016 namespace {
00017   char RCSID[] = "$Id: EllipticFunction.cpp 6568 2009-03-01 17:58:41Z ckarney $";
00018   char RCSID_H[] = ELLIPTICFUNCTION_HPP;
00019 }
00020 
00021 namespace GeographicLib {
00022 
00023   using namespace std;
00024 
00025   const double EllipticFunction::tol =
00026     numeric_limits<double>::epsilon() * 0.01;
00027   const double EllipticFunction::tolRF = pow(3 * tol, 1/6.0);
00028   const double EllipticFunction::tolRD = pow(0.25 * tol, 1/6.0);
00029   const double EllipticFunction::tolRG0 = 2.7 * sqrt(tol);
00030   const double EllipticFunction::tolJAC = sqrt(tol);
00031   const double EllipticFunction::tolJAC1 = sqrt(6 * tol);
00032 
00033   /*
00034    * Implementation of methods given in
00035    *
00036    *   B. C. Carlson
00037    *   Computation of elliptic integrals
00038    *   Numerical Algorithms 10, 13-26 (1995)
00039    */
00040 
00041   double EllipticFunction::RF(double x, double y, double z) throw() {
00042     // Carlson, eqs 2.2 - 2.7
00043     double
00044       a0 = (x + y + z)/3,
00045       an = a0,
00046       q = max(max(abs(a0-x), abs(a0-y)), abs(a0-z)) / tolRF,
00047       x0 = x,
00048       y0 = y,
00049       z0 = z,
00050       mul = 1;
00051     while (q >= mul * abs(an)) {
00052       // Max 6 trips
00053       double ln = sqrt(x0)*sqrt(y0) + sqrt(y0)*sqrt(z0) + sqrt(z0)*sqrt(x0);
00054       an = (an + ln)/4;
00055       x0 = (x0 + ln)/4;
00056       y0 = (y0 + ln)/4;
00057       z0 = (z0 + ln)/4;
00058       mul *= 4;
00059     }
00060     double
00061       xx = (a0 - x) / (mul * an),
00062       yy = (a0 - y) / (mul * an),
00063       zz = - xx - yy,
00064       e2 = xx * yy - zz * zz,
00065       e3 = xx * yy * zz;
00066     return (1 - e2 / 10 + e3 / 14 + e2 * e2 / 24 - 3 * e2 * e3 / 44) / sqrt(an);
00067   }
00068 
00069 
00070   double EllipticFunction::RD(double x, double y, double z) throw() {
00071     // Carlson, eqs 2.28 - 2.34
00072     double
00073       a0 = (x + y + 3 * z)/5,
00074       an = a0,
00075       q = max(max(abs(a0-x), abs(a0-y)), abs(a0-z)) / tolRD,
00076       x0 = x,
00077       y0 = y,
00078       z0 = z,
00079       mul = 1,
00080       s = 0;
00081     while (q >= mul * abs(an)) {
00082       // Max 7 trips
00083       double ln = sqrt(x0)*sqrt(y0) +
00084         sqrt(y0)*sqrt(z0) +
00085         sqrt(z0)*sqrt(x0);
00086       s += 1/(mul * sqrt(z0) * (z0 + ln ));
00087       an = (an + ln)/4;
00088       x0 = (x0 + ln)/4;
00089       y0 = (y0 + ln)/4;
00090       z0 = (z0 + ln)/4;
00091       mul *= 4;
00092     }
00093     double
00094       xx = (a0 - x) / (mul * an),
00095       yy = (a0 - y) / (mul * an),
00096       zz = -(xx + yy) / 3,
00097       e2 = xx * yy - 6 * zz * zz,
00098       e3 = (3 * xx * yy - 8 * zz * zz)*zz,
00099       e4 = 3 * (xx * yy - zz * zz) * zz * zz,
00100       e5 = xx * yy * zz * zz * zz;
00101     return (1 - 3 * e2 / 14 + e3 / 6 + 9 * e2 * e2 / 88 - 3 * e4 / 22
00102             - 9 * e2 * e3 / 52 + 3 * e5 / 26) / (mul * an * sqrt(an))
00103       + 3 * s;
00104   }
00105 
00106   double EllipticFunction::RG0(double x, double y) throw() {
00107     // Carlson, eqs 2.36 - 2.39
00108     double
00109       x0 = sqrt(x),
00110       y0 = sqrt(y),
00111       xn = x0,
00112       yn = y0,
00113       s = 0,
00114       mul = 0.25;
00115     while (abs(xn-yn) >= tolRG0 * abs(xn)) {
00116       // Max 4 trips
00117       double t = (xn + yn) /2;
00118       yn = sqrt(xn * yn);
00119       xn = t;
00120       mul *= 2;
00121       t = xn - yn;
00122       s += mul * t * t;
00123     }
00124     x0 = (x0 + y0)/2;
00125     return  (x0 * x0 - s) * Constants::pi() / (2 * (xn + yn));
00126   }
00127 
00128   EllipticFunction::EllipticFunction(double m) throw()
00129     : _m(m)
00130     , _m1(1 - m)
00131       // Don't initialize _kc, _ec, _kec since this constructor might be called
00132       // before the static double constants tolRF, etc., are initialized.
00133     , _init(false)
00134   {}
00135 
00136   bool EllipticFunction::Init() const throw() {
00137     // Complete elliptic integral K(m), Carlson eq. 4.1
00138     _kc = RF(0.0, _m1, 1.0);
00139     // Complete elliptic integral E(m), Carlson eq. 4.2
00140     _ec = 2 * RG0(_m1, 1.0);
00141     // K - E, Carlson eq.4.3
00142     _kec = _m / 3 * RD(0.0, _m1, 1.0);
00143     return _init = true;
00144   }
00145 
00146   /*
00147    * Implementation of methods given in
00148    *
00149    *   R. Bulirsch
00150    *   Numerical Calculation of Elliptic Integrals and Elliptic Functions
00151    *   Numericshe Mathematik 7, 78-90 (1965)
00152    */
00153 
00154   void EllipticFunction::sncndn(double x,
00155                                 double& sn, double& cn, double& dn)
00156     const throw() {
00157     // Bulirsch's sncndn routine, p 89.
00158     //
00159     // Assume _m1 is in [0, 1].  See Bulirsch article for code to treat
00160     // negative _m1.
00161     if (_m1 != 0) {
00162       double mc = _m1;
00163       double c;
00164       double m[num], n[num];
00165       unsigned l = 0;
00166       for (double a = 1; l < num; ++l) {
00167         // Max 5 trips
00168         m[l] = a;
00169         n[l] = mc = sqrt(mc);
00170         c = (a + mc) / 2;
00171         if (abs(a - mc) <= tolJAC * a) {
00172           ++l;
00173           break;
00174         }
00175         mc = a * mc;
00176         a = c;
00177       }
00178       x = c * x;
00179       sn = sin(x);
00180       cn = cos(x);
00181       dn = 1;
00182       if (sn != 0) {
00183         double a = cn / sn;
00184         c = a * c;
00185         while (l--) {
00186           double b = m[l];
00187           a = c * a;
00188           c = dn * c;
00189           dn = (n[l] + a) / (b + a);
00190           a = c / b;
00191         }
00192         a = 1 / sqrt(c * c + 1);
00193         sn = sn < 0 ? -a : a;
00194         cn = c * sn;
00195       }
00196     } else {
00197       sn = tanh(x);
00198       dn = cn = 1 / cosh(x);
00199     }
00200   }
00201 
00202   double EllipticFunction::E(double sn, double cn, double dn) const throw() {
00203     double ei;
00204     if (abs(sn) > tolJAC1) {
00205       double
00206         s = 1 / sn,
00207         c = cn * s,
00208         d = dn * s;
00209       s *= s;
00210       c *= c;
00211       d *= d;
00212       // Carlson, eq. 4.6
00213       ei = RF(c, d, s) - _m / 3 * RD(c, d, s);
00214     } else
00215       // From A+S 16.22.1, 16.22.3, 17.2.10, we have for small sn, and cn > 0
00216       // ei = sn
00217       //      - (m-1)*sn^3/6
00218       //      - (m^2+2*m-3)*sn^5/40
00219       //      - (m^3+m^2+3*m-5)*sn^7/112
00220       //    approx sn,  for sn < sqrt(6 * eps)
00221       ei = abs(sn);
00222     // Enforce usual trig-like symmetries
00223     if (cn < 0) {
00224       ei = 2 * E() - ei;
00225     }
00226     if (sn < 0)
00227       ei = -ei;
00228     return ei;
00229   }
00230 
00231 } // namespace GeographicLib