pygeodesy 24.8.24__py2.py3-none-any.whl → 24.9.9__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.
pygeodesy/fsums.py CHANGED
@@ -4,6 +4,11 @@
4
4
  u'''Class L{Fsum} for precision floating point summation and I{running}
5
5
  summation based on, respectively similar to Python's C{math.fsum}.
6
6
 
7
+ Class L{Fsum} also supports accurate multiplication for Python 3.13 and
8
+ later, but as an option for older Python versions. For more details, see
9
+ method L{f2product<Fsum.f2product>}, class L{Fsum2product} and U{Accurate
10
+ Sum and Dot Product<https://www.TUHH.De/ti3/paper/rump/OgRuOi05.pdf>}.
11
+
7
12
  Generally, an L{Fsum} instance is considered a C{float} plus a small or zero
8
13
  C{residual} value, see property L{Fsum.residual}. However, there are several
9
14
  C{integer} L{Fsum} cases, for example the result of C{ceil}, C{floor},
@@ -25,13 +30,13 @@ from __future__ import division as _; del _ # PYCHOK semicolon
25
30
 
26
31
  from pygeodesy.basics import isbool, iscomplex, isint, isscalar, \
27
32
  _signOf, itemsorted, signOf, _xiterable, \
28
- _xiterablen, _enquote
29
- from pygeodesy.constants import INT0, _isfinite, NEG0, _pos_self, \
33
+ _xiterablen
34
+ from pygeodesy.constants import INT0, _isfinite, MANT_DIG, NEG0, _pos_self, \
30
35
  _0_0, _1_0, _N_1_0, Float, Int
31
36
  from pygeodesy.errors import _OverflowError, _TypeError, _UnexpectedError, \
32
37
  _ValueError, _xError, _xError2, _xkwds_get1, \
33
38
  _xkwds_pop2
34
- # from pygeodesy.internals import _enquote # from .basics
39
+ from pygeodesy.internals import _enquote, _passarg
35
40
  from pygeodesy.interns import NN, _arg_, _COMMASPACE_, _DASH_, _DOT_, \
36
41
  _EQUAL_, _from_, _LANGLE_, _NOTEQUAL_, \
37
42
  _not_finite_, _PERCENT_, _PLUS_, \
@@ -47,7 +52,7 @@ from pygeodesy.streprs import Fmt, fstr, unstr
47
52
  from math import ceil as _ceil, fabs, floor as _floor # PYCHOK used! .ltp
48
53
 
49
54
  __all__ = _ALL_LAZY.fsums
50
- __version__ = '24.08.13'
55
+ __version__ = '24.09.10'
51
56
 
52
57
  _add_op_ = _PLUS_ # in .auxilats.auxAngle
53
58
  _eq_op_ = _EQUAL_ * 2 # _DEQUAL_
@@ -132,6 +137,81 @@ def _2floats(xs, origin=0, _X=_X_ps, _x=float):
132
137
  _xError(X, Fmt.INDEX(xs=i), x)
133
138
 
134
139
 
140
+ try: # MCCABE 14
141
+ from math import fma as _fma
142
+
143
+ def _2products(x, ys, **unused):
144
+ # TwoProductFMA U{Algorithm 3.5
145
+ # <https://www.TUHH.De/ti3/paper/rump/OgRuOi05.pdf>}
146
+ for y in ys:
147
+ f = x * y
148
+ yield f
149
+ yield _fma(x, y, -f)
150
+
151
+ _2split3s = _passarg # NOP
152
+
153
+ except ImportError: # Python 3.12-
154
+
155
+ def _fma(*a_b_c): # in .fmath
156
+ # mimick C{math.fma} from Python 3.13+
157
+ # <https://MomentsInGraphics.De/FMA.html>
158
+ # >>> a = 1.00000011920929
159
+ # >>> b = 53400708
160
+ # >>> c = -b
161
+ # >>> _fma(a, b, c)
162
+ # 6.365860485903399
163
+ # >>> (a * b) + c
164
+ # 6.3658604845404625
165
+
166
+ def _as_n_d(x):
167
+ try:
168
+ if _isfinite(x):
169
+ # int.as_integer_ratio since 3.8
170
+ return x.as_integer_ratio()
171
+ except (AttributeError, OverflowError, TypeError, ValueError):
172
+ pass
173
+ return float(x), 1
174
+
175
+ (na, da), (nb, db), (nc, dc) = map(_as_n_d, a_b_c)
176
+ n = na * nb * dc + da * db * nc
177
+ d = da * db * dc
178
+ return float(n / d)
179
+
180
+ def _2products(x, y3s, two=False): # PYCHOK redef
181
+ # TwoProduct U{Algorithm 3.3
182
+ # <https://www.TUHH.De/ti3/paper/rump/OgRuOi05.pdf>}
183
+ _, a, b = _2split3(x)
184
+ for y, c, d in y3s:
185
+ y *= x
186
+ yield y
187
+ if two:
188
+ yield b * d - (((y - a * c) - b * c) - a * d)
189
+ # = b * d + (a * d - ((y - a * c) - b * c))
190
+ # = b * d + (a * d + (b * c - (y - a * c)))
191
+ # = b * d + (a * d + (b * c + (a * c - y)))
192
+ else:
193
+ yield a * c - y
194
+ yield b * c
195
+ if d:
196
+ yield a * d
197
+ yield b * d
198
+
199
+ _2FACTOR = pow(2, (MANT_DIG + 1) // 2) + 1
200
+
201
+ def _2split3(x):
202
+ # Split U{Algorithm 3.2
203
+ # <ttps://www.TUHH.De/ti3/paper/rump/OgRuOi05.pdf>}
204
+ a = c = x * _2FACTOR
205
+ a -= c - x
206
+ b = x - a
207
+ return x, a, b
208
+
209
+ def _2split3s(xs): # PYCHOK redef
210
+ return map(_2split3, xs)
211
+
212
+ del MANT_DIG
213
+
214
+
135
215
  def _Fsumf_(*xs): # floats=True, in .auxLat, ...
136
216
  '''(INTERNAL) An C{Fsum} of I{known scalars}.
137
217
  '''
@@ -325,6 +405,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
325
405
  file I{Modules/mathmodule.c} and the issue log U{Full precision summation
326
406
  <https://Bugs.Python.org/issue2819>}.
327
407
  '''
408
+ _f2product = _2split3s is _passarg # True for 3.13+
409
+ _math_fma = _fma if _f2product else None
328
410
  _math_fsum = None
329
411
  _n = 0
330
412
  # _ps = [] # partial sums
@@ -467,7 +549,9 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
467
549
  def __hash__(self): # PYCHOK no cover
468
550
  '''Return this instance' C{hash}.
469
551
  '''
470
- return hash(self._ps) # XXX id(self)?
552
+ # @see: U{Notes for type implementors<https://docs.Python.org/
553
+ # 3/library/numbers.html#numbers.Rational>}
554
+ return hash(self.partials) # tuple.__hash__()
471
555
 
472
556
  def __iadd__(self, other):
473
557
  '''Apply C{B{self} += B{other}} to this instance.
@@ -890,6 +974,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
890
974
  f._ps = list(self._ps) # separate list
891
975
  if not deep:
892
976
  f._n = 1
977
+ # assert f._f2product == self._f2product
893
978
  # assert f._Fsum is f
894
979
  return f
895
980
 
@@ -901,6 +986,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
901
986
  f = _Named.copy(self, deep=False, name=n)
902
987
  f._ps = list(self._ps) # separate list
903
988
  # assert f._n == self._n
989
+ # assert f._f2product == self._f2product
904
990
  # assert f._Fsum is f
905
991
  return f
906
992
 
@@ -1111,9 +1197,10 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1111
1197
  # raise self._Error(op, other, _AssertionError, txt__=signOf)
1112
1198
  return DivMod2Tuple(q, self) # q is C{int} in Python 3+, but C{float} in Python 2-
1113
1199
 
1114
- def _fhorner(self, x, cs, op): # in .fmath
1200
+ def _fhorner(self, x, cs, op, incx=True): # in .fmath
1115
1201
  '''(INTERNAL) Add an L{Fhorner} evaluation of polynomial
1116
- M{sum(cs[i] * x**i for i=0..len(cs)-1)}.
1202
+ C{sum(cs[i] * B{x}**i for i=0..len(cs)-1) if B{incx}
1203
+ else sum(... i=len(cs)-1..0)}.
1117
1204
  '''
1118
1205
  if _xiterablen(cs):
1119
1206
  H = Fsum(name__=self._fhorner)
@@ -1123,11 +1210,11 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1123
1210
  _mul = H._mul_scalar
1124
1211
  x = _2float(x=x)
1125
1212
  if len(cs) > 1 and x:
1126
- for c in reversed(cs):
1213
+ for c in (reversed(cs) if incx else cs):
1127
1214
  H._fset_ps(_mul(x, op))
1128
1215
  H._fadd(c, op, up=False)
1129
1216
  else: # x == 0
1130
- H = cs[0]
1217
+ H = cs[0] if cs else _0_0
1131
1218
  self._fadd(H, op)
1132
1219
  return self
1133
1220
 
@@ -1222,6 +1309,38 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1222
1309
  q = self._ftruediv(other, op, **raiser_RESIDUAL) # == self
1223
1310
  return self._fset(q.floor) # floor(q)
1224
1311
 
1312
+ def fma(self, other1, other2): #
1313
+ '''Fused-multiply-add C{self *= B{other1}; self += B{other2}}.
1314
+
1315
+ @arg other1: A C{scalar}, an L{Fsum} or L{Fsum2Tuple} instance.
1316
+ @arg other2: A C{scalar}, an L{Fsum} or L{Fsum2Tuple} instance.
1317
+
1318
+ @note: Uses C{math.fma} in Python 3.13+, provided C{self},
1319
+ B{C{other1}} and B{C{other2}} are all C{scalar}.
1320
+ '''
1321
+ if len(self._ps) == 1 and isscalar(other1, both=True) \
1322
+ and isscalar(other2, both=True):
1323
+ p = _fma(self._ps[0], other1, other2)
1324
+ self._ps[:] = self._finite(p, self.fma.__name__),
1325
+ if other2:
1326
+ self._n += 1
1327
+ else:
1328
+ self._f2mul(self.fma.__name__, other1)
1329
+ self += other2
1330
+ return self
1331
+
1332
+ # def _fma_scalar(self, op, x, *ys): # in .karney
1333
+ # '''(INTERNAL) Apply C{self.fma(B{x}, B{y}) for B{y} in B{ys}}
1334
+ # for scalar C{x} and C{y}s.
1335
+ # '''
1336
+ # ps = self._ps
1337
+ # if ps and ys:
1338
+ # for y in ys:
1339
+ # ps[:] = self._ps_acc(list(y), _2products(x, _2split3s(ps)))
1340
+ # for p in (ps if op else()):
1341
+ # self._finite(p, op)
1342
+ # return self
1343
+
1225
1344
  fmul = __imul__
1226
1345
 
1227
1346
  def _fmul(self, other, op):
@@ -1232,6 +1351,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1232
1351
  f = self._mul_Fsum(other, op)
1233
1352
  elif len(other._ps) != 1: # and len(self._ps) == 1
1234
1353
  f = other._mul_scalar(self._ps[0], op)
1354
+ elif self._f2product: # len(other._ps) == 1
1355
+ f = self._mul_scalar(other._ps[0], op)
1235
1356
  else: # len(other._ps) == len(self._ps) == 1
1236
1357
  f = self._finite(self._ps[0] * other._ps[0])
1237
1358
  else:
@@ -1239,6 +1360,29 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1239
1360
  f = self._mul_scalar(s, op)
1240
1361
  return self._fset(f) # n=len(self) + 1
1241
1362
 
1363
+ def f2mul(self, *others):
1364
+ '''Apply C{B{self} *= B{other} for B{other} in B{others}} where each B{other}
1365
+ is C{scalar}, an L{Fsum} or L{Fsum2Tuple} applying accurate multiplication
1366
+ as if L{f2product<Fsum.f2product>}C{=True}.
1367
+
1368
+ @see: U{Equations 2.3<https://www.TUHH.De/ti3/paper/rump/OzOgRuOi06.pdf>}
1369
+ '''
1370
+ return self._f2mul(self.f2mul.__name__, *others)
1371
+
1372
+ def _f2mul(self, op, *others):
1373
+ '''(INTERNAL) See method C{f2mul}.
1374
+ '''
1375
+ P = _Psum(self._ps)
1376
+ ps = P._ps
1377
+ if ps and others:
1378
+ for p in self._ps_other(op, *others):
1379
+ pfs = _2products(p, _2split3s(ps))
1380
+ ps[:] = P._ps_acc([], pfs, up=False)
1381
+ for p in ps:
1382
+ self._finite(p, op)
1383
+ self._fset(P, op=op)
1384
+ return self
1385
+
1242
1386
  def fover(self, over, **raiser_RESIDUAL):
1243
1387
  '''Apply C{B{self} /= B{over}} and summate.
1244
1388
 
@@ -1276,6 +1420,24 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1276
1420
  f = self._pow(other, other, op, **raiser_RESIDUAL)
1277
1421
  return self._fset(f) # n=max(len(self), 1)
1278
1422
 
1423
+ def f2product(self, *two):
1424
+ '''Turn this instance' accurate I{TwoProduct} multiplication or or off.
1425
+
1426
+ @arg two: If C{True}, turn I{TwoProduct} on, if C{False} off or if
1427
+ C{None} or if omitted, keep the current setting.
1428
+
1429
+ @return: The previous C{f2product} setting (C{bool}).
1430
+
1431
+ @see: On Python 3.13 and later I{TwoProduct} is based on I{TwoProductFMA}
1432
+ U{Algorithm 3.5<https://www.TUHH.De/ti3/paper/rump/OgRuOi05.pdf>}
1433
+ otherwise on the slower I{TwoProduct} and I{Split} U{Algorithms
1434
+ 3.3 and 3.2<https://www.TUHH.De/ti3/paper/rump/OgRuOi05.pdf>}.
1435
+ '''
1436
+ t = self._f2product
1437
+ if two and two[0] is not None:
1438
+ self._f2product = bool(two[0])
1439
+ return t
1440
+
1279
1441
  @Property
1280
1442
  def _fprs(self):
1281
1443
  '''(INTERNAL) Get and cache this instance' precision
@@ -1834,10 +1996,16 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1834
1996
  def _pfs(ps, fs):
1835
1997
  if len(ps) < len(fs):
1836
1998
  ps, fs = fs, ps
1999
+ if self._f2product:
2000
+ ps = tuple(_2split3s(ps))
2001
+ _xys = _2products
2002
+ else:
2003
+ def _xys(x, ys):
2004
+ return (x * y for y in ys)
2005
+
1837
2006
  _fin = _isfinite
1838
2007
  for f in fs:
1839
- for p in ps:
1840
- p *= f
2008
+ for p in _xys(f, ps):
1841
2009
  yield p if _fin(p) else self._finite(p, op)
1842
2010
 
1843
2011
  return Fsum()._facc_scalar(_pfs(self._ps, factors), up=False)
@@ -1849,6 +2017,16 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1849
2017
  for p in self._ps:
1850
2018
  yield -p
1851
2019
 
2020
+ def _ps_other(self, op, *others):
2021
+ '''(INTERNAL) Yield the partials of all C{other}s.
2022
+ '''
2023
+ for other in others:
2024
+ if _isFsumTuple(other):
2025
+ for p in other._ps:
2026
+ yield p
2027
+ else:
2028
+ yield self._scalar(other, op)
2029
+
1852
2030
  def _ps_1sum(self, *less):
1853
2031
  '''(INTERNAL) Return the partials sum, 1-primed C{less} some scalars.
1854
2032
  '''
@@ -2034,6 +2212,15 @@ def _Float_Int(arg, **name_Error):
2034
2212
  return U(arg, **name_Error)
2035
2213
 
2036
2214
 
2215
+ def Fsum2product(*xs, **name_RESIDUAL):
2216
+ '''Return an L{Fsum} with L{f2product<Fsum.f2product>} accurate
2217
+ multiplication I{turned on}.
2218
+ '''
2219
+ F = Fsum(*xs, **name_RESIDUAL)
2220
+ F.f2product(True)
2221
+ return F
2222
+
2223
+
2037
2224
  class DivMod2Tuple(_NamedTuple):
2038
2225
  '''2-Tuple C{(div, mod)} with the quotient C{div} and remainder
2039
2226
  C{mod} results of a C{divmod} operation.
pygeodesy/geodesici.py CHANGED
@@ -28,8 +28,9 @@ from __future__ import division as _; del _ # PYCHOK semicolon
28
28
  from pygeodesy.basics import _copy, _enumereverse, map1, \
29
29
  _xinstanceof, _xor
30
30
  from pygeodesy.constants import EPS, INF, INT0, PI, PI2, PI_4, \
31
- _0_0, _0_5, _1_0, _1_5, _2_0, _3_0, \
32
- _90_0, isfinite
31
+ _0_0, _0_5, _1_0, _1_5, _2_0, \
32
+ _3_0, _64_0, _90_0, isfinite, \
33
+ _EPSjam # PYCHOK used!
33
34
  from pygeodesy.ellipsoids import _EWGS84, Fmt, unstr
34
35
  from pygeodesy.errors import GeodesicError, IntersectionError, _an, \
35
36
  _xgeodesics, _xkwds_get, _xkwds_kwds, \
@@ -56,7 +57,7 @@ from pygeodesy.utily import sincos2, atan2, fabs, radians
56
57
  # from math import atan2, ceil as _ceil, fabs, radians # .fsums, .utily
57
58
 
58
59
  __all__ = _ALL_LAZY.geodesici
59
- __version__ = '24.07.25'
60
+ __version__ = '24.08.28'
60
61
 
61
62
  _0t = 0, # int
62
63
  _1_1t = -1, +1
@@ -1237,7 +1238,7 @@ class Intersector(_IntersectBase):
1237
1238
  return sx, zx, sAx, sBx
1238
1239
 
1239
1240
  def _polarB3(self, lats=False): # PYCHOK no cover
1240
- latx = 64.0
1241
+ latx = _64_0
1241
1242
  lat = _90_0 - latx
1242
1243
  if self.f:
1243
1244
  _d, _pD2 = fdot, self._polarDist2
@@ -1393,7 +1394,7 @@ class Intersector(_IntersectBase):
1393
1394
 
1394
1395
  @Property_RO
1395
1396
  def _Tol(self): # convergence tolerance
1396
- return self._cHalf * pow(EPS, 0.75) # _0_75
1397
+ return self._cHalf * _EPSjam
1397
1398
 
1398
1399
  def toStr(self, **prec_sep_name): # PYCHOK signature
1399
1400
  '''Return this C{Intersector} as string.
@@ -13,7 +13,7 @@ U{GeographicLib<https://GeographicLib.SourceForge.io>} documentation.
13
13
  from pygeodesy.geodesicx.gxbases import _Gfloats, _f, _f2
14
14
 
15
15
  __all__ = ()
16
- __version__ = '23.08.19'
16
+ __version__ = '24.09.04'
17
17
 
18
18
  _g = _Gfloats(24)
19
19
  _coeffs_24 = _g(( # GEOGRAPHICLIB_GEODESICEXACT_ORDER == 24
@@ -1672,8 +1672,6 @@ _coeffs_24 = _g(( # GEOGRAPHICLIB_GEODESICEXACT_ORDER == 24
1672
1672
  # C4[23], coeff of eps^23, polynomial in n of order 0
1673
1673
  _f(1 << 41), _f(0xc5e28ed2c935ab), # PYCHOK exported
1674
1674
  )) # 2900 / 2708
1675
- if __name__ == '__main__':
1676
- _g.prints()
1677
1675
  del _g, _Gfloats, _f, _f2
1678
1676
 
1679
1677
  # **) MIT License
@@ -13,7 +13,7 @@ U{GeographicLib<https://GeographicLib.SourceForge.io>} documentation.
13
13
  from pygeodesy.geodesicx.gxbases import _Gfloats, _f, _f2
14
14
 
15
15
  __all__ = ()
16
- __version__ = '23.08.19'
16
+ __version__ = '24.09.04'
17
17
 
18
18
  _g = _Gfloats(27)
19
19
  _coeffs_27 = _g(( # GEOGRAPHICLIB_GEODESICEXACT_ORDER == 27
@@ -2368,8 +2368,6 @@ _coeffs_27 = _g(( # GEOGRAPHICLIB_GEODESICEXACT_ORDER == 27
2368
2368
  # C4[26], coeff of eps^26, polynomial in n of order 0
2369
2369
  _f(1 << 48), _f2(2126, 0x8c0e9e949456f), # PYCHOK exported
2370
2370
  )) # 4032 / 3764
2371
- if __name__ == '__main__':
2372
- _g.prints()
2373
2371
  del _g, _Gfloats, _f, _f2
2374
2372
 
2375
2373
  # **) MIT License
@@ -30,7 +30,7 @@ U{GeographicLib<https://GeographicLib.SourceForge.io>} documentation.
30
30
  from pygeodesy.geodesicx.gxbases import _Gfloats, _f, _f2
31
31
 
32
32
  __all__ = ()
33
- __version__ = '23.08.19'
33
+ __version__ = '24.09.04'
34
34
 
35
35
  _g = _Gfloats(30)
36
36
  _coeffs_30 = _g(( # GEOGRAPHICLIB_GEODESICEXACT_ORDER == 30
@@ -3273,8 +3273,6 @@ _coeffs_30 = _g(( # GEOGRAPHICLIB_GEODESICEXACT_ORDER == 30
3273
3273
  # C4[29], coeff of eps^29, polynomial in n of order 0
3274
3274
  _f(1 << 53), _f2(88602, 0xec373d36a45df), # PYCHOK exported
3275
3275
  )) # 5425 / 5107
3276
- if __name__ == '__main__':
3277
- _g.prints()
3278
3276
  del _g, _Gfloats, _f, _f2
3279
3277
 
3280
3278
 
@@ -23,7 +23,7 @@ from pygeodesy.karney import Caps, GeodesicError
23
23
  from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY
24
24
 
25
25
  __all__ = _ALL_LAZY.geodesicx + _ALL_DOCS(Caps, GeodesicError)
26
- __version__ = '24.07.09'
26
+ __version__ = '24.09.06'
27
27
 
28
28
  # **) MIT License
29
29
  #
@@ -5,65 +5,63 @@ u'''Print L{geodesicx} version, etc. using C{python -m pygeodesy.geodesicx}.
5
5
  '''
6
6
 
7
7
  __all__ = ()
8
- __version__ = '24.05.31'
8
+ __version__ = '24.09.06'
9
9
 
10
10
 
11
- def _C4stats(nC4=None): # PYCHOK no cover
12
- '''(INTERNAL) Get the C{C4} stats.
13
- '''
14
- from pygeodesy import GeodesicExact, geodesicx
15
-
16
- gX = GeodesicExact(C4order=nC4)
17
- cs = geodesicx.gx._C4coeffs(gX.C4order)
18
- ss = set(cs) # without duplicates
19
- pc = '%.1f%%' % (len(ss) * 100.0 / len(cs))
20
- cx = gX._C4x
21
- return dict(C4order=gX.C4order, C4len=len(cs), C4set=len(ss), C4set_len=pc, C4x=len(cx))
22
-
23
-
24
- def _main(): # PYCHOK no cover
25
-
26
- import os.path as os_path
11
+ def _main(**C4order): # PYCHOK no cover
27
12
 
28
13
  try:
29
- from pygeodesy import geodesicx as _gx, GeodesicError, \
30
- GeodesicSolve, printf, pygeodesy_abspath
31
- from pygeodesy.internals import _Pythonarchine, _usage
32
- from pygeodesy.interns import _COMMASPACE_, _DOT_, _SPACE_, _version_
33
- from pygeodesy.streprs import Fmt
34
-
35
- def _dot_attr(name, value):
36
- return Fmt.DOT(Fmt.EQUAL(name, value))
37
-
38
- s = tuple(sorted(_C4stats().items()))
39
- p = [_dot_attr(*t) for t in (((_version_, _gx.__version__),) + s)]
40
-
41
- def _name_version(pkg):
42
- return _SPACE_(pkg.__name__, pkg.__version__)
43
-
44
- v = _Pythonarchine()
14
+ from pygeodesy import GeodesicExact, geodesicx
15
+ from pygeodesy.internals import _fper, _name_version, \
16
+ printf, _sizeof, _versions
17
+ from pygeodesy.interns import _COMMASPACE_, _EQUAL_
45
18
  try:
46
- import geographiclib
47
- v.append(_name_version(geographiclib))
19
+ import numpy
48
20
  except ImportError:
49
- pass
21
+ numpy = None
22
+
23
+ gX = GeodesicExact(**C4order)
24
+ cs = geodesicx.gx._C4coeffs(gX.C4order)
25
+ n = len(cs)
26
+ u = n if numpy else len(set(cs))
27
+ z = cs.nbytes if numpy else _sizeof(cs)
28
+ p = dict(C4order=gX.C4order, C4n=n, C4u=u,
29
+ C4u_n=_fper(u, n), C4x=len(gX._C4x),
30
+ C4t=type(cs).__name__, C4z=z)
31
+ p = list(_EQUAL_(*t) for t in p.items())
32
+ if numpy:
33
+ p.append(_name_version(numpy))
50
34
  try:
51
- g = GeodesicSolve()
52
- v.append(g.version)
53
- except GeodesicError:
35
+ import geographiclib
36
+ p.append(_name_version(geographiclib))
37
+ except ImportError:
54
38
  pass
55
39
 
56
- g = _gx.__name__
57
- x = os_path.basename(pygeodesy_abspath)
58
- if not g.startswith(x):
59
- g = _DOT_(x, g)
60
- printf('%s%s (%s)', g, _COMMASPACE_.join(p), _COMMASPACE_.join(v))
40
+ g = _name_version(geodesicx)
41
+ printf('%s: %s (%s)', g, _COMMASPACE_.join(p), _versions())
61
42
 
62
43
  except ImportError:
63
- printf(_usage(__file__))
44
+ from pygeodesy.internals import _usage
45
+ print(_usage(__file__))
46
+
47
+
48
+ from sys import argv # .internals._isPyChecker
49
+ _main(C4order=int(argv[1])) if len(argv) == 2 and argv[1].isdigit() else _main()
50
+
51
+ # % python3.13 -m pygeodesy.geodesicx 30
52
+ # pygeodesy.geodesicx 24.09.06: C4order=30, C4n=5425, C4u=5107, C4u_n=94.1%, C4x=465, C4t=tuple, C4z=166008 (pygeodesy 24.9.6 Python 3.13.0rc1 64bit arm64 macOS 14.6.1)
53
+ # % python3.12 -m pygeodesy.geodesicx 30
54
+ # pygeodesy.geodesicx 24.09.06: C4order=30, C4n=5425, C4u=5425, C4u_n=100.0%, C4x=465, C4t=ndarray, C4z=43400, numpy 2.1.0, geographiclib 2.0 (pygeodesy 24.9.6 Python 3.12.5 64bit arm64 macOS 14.6.1)
64
55
 
56
+ # % python3.13 -m pygeodesy.geodesicx 27
57
+ # pygeodesy.geodesicx 24.09.06: C4order=27, C4n=4032, C4u=3764, C4u_n=93.4%, C4x=378, C4t=tuple, C4z=122632 (pygeodesy 24.9.6 Python 3.13.0rc1 64bit arm64 macOS 14.6.1)
58
+ # % python3.12 -m pygeodesy.geodesicx 27
59
+ # pygeodesy.geodesicx 24.09.06: C4order=27, C4n=4032, C4u=4032, C4u_n=100.0%, C4x=378, C4t=ndarray, C4z=32256, numpy 2.1.0, geographiclib 2.0 (pygeodesy 24.9.6 Python 3.12.5 64bit arm64 macOS 14.6.1)
65
60
 
66
- _main()
61
+ # % python3.13 -m pygeodesy.geodesicx 24
62
+ # pygeodesy.geodesicx 24.09.06: C4order=24, C4n=2900, C4u=2708, C4u_n=93.4%, C4x=300, C4t=tuple, C4z=88232 (pygeodesy 24.9.6 Python 3.13.0rc1 64bit arm64 macOS 14.6.1)
63
+ # % python3.12 -m pygeodesy.geodesicx 24
64
+ # pygeodesy.geodesicx 24.09.06: C4order=24, C4n=2900, C4u=2900, C4u_n=100.0%, C4x=300, C4t=ndarray, C4z=23200, numpy 2.1.0, geographiclib 2.0 (pygeodesy 24.9.6 Python 3.12.5 64bit arm64 macOS 14.6.1)
67
65
 
68
66
  # **) MIT License
69
67
  #
@@ -21,7 +21,7 @@ from pygeodesy.basics import isodd, unsigned0
21
21
  from pygeodesy.constants import NAN, _0_0, _0_5, _720_0
22
22
  # from pygeodesy.interns import _COMMASPACE_ # from .lazily
23
23
  from pygeodesy.karney import Area3Tuple, _diff182, GeodesicError, \
24
- _norm180, _remainder, _sum2_
24
+ _norm180, _remainder, _sum3
25
25
  from pygeodesy.lazily import _ALL_DOCS, printf, _COMMASPACE_
26
26
  from pygeodesy.named import ADict, callername, _NamedBase, pairs
27
27
  from pygeodesy.props import Property, Property_RO, property_RO
@@ -30,7 +30,7 @@ from pygeodesy.props import Property, Property_RO, property_RO
30
30
  from math import fmod as _fmod
31
31
 
32
32
  __all__ = ()
33
- __version__ = '24.08.13'
33
+ __version__ = '24.09.04'
34
34
 
35
35
 
36
36
  class GeodesicAreaExact(_NamedBase):
@@ -451,7 +451,7 @@ class _Accumulator(_NamedBase):
451
451
  @return: Current C{sum}.
452
452
  '''
453
453
  self._n += 1
454
- self._s, self._t = _sum2_(self._s, self._t, y)
454
+ self._s, self._t, _ = _sum3(self._s, self._t, y)
455
455
  return self._s # current .Sum()
456
456
 
457
457
  def Negate(self):
@@ -9,17 +9,18 @@ U{GeographicLib<https://GeographicLib.SourceForge.io>} documentation.
9
9
  '''
10
10
 
11
11
  from pygeodesy.basics import isodd, _MODS
12
- from pygeodesy.constants import _EPSmin as _TINY, _0_0, _100_0
12
+ from pygeodesy.constants import _EPSmin as _TINY, _0_0
13
13
  from pygeodesy.errors import _or, _xkwds_item2
14
14
  from pygeodesy.fmath import hypot as _hypot
15
+ # from pygeodesy.interns import _numpy_ # _MODS
15
16
  from pygeodesy.karney import _CapsBase, GeodesicError, _2cos2x, \
16
- _norm2, _sincos2d, _sum2_
17
- # from pygeodesy.lazily import _MODS, printf # .basics, _MODS
17
+ _norm2, _sincos2d, _sum3
18
+ # from pygeodesy.lazily import _ALL_MODS as _MODS # from .basics
18
19
 
19
20
  from math import fabs, ldexp as _ldexp
20
21
 
21
22
  __all__ = ()
22
- __version__ = '24.06.16'
23
+ __version__ = '24.09.07'
23
24
 
24
25
  # valid C{nC4}s and C{C4order}s, see _xnC4 below
25
26
  _nC4s = {24: 2900, 27: 4032, 30: 5425}
@@ -44,7 +45,7 @@ class _GeodesicBase(_CapsBase): # in .geodsolve
44
45
 
45
46
 
46
47
  class _Gfloats(dict):
47
- '''(INTERNAL) Uniquify floats.
48
+ '''(INTERNAL) Numpy or "Unique" floats.
48
49
  '''
49
50
  n = 0 # total number of floats
50
51
  nC4 = 0
@@ -53,16 +54,16 @@ class _Gfloats(dict):
53
54
  self.nC4 = nC4
54
55
 
55
56
  def __call__(self, fs):
56
- '''Return a tuple of "uniquified" floats.
57
+ '''Return a C{numpy.array} or C{tuple} of C{float}s.
57
58
  '''
59
+ np = _MODS.imported(_MODS.interns._numpy_)
60
+ if np: # use numpy, already imported
61
+ cs = np.array(fs, dtype=float)
62
+ else:
63
+ _f = self.setdefault # avoid duplicates
64
+ cs = tuple(_f(f, f) for f in map(float, fs)) # PYCHOK as attr
58
65
  self.n += len(fs)
59
- _f = self.setdefault
60
- return tuple(_f(f, f) for f in map(float, fs)) # PYCHOK as attr
61
-
62
- def prints(self):
63
- n, u = self.n, len(self.keys())
64
- d = (n - u) * _100_0 / n
65
- _MODS.lazily.printf('_CX_%d: n=%d, u=%d, d=%.1f%%', self.nC4, n, u, d) # XXX
66
+ return cs
66
67
 
67
68
 
68
69
  def _cosSeries(c4s, sx, cx): # PYCHOK shared .geodesicx.gx and -.gxline
@@ -75,15 +76,28 @@ def _cosSeries(c4s, sx, cx): # PYCHOK shared .geodesicx.gx and -.gxline
75
76
  _c4 = c4.pop
76
77
  if isodd(len(c4)):
77
78
  y0 = _c4()
78
- _s2 = _sum2_
79
79
  while c4:
80
80
  # y1 = ar * y0 - y1 + c4.pop()
81
81
  # y0 = ar * y1 - y0 + c4.pop()
82
- y1, t1 = _s2(ar * y0, ar * t0, -y1, -t1, _c4())
83
- y0, t0 = _s2(ar * y1, ar * t1, -y0, -t0, _c4())
82
+ y1, t1, _ = _sum3(-y1, -t1, ar * y0, ar * t0, _c4())
83
+ y0, t0, _ = _sum3(-y0, -t0, ar * y1, ar * t1, _c4())
84
84
  # s = (y0 - y1) * cx
85
- s, _ = _s2(cx * y0, _0_0, cx * t0, -cx * y1, -cx * t1)
86
- return s
85
+ s, t, _ = _sum3(cx * y0, _0_0, cx * t0, -cx * y1, -cx * t1)
86
+ return s + t
87
+
88
+ # Y0, Y1 = Fsum(), Fsum()
89
+ # ar = _2cos2x(cx, sx)
90
+ # c4 = list(c4s)
91
+ # _c4 = c4.pop
92
+ # if isodd(len(c4)):
93
+ # Y0 += _c4()
94
+ # while c4:
95
+ # # y1 = ar * y0 - y1 + c4.pop()
96
+ # # y0 = ar * y1 - y0 + c4.pop()
97
+ # Y1 = Y0 * ar - Y1 + _c4()
98
+ # Y0 = Y1 * ar - Y0 + _c4()
99
+ # # s = (y0 - y1) * cx
100
+ # return float((Y0 - Y1) * cx)
87
101
 
88
102
 
89
103
  _f = float # in _f2 and .geodesicx._C4_24, _27 and _30