pygeodesy 24.3.24__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. PyGeodesy-24.3.24.dist-info/METADATA +272 -0
  2. PyGeodesy-24.3.24.dist-info/RECORD +115 -0
  3. PyGeodesy-24.3.24.dist-info/WHEEL +6 -0
  4. PyGeodesy-24.3.24.dist-info/top_level.txt +1 -0
  5. pygeodesy/LICENSE +21 -0
  6. pygeodesy/__init__.py +615 -0
  7. pygeodesy/__main__.py +103 -0
  8. pygeodesy/albers.py +867 -0
  9. pygeodesy/auxilats/_CX_4.py +218 -0
  10. pygeodesy/auxilats/_CX_6.py +314 -0
  11. pygeodesy/auxilats/_CX_8.py +475 -0
  12. pygeodesy/auxilats/__init__.py +54 -0
  13. pygeodesy/auxilats/__main__.py +86 -0
  14. pygeodesy/auxilats/auxAngle.py +548 -0
  15. pygeodesy/auxilats/auxDLat.py +302 -0
  16. pygeodesy/auxilats/auxDST.py +296 -0
  17. pygeodesy/auxilats/auxLat.py +848 -0
  18. pygeodesy/auxilats/auxily.py +272 -0
  19. pygeodesy/azimuthal.py +1150 -0
  20. pygeodesy/basics.py +892 -0
  21. pygeodesy/booleans.py +2031 -0
  22. pygeodesy/cartesianBase.py +1062 -0
  23. pygeodesy/clipy.py +704 -0
  24. pygeodesy/constants.py +516 -0
  25. pygeodesy/css.py +660 -0
  26. pygeodesy/datums.py +752 -0
  27. pygeodesy/deprecated/__init__.py +61 -0
  28. pygeodesy/deprecated/bases.py +40 -0
  29. pygeodesy/deprecated/classes.py +262 -0
  30. pygeodesy/deprecated/consterns.py +54 -0
  31. pygeodesy/deprecated/datum.py +40 -0
  32. pygeodesy/deprecated/functions.py +375 -0
  33. pygeodesy/deprecated/nvector.py +48 -0
  34. pygeodesy/deprecated/rhumbBase.py +32 -0
  35. pygeodesy/deprecated/rhumbaux.py +33 -0
  36. pygeodesy/deprecated/rhumbsolve.py +33 -0
  37. pygeodesy/deprecated/rhumbx.py +33 -0
  38. pygeodesy/dms.py +986 -0
  39. pygeodesy/ecef.py +1348 -0
  40. pygeodesy/elevations.py +279 -0
  41. pygeodesy/ellipsoidalBase.py +1224 -0
  42. pygeodesy/ellipsoidalBaseDI.py +913 -0
  43. pygeodesy/ellipsoidalExact.py +343 -0
  44. pygeodesy/ellipsoidalGeodSolve.py +343 -0
  45. pygeodesy/ellipsoidalKarney.py +403 -0
  46. pygeodesy/ellipsoidalNvector.py +685 -0
  47. pygeodesy/ellipsoidalVincenty.py +590 -0
  48. pygeodesy/ellipsoids.py +2476 -0
  49. pygeodesy/elliptic.py +1198 -0
  50. pygeodesy/epsg.py +243 -0
  51. pygeodesy/errors.py +804 -0
  52. pygeodesy/etm.py +1190 -0
  53. pygeodesy/fmath.py +1013 -0
  54. pygeodesy/formy.py +1818 -0
  55. pygeodesy/frechet.py +865 -0
  56. pygeodesy/fstats.py +760 -0
  57. pygeodesy/fsums.py +1898 -0
  58. pygeodesy/gars.py +358 -0
  59. pygeodesy/geodesicw.py +581 -0
  60. pygeodesy/geodesicx/_C4_24.py +1699 -0
  61. pygeodesy/geodesicx/_C4_27.py +2395 -0
  62. pygeodesy/geodesicx/_C4_30.py +3301 -0
  63. pygeodesy/geodesicx/__init__.py +48 -0
  64. pygeodesy/geodesicx/__main__.py +91 -0
  65. pygeodesy/geodesicx/gx.py +1382 -0
  66. pygeodesy/geodesicx/gxarea.py +535 -0
  67. pygeodesy/geodesicx/gxbases.py +154 -0
  68. pygeodesy/geodesicx/gxline.py +669 -0
  69. pygeodesy/geodsolve.py +426 -0
  70. pygeodesy/geohash.py +914 -0
  71. pygeodesy/geoids.py +1884 -0
  72. pygeodesy/hausdorff.py +892 -0
  73. pygeodesy/heights.py +1155 -0
  74. pygeodesy/interns.py +687 -0
  75. pygeodesy/iters.py +545 -0
  76. pygeodesy/karney.py +919 -0
  77. pygeodesy/ktm.py +633 -0
  78. pygeodesy/latlonBase.py +1766 -0
  79. pygeodesy/lazily.py +960 -0
  80. pygeodesy/lcc.py +684 -0
  81. pygeodesy/ltp.py +1107 -0
  82. pygeodesy/ltpTuples.py +1563 -0
  83. pygeodesy/mgrs.py +721 -0
  84. pygeodesy/named.py +1324 -0
  85. pygeodesy/namedTuples.py +683 -0
  86. pygeodesy/nvectorBase.py +695 -0
  87. pygeodesy/osgr.py +781 -0
  88. pygeodesy/points.py +1686 -0
  89. pygeodesy/props.py +628 -0
  90. pygeodesy/resections.py +1048 -0
  91. pygeodesy/rhumb/__init__.py +46 -0
  92. pygeodesy/rhumb/aux_.py +397 -0
  93. pygeodesy/rhumb/bases.py +1148 -0
  94. pygeodesy/rhumb/ekx.py +563 -0
  95. pygeodesy/rhumb/solve.py +572 -0
  96. pygeodesy/simplify.py +647 -0
  97. pygeodesy/solveBase.py +472 -0
  98. pygeodesy/sphericalBase.py +724 -0
  99. pygeodesy/sphericalNvector.py +1264 -0
  100. pygeodesy/sphericalTrigonometry.py +1447 -0
  101. pygeodesy/streprs.py +627 -0
  102. pygeodesy/trf.py +2079 -0
  103. pygeodesy/triaxials.py +1484 -0
  104. pygeodesy/units.py +969 -0
  105. pygeodesy/unitsBase.py +349 -0
  106. pygeodesy/ups.py +538 -0
  107. pygeodesy/utily.py +1231 -0
  108. pygeodesy/utm.py +762 -0
  109. pygeodesy/utmups.py +318 -0
  110. pygeodesy/utmupsBase.py +517 -0
  111. pygeodesy/vector2d.py +785 -0
  112. pygeodesy/vector3d.py +968 -0
  113. pygeodesy/vector3dBase.py +1049 -0
  114. pygeodesy/webmercator.py +383 -0
  115. pygeodesy/wgrs.py +439 -0
@@ -0,0 +1,302 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ u'''Class L{AuxDLat} transcoded to Python from I{Karney}'s C++ class U{DAuxLatitude
4
+ <https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1DAuxLatitude.html>}
5
+ in I{GeographicLib version 2.2+}.
6
+
7
+ Copyright (C) U{Charles Karney<mailto:Karney@Alum.MIT.edu>} (2022-2023) and licensed
8
+ under the MIT/X11 License. For more information, see the U{GeographicLib
9
+ <https://GeographicLib.SourceForge.io>} documentation.
10
+ '''
11
+ # make sure int/int division yields float quotient, see .basics
12
+ from __future__ import division as _; del _ # PYCHOK semicolon
13
+
14
+ from pygeodesy.auxilats.auxily import Aux, _Datan, _Dasinh, _Dm, _sc, _sn, \
15
+ atan1, AuxError
16
+ from pygeodesy.auxilats.auxLat import AuxLat, _ALL_DOCS
17
+ from pygeodesy.basics import map1, _reverange
18
+ from pygeodesy.constants import INF, NAN, isfinite, isinf, isnan, _0_0, _0_5, \
19
+ _1_0, _2_0, _N_2_0, _naninf, _over, _1_over
20
+ from pygeodesy.elliptic import Elliptic as _Ef, Fsum
21
+ # from pygeodesy.errors import AuxError # from .auxilats.auxily
22
+ # from pygeodesy.fsums import Fsum # from .elliptic
23
+ # from pygeodesy.lazily import _ALL_DOCS # from .auxilats.auxLat
24
+ # from pygeodesy.utily import atan1 # from .auxilats.auxily
25
+
26
+ from math import atan2, cos, sin, sqrt
27
+
28
+ __all__ = ()
29
+ __version__ = '23.12.01'
30
+
31
+
32
+ class AuxDLat(AuxLat):
33
+ '''Class to compute C{Divided Differences} of I{Auxiliary}
34
+ latitudes and other C{Divided Differences} needed for
35
+ L{RhumbAux} and L{RhumbLineAux} calculations.
36
+ '''
37
+
38
+ def CParametric(self, Zeta1, Zeta2):
39
+ '''Short for C{.Dconvert(Aux.BETA, B{Zeta1}, B{Zeta2})}.
40
+ '''
41
+ return self.Dconvert(Aux.BETA, Zeta1, Zeta2)
42
+
43
+ def CRectifying(self, Zeta1, Zeta2):
44
+ '''Short for C{.Dconvert(Aux.MU, B{Zeta1}, B{Zeta2})}.
45
+ '''
46
+ return self.Dconvert(Aux.MU, Zeta1, Zeta2)
47
+
48
+ def _Datanhee(self, x, y):
49
+ # atan(e*sn(tphi))/e:
50
+ # Datan(e*sn(x),e*sn(y))*Dsn(x,y)/Datan(x,y)
51
+ # asinh(e1*sn(fm1*tphi)):
52
+ # Dasinh(e1*sn(fm1*x)), e1*sn(fm1*y)) *
53
+ # e1 * Dsn(fm1*x, fm1*y) *fm1 / (e * Datan(x,y))
54
+ # = Dasinh(e1*sn(fm1*x)), e1*sn(fm1*y)) *
55
+ # Dsn(fm1*x, fm1*y) / Datan(x,y)
56
+ if self.f < 0:
57
+ e = self._e
58
+ r = _Datan(e * _sn(x), e * _sn(y))
59
+ else:
60
+ x *= self._fm1
61
+ y *= self._fm1
62
+ e1 = self._e1
63
+ r = _Dasinh(e1 * _sn(x), e1 * _sn(y))
64
+ return _Dsn(x, y) * r
65
+
66
+ def Dconvert(self, auxout, Zeta1, Zeta2):
67
+ '''I{Divided Difference} of one auxiliary latitude wrt another.
68
+ '''
69
+ auxin = Zeta1._AUX
70
+ # assert Zeta2._AUX == auxin
71
+ try:
72
+ if auxout != auxin:
73
+ cs = self._coeffs(auxout, auxin)
74
+ # assert len(cs) == self.ALorder
75
+ r = _DClenshaw(True, Zeta1, Zeta2, cs, self.ALorder)
76
+ else:
77
+ r = _1_0
78
+ except AuxError: # no _coeffs
79
+ r = NAN
80
+ return r
81
+
82
+ def DE(self, X, Y):
83
+ # We assume that X and Y are in [-90d, 90d] and
84
+ # have the same sign. If not we would include
85
+ # if (Xn.y() * Yn.y() < 0)
86
+ # return d != 0 ? (E(X) - E(Y)) / d : 1
87
+ # The general formula fails for x = y = 0d and
88
+ # x = y = 90d. Probably this is fixable (the
89
+ # formula works for other x = y. But let's
90
+ # also stipulate that x != y.
91
+
92
+ # Make both y positive, so we can do the swap a <-> b trick
93
+ sx, cx, x = X._yxr_normalized(True)
94
+ sy, cy, y = Y._yxr_normalized(True)
95
+ k2, d = -self._e12, (y - x)
96
+ # Switch prolate to oblate, then use formulas for k2 < 0
97
+ if self.f < 0: # XXX and False?
98
+ sx, cx = cx, sx
99
+ sy, cy = cy, sy
100
+ d, k2 = -d, self._e2
101
+ # See DLMF: Eqs (19.11.2) and (19.11.4) letting
102
+ Dt = _Dsin(x, y) * (sx + sy)
103
+ if Dt:
104
+ t = _sxk2y(sx, sy, k2) + _sxk2y(sy, sx, k2)
105
+ Dt = _over(Dt, t * (cx + cy))
106
+ t = d * Dt
107
+ t2 = _1_0 + t**2
108
+ Dt *= _2_0 / t2
109
+ sk2 = (d * Dt)**2 * k2
110
+ d2 = _1_0 - sk2
111
+ c2 = ((_1_0 - t) * (_1_0 + t) / t2)**2 if t else _1_0
112
+ # E(z)/sin(z)
113
+ Dt *= _Ef._RFRD(c2, d2, _1_0, sk2) - k2 * sx * sy
114
+ return Dt
115
+
116
+ def DIsometric(self, Phi1, Phi2):
117
+ '''I{Divided Difference} of the isometric wrt the geographic latitude.
118
+ '''
119
+ tx, ty = Phi1.tan, Phi2.tan
120
+ if isnan(ty) or isnan(tx): # PYCHOK no cover
121
+ r = NAN
122
+ elif isinf(ty) or isinf(tx): # PYCHOK no cover
123
+ r = INF
124
+ else: # psi = asinh(tan(Phi)) - e^2 * atanhee(tan(Phi))
125
+ r = self._Datanhee(tx, ty) * self._e2
126
+ r = _over(_Dasinh(tx, ty) - r, _Datan(tx, ty))
127
+ return r
128
+
129
+ def DParametric(self, Phi1, Phi2):
130
+ '''I{Divided Difference} of the parametric wrt the geographic latitude.
131
+ '''
132
+ fm1, e2m1 = self._fm1, self._e2m1
133
+ tx, ty = Phi1.tan, Phi2.tan
134
+ # DbetaDphi = Datan(fm1*tx, fm1*ty) * fm1 / Datan(tx, ty)
135
+ # Datan(x, y) = 1 / (1 + x^2) if x == y
136
+ # = (atan1(y) - atan1(x)) / (y-x) if x*y < 0
137
+ # = atan1(y-x, x*y + 1) / (y-x) if x*y > 0
138
+ txy = tx * ty
139
+ if txy < 0 or (isinf(ty) and not tx):
140
+ _a = atan1
141
+ r = _over(_a(fm1 * ty) - _a(fm1 * tx), _a(ty) - _a(tx))
142
+ elif tx == ty: # includes tx = ty = inf
143
+ if txy > 1: # == tx**2
144
+ txy = _1_over(txy)
145
+ r = txy + e2m1
146
+ else:
147
+ r = txy * e2m1 + _1_0
148
+ r = _over((txy + _1_0) * fm1, r)
149
+ else:
150
+ if txy > 1:
151
+ tx = _1_over(tx)
152
+ ty = _1_over(ty)
153
+ txy = _1_over(txy)
154
+ t = txy + e2m1
155
+ else:
156
+ t = txy * e2m1 + _1_0
157
+ r = ty - tx
158
+ r = _over(atan2(r * fm1, t), atan2(r, txy + _1_0))
159
+ return r
160
+
161
+ def DRectifying(self, Phi1, Phi2):
162
+ '''I{Divided Difference} of the rectifying wrt the geographic latitude.
163
+ '''
164
+ # Stipulate that Phi1 and Phi2 are in [-90d, 90d]
165
+ x, y = Phi1.toRadians, Phi2.toRadians
166
+ if y == x: # isnear0
167
+ Mu1 = self.Rectifying(Phi1, diff=True)
168
+ tphi1, r = Phi1.tan, Mu1.diff
169
+ if isfinite(tphi1):
170
+ r *= _over(_sc(tphi1), _sc(Mu1.tan))**2
171
+ else: # PYCHOK no cover
172
+ r = _1_over(r)
173
+ elif (x * y) < 0:
174
+ r = _over(self.Rectifying(Phi2).toRadians -
175
+ self.Rectifying(Phi1).toRadians, y - x)
176
+ else:
177
+ r = _over(self.b, self.RectifyingRadius(True))
178
+ r *= self.DE(*map1(self.Parametric, Phi1, Phi2))
179
+ r *= self.DParametric(Phi1, Phi2)
180
+ return r # or INF or NAN
181
+
182
+
183
+ def _DClenshaw(sinp, Zeta1, Zeta2, cs, K):
184
+ '''(INTERNAL) I{Divided Difference} of L{AuxLat._Clenshaw}.
185
+
186
+ @return: C{float} if B{C{sinp}} otherwise a C{Fsum}.
187
+ '''
188
+ s1, c1, r1 = Zeta1._yxr_normalized(False)
189
+ s2, c2, r2 = Zeta2._yxr_normalized(False)
190
+ Delta = r2 - r1
191
+ # Evaluate (Clenshaw(sinp, szeta2, czeta2, cs, K) -
192
+ # Clenshaw(sinp, szeta1, czeta1, cs, K)) / Delta
193
+ # or f = sin if sinp else cos
194
+ # sum(cs[k] * (f((2*k+2) * Zeta2) -
195
+ # f((2*k+2) * Zeta2))) / Delta
196
+ #
197
+ # Delta is EITHER 1, giving the plain difference OR (Zeta2 - Zeta1)
198
+ # in radians, giving the I{Divided Difference}. Other values will
199
+ # produce nonsense.
200
+ #
201
+ # Suffices a and b denote [1,1], [2,1] elements of matrix/vector
202
+ cp = cm = c2 * c1
203
+ t = s2 * s1
204
+ cp -= t # not +
205
+ cm += t # not -
206
+
207
+ sp = s2 * c1
208
+ t = c2 * s1
209
+ smd = ((sin(Delta) / Delta) if Delta != _1_0 else
210
+ (sp - t)) if Delta else _1_0
211
+ sp += t
212
+
213
+ xa = cp * cm * _2_0
214
+ xb = sp * smd * _N_2_0
215
+ xD = xb * Delta**2
216
+
217
+ if isfinite(xD) and isfinite(xb) and isfinite(xa):
218
+ U0a, U1a = Fsum(), Fsum()
219
+ U0b, U1b = Fsum(), Fsum()
220
+ for k in _reverange(K): # assert len(cs) == K
221
+ # t = x . U0 - U1 + cs[k] * I
222
+ U1a -= U0a * xa + U0b * xD + cs[k]
223
+ U1b -= U0a * xb + U0b * xa
224
+ U1a, U0a = U0a, -U1a
225
+ U1b, U0b = U0b, -U1b
226
+ # F0a = (sp if sinp else cp) * cm
227
+ # F0b = (cp if sinp else -sp) * smd
228
+ # Fm1a = 0 if sinp else 1 # Fm1b = 0
229
+ # return (U0b * F0a + U0a * F0b - U1b * Fm1a) * 2
230
+ if sinp:
231
+ U1b = _0_0
232
+ else:
233
+ sp, cp = cp, -sp
234
+ U0b *= sp * cm
235
+ U0a *= cp * smd
236
+ U0a += U0b
237
+ U0a = _Dm(U0a, U1b, _2_0)
238
+ r = float(U0a) if sinp else U0a # Fsum
239
+ else:
240
+ r = _naninf(xD, xb, xa)
241
+ return r
242
+
243
+
244
+ def _Dsin(x, y): # see also .rhumb.ekx._Dsin
245
+ r = cos((x + y) * _0_5)
246
+ d = (x - y) * _0_5
247
+ if d:
248
+ r *= sin(d) / d
249
+ return r
250
+
251
+
252
+ def _Dsn(x, y):
253
+ # (sn(y) - sn(x)) / (y - x)
254
+ if x != y:
255
+ snx, sny = _sn(x), _sn(y)
256
+ if (x * y) > 0:
257
+ scx, scy = _sc(x), _sc(y)
258
+ r = _over((snx / scy) + (sny / scx),
259
+ (snx + sny) * scy * scx)
260
+ else:
261
+ r = (sny - snx) / (y - x)
262
+ elif x:
263
+ r = _1_over(_sc(x) * (x**2 + _1_0)) # == 1 / sqrt3(x**2 + 1)
264
+ else:
265
+ r = _1_0
266
+ return r
267
+
268
+
269
+ def _sxk2y(sx, sy, k2):
270
+ # .DE helper
271
+ sy *= sy * k2
272
+ if sy:
273
+ try:
274
+ sx *= sqrt(_1_0 - sy)
275
+ except ValueError: # domain error
276
+ sx = NAN
277
+ return sx
278
+
279
+
280
+ __all__ += _ALL_DOCS(AuxDLat)
281
+
282
+ # **) MIT License
283
+ #
284
+ # Copyright (C) 2023-2024 -- mrJean1 at Gmail -- All Rights Reserved.
285
+ #
286
+ # Permission is hereby granted, free of charge, to any person obtaining a
287
+ # copy of this software and associated documentation files (the "Software"),
288
+ # to deal in the Software without restriction, including without limitation
289
+ # the rights to use, copy, modify, merge, publish, distribute, sublicense,
290
+ # and/or sell copies of the Software, and to permit persons to whom the
291
+ # Software is furnished to do so, subject to the following conditions:
292
+ #
293
+ # The above copyright notice and this permission notice shall be included
294
+ # in all copies or substantial portions of the Software.
295
+ #
296
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
297
+ # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
298
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
299
+ # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
300
+ # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
301
+ # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
302
+ # OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,296 @@
1
+
2
+ # -*- coding: utf-8 -*-
3
+
4
+ u'''Discrete Sine Transforms (AuxDST) in Python, transcoded from I{Karney}'s C++ class
5
+ U{DST<https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1DST.html>}
6
+ in I{GeographicLib version 2.2+}.
7
+
8
+ Copyright (C) U{Charles Karney<mailto:Karney@Alum.MIT.edu>} (2022-2023) and licensed
9
+ under the MIT/X11 License. For more information, see the U{GeographicLib
10
+ <https://GeographicLib.SourceForge.io>} documentation.
11
+
12
+ @note: Class L{AuxDST} requires U{numpy<https://PyPI.org/project/numpy>} to be
13
+ installed, version 1.16 or newer.
14
+ '''
15
+ # make sure int/int division yields float quotient, see .basics
16
+ from __future__ import division as _; del _ # PYCHOK semicolon
17
+
18
+ from pygeodesy.auxilats.auxily import _Dm
19
+ from pygeodesy.basics import isodd, neg, _reverange, _xnumpy
20
+ from pygeodesy.constants import PI_2, PI_4, isfinite, _0_0, _0_5, _naninf
21
+ from pygeodesy.fsums import Fsum, property_RO
22
+ from pygeodesy.karney import _2cos2x, _ALL_DOCS
23
+ # from pygeodesy.lazily import _ALL_DOCS # from .karney
24
+ # from pygeodesy.props import property_RO # from .fsums
25
+
26
+ __all__ = ()
27
+ __version__ = '23.12.02'
28
+
29
+
30
+ class AuxDST(object):
31
+ '''Discrete Sine Transforms (DST) for I{Auxiliary} latitudes.
32
+
33
+ @see: I{Karney}'s C++ class U{DST
34
+ <https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1DST.html>}.
35
+ '''
36
+ _N = 0
37
+
38
+ def __init__(self, N):
39
+ '''New L{AuxDST} instance.
40
+
41
+ @arg N: Size, number of points (C{int}).
42
+ '''
43
+ if N > 0:
44
+ self._N = int(N)
45
+ # kissfft(N, False) # size, inverse
46
+
47
+ @staticmethod
48
+ def evaluate(sinx, cosx, F, *N):
49
+ '''Evaluate the Fourier sum given the sine and cosine of the angle,
50
+ using precision I{Clenshaw} summation.
51
+
52
+ @arg sinx: The sin(I{sigma}) (C{float}).
53
+ @arg cosx: The cos(I{sigma}) (C{float}).
54
+ @arg F: The Fourier coefficients (C{float}[]).
55
+ @arg N: Optional, (smaller) number of terms to evaluate (C{int}).
56
+
57
+ @return: Precison I{Clenshaw} sum (C{float}).
58
+
59
+ @see: Methods C{AuxDST.integral} and C{AuxDST.integral2}.
60
+ '''
61
+ a = -_2cos2x(cosx, sinx)
62
+ if isfinite(a):
63
+ Y0, Y1 = Fsum(), Fsum()
64
+ n = _len_N(F, *N)
65
+ Fn = list(F[:n])
66
+ _F = Fn.pop
67
+ if isodd(n):
68
+ Y0 -= _F()
69
+ while Fn: # Y0, Y1 negated
70
+ Y1 -= Y0 * a + _F()
71
+ Y0 -= Y1 * a + _F()
72
+ r = float(_Dm(-Y0, Y1, sinx))
73
+ else:
74
+ r = _naninf(-a)
75
+ return r
76
+
77
+ @property_RO
78
+ def _fft_numpy(self):
79
+ '''(INTERNAL) Get the C{numpy.fft} module, I{once}.
80
+ '''
81
+ AuxDST._fft_numpy = fft = _xnumpy(AuxDST, 1, 16).fft # overwrite property_RO
82
+ return fft
83
+
84
+ def _fft_real(self, data):
85
+ '''(INTERNAL) NumPy's I{kissfft}-like C{transform_real} function,
86
+ taking C{float}[:N] B{C{data}} and returning C{complex}[:N*2].
87
+ '''
88
+ # <https://GitHub.com/mborgerding/kissfft/blob/master/test/testkiss.py>
89
+ return self._fft_numpy.rfftn(data)
90
+
91
+ def _ffts(self, data, cIV):
92
+ '''(INTERNAL) Compute the DST-III or DST-IV FFTransforms.
93
+
94
+ @arg data: Elements DST-III[0:N+1] or DST-IV[0:N] (C{float}[])
95
+ with DST_III[0] = 0.
96
+ @arg cIV: If C{True} DST-IV, otherwise DST-III.
97
+
98
+ @return: FFTransforms (C{float}[0:N]).
99
+ '''
100
+ T, N = (), self.N
101
+ if N > 0:
102
+ N2 = N * 2
103
+ d = tuple(data)
104
+ # assert len(d) == N + (0 if cIV else 1)
105
+
106
+ if cIV: # DST-IV
107
+ from cmath import exp as _cexp
108
+
109
+ def _cF(c, j, r=-PI_4 / N):
110
+ return c * _cexp(complex(0, r * j))
111
+
112
+ i = 0
113
+ else: # DST-III
114
+ i = 1
115
+ # assert d[0] == _0_0
116
+
117
+ def _cF(c, *unused): # PYCHOK redef
118
+ return c
119
+
120
+ d += tuple(reversed(d[i:N])) # i == len(d) - N
121
+ d += tuple(map(neg, d[:N2]))
122
+ c = self._fft_real(d) # complex[0:N*2]
123
+ n2 = float(-N2)
124
+ T = tuple(_cF(c[j], j).imag / n2 for j in range(1, N2, 2))
125
+ return T
126
+
127
+ def _ffts2(self, data, F):
128
+ '''(INTERNAL) Doubled FFTransforms.
129
+
130
+ @arg data: Grid centers (C{float}[N]).
131
+ @arg F: The transforms (C{float}[N])
132
+
133
+ @return: Doubled FFTransforms (C{float}[N*2]).
134
+ '''
135
+ __2 = _0_5 # N = self._N
136
+ # copy DST-IV order N transform to D[0:N]
137
+ D = self._ffts(data, True)
138
+ # assert len(D) == N and len(F) >= N
139
+ # (DST-IV order N - DST-III order N) / 2
140
+ M = tuple((d - f) * __2 for d, f in zip(D, F)) # strict=False
141
+ # (DST-IV order N + DST-III order N) / 2
142
+ P = tuple((d + f) * __2 for d, f in zip(D, F)) # strict=False
143
+ # assert len(M) == len(P) == self._N
144
+ return P + tuple(reversed(M))
145
+
146
+ @staticmethod
147
+ def integral(sinx, cosx, F, *N):
148
+ '''Evaluate the integral of Fourier sum given the sine and
149
+ cosine of the angle, using precision I{Clenshaw} summation.
150
+
151
+ @arg sinx: The sin(I{sigma}) (C{float}).
152
+ @arg cosx: The cos(I{sigma}) (C{float}).
153
+ @arg F: The Fourier coefficients (C{float}[]).
154
+ @arg N: Optional, C{len(B{F})} or a (smaller) number of
155
+ terms to evaluate (C{int}).
156
+
157
+ @return: Precison I{Clenshaw} intergral (C{float}).
158
+
159
+ @see: Methods C{AuxDST.evaluate} and C{AuxDST.integral2}.
160
+ '''
161
+ a = _2cos2x(cosx - sinx, cosx + sinx)
162
+ if isfinite(a):
163
+ Y0, Y1 = Fsum(), Fsum()
164
+ for r in _reverscaled(F, *N):
165
+ Y1 -= Y0 * a + r
166
+ Y1, Y0 = Y0, -Y1
167
+ r = float(_Dm(Y1, Y0, cosx))
168
+ else:
169
+ r = _naninf(a)
170
+ return r
171
+
172
+ @staticmethod
173
+ def integral2(sinx, cosx, siny, cosy, F, *N): # PYCHOK no cover
174
+ '''Compute the definite integral of Fourier sum given the
175
+ sine and cosine of the angles at the end points, using
176
+ precision I{Clenshaw} summation.
177
+
178
+ @arg sinx: The sin(I{sigma1}) (C{float}).
179
+ @arg cosx: The cos(I{sigma1}) (C{float}).
180
+ @arg siny: The sin(I{sigma2}) (C{float}).
181
+ @arg cosy: The cos(I{sigma2}) (C{float}).
182
+ @arg F: The Fourier coefficients (C{float}[]).
183
+ @arg N: Optional, C{len(B{F})} or a (smaller) number of
184
+ terms to evaluate (C{int}).
185
+
186
+ @return: Precison I{Clenshaw} integral (C{float}).
187
+
188
+ @see: Methods C{AuxDST.evaluate} and C{AuxDST.integral}.
189
+ '''
190
+ # 2 * cos(y - x) * cos(y + x) -> 2 * cos(2 * x)
191
+ c = _2cos2x(cosy * cosx, siny * sinx)
192
+ # -2 * sin(y - x) * sin(y + x) -> 0
193
+ s = -_2cos2x(siny * cosx, cosy * sinx)
194
+ if isfinite(c) and isfinite(s):
195
+ Y0, Y1 = Fsum(), Fsum()
196
+ Z0, Z1 = Fsum(), Fsum()
197
+ for r in _reverscaled(F, *N):
198
+ Y1 -= Y0 * c + Z0 * s + r
199
+ Z1 -= Y0 * s + Z0 * c
200
+ Y1, Y0 = Y0, -Y1
201
+ Z1, Z0 = Z0, -Z1
202
+ r = float(_Dm(Y1, Y0, cosy - cosx) +
203
+ _Dm(Z1, Z0, cosy + cosx))
204
+ else:
205
+ r = _naninf(c, s)
206
+ return r
207
+
208
+ @property_RO
209
+ def N(self):
210
+ '''Get this DST's size, number of points (C{int}).
211
+ '''
212
+ return self._N
213
+
214
+ def refine(self, f, F, *sentinel):
215
+ '''Refine the Fourier series by doubling the sampled points.
216
+
217
+ @arg f: Single-argument callable (C{B{f}(sigma)}).
218
+ @arg F: Initial Fourier series coefficients (C{float}[:N]).
219
+ @arg sentinel: Optional coefficient(s) to append (C{float}(s)).
220
+
221
+ @return: Fourier series coefficients (C{float}[:N*2]).
222
+
223
+ @note: Any initial C{B{F}[N:]} sentinel coefficients are ignored.
224
+ '''
225
+ def _data(_f, N): # [:N]
226
+ if N > 0:
227
+ r = PI_4 / N
228
+ for j in range(1, N*2, 2):
229
+ yield _f(r * j)
230
+
231
+ # F = F[:self.N] handled by zip strict=False in ._ffts2 above
232
+ return self._ffts2(_data(f, self.N), F) + sentinel
233
+
234
+ def reset(self, N):
235
+ '''Reset this DST.
236
+
237
+ @arg N: Size, number of points (C{int}).
238
+
239
+ @return: The new size (C{int}, non-negative).
240
+ '''
241
+ self._N = N = max(0, N)
242
+ # kissfft.assign(N*2, False) # "reset" size, inverse
243
+ return N
244
+
245
+ def transform(self, f):
246
+ '''Determine C{[N + 1]} terms in the Fourier series.
247
+
248
+ @arg f: Single-argument callable (C{B{f}(sigma)}).
249
+
250
+ @return: Fourier series coefficients (C{float}[:N+1],
251
+ leading 0).
252
+ '''
253
+ def _data(_f, N): # [:N + 1]
254
+ yield _0_0 # data[0] = 0
255
+ if N > 0:
256
+ r = PI_2 / N
257
+ for i in range(1, N + 1):
258
+ yield _f(r * i)
259
+
260
+ return self._ffts(_data(f, self.N), False)
261
+
262
+
263
+ def _len_N(F, *N):
264
+ # Adjusted C{len(B{F})}.
265
+ return min(len(F), *N) if N else len(F)
266
+
267
+
268
+ def _reverscaled(F, *N):
269
+ # Yield F[:N], reversed and scaled
270
+ for n in _reverange(_len_N(F, *N)):
271
+ yield F[n] / float(n * 2 + 1)
272
+
273
+
274
+ __all__ += _ALL_DOCS(AuxDST)
275
+
276
+ # **) MIT License
277
+ #
278
+ # Copyright (C) 2023-2024 -- mrJean1 at Gmail -- All Rights Reserved.
279
+ #
280
+ # Permission is hereby granted, free of charge, to any person obtaining a
281
+ # copy of this software and associated documentation files (the "Software"),
282
+ # to deal in the Software without restriction, including without limitation
283
+ # the rights to use, copy, modify, merge, publish, distribute, sublicense,
284
+ # and/or sell copies of the Software, and to permit persons to whom the
285
+ # Software is furnished to do so, subject to the following conditions:
286
+ #
287
+ # The above copyright notice and this permission notice shall be included
288
+ # in all copies or substantial portions of the Software.
289
+ #
290
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
291
+ # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
292
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
293
+ # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
294
+ # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
295
+ # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
296
+ # OTHER DEALINGS IN THE SOFTWARE.