ell_carlson.go 5.8 KB

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