diff tango/tango/math/Elliptic.d @ 132:1700239cab2e trunk

[svn r136] MAJOR UNSTABLE UPDATE!!! Initial commit after moving to Tango instead of Phobos. Lots of bugfixes... This build is not suitable for most things.
author lindquist
date Fri, 11 Jan 2008 17:57:40 +0100
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tango/tango/math/Elliptic.d	Fri Jan 11 17:57:40 2008 +0100
@@ -0,0 +1,346 @@
+/**
+ * Elliptic integrals.
+ * The functions are named similarly to the names used in Mathematica. 
+ *
+ * License:   BSD style: $(LICENSE)
+ * Copyright: Based on the CEPHES math library, which is
+ *            Copyright (C) 1994 Stephen L. Moshier (moshier@world.std.com).
+ * Authors:   Stephen L. Moshier (original C code). Conversion to D by Don Clugston
+ *
+ * References:
+ * $(LINK http://en.wikipedia.org/wiki/Elliptic_integral)
+ *
+ * Eric W. Weisstein. "Elliptic Integral of the First Kind." From MathWorld--A Wolfram Web Resource. $(LINK http://mathworld.wolfram.com/EllipticIntegraloftheFirstKind.html)
+ *
+ * $(LINK http://www.netlib.org/cephes/ldoubdoc.html)
+ *
+ * Macros:
+ *  TABLE_SV = <table border=1 cellpadding=4 cellspacing=0>
+ *      <caption>Special Values</caption>
+ *      $0</table>
+ *  SVH = $(TR $(TH $1) $(TH $2))
+ *  SV  = $(TR $(TD $1) $(TD $2))
+ *  GAMMA =  &#915;
+ *  INTEGRATE = $(BIG &#8747;<sub>$(SMALL $1)</sub><sup>$2</sup>)
+ *  POWER = $1<sup>$2</sup>
+ *  NAN = $(RED NAN)
+ */
+/**
+ * Macros:
+ *  TABLE_SV = <table border=1 cellpadding=4 cellspacing=0>
+ *      <caption>Special Values</caption>
+ *      $0</table>
+ *  SVH = $(TR $(TH $1) $(TH $2))
+ *  SV  = $(TR $(TD $1) $(TD $2))
+ *
+ *  NAN = $(RED NAN)
+ *  SUP = <span style="vertical-align:super;font-size:smaller">$0</span>
+ *  GAMMA =  &#915;
+ *  INTEGRAL = &#8747;
+ *  INTEGRATE = $(BIG &#8747;<sub>$(SMALL $1)</sub><sup>$2</sup>)
+ *  POWER = $1<sup>$2</sup>
+ *  BIGSUM = $(BIG &Sigma; <sup>$2</sup><sub>$(SMALL $1)</sub>)
+ *  CHOOSE = $(BIG &#40;) <sup>$(SMALL $1)</sup><sub>$(SMALL $2)</sub> $(BIG &#41;)
+ */
+
+module tango.math.Elliptic;
+
+import tango.math.Math;
+import tango.math.IEEE;
+
+/* These functions are based on code from:
+Cephes Math Library, Release 2.3:  October, 1995
+Copyright 1984, 1987, 1995 by Stephen L. Moshier
+*/
+ 
+
+/**
+ *  Incomplete elliptic integral of the first kind
+ *
+ * Approximates the integral
+ *   F(phi | m) = $(INTEGRATE 0, phi) dt/ (sqrt( 1- m $(POWER sin, 2) t))
+ *
+ * of amplitude phi and modulus m, using the arithmetic -
+ * geometric mean algorithm.
+ */
+
+real ellipticF(real phi, real m )
+{
+    real a, b, c, e, temp, t, K;
+    int d, mod, sign, npio2;
+
+    if( m == 0.0L )
+        return phi;
+    a = 1.0L - m;
+    if( a == 0.0L ) {
+        if ( fabs(phi) >= PI_2 )  return real.infinity;
+        return  log(  tan( 0.5L*(PI_2 + phi) )  );
+    }
+    npio2 = cast(int)floor( phi/PI_2 );
+    if ( npio2 & 1 )
+        npio2 += 1;
+    if ( npio2 ) {
+        K = ellipticKComplete( a );
+        phi = phi - npio2 * PI_2;
+    } else
+        K = 0.0L;
+    if( phi < 0.0L ){
+        phi = -phi;
+        sign = -1;
+    } else
+    sign = 0;
+    b = sqrt(a);
+    t = tan( phi );
+    if( fabs(t) > 10.0L ) {
+    /* Transform the amplitude */
+    e = 1.0L/(b*t);
+    /* ... but avoid multiple recursions.  */
+    if( fabs(e) < 10.0L ){
+        e = atan(e);
+        if( npio2 == 0 )
+            K = ellipticKComplete( a );
+            temp = K - ellipticF( e, m );
+            goto done;
+        }
+    }
+    a = 1.0L;
+    c = sqrt(m);
+    d = 1;
+    mod = 0;
+
+    while( fabs(c/a) > real.epsilon ) {
+        temp = b/a;
+        phi = phi + atan(t*temp) + mod * PI;
+        mod = cast(int)((phi + PI_2)/PI);
+        t = t * ( 1.0L + temp )/( 1.0L - temp * t * t );
+        c = 0.5L * ( a - b );
+        temp = sqrt( a * b );
+        a = 0.5L * ( a + b );
+        b = temp;
+        d += d;
+    }
+    temp = (atan(t) + mod * PI)/(d * a);
+
+done:
+    if ( sign < 0 )
+        temp = -temp;
+    temp += npio2 * K;
+    return temp;
+}
+
+
+/**
+ *  Incomplete elliptic integral of the second kind
+ *
+ * Approximates the integral
+ *
+ * E(phi | m) = $(INTEGRATE 0, phi) sqrt( 1- m $(POWER sin, 2) t) dt
+ *
+ * of amplitude phi and modulus m, using the arithmetic -
+ * geometric mean algorithm.
+ */
+
+real ellipticE(real phi, real m )
+{
+    real a, b, c, e, temp, t, E;
+    int d, mod, npio2, sign;
+
+    if ( m == 0.0L ) return phi;
+    real lphi = phi;
+    npio2 = cast(int)floor( lphi/PI_2 );
+    if( npio2 & 1 )
+        npio2 += 1;
+    lphi = lphi - npio2 * PI_2;
+    if( lphi < 0.0L ){
+        lphi = -lphi;
+        sign = -1;
+    } else  {
+        sign = 1;
+    }
+    a = 1.0L - m;
+    E = ellipticEComplete( a );
+    if( a == 0.0L ) {
+        temp = sin( lphi );
+        goto done;
+    }
+    t = tan( lphi );
+    b = sqrt(a);
+    if ( fabs(t) > 10.0L ) {
+        /* Transform the amplitude */
+        e = 1.0L/(b*t);
+        /* ... but avoid multiple recursions.  */
+        if( fabs(e) < 10.0L ){
+            e = atan(e);
+            temp = E + m * sin( lphi ) * sin( e ) - ellipticE( e, m );
+            goto done;
+        }
+    }
+    c = sqrt(m);
+    a = 1.0L;
+    d = 1;
+    e = 0.0L;
+    mod = 0;
+
+    while( fabs(c/a) > real.epsilon ) {
+        temp = b/a;
+        lphi = lphi + atan(t*temp) + mod * PI;
+        mod = cast(int)((lphi + PI_2)/PI);
+        t = t * ( 1.0L + temp )/( 1.0L - temp * t * t );
+        c = 0.5L*( a - b );
+        temp = sqrt( a * b );
+        a = 0.5L*( a + b );
+        b = temp;
+        d += d;
+        e += c * sin(lphi);
+    }
+
+    temp = E / ellipticKComplete( 1.0L - m );
+    temp *= (atan(t) + mod * PI)/(d * a);
+    temp += e;
+
+done:
+    if( sign < 0 )
+        temp = -temp;
+    temp += npio2 * E;
+    return temp;
+}
+
+
+/**
+ *  Complete elliptic integral of the first kind
+ *
+ * Approximates the integral
+ *
+ *   K(m) = $(INTEGRATE 0, &pi/2) dt/ (sqrt( 1- m $(POWER sin, 2) t))
+ *
+ * where m = 1 - x, using the approximation
+ *
+ *     P(x)  -  log x Q(x).
+ *
+ * The argument x is used rather than m so that the logarithmic
+ * singularity at x = 1 will be shifted to the origin; this
+ * preserves maximum accuracy. 
+ *
+ * x must be in the range
+ *  0 <= x <= 1
+ *
+ * This is equivalent to ellipticF(PI_2, 1-x).
+ *
+ * K(0) = &pi/2.
+ */
+
+real ellipticKComplete(real x)
+in {
+//    assert(x>=0.0L && x<=1.0L);
+}
+body{
+
+const real [] P = [
+   0x1.62e42fefa39ef35ap+0, // 1.3862943611198906189
+   0x1.8b90bfbe8ed811fcp-4, // 0.096573590279993142323
+   0x1.fa05af797624c586p-6, // 0.030885144578720423267
+   0x1.e979cdfac7249746p-7, // 0.01493761594388688915
+   0x1.1f4cc8890cff803cp-7, // 0.0087676982094322259125
+   0x1.7befb3bb1fa978acp-8, // 0.0057973684116620276454
+   0x1.2c2566aa1d5fe6b8p-8, // 0.0045798659940508010431
+   0x1.7333514e7fe57c98p-8, // 0.0056640695097481470287
+   0x1.09292d1c8621348cp-7, // 0.0080920667906392630755
+   0x1.b89ab5fe793a6062p-8, // 0.0067230886765842542487
+   0x1.28e9c44dc5e26e66p-9, // 0.002265267575136470585
+   0x1.c2c43245d445addap-13,    // 0.00021494216542320112406
+   0x1.4ee247035a03e13p-20  // 1.2475397291548388385e-06
+];
+
+const real [] Q = [
+   0x1p-1,  // 0.5
+   0x1.fffffffffff635eap-4, // 0.12499999999999782631
+   0x1.1fffffff8a2bea1p-4,  // 0.070312499993302227507
+   0x1.8ffffe6f40ec2078p-5, // 0.04882812208418620146
+   0x1.323f4dbf7f4d0c2ap-5, // 0.037383701182969303058
+   0x1.efe8a028541b50bp-6,  // 0.030267864612427881354
+   0x1.9d58c49718d6617cp-6, // 0.025228683455123323041
+   0x1.4d1a8d2292ff6e2ep-6, // 0.020331037356569904872
+   0x1.b637687027d664aap-7, // 0.013373304362459048444
+   0x1.687a640ae5c71332p-8, // 0.0055004591221382442135
+   0x1.0f9c30a94a1dcb4ep-10,    // 0.001036110372590318803
+   0x1.d321746708e92d48p-15     // 5.568631677757315399e-05
+];
+
+    const real LOG4 = 0x1.62e42fefa39ef358p+0;  // log(4)
+
+    if( x > real.epsilon )
+        return poly(x,P) - log(x) * poly(x,Q);
+    if ( x == 0.0L )
+        return real.infinity;    
+    return LOG4 - 0.5L * log(x);
+}
+
+/**
+ *  Complete elliptic integral of the second kind
+ *
+ * Approximates the integral
+ *
+ * E(m) = $(INTEGRATE 0, &pi/2) sqrt( 1- m $(POWER sin, 2) t) dt
+ *
+ * Where m = 1 - m1, using the approximation
+ *
+ *      P(x)  -  x log x Q(x).
+ *
+ * Though there are no singularities, the argument m1 is used
+ * rather than m for compatibility with ellipticKComplete().
+ *
+ * E(1) = 1; E(0) = &pi/2.
+ * m must be in the range 0 <= m <= 1.
+ */
+
+real ellipticEComplete(real x)
+in {
+ assert(x>=0 && x<=1.0);
+}
+body {
+const real [] P = [
+   0x1.c5c85fdf473f78f2p-2, // 0.44314718055994670505
+   0x1.d1591f9e9a66477p-5,  // 0.056805192715569305834
+   0x1.65af6a7a61f587cp-6,  // 0.021831373198011179718
+   0x1.7a4d48ed00d5745ap-7, // 0.011544857605264509506
+   0x1.d4f5fe4f93b60688p-8, // 0.0071557756305783152481
+   0x1.4cb71c73bac8656ap-8, // 0.0050768322432573952962
+   0x1.4a9167859a1d0312p-8, // 0.0050440671671840438539
+   0x1.dd296daa7b1f5b7ap-8, // 0.0072809117068399675418
+   0x1.04f2c29224ba99b6p-7, // 0.0079635095646944542686
+   0x1.0f5820e2d80194d8p-8, // 0.0041403847015715420009
+   0x1.95ee634752ca69b6p-11,    // 0.00077425232385887751162
+   0x1.0c58aa9ab404f4fp-15  // 3.1989378120323412946e-05
+];
+
+const real [] Q = [
+   0x1.ffffffffffffb1cep-3, // 0.24999999999999986434
+   0x1.7ffffffff29eaa0cp-4, // 0.093749999999239422678
+   0x1.dfffffbd51eb098p-5,  // 0.058593749514839092674
+   0x1.5dffd791cb834c92p-5, // 0.04272453406734691973
+   0x1.1397b63c2f09a8ep-5,  // 0.033641677787700181541
+   0x1.c567cde5931e75bcp-6, // 0.02767367465121309044
+   0x1.75e0cae852be9ddcp-6, // 0.022819708015315777007
+   0x1.12bb968236d4e434p-6, // 0.016768357258894633433
+   0x1.1f6572c1c402d07cp-7, // 0.0087706384979640787504
+   0x1.452c6909f88b8306p-9, // 0.0024808767529843311337
+   0x1.1f7504e72d664054p-12,    // 0.00027414045912208516032
+   0x1.ad17054dc46913e2p-18     // 6.3939381343012054851e-06
+];
+    if (x==0)
+        return 1.0L;
+    return 1.0L + x * poly(x,P) - log(x) * (x * poly(x,Q) );
+}
+
+unittest {
+    assert( ellipticF(1, 0)==1);
+    assert(ellipticEComplete(0)==1);
+    assert(ellipticEComplete(1)==PI_2);
+    assert(feqrel(ellipticKComplete(1),PI_2)>= real.mant_dig-1);
+    assert(ellipticKComplete(0)==real.infinity);
+//    assert(ellipticKComplete(1)==0); //-real.infinity);
+    
+    real x=0.5653L;
+    assert(ellipticKComplete(1-x) == ellipticF(PI_2, x) );
+    assert(ellipticEComplete(1-x) == ellipticE(PI_2, x) );
+}