Coverage for pygeodesy/elliptic.py: 96%
530 statements
« prev ^ index » next coverage.py v7.6.1, created at 2025-05-29 12:40 -0400
« prev ^ index » next coverage.py v7.6.1, created at 2025-05-29 12:40 -0400
2# -*- coding: utf-8 -*-
4u'''I{Karney}'s elliptic functions and integrals.
6Class L{Elliptic} transcoded from I{Charles Karney}'s C++ class U{EllipticFunction
7<https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1EllipticFunction.html>}
8to pure Python, including symmetric integrals L{Elliptic.fRC}, L{Elliptic.fRD},
9L{Elliptic.fRF}, L{Elliptic.fRG} and L{Elliptic.fRJ} as C{static methods}.
11Python method names follow the C++ member functions, I{except}:
13 - member functions I{without arguments} are mapped to Python properties
14 prefixed with C{"c"}, for example C{E()} is property C{cE},
16 - member functions with 1 or 3 arguments are renamed to Python methods
17 starting with an C{"f"}, example C{E(psi)} to C{fE(psi)} and C{E(sn,
18 cn, dn)} to C{fE(sn, cn, dn)},
20 - other Python method names conventionally start with a lower-case
21 letter or an underscore if private.
23Following is a copy of I{Karney}'s U{EllipticFunction.hpp
24<https://GeographicLib.SourceForge.io/C++/doc/EllipticFunction_8hpp_source.html>}
25file C{Header}.
27Copyright (C) U{Charles Karney<mailto:Karney@Alum.MIT.edu>} (2008-2024)
28and licensed under the MIT/X11 License. For more information, see the
29U{GeographicLib<https://GeographicLib.SourceForge.io>} documentation.
31B{Elliptic integrals and functions.}
33This provides the elliptic functions and integrals needed for
34C{Ellipsoid}, C{GeodesicExact}, and C{TransverseMercatorExact}. Two
35categories of function are provided:
37 - functions to compute U{symmetric elliptic integrals
38 <https://DLMF.NIST.gov/19.16.i>}
40 - methods to compute U{Legrendre's elliptic integrals
41 <https://DLMF.NIST.gov/19.2.ii>} and U{Jacobi elliptic
42 functions<https://DLMF.NIST.gov/22.2>}.
44In the latter case, an object is constructed giving the modulus
45C{k} (and optionally the parameter C{alpha}). The modulus (and
46parameter) are always passed as squares which allows C{k} to be
47pure imaginary. (Confusingly, Abramowitz and Stegun call C{m = k**2}
48the "parameter" and C{n = alpha**2} the "characteristic".)
50In geodesic applications, it is convenient to separate the incomplete
51integrals into secular and periodic components, e.g.
53I{C{E(phi, k) = (2 E(k) / pi) [ phi + delta E(phi, k) ]}}
55where I{C{delta E(phi, k)}} is an odd periodic function with
56period I{C{pi}}.
58The computation of the elliptic integrals uses the algorithms given
59in U{B. C. Carlson, Computation of real or complex elliptic integrals
60<https://DOI.org/10.1007/BF02198293>} (also available U{here
61<https://ArXiv.org/pdf/math/9409227.pdf>}), Numerical Algorithms 10,
6213--26 (1995) with the additional optimizations given U{here
63<https://DLMF.NIST.gov/19.36.i>}.
65The computation of the Jacobi elliptic functions uses the algorithm
66given in U{R. Bulirsch, Numerical Calculation of Elliptic Integrals
67and Elliptic Functions<https://DOI.org/10.1007/BF01397975>},
68Numerische Mathematik 7, 78--90 (1965) or optionally the C{Jacobi
69amplitude} in method L{Elliptic.sncndn<pygeodesy.Elliptic.sncndn>}.
71The notation follows U{NIST Digital Library of Mathematical Functions
72<https://DLMF.NIST.gov>} chapters U{19<https://DLMF.NIST.gov/19>} and
73U{22<https://DLMF.NIST.gov/22>}.
74'''
75# make sure int/int division yields float quotient, see .basics
76from __future__ import division as _; del _ # noqa: E702 ;
78from pygeodesy.basics import copysign0, map2, neg, neg_, typename
79from pygeodesy.constants import EPS, INF, NAN, PI, PI_2, PI_4, _0_0, \
80 _0_25, _0_5, _1_0, _2_0, _N_2_0, _3_0, \
81 _4_0, _6_0, _8_0, _64_0, _180_0, _360_0, \
82 _EPStol as _TolJAC, _over, \
83 _EPSjam as _TolJAM # PYCHOK used!
84# from pygeodesy.errors import _ValueError # from .fmath
85from pygeodesy.fmath import favg, hypot1, zqrt, _ValueError
86from pygeodesy.fsums import Fsum, _sum
87# from pygeodesy.internals import typename # from .basics
88from pygeodesy.interns import NN, _delta_, _DOT_, _f_, _invalid_, _invokation_, \
89 _negative_, _SPACE_
90from pygeodesy.karney import _K_2_0, _norm180, _signBit, _sincos2
91# from pygeodesy.lazily import _ALL_LAZY # from .named
92from pygeodesy.named import _Named, _NamedTuple, _ALL_LAZY, Fmt, unstr
93from pygeodesy.props import _allPropertiesOf_n, Property_RO, _update_all
94# from pygeodesy.streprs import Fmt, unstr # from .named
95from pygeodesy.units import Scalar, Scalar_
96from pygeodesy.utily import atan2 # sincos2 as _sincos2
98from math import asin, asinh, atan, ceil, cosh, fabs, floor, radians, \
99 sin, sinh, sqrt, tan, tanh # tan as _tan
101__all__ = _ALL_LAZY.elliptic
102__version__ = '25.05.28'
104_TolRD = zqrt(EPS * 0.002)
105_TolRF = zqrt(EPS * 0.030)
106_TolRG0 = _TolJAC * 2.7
107_TRIPS = 25 # Max depth, 6-7 might be sufficient
110class _Cs(object):
111 '''(INTERAL) Complete integrals cache.
112 '''
113 def __init__(self, **kwds):
114 self.__dict__ = kwds
117class _Dsum(list):
118 '''(INTERNAL) Deferred C{Fsum}.
119 '''
120 def __call__(self, s):
121 try: # Fsum *= s
122 return Fsum(*self).fmul(s)
123 except ValueError: # Fsum(NAN) exception
124 return _sum(self) * s
126 def __iadd__(self, x):
127 list.append(self, x)
128 return self
131class _Dadd(_Dsum):
132 '''(INTERNAL) Deferred C{Fsum} for C{_List.amrs4}.
133 '''
134 def __init__(self, mul):
135 self._mul = mul
137 def __add__(self, x):
138 self += x
139 r = self(self._mul)
140 _ = self.pop()
141 return r # Fsum or float
144class Elliptic(_Named):
145 '''Elliptic integrals and functions.
147 @see: I{Karney}'s U{Detailed Description<https://GeographicLib.SourceForge.io/
148 C++/doc/classGeographicLib_1_1EllipticFunction.html#details>}.
149 '''
150# _alpha2 = 0
151# _alphap2 = 0
152# _eps = EPS
153# _k2 = 0
154# _kp2 = 0
156 def __init__(self, k2=0, alpha2=0, kp2=None, alphap2=None, **name):
157 '''Constructor, specifying the C{modulus} and C{parameter}.
159 @kwarg name: Optional C{B{name}=NN} (C{str}).
161 @see: Method L{Elliptic.reset} for further details.
163 @note: If only elliptic integrals of the first and second kinds
164 are needed, use C{B{alpha2}=0}, the default value. In
165 that case, we have C{Π(φ, 0, k) = F(φ, k), G(φ, 0, k) =
166 E(φ, k)} and C{H(φ, 0, k) = F(φ, k) - D(φ, k)}.
167 '''
168 if name:
169 self.name = name
170 self.reset(k2=k2, alpha2=alpha2, kp2=kp2, alphap2=alphap2)
172 @Property_RO
173 def alpha2(self):
174 '''Get α^2, the square of the parameter (C{float}).
175 '''
176 return self._alpha2
178 @Property_RO
179 def alphap2(self):
180 '''Get α'^2, the square of the complementary parameter (C{float}).
181 '''
182 return self._alphap2
184 @Property_RO
185 def cD(self):
186 '''Get Jahnke's complete integral C{D(k)} (C{float}),
187 U{defined<https://DLMF.NIST.gov/19.2.E6>}.
188 '''
189 return self._cDEKEeps.cD
191 @Property_RO
192 def _cDEKEeps(self):
193 '''(INTERNAL) Get the complete integrals D, E, K and KE plus C{eps}.
194 '''
195 k2, kp2 = self.k2, self.kp2
196 if k2:
197 if kp2:
198 try:
199 self._iteration = 0
200 # D(k) = (K(k) - E(k))/k2, Carlson eq.4.3
201 # <https://DLMF.NIST.gov/19.25.E1>
202 D = _RD(self, _0_0, kp2, _1_0, _3_0)
203 cD = float(D)
204 # Complete elliptic integral E(k), Carlson eq. 4.2
205 # <https://DLMF.NIST.gov/19.25.E1>
206 cE = _rG2(self, kp2, _1_0, PI_=PI_2)
207 # Complete elliptic integral K(k), Carlson eq. 4.1
208 # <https://DLMF.NIST.gov/19.25.E1>
209 cK = _rF2(self, kp2, _1_0)
210 cKE = float(D.fmul(k2))
211 eps = k2 / (sqrt(kp2) + _1_0)**2
213 except Exception as e:
214 raise _ellipticError(self.reset, k2=k2, kp2=kp2, cause=e)
215 else:
216 cD = cK = cKE = INF
217 cE = _1_0
218 eps = k2
219 else:
220 cD = PI_4
221 cE = cK = PI_2
222 cKE = _0_0 # k2 * cD
223 eps = EPS
225 return _Cs(cD=cD, cE=cE, cK=cK, cKE=cKE, eps=eps)
227 @Property_RO
228 def cE(self):
229 '''Get the complete integral of the second kind C{E(k)}
230 (C{float}), U{defined<https://DLMF.NIST.gov/19.2.E5>}.
231 '''
232 return self._cDEKEeps.cE
234 @Property_RO
235 def cG(self):
236 '''Get Legendre's complete geodesic longitude integral
237 C{G(α^2, k)} (C{float}).
238 '''
239 return self._cGHPi.cG
241 @Property_RO
242 def _cGHPi(self):
243 '''(INTERNAL) Get the complete integrals G, H and Pi.
244 '''
245 alpha2, alphap2, kp2 = self.alpha2, self.alphap2, self.kp2
246 try:
247 self._iteration = 0
248 if alpha2:
249 if alphap2:
250 if kp2: # <https://DLMF.NIST.gov/19.25.E2>
251 cK = self.cK
252 Rj = _RJ(self, _0_0, kp2, _1_0, alphap2, _3_0)
253 cG = float(Rj * (alpha2 - self.k2) + cK) # G(alpha2, k)
254 cH = -float(Rj * alphap2 - cK) # H(alpha2, k)
255 cPi = float(Rj * alpha2 + cK) # Pi(alpha2, k)
256 else:
257 cG = cH = _rC(self, _1_0, alphap2)
258 cPi = INF # XXX or NAN?
259 else:
260 cG = cH = cPi = INF # XXX or NAN?
261 else:
262 cG, cPi = self.cE, self.cK
263 # H = K - D but this involves large cancellations if k2 is near 1.
264 # So write (for alpha2 = 0)
265 # H = int(cos(phi)**2 / sqrt(1-k2 * sin(phi)**2), phi, 0, pi/2)
266 # = 1 / sqrt(1-k2) * int(sin(phi)**2 / sqrt(1-k2/kp2 * sin(phi)**2,...)
267 # = 1 / kp * D(i * k/kp)
268 # and use D(k) = RD(0, kp2, 1) / 3, so
269 # H = 1/kp * RD(0, 1/kp2, 1) / 3
270 # = kp2 * RD(0, 1, kp2) / 3
271 # using <https://DLMF.NIST.gov/19.20.E18>. Equivalently
272 # RF(x, 1) - RD(0, x, 1) / 3 = x * RD(0, 1, x) / 3 for x > 0
273 # For k2 = 1 and alpha2 = 0, we have
274 # H = int(cos(phi),...) = 1
275 cH = float(_RD(self, _0_0, _1_0, kp2, _3_0 / kp2)) if kp2 else _1_0
277 except Exception as e:
278 raise _ellipticError(self.reset, kp2=kp2, alpha2 =alpha2,
279 alphap2=alphap2, cause=e)
280 return _Cs(cG=cG, cH=cH, cPi=cPi)
282 @Property_RO
283 def cH(self):
284 '''Get Cayley's complete geodesic longitude difference integral
285 C{H(α^2, k)} (C{float}).
286 '''
287 return self._cGHPi.cH
289 @Property_RO
290 def cK(self):
291 '''Get the complete integral of the first kind C{K(k)}
292 (C{float}), U{defined<https://DLMF.NIST.gov/19.2.E4>}.
293 '''
294 return self._cDEKEeps.cK
296 @Property_RO
297 def cKE(self):
298 '''Get the difference between the complete integrals of the
299 first and second kinds, C{K(k) − E(k)} (C{float}).
300 '''
301 return self._cDEKEeps.cKE
303 @Property_RO
304 def cPi(self):
305 '''Get the complete integral of the third kind C{Pi(α^2, k)}
306 (C{float}), U{defined<https://DLMF.NIST.gov/19.2.E7>}.
307 '''
308 return self._cGHPi.cPi
310 def deltaD(self, sn, cn, dn):
311 '''Jahnke's periodic incomplete elliptic integral.
313 @arg sn: sin(φ).
314 @arg cn: cos(φ).
315 @arg dn: sqrt(1 − k2 * sin(2φ)).
317 @return: Periodic function π D(φ, k) / (2 D(k)) - φ (C{float}).
319 @raise EllipticError: Invalid invokation or no convergence.
320 '''
321 return _deltaX(sn, cn, dn, self.cD, self.fD)
323 def deltaE(self, sn, cn, dn):
324 '''The periodic incomplete integral of the second kind.
326 @arg sn: sin(φ).
327 @arg cn: cos(φ).
328 @arg dn: sqrt(1 − k2 * sin(2φ)).
330 @return: Periodic function π E(φ, k) / (2 E(k)) - φ (C{float}).
332 @raise EllipticError: Invalid invokation or no convergence.
333 '''
334 return _deltaX(sn, cn, dn, self.cE, self.fE)
336 def deltaEinv(self, stau, ctau):
337 '''The periodic inverse of the incomplete integral of the second kind.
339 @arg stau: sin(τ)
340 @arg ctau: cos(τ)
342 @return: Periodic function E^−1(τ (2 E(k)/π), k) - τ (C{float}).
344 @raise EllipticError: No convergence.
345 '''
346 try:
347 if _signBit(ctau): # pi periodic
348 stau, ctau = neg_(stau, ctau)
349 t = atan2(stau, ctau)
350 return self._Einv(t * self.cE / PI_2) - t
352 except Exception as e:
353 raise _ellipticError(self.deltaEinv, stau, ctau, cause=e)
355 def deltaF(self, sn, cn, dn):
356 '''The periodic incomplete integral of the first kind.
358 @arg sn: sin(φ).
359 @arg cn: cos(φ).
360 @arg dn: sqrt(1 − k2 * sin(2φ)).
362 @return: Periodic function π F(φ, k) / (2 K(k)) - φ (C{float}).
364 @raise EllipticError: Invalid invokation or no convergence.
365 '''
366 return _deltaX(sn, cn, dn, self.cK, self.fF)
368 def deltaG(self, sn, cn, dn):
369 '''Legendre's periodic geodesic longitude integral.
371 @arg sn: sin(φ).
372 @arg cn: cos(φ).
373 @arg dn: sqrt(1 − k2 * sin(2φ)).
375 @return: Periodic function π G(φ, k) / (2 G(k)) - φ (C{float}).
377 @raise EllipticError: Invalid invokation or no convergence.
378 '''
379 return _deltaX(sn, cn, dn, self.cG, self.fG)
381 def deltaH(self, sn, cn, dn):
382 '''Cayley's periodic geodesic longitude difference integral.
384 @arg sn: sin(φ).
385 @arg cn: cos(φ).
386 @arg dn: sqrt(1 − k2 * sin(2φ)).
388 @return: Periodic function π H(φ, k) / (2 H(k)) - φ (C{float}).
390 @raise EllipticError: Invalid invokation or no convergence.
391 '''
392 return _deltaX(sn, cn, dn, self.cH, self.fH)
394 def deltaPi(self, sn, cn, dn):
395 '''The periodic incomplete integral of the third kind.
397 @arg sn: sin(φ).
398 @arg cn: cos(φ).
399 @arg dn: sqrt(1 − k2 * sin(2φ)).
401 @return: Periodic function π Π(φ, α2, k) / (2 Π(α2, k)) - φ
402 (C{float}).
404 @raise EllipticError: Invalid invokation or no convergence.
405 '''
406 return _deltaX(sn, cn, dn, self.cPi, self.fPi)
408 def _Einv(self, x):
409 '''(INTERNAL) Helper for C{.deltaEinv} and C{.fEinv}.
410 '''
411 E2 = self.cE * _2_0
412 n = floor(x / E2 + _0_5)
413 r = x - E2 * n # r in [-cE, cE)
414 # linear approximation
415 phi = PI * r / E2 # phi in [-PI_2, PI_2)
416 Phi = Fsum(phi)
417 # first order correction
418 phi = Phi.fsum_(self.eps * sin(phi * _2_0) / _N_2_0)
419 self._iteration = 0
420 # For kp2 close to zero use asin(r / cE) or J. P. Boyd,
421 # Applied Math. and Computation 218, 7005-7013 (2012)
422 # <https://DOI.org/10.1016/j.amc.2011.12.021>
423 _Phi2 = Phi.fsum2f_ # aggregate
424 for i in range(1, _TRIPS): # GEOGRAPHICLIB_PANIC
425 sn, cn, dn = self._sncndn3(phi)
426 if dn:
427 sn = self.fE(sn, cn, dn)
428 phi, d = _Phi2((r - sn) / dn)
429 else: # PYCHOK no cover
430 d = _0_0 # XXX or continue?
431 if fabs(d) < _TolJAC: # 3-4 trips
432 _iterations(self, i)
433 break
434 else: # PYCHOK no cover
435 raise _convergenceError(d, _TolJAC)
436 return Phi.fsum_(n * PI) if n else phi
438 @Property_RO
439 def eps(self):
440 '''Get epsilon (C{float}).
441 '''
442 return self._cDEKEeps.eps
444 def fD(self, phi_or_sn, cn=None, dn=None):
445 '''Jahnke's incomplete elliptic integral in terms of
446 Jacobi elliptic functions.
448 @arg phi_or_sn: φ or sin(φ).
449 @kwarg cn: C{None} or cos(φ).
450 @kwarg dn: C{None} or sqrt(1 − k2 * sin(2φ)).
452 @return: D(φ, k) as though φ ∈ (−π, π] (C{float}),
453 U{defined<https://DLMF.NIST.gov/19.2.E4>}.
455 @raise EllipticError: Invalid invokation or no convergence.
456 '''
457 def _fD(sn, cn, dn):
458 r = fabs(sn)**3
459 if r:
460 r = float(_RD(self, cn**2, dn**2, _1_0, _3_0 / r))
461 return r
463 return self._fXf(phi_or_sn, cn, dn, self.cD,
464 self.deltaD, _fD)
466 def fDelta(self, sn, cn):
467 '''The C{Delta} amplitude function.
469 @arg sn: sin(φ).
470 @arg cn: cos(φ).
472 @return: sqrt(1 − k2 * sin(2φ)) (C{float}).
473 '''
474 try:
475 k2 = self.k2
476 s = (self.kp2 + cn**2 * k2) if k2 > 0 else (
477 (_1_0 - sn**2 * k2) if k2 < 0 else self.kp2)
478 return sqrt(s) if s else _0_0
480 except Exception as e:
481 raise _ellipticError(self.fDelta, sn, cn, k2=k2, cause=e)
483 def fE(self, phi_or_sn, cn=None, dn=None):
484 '''The incomplete integral of the second kind in terms of
485 Jacobi elliptic functions.
487 @arg phi_or_sn: φ or sin(φ).
488 @kwarg cn: C{None} or cos(φ).
489 @kwarg dn: C{None} or sqrt(1 − k2 * sin(2φ)).
491 @return: E(φ, k) as though φ ∈ (−π, π] (C{float}),
492 U{defined<https://DLMF.NIST.gov/19.2.E5>}.
494 @raise EllipticError: Invalid invokation or no convergence.
495 '''
496 def _fE(sn, cn, dn):
497 '''(INTERNAL) Core of C{.fE}.
498 '''
499 if sn:
500 cn2, dn2 = cn**2, dn**2
501 kp2, k2 = self.kp2, self.k2
502 if k2 <= 0: # Carlson, eq. 4.6, <https://DLMF.NIST.gov/19.25.E9>
503 Ei = _RF3(self, cn2, dn2, _1_0)
504 if k2:
505 Ei -= _RD(self, cn2, dn2, _1_0, _3over(k2, sn**2))
506 elif kp2 >= 0: # k2 > 0, <https://DLMF.NIST.gov/19.25.E10>
507 Ei = _over(k2 * fabs(cn), dn) # float
508 if kp2:
509 Ei += (_RD( self, cn2, _1_0, dn2, _3over(k2, sn**2)) +
510 _RF3(self, cn2, dn2, _1_0)) * kp2
511 else: # kp2 < 0, <https://DLMF.NIST.gov/19.25.E11>
512 Ei = _over(dn, fabs(cn))
513 Ei -= _RD(self, dn2, _1_0, cn2, _3over(kp2, sn**2))
514 Ei *= fabs(sn)
515 ei = float(Ei)
516 else: # PYCHOK no cover
517 ei = _0_0
518 return ei
520 return self._fXf(phi_or_sn, cn, dn, self.cE,
521 self.deltaE, _fE,
522 k2_0=self.k2==0)
524 def fEd(self, deg):
525 '''The incomplete integral of the second kind with
526 the argument given in C{degrees}.
528 @arg deg: Angle (C{degrees}).
530 @return: E(π B{C{deg}} / 180, k) (C{float}).
532 @raise EllipticError: No convergence.
533 '''
534 if _K_2_0:
535 e = round((deg - _norm180(deg)) / _360_0)
536 elif fabs(deg) < _180_0:
537 e = _0_0
538 else:
539 e = ceil(deg / _360_0 - _0_5)
540 deg -= e * _360_0
541 return self.fE(radians(deg)) + e * self.cE * _4_0
543 def fEinv(self, x):
544 '''The inverse of the incomplete integral of the second kind.
546 @arg x: Argument (C{float}).
548 @return: φ = 1 / E(B{C{x}}, k), such that E(φ, k) = B{C{x}}
549 (C{float}).
551 @raise EllipticError: No convergence.
552 '''
553 try:
554 return self._Einv(x)
555 except Exception as e:
556 raise _ellipticError(self.fEinv, x, cause=e)
558 def fF(self, phi_or_sn, cn=None, dn=None):
559 '''The incomplete integral of the first kind in terms of
560 Jacobi elliptic functions.
562 @arg phi_or_sn: φ or sin(φ).
563 @kwarg cn: C{None} or cos(φ).
564 @kwarg dn: C{None} or sqrt(1 − k2 * sin(2φ)).
566 @return: F(φ, k) as though φ ∈ (−π, π] (C{float}),
567 U{defined<https://DLMF.NIST.gov/19.2.E4>}.
569 @raise EllipticError: Invalid invokation or no convergence.
570 '''
571 def _fF(sn, cn, dn):
572 r = fabs(sn)
573 if r:
574 r = float(_RF3(self, cn**2, dn**2, _1_0).fmul(r))
575 return r
577 return self._fXf(phi_or_sn, cn, dn, self.cK,
578 self.deltaF, _fF,
579 k2_0=self.k2==0, kp2_0=self.kp2==0)
581 def fG(self, phi_or_sn, cn=None, dn=None):
582 '''Legendre's geodesic longitude integral in terms of
583 Jacobi elliptic functions.
585 @arg phi_or_sn: φ or sin(φ).
586 @kwarg cn: C{None} or cos(φ).
587 @kwarg dn: C{None} or sqrt(1 − k2 * sin(2φ)).
589 @return: G(φ, k) as though φ ∈ (−π, π] (C{float}).
591 @raise EllipticError: Invalid invokation or no convergence.
593 @note: Legendre expresses the longitude of a point on the
594 geodesic in terms of this combination of elliptic
595 integrals in U{Exercices de Calcul Intégral, Vol 1
596 (1811), p 181<https://Books.Google.com/books?id=
597 riIOAAAAQAAJ&pg=PA181>}.
599 @see: U{Geodesics in terms of elliptic integrals<https://
600 GeographicLib.SourceForge.io/C++/doc/geodesic.html#geodellip>}
601 for the expression for the longitude in terms of this function.
602 '''
603 return self._fXa(phi_or_sn, cn, dn, self.alpha2 - self.k2,
604 self.cG, self.deltaG)
606 def fH(self, phi_or_sn, cn=None, dn=None):
607 '''Cayley's geodesic longitude difference integral in terms of
608 Jacobi elliptic functions.
610 @arg phi_or_sn: φ or sin(φ).
611 @kwarg cn: C{None} or cos(φ).
612 @kwarg dn: C{None} or sqrt(1 − k2 * sin(2φ)).
614 @return: H(φ, k) as though φ ∈ (−π, π] (C{float}).
616 @raise EllipticError: Invalid invokation or no convergence.
618 @note: Cayley expresses the longitude difference of a point
619 on the geodesic in terms of this combination of
620 elliptic integrals in U{Phil. Mag. B{40} (1870), p 333
621 <https://Books.Google.com/books?id=Zk0wAAAAIAAJ&pg=PA333>}.
623 @see: U{Geodesics in terms of elliptic integrals<https://
624 GeographicLib.SourceForge.io/C++/doc/geodesic.html#geodellip>}
625 for the expression for the longitude in terms of this function.
626 '''
627 return self._fXa(phi_or_sn, cn, dn, -self.alphap2,
628 self.cH, self.deltaH)
630 def fPi(self, phi_or_sn, cn=None, dn=None):
631 '''The incomplete integral of the third kind in terms of
632 Jacobi elliptic functions.
634 @arg phi_or_sn: φ or sin(φ).
635 @kwarg cn: C{None} or cos(φ).
636 @kwarg dn: C{None} or sqrt(1 − k2 * sin(2φ)).
638 @return: Π(φ, α2, k) as though φ ∈ (−π, π] (C{float}).
640 @raise EllipticError: Invalid invokation or no convergence.
641 '''
642 if dn is None and cn is not None: # and isscalar(phi_or_sn)
643 dn = self.fDelta(phi_or_sn, cn) # in .triaxial
644 return self._fXa(phi_or_sn, cn, dn, self.alpha2,
645 self.cPi, self.deltaPi)
647 def _fXa(self, phi_or_sn, cn, dn, aX, cX, deltaX):
648 '''(INTERNAL) Helper for C{.fG}, C{.fH} and C{.fPi}.
649 '''
650 def _fX(sn, cn, dn):
651 if sn:
652 cn2, dn2 = cn**2, dn**2
653 R = _RF3(self, cn2, dn2, _1_0)
654 if aX:
655 sn2 = sn**2
656 p = sn2 * self.alphap2 + cn2
657 R += _RJ(self, cn2, dn2, _1_0, p, _3over(aX, sn2))
658 R *= fabs(sn)
659 r = float(R)
660 else: # PYCHOK no cover
661 r = _0_0
662 return r
664 return self._fXf(phi_or_sn, cn, dn, cX, deltaX, _fX)
666 def _fXf(self, phi_or_sn, cn, dn, cX, deltaX, fX, k2_0=False, kp2_0=False):
667 '''(INTERNAL) Helper for C{.fD}, C{.fE}, C{.fF} and C{._fXa}.
668 '''
669 self._iteration = 0 # aggregate
670 phi = sn = phi_or_sn
671 if cn is dn is None: # fX(phi) call
672 if k2_0: # C++ version 2.4
673 return phi
674 elif kp2_0:
675 return asinh(tan(phi))
676 sn, cn, dn = self._sncndn3(phi)
677 if fabs(phi) >= PI:
678 return (deltaX(sn, cn, dn) + phi) * cX / PI_2
679 # fall through
680 elif cn is None or dn is None:
681 n = NN(_f_, typename(deltaX)[5:])
682 raise _ellipticError(n, sn, cn, dn)
684 if _signBit(cn): # enforce usual trig-like symmetries
685 xi = cX * _2_0 - fX(sn, cn, dn)
686 else:
687 xi = fX(sn, cn, dn) if cn > 0 else cX
688 return copysign0(xi, sn)
690 def _jam(self, x):
691 '''Jacobi amplitude function.
693 @return: C{phi} (C{float}).
694 '''
695 # implements DLMF Sec 22.20(ii), see also U{Sala
696 # (1989)<https://doi.org/10.1137/0520100>}, Sec 5
697 if self.k2:
698 if self.kp2:
699 r, ac = self._jamac2
700 x *= r # phi
701 for a, c in ac:
702 p = x
703 x = favg(asin(c * sin(x) / a), x)
704 if self.k2 < 0: # Sala Eq. 5.8
705 x = p - x
706 else: # PYCHOK no cover
707 x = atan(sinh(x)) # gd(x)
708 return x
710 @Property_RO
711 def _jamac2(self):
712 '''(INTERNAL) Get Jacobi amplitude 2-tuple C{(r, ac)}.
713 '''
714 a = r = _1_0
715 b, c = self.kp2, self.k2
716 # assert b and c
717 if c < 0: # Sala Eq. 5.8
718 r = sqrt(b)
719 b = _1_0 / b
720# c *= b # unused
721 ac = [] # [(a, sqrt(c))] unused
722 for i in range(1, _TRIPS): # GEOGRAPHICLIB_PANIC
723 b = sqrt(a * b)
724 c = favg(a, -b)
725 a = favg(a, b) # == PI_2 / K
726 ac.append((a, c))
727 if c <= (a * _TolJAM): # 7 trips, quadratic
728 _iterations(self, i)
729 break
730 else: # PYCHOK no cover
731 raise _convergenceError(c / a, _TolJAM)
732 r *= a * float(1 << i) # 2**i == 2**len(ac)
733 return r, tuple(reversed(ac))
735 @Property_RO
736 def k2(self):
737 '''Get k^2, the square of the modulus (C{float}).
738 '''
739 return self._k2
741 @Property_RO
742 def kp2(self):
743 '''Get k'^2, the square of the complementary modulus (C{float}).
744 '''
745 return self._kp2
747 def reset(self, k2=0, alpha2=0, kp2=None, alphap2=None): # MCCABE 13
748 '''Reset the modulus, parameter and the complementaries.
750 @kwarg k2: Modulus squared (C{float}, NINF <= k^2 <= 1).
751 @kwarg alpha2: Parameter squared (C{float}, NINF <= α^2 <= 1).
752 @kwarg kp2: Complementary modulus squared (C{float}, k'^2 >= 0).
753 @kwarg alphap2: Complementary parameter squared (C{float}, α'^2 >= 0).
755 @raise EllipticError: Invalid B{C{k2}}, B{C{alpha2}}, B{C{kp2}}
756 or B{C{alphap2}}.
758 @note: The arguments must satisfy C{B{k2} + B{kp2} = 1} and
759 C{B{alpha2} + B{alphap2} = 1}. No checking is done
760 that these conditions are met to enable accuracy to be
761 maintained, e.g., when C{k} is very close to unity.
762 '''
763 def _1p2(kp2, k2):
764 return (_1_0 - k2) if kp2 is None else kp2
766 def _S(**kwds):
767 return Scalar_(Error=EllipticError, **kwds)
769 if self.__dict__:
770 _update_all(self, _Named.iteration._uname, Base=Property_RO)
772 self._k2 = _S(k2 = k2, low=None, high=_1_0)
773 self._kp2 = _S(kp2=_1p2(kp2, k2)) # low=_0_0
775 self._alpha2 = _S(alpha2 = alpha2, low=None, high=_1_0)
776 self._alphap2 = _S(alphap2=_1p2(alphap2, alpha2)) # low=_0_0
778 # Values of complete elliptic integrals for k = 0,1 and alpha = 0,1
779 # K E D
780 # k = 0: pi/2 pi/2 pi/4
781 # k = 1: inf 1 inf
782 # Pi G H
783 # k = 0, alpha = 0: pi/2 pi/2 pi/4
784 # k = 1, alpha = 0: inf 1 1
785 # k = 0, alpha = 1: inf inf pi/2
786 # k = 1, alpha = 1: inf inf inf
787 #
788 # G(0, k) = Pi(0, k) = H(1, k) = E(k)
789 # H(0, k) = K(k) - D(k)
790 # Pi(alpha2, 0) = G(alpha2, 0) = pi / (2 * sqrt(1 - alpha2))
791 # H( alpha2, 0) = pi / (2 * (sqrt(1 - alpha2) + 1))
792 # Pi(alpha2, 1) = inf
793 # G( alpha2, 1) = H(alpha2, 1) = RC(1, alphap2)
795 def sncndn(self, x, jam=False):
796 '''The Jacobi amplitude and elliptic function.
798 @arg x: The argument (C{float}).
799 @kwarg jam: If C{True}, use the Jacobi amplitude otherwise the
800 Bulirsch' function (C{bool}).
802 @return: An L{Elliptic3Tuple}C{(sn, cn, dn)} with C{*n(B{x}, k)}.
804 @raise EllipticError: No convergence.
805 '''
806 self._iteration = 0 # reset
807 try:
808 if self.kp2:
809 if jam: # Jacobi amplitude, C++ version 2.4
810 sn, cn, dn = self._sncndn3(self._jam(x))
812 else: # Bulirsch's sncndn routine, p 89 of
813 # Numerische Mathematik 7, 78-90 (1965).
814 # Implements DLMF Eqs 22.17.2 - 22.17.4,
815 # but only good for .k2 > 1 or .kp2 < 0
816 c, d, cd, mn = self._sncndn4
817 dn = _1_0
818 sn, cn = _sincos2(x * cd)
819 if sn:
820 a = cn / sn
821 c *= a
822 for m, n in mn:
823 a *= c
824 c *= dn
825 dn = (n + a) / (m + a)
826 a = c / m
827 a = _1_0 / hypot1(c)
828 sn = neg(a) if _signBit(sn) else a
829 cn = sn * c
830 if d: # and _signBit(self.kp2): # implied
831 cn, dn = dn, cn
832 sn = sn / d # /= chokes PyChecker
833 else:
834 sn = tanh(x) # accurate for large abs(x)
835 cn = dn = _1_0 / cosh(x)
837 except Exception as e:
838 raise _ellipticError(self.sncndn, x, kp2=self.kp2, cause=e)
840 return Elliptic3Tuple(sn, cn, dn, iteration=self._iteration)
842 def _sncndn3(self, phi):
843 '''(INTERNAL) Helper for C{.fEinv}, C{._fXf} and C{.sncndn}.
844 '''
845 sn, cn = _sincos2(phi)
846 return sn, cn, self.fDelta(sn, cn)
848 @Property_RO
849 def _sncndn4(self):
850 '''(INTERNAL) Get Bulirsch' 4-tuple C{(c, d, cd, mn)}.
851 '''
852 d, b = 0, self.kp2
853 if _signBit(b):
854 d = _1_0 - b
855 b = neg(b / d)
856 d = sqrt(d)
857 ab, a = [], _1_0
858 for i in range(1, _TRIPS): # GEOGRAPHICLIB_PANIC
859 b = sqrt(b)
860 ab.append((a, b))
861 c = favg(a, b)
862 r = fabs(a - b)
863 if r <= (a * _TolJAC): # 6 trips, quadratic
864 _iterations(self, i)
865 break
866 t = a
867 b *= a
868 a = c
869 else: # PYCHOK no cover
870 raise _convergenceError(r / t, _TolJAC)
871 cd = (c * d) if d else c
872 return c, d, cd, tuple(reversed(ab))
874 @staticmethod
875 def fRC(x, y):
876 '''Degenerate symmetric integral of the first kind C{RC(x, y)}.
878 @return: C{RC(x, y)}, equivalent to C{RF(x, y, y)}.
880 @see: U{C{RC} definition<https://DLMF.NIST.gov/19.2.E17>} and
881 U{Carlson<https://ArXiv.org/pdf/math/9409227.pdf>}.
882 '''
883 return _rC(None, x, y)
885 @staticmethod
886 def fRD(x, y, z, *over):
887 '''Degenerate symmetric integral of the third kind C{RD(x, y, z)}.
889 @return: C{RD(x, y, z) / over}, equivalent to C{RJ(x, y, z, z)
890 / over} with C{over} typically 3.
892 @see: U{C{RD} definition<https://DLMF.NIST.gov/19.16.E5>} and
893 U{Carlson<https://ArXiv.org/pdf/math/9409227.pdf>}.
894 '''
895 try:
896 return float(_RD(None, x, y, z, *over))
897 except Exception as e:
898 raise _ellipticError(Elliptic.fRD, x, y, z, *over, cause=e)
900 @staticmethod
901 def fRF(x, y, z=0):
902 '''Symmetric or complete symmetric integral of the first kind
903 C{RF(x, y, z)} respectively C{RF(x, y)}.
905 @return: C{RF(x, y, z)} or C{RF(x, y)} for missing or zero B{C{z}}.
907 @see: U{C{RF} definition<https://DLMF.NIST.gov/19.16.E1>} and
908 U{Carlson<https://ArXiv.org/pdf/math/9409227.pdf>}.
909 '''
910 try:
911 return float(_RF3(None, x, y, z)) if z else _rF2(None, x, y)
912 except Exception as e:
913 raise _ellipticError(Elliptic.fRF, x, y, z, cause=e)
915 @staticmethod
916 def fRG(x, y, z=0):
917 '''Symmetric or complete symmetric integral of the second kind
918 C{RG(x, y, z)} respectively C{RG(x, y)}.
920 @return: C{RG(x, y, z)} or C{RG(x, y)} for missing or zero B{C{z}}.
922 @see: U{C{RG} definition<https://DLMF.NIST.gov/19.16.E3>},
923 U{Carlson<https://ArXiv.org/pdf/math/9409227.pdf>} and
924 U{RG<https://GeographicLib.SourceForge.io/C++/doc/
925 EllipticFunction_8cpp_source.html#l00096>} version 2.3.
926 '''
927 try:
928 return _rG2(None, x, y) if z == 0 else (
929 _rG2(None, z, x) if y == 0 else (
930 _rG2(None, y, z) if x == 0 else _rG3(None, x, y, z)))
931 except Exception as e:
932 t = _negative_ if min(x, y, z) < 0 else NN
933 raise _ellipticError(Elliptic.fRG, x, y, z, cause=e, txt=t)
935 @staticmethod
936 def fRJ(x, y, z, p):
937 '''Symmetric integral of the third kind C{RJ(x, y, z, p)}.
939 @return: C{RJ(x, y, z, p)}.
941 @see: U{C{RJ} definition<https://DLMF.NIST.gov/19.16.E2>} and
942 U{Carlson<https://ArXiv.org/pdf/math/9409227.pdf>}.
943 '''
944 try:
945 return float(_RJ(None, x, y, z, p))
946 except Exception as e:
947 raise _ellipticError(Elliptic.fRJ, x, y, z, p, cause=e)
949 @staticmethod
950 def _RFRD(x, y, z, m):
951 # in .auxilats.AuxDLat.DE, .auxilats.AuxLat.Rectifying
952 try: # float(RF(x, y, z) - RD(x, y, z, 3 / m))
953 R = _RF3(None, x, y, z)
954 if m:
955 R -= _RD(None, x, y, z, _3_0 / m)
956 except Exception as e:
957 raise _ellipticError(Elliptic._RFRD, x, y, z, m, cause=e)
958 return float(R)
960_allPropertiesOf_n(16, Elliptic) # # PYCHOK assert, see Elliptic.reset
963class EllipticError(_ValueError):
964 '''Elliptic function, integral, convergence or other L{Elliptic} issue.
965 '''
966 pass
969class Elliptic3Tuple(_NamedTuple):
970 '''3-Tuple C{(sn, cn, dn)} all C{scalar}.
971 '''
972 _Names_ = ('sn', 'cn', 'dn')
973 _Units_ = ( Scalar, Scalar, Scalar)
976class _List(list):
977 '''(INTERNAL) Helper for C{_RD}, C{_RF3} and C{_RJ}.
978 '''
979 _a0 = None
981 def __init__(self, *xyzp): # x, y, z [, p]
982 list.__init__(self, xyzp)
984 def a0(self, n):
985 '''Compute the initial C{a}.
986 '''
987 A = _Dsum(self)
988 while len(A) < n:
989 A += A[-1]
990 self._a0 = a = float(A(_1_0 / n))
991 return a
993 def amrs4(self, inst, y, Tol):
994 '''Yield Carlson 4-tuples C{(An, mul, lam, s)} plus sentinel, with
995 C{lam = fdot(sqrt(x), ... (z))} and C{s = (sqrt(x), ... (p))}.
996 '''
997 L = self
998 a = L.a0(5 if y else 3)
999 m = 1
1000 t = max(fabs(a - _) for _ in L) / Tol # thresh
1001 for i in range(_TRIPS):
1002 d = fabs(a * m)
1003 if d > t: # 3-6 trips
1004 _iterations(inst, i)
1005 break
1006 s = map2(sqrt, L) # sqrt(x), srqt(y), sqrt(z) [, sqrt(p)]
1007 # Deferred fdot(s[:3], s[1], s[2], s[0]) + ...
1008 Q = _Dadd(_0_25)
1009 Q += s[0] * s[1]
1010 Q += s[1] * s[2]
1011 Q += s[2] * s[0]
1012 L[:] = (float(Q + _) for _ in L)
1013 a = float(Q + a)
1014 if y: # yield only if used
1015 r = _sum(Q) # fdot
1016 yield a, m, r, s # L[2] is next z
1017 m *= 4
1018 else: # PYCHOK no cover
1019 raise _convergenceError(d, t, thresh=True)
1020 yield a, m, None, () # sentinel: same a, next m, no r and s
1022 def rescale(self, am, *xs):
1023 '''Rescale C{x}, C{y}, ...
1024 '''
1025 # assert am
1026 a0 = self._a0
1027 _am = _1_0 / am
1028 for x in xs:
1029 yield (a0 - x) * _am
1032def _ab2(inst, x, y):
1033 '''(INTERNAL) Yield Carlson 2-tuples C{(xn, yn)}.
1034 '''
1035 a, b = sqrt(x), sqrt(y)
1036 if b > a:
1037 b, a = a, b
1038 for i in range(_TRIPS):
1039 yield a, b # xi, yi
1040 d = fabs(a - b)
1041 if d <= (a * _TolRG0): # 3-4 trips
1042 _iterations(inst, i)
1043 break
1044 t = a
1045 a = favg(t, b)
1046 b = sqrt(t * b)
1047 else: # PYCHOK no cover
1048 raise _convergenceError(d / t, _TolRG0)
1051def _convergenceError(d, tol, **thresh):
1052 '''(INTERNAL) Format a no-convergence Error.
1053 '''
1054 t = Fmt.no_convergence(d, tol, **thresh)
1055 return ValueError(t) # txt only
1058def _deltaX(sn, cn, dn, cX, fX):
1059 '''(INTERNAL) Helper for C{Elliptic.deltaD} thru C{.deltaPi}.
1060 '''
1061 try:
1062 if cn is None or dn is None:
1063 raise ValueError(_invalid_)
1065 if _signBit(cn):
1066 sn, cn = neg_(sn, cn)
1067 r = fX(sn, cn, dn) * PI_2 / cX
1068 return r - atan2(sn, cn)
1070 except Exception as e:
1071 n = NN(_delta_, typename(fX)[1:])
1072 raise _ellipticError(n, sn, cn, dn, cause=e)
1075def _ellipticError(where, *args, **kwds_cause_txt):
1076 '''(INTERNAL) Format an L{EllipticError}.
1077 '''
1078 def _x_t_kwds(cause=None, txt=NN, **kwds):
1079 return cause, txt, kwds
1081 x, t, kwds = _x_t_kwds(**kwds_cause_txt)
1083 n = typename(where, where)
1084 n = _DOT_(typename(Elliptic), n)
1085 n = _SPACE_(_invokation_, n)
1086 u = unstr(n, *args, **kwds)
1087 return EllipticError(u, cause=x, txt=t)
1090def _Horner(S, e1, E2, E3, E4, E5, *over):
1091 '''(INTERNAL) Horner form for C{_RD} and C{_RJ} below.
1092 '''
1093 E22 = E2**2
1094 # Polynomial is <https://DLMF.NIST.gov/19.36.E2>
1095 # (1 - 3*E2/14 + E3/6 + 9*E2**2/88 - 3*E4/22 - 9*E2*E3/52
1096 # + 3*E5/26 - E2**3/16 + 3*E3**2/40 + 3*E2*E4/20
1097 # + 45*E2**2*E3/272 - 9*(E3*E4+E2*E5)/68)
1098 # converted to Horner-like form ...
1099 e = e1 * 4084080
1100 S *= e
1101 S += Fsum(E2 * -540540, 471240).fmul(E5)
1102 S += Fsum(E2 * 612612, E3 * -540540, -556920).fmul(E4)
1103 S += Fsum(E2 * -706860, E22 * 675675, E3 * 306306, 680680).fmul(E3)
1104 S += Fsum(E2 * 417690, E22 * -255255, -875160).fmul(E2)
1105 S += 4084080
1106 if over:
1107 e *= over[0]
1108 return S.fdiv(e) # Fsum
1111def _iterations(inst, i):
1112 '''(INTERNAL) Aggregate iterations B{C{i}}.
1113 '''
1114 if inst and i > 0:
1115 inst._iteration += i
1118def _3over(a, b):
1119 '''(INTERNAL) Return C{3 / (a * b)}.
1120 '''
1121 return _over(_3_0, a * b)
1124def _rC(unused, x, y):
1125 '''(INTERNAL) Defined only for C{y != 0} and C{x >= 0}.
1126 '''
1127 d = x - y
1128 if x < y: # catch NaN
1129 # <https://DLMF.NIST.gov/19.2.E18>
1130 d = y - x
1131 r = atan(sqrt(d / x)) if x > 0 else PI_2
1132 elif x == y: # XXX d < EPS0? or EPS02 or _EPSmin
1133 d, r = y, _1_0
1134 elif y > 0: # <https://DLMF.NIST.gov/19.2.E19>
1135 r = asinh(sqrt(d / y)) # atanh(sqrt((x - y) / x))
1136 elif y < 0: # <https://DLMF.NIST.gov/19.2.E20>
1137 r = asinh(sqrt(-x / y)) # atanh(sqrt(x / (x - y)))
1138 else: # PYCHOK no cover
1139 raise _ellipticError(Elliptic.fRC, x, y)
1140 return r / sqrt(d) # float
1143def _RD(inst, x, y, z, *over):
1144 '''(INTERNAL) Carlson, eqs 2.28 - 2.34.
1145 '''
1146 L = _List(x, y, z)
1147 S = _Dsum()
1148 for a, m, r, s in L.amrs4(inst, True, _TolRF):
1149 if s:
1150 S += _over(_3_0, (r + z) * s[2] * m)
1151 z = L[2] # s[2] = sqrt(z)
1152 x, y = L.rescale(-a * m, x, y)
1153 xy = x * y
1154 z = (x + y) / _3_0
1155 z2 = z**2
1156 return _Horner(S(_1_0), sqrt(a) * a * m,
1157 (xy - z2 * _6_0),
1158 (xy * _3_0 - z2 * _8_0) * z,
1159 (xy - z2) * z2 * _3_0,
1160 (xy * z2 * z), *over) # Fsum
1163def _rF2(inst, x, y): # 2-arg version, z=0
1164 '''(INTERNAL) Carlson, eqs 2.36 - 2.38.
1165 '''
1166 for a, b in _ab2(inst, x, y): # PYCHOK yield
1167 pass
1168 return _over(PI, a + b) # float
1171def _RF3(inst, x, y, z): # 3-arg version
1172 '''(INTERNAL) Carlson, eqs 2.2 - 2.7.
1173 '''
1174 L = _List(x, y, z)
1175 for a, m, _, _ in L.amrs4(inst, False, _TolRF):
1176 pass
1177 x, y = L.rescale(a * m, x, y)
1178 z = neg(x + y)
1179 xy = x * y
1180 e2 = xy - z**2
1181 e3 = xy * z
1182 e4 = e2**2
1183 # Polynomial is <https://DLMF.NIST.gov/19.36.E1>
1184 # (1 - E2/10 + E3/14 + E2**2/24 - 3*E2*E3/44
1185 # - 5*E2**3/208 + 3*E3**2/104 + E2**2*E3/16)
1186 # converted to Horner-like form ...
1187 S = Fsum(e4 * 15015, e3 * 6930, e2 * -16380, 17160).fmul(e3)
1188 S += Fsum(e4 * -5775, e2 * 10010, -24024).fmul(e2)
1189 S += 240240
1190 return S.fdiv(sqrt(a) * 240240) # Fsum
1193def _rG2(inst, x, y, PI_=PI_4): # 2-args
1194 '''(INTERNAL) Carlson, eqs 2.36 - 2.39.
1195 '''
1196 m = -1 # neg!
1197 S = None
1198 for a, b in _ab2(inst, x, y): # PYCHOK yield
1199 if S is None: # initial
1200 S = _Dsum()
1201 S += (a + b)**2 * _0_5
1202 else:
1203 S += (a - b)**2 * m
1204 m *= 2
1205 return S(PI_).fover(a + b)
1208def _rG3(inst, x, y, z): # 3-arg version
1209 '''(INTERNAL) C{x}, C{y} and C{z} all non-zero, see C{.fRG}.
1210 '''
1211 R = _RF3(inst, x, y, z) * z
1212 rd = (x - z) * (z - y) # - (y - z)
1213 if rd: # Carlson, eq 1.7
1214 R += _RD(inst, x, y, z, _3_0 / rd)
1215 R += sqrt(x * y / z)
1216 return R.fover(_2_0)
1219def _RJ(inst, x, y, z, p, *over):
1220 '''(INTERNAL) Carlson, eqs 2.17 - 2.25.
1221 '''
1222 def _xyzp(x, y, z, p):
1223 return (x + p) * (y + p) * (z + p)
1225 L = _List(x, y, z, p)
1226 n = neg(_xyzp(x, y, z, -p))
1227 S = _Dsum()
1228 for a, m, _, s in L.amrs4(inst, True, _TolRD):
1229 if s:
1230 d = _xyzp(*s)
1231 if d:
1232 if n:
1233 rc = _rC(inst, _1_0, n / d**2 + _1_0)
1234 n = n / _64_0 # /= chokes PyChecker
1235 else:
1236 rc = _1_0 # == _rC(None, _1_0, _1_0)
1237 S += rc / (d * m)
1238 else: # PYCHOK no cover
1239 return NAN
1240 x, y, z = L.rescale(a * m, x, y, z)
1241 p = Fsum(x, y, z).fover(_N_2_0)
1242 p2 = p**2
1243 p3 = p2 * p
1244 E2 = Fsum(x * y, x * z, y * z, -p2 * _3_0)
1245 E2p = E2 * p
1246 xyz = x * y * z
1247 return _Horner(S(_6_0), sqrt(a) * a * m, E2,
1248 Fsum(p3 * _4_0, xyz, E2p * _2_0),
1249 Fsum(p3 * _3_0, E2p, xyz * _2_0).fmul(p),
1250 xyz * p2, *over) # Fsum
1252# **) MIT License
1253#
1254# Copyright (C) 2016-2025 -- mrJean1 at Gmail -- All Rights Reserved.
1255#
1256# Permission is hereby granted, free of charge, to any person obtaining a
1257# copy of this software and associated documentation files (the "Software"),
1258# to deal in the Software without restriction, including without limitation
1259# the rights to use, copy, modify, merge, publish, distribute, sublicense,
1260# and/or sell copies of the Software, and to permit persons to whom the
1261# Software is furnished to do so, subject to the following conditions:
1262#
1263# The above copyright notice and this permission notice shall be included
1264# in all copies or substantial portions of the Software.
1265#
1266# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
1267# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1268# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
1269# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
1270# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
1271# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
1272# OTHER DEALINGS IN THE SOFTWARE.