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 }