gonum.org/v1/gonum@v0.15.1-0.20240517103525-f853624cb1bb/mathext/ell_carlson.go (about)

     1  // Copyright ©2017 The Gonum Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package mathext
     6  
     7  import (
     8  	"math"
     9  )
    10  
    11  // EllipticRF computes the symmetric elliptic integral R_F(x,y,z):
    12  //
    13  //	R_F(x,y,z) = (1/2)\int_{0}^{\infty}{1/s(t)} dt,
    14  //	s(t) = \sqrt{(t+x)(t+y)(t+z)}.
    15  //
    16  // The arguments x, y, z must satisfy the following conditions, otherwise the function returns math.NaN():
    17  //
    18  //	0 ≤ x,y,z ≤ upper,
    19  //	lower ≤ x+y,y+z,z+x,
    20  //
    21  // where:
    22  //
    23  //	lower = 5/(2^1022) = 1.112536929253601e-307,
    24  //	upper = (2^1022)/5 = 8.988465674311580e+306.
    25  //
    26  // The definition of the symmetric elliptic integral R_F can be found in NIST
    27  // Digital Library of Mathematical Functions (http://dlmf.nist.gov/19.16.E1).
    28  func EllipticRF(x, y, z float64) float64 {
    29  	// The original Fortran code was published as Algorithm 577 in ACM TOMS (http://doi.org/10.1145/355958.355970).
    30  	// This code is also available as a part of SLATEC Common Mathematical Library (http://netlib.org/slatec/index.html). Later, Carlson described
    31  	// an improved version in http://dx.doi.org/10.1007/BF02198293 (also available at https://arxiv.org/abs/math/9409227).
    32  	const (
    33  		lower = 5.0 / (1 << 256) / (1 << 256) / (1 << 256) / (1 << 254) // 5*2^-1022
    34  		upper = 1 / lower
    35  		tol   = 1.2674918778210762260320167734407048051023273568443e-02 // (3ε)^(1/8)
    36  	)
    37  	if x < 0 || y < 0 || z < 0 || math.IsNaN(x) || math.IsNaN(y) || math.IsNaN(z) {
    38  		return math.NaN()
    39  	}
    40  	if upper < x || upper < y || upper < z {
    41  		return math.NaN()
    42  	}
    43  	if x+y < lower || y+z < lower || z+x < lower {
    44  		return math.NaN()
    45  	}
    46  
    47  	A0 := (x + y + z) / 3
    48  	An := A0
    49  	Q := math.Max(math.Max(math.Abs(A0-x), math.Abs(A0-y)), math.Abs(A0-z)) / tol
    50  	xn, yn, zn := x, y, z
    51  	mul := 1.0
    52  
    53  	for Q >= mul*math.Abs(An) {
    54  		xnsqrt, ynsqrt, znsqrt := math.Sqrt(xn), math.Sqrt(yn), math.Sqrt(zn)
    55  		lambda := xnsqrt*ynsqrt + ynsqrt*znsqrt + znsqrt*xnsqrt
    56  		An = (An + lambda) * 0.25
    57  		xn = (xn + lambda) * 0.25
    58  		yn = (yn + lambda) * 0.25
    59  		zn = (zn + lambda) * 0.25
    60  		mul *= 4
    61  	}
    62  
    63  	X := (A0 - x) / (mul * An)
    64  	Y := (A0 - y) / (mul * An)
    65  	Z := -(X + Y)
    66  	E2 := X*Y - Z*Z
    67  	E3 := X * Y * Z
    68  
    69  	// http://dlmf.nist.gov/19.36.E1
    70  	return (1 - 1/10.0*E2 + 1/14.0*E3 + 1/24.0*E2*E2 - 3/44.0*E2*E3 - 5/208.0*E2*E2*E2 + 3/104.0*E3*E3 + 1/16.0*E2*E2*E3) / math.Sqrt(An)
    71  }
    72  
    73  // EllipticRD computes the symmetric elliptic integral R_D(x,y,z):
    74  //
    75  //	R_D(x,y,z) = (1/2)\int_{0}^{\infty}{1/(s(t)(t+z))} dt,
    76  //	s(t) = \sqrt{(t+x)(t+y)(t+z)}.
    77  //
    78  // The arguments x, y, z must satisfy the following conditions, otherwise the function returns math.NaN():
    79  //
    80  //	0 ≤ x,y ≤ upper,
    81  //	lower ≤ z ≤ upper,
    82  //	lower ≤ x+y,
    83  //
    84  // where:
    85  //
    86  //	lower = (5/(2^1022))^(1/3) = 4.809554074311679e-103,
    87  //	upper = ((2^1022)/5)^(1/3) = 2.079194837087086e+102.
    88  //
    89  // The definition of the symmetric elliptic integral R_D can be found in NIST
    90  // Digital Library of Mathematical Functions (http://dlmf.nist.gov/19.16.E5).
    91  func EllipticRD(x, y, z float64) float64 {
    92  	// The original Fortran code was published as Algorithm 577 in ACM TOMS (http://doi.org/10.1145/355958.355970).
    93  	// This code is also available as a part of SLATEC Common Mathematical Library (http://netlib.org/slatec/index.html). Later, Carlson described
    94  	// an improved version in http://dx.doi.org/10.1007/BF02198293 (also available at https://arxiv.org/abs/math/9409227).
    95  	const (
    96  		lower = 4.8095540743116787026618007863123676393525016818363e-103 // (5*2^-1022)^(1/3)
    97  		upper = 1 / lower
    98  		tol   = 9.0351169339315770474760122547068324993857488849382e-03 // (ε/5)^(1/8)
    99  	)
   100  	if x < 0 || y < 0 || math.IsNaN(x) || math.IsNaN(y) || math.IsNaN(z) {
   101  		return math.NaN()
   102  	}
   103  	if upper < x || upper < y || upper < z {
   104  		return math.NaN()
   105  	}
   106  	if x+y < lower || z < lower {
   107  		return math.NaN()
   108  	}
   109  
   110  	A0 := (x + y + 3*z) / 5
   111  	An := A0
   112  	Q := math.Max(math.Max(math.Abs(A0-x), math.Abs(A0-y)), math.Abs(A0-z)) / tol
   113  	xn, yn, zn := x, y, z
   114  	mul, s := 1.0, 0.0
   115  
   116  	for Q >= mul*math.Abs(An) {
   117  		xnsqrt, ynsqrt, znsqrt := math.Sqrt(xn), math.Sqrt(yn), math.Sqrt(zn)
   118  		lambda := xnsqrt*ynsqrt + ynsqrt*znsqrt + znsqrt*xnsqrt
   119  		s += 1 / (mul * znsqrt * (zn + lambda))
   120  		An = (An + lambda) * 0.25
   121  		xn = (xn + lambda) * 0.25
   122  		yn = (yn + lambda) * 0.25
   123  		zn = (zn + lambda) * 0.25
   124  		mul *= 4
   125  	}
   126  
   127  	X := (A0 - x) / (mul * An)
   128  	Y := (A0 - y) / (mul * An)
   129  	Z := -(X + Y) / 3
   130  	E2 := X*Y - 6*Z*Z
   131  	E3 := (3*X*Y - 8*Z*Z) * Z
   132  	E4 := 3 * (X*Y - Z*Z) * Z * Z
   133  	E5 := X * Y * Z * Z * Z
   134  
   135  	// http://dlmf.nist.gov/19.36.E2
   136  	return (1-3/14.0*E2+1/6.0*E3+9/88.0*E2*E2-3/22.0*E4-9/52.0*E2*E3+3/26.0*E5-1/16.0*E2*E2*E2+3/40.0*E3*E3+3/20.0*E2*E4+45/272.0*E2*E2*E3-9/68.0*(E3*E4+E2*E5))/(mul*An*math.Sqrt(An)) + 3*s
   137  }
   138  
   139  // EllipticF computes the Legendre's elliptic integral of the 1st kind F(phi,m), 0≤m<1:
   140  //
   141  //	F(\phi,m) = \int_{0}^{\phi} 1 / \sqrt{1-m\sin^2(\theta)} d\theta
   142  //
   143  // Legendre's elliptic integrals can be expressed as symmetric elliptic integrals, in this case:
   144  //
   145  //	F(\phi,m) = \sin\phi R_F(\cos^2\phi,1-m\sin^2\phi,1)
   146  //
   147  // The definition of F(phi,k) where k=sqrt(m) can be found in NIST Digital Library of Mathematical
   148  // Functions (http://dlmf.nist.gov/19.2.E4).
   149  func EllipticF(phi, m float64) float64 {
   150  	s, c := math.Sincos(phi)
   151  	return s * EllipticRF(c*c, 1-m*s*s, 1)
   152  }
   153  
   154  // EllipticE computes the Legendre's elliptic integral of the 2nd kind E(phi,m), 0≤m<1:
   155  //
   156  //	E(\phi,m) = \int_{0}^{\phi} \sqrt{1-m\sin^2(\theta)} d\theta
   157  //
   158  // Legendre's elliptic integrals can be expressed as symmetric elliptic integrals, in this case:
   159  //
   160  //	E(\phi,m) = \sin\phi R_F(\cos^2\phi,1-m\sin^2\phi,1)-(m/3)\sin^3\phi R_D(\cos^2\phi,1-m\sin^2\phi,1)
   161  //
   162  // The definition of E(phi,k) where k=sqrt(m) can be found in NIST Digital Library of Mathematical
   163  // Functions (http://dlmf.nist.gov/19.2.E5).
   164  func EllipticE(phi, m float64) float64 {
   165  	s, c := math.Sincos(phi)
   166  	x, y := c*c, 1-m*s*s
   167  	return s * (EllipticRF(x, y, 1) - (m/3)*s*s*EllipticRD(x, y, 1))
   168  }