pygeodesy 24.4.2__py2.py3-none-any.whl → 24.4.4__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PyGeodesy
3
- Version: 24.4.2
3
+ Version: 24.4.4
4
4
  Summary: Pure Python geodesy tools
5
5
  Home-page: https://GitHub.com/mrJean1/PyGeodesy
6
6
  Author: Jean M. Brouwers
@@ -118,7 +118,7 @@ The tests ran with Python 3.12.2 (with geographiclib_ 2.0), 3.11.5 (with geograp
118
118
  1.24.2 and scipy_ 1.10.1), Python 3.10.8 (with geographiclib_ 2.0, numpy_ 1.23.3, scipy_ 1.9.1,
119
119
  GeoConvert_ 2.2, GeodSolve_ 2.2 and RhumbSolve_ 2.2), Python 3.9.6 and Python 2.7.18 (with geographiclib_
120
120
  1.50, numpy_ 1.16.6, scipy_ 1.2.2, GeoConvert_ 2.2, GeodSolve_ 2.2 and RhumbSolve_ 2.2), all on macOS
121
- 14.3.1 Sonoma and in 64-bit only.
121
+ 14.4.1 Sonoma and in 64-bit only.
122
122
 
123
123
  All tests ran with and without ``lazy import`` for Python 3 and with command line option ``-W default``
124
124
  and env variable ``PYGEODESY_WARNINGS=on`` for all Python versions. The results of those tests are
@@ -143,7 +143,7 @@ numpy_ 1.16.3, 1.16.4, 1.16.6, 1.19.0, 1.19.4, 1.19.5 or 1.22.4 and scipy_ 1.2.1
143
143
  on `Ubuntu 16.04`_, with Python 3.10.0-1, 3.9.0-5, 3.8.0-6, 3.7.2-6, 3.7.0, 3.6.2-5, 3.5.3, 2.7.13-17,
144
144
  2.7.10 and 2.6.9 (and numpy_ 1.19.0, 1.16.5, 1.16.2, 1.15.2, 1.14.0, 1.13.1, 1.8.0rc1 or 1.6.2 and scipy_
145
145
  1.5.0), PyPy_ 7.3.0 (Python 2.7.13 and 3.6.9), PyPy_ 6.0.0 (Python 2.7.13 and 3.5.3) and `Intel-Python`_
146
- 3.5.3 (and numpy_ 1.11.3) on macOS 14.0-2.1 Sonoma, 13.0-5.2 Ventura, 12.1-6 Monterey, 11.0-5.2-6.1 Big
146
+ 3.5.3 (and numpy_ 1.11.3) on macOS 14.0-3.1 Sonoma, 13.0-5.2 Ventura, 12.1-6 Monterey, 11.0-5.2-6.1 Big
147
147
  Sur (aka 10.16), 10.15.3, 10.15.5-7 Catalina, 10.14 Mojave, 10.13.6 High Sierra and 10.12 Sierra, MacOS X
148
148
  10.11 El Capitan and/or MacOS X 10.10 Yosemite, with Pythonista_ 3.2 (with geographiclib 1.50 or 1.49 and
149
149
  numpy 1.8.0) on iOS 14.4.2, 11.4.1, 12.0-3 on iPad4, iPhone6, iPhone10 and/or iPhone12, with Pythonista_
@@ -154,11 +154,11 @@ Notes
154
154
  =====
155
155
 
156
156
  All Python source code has been statically checked_ with PyChecker_, PyFlakes_, PyCodeStyle_ (formerly Pep8)
157
- and McCabe_ using Python 2.7.18 and with Flake8_ using Python 3.11.5, both in 64-bit on macOS 14.3.1 Sonoma.
157
+ and McCabe_ using Python 2.7.18 and with Flake8_ using Python 3.11.5, both in 64-bit on macOS 14.4.1 Sonoma.
158
158
 
159
159
  For a summary of all *Karney*-based functionality in ``pygeodesy``, see module karney_.
160
160
 
161
- *Last updated: April 02, 2024.*
161
+ *Last updated: April 04, 2024.*
162
162
 
163
163
  License
164
164
  =======
@@ -1,13 +1,13 @@
1
1
  pygeodesy/LICENSE,sha256=YfgAiyxOwY6P9Kkb1_5XN81nueTLrpb3Ffkv3EuPgFU,1144
2
- pygeodesy/__init__.py,sha256=_3ZB1L61RzVekqZjnjG1Q-nWx4O6g6fbCUm9ygiBoQU,40824
2
+ pygeodesy/__init__.py,sha256=3Zu_6m7CtOUuHraPspTn-gTN03b8oW_SyNpfhSibkTI,40824
3
3
  pygeodesy/__main__.py,sha256=qMFG3caY8ZXWu6uGiemzyT4OqTFZnsFtlxcGCAgkVJw,4637
4
4
  pygeodesy/albers.py,sha256=UpWNezHXFuA_kT5PhH_2V_gGFJjcmVLLogl5ScJloPU,31098
5
5
  pygeodesy/azimuthal.py,sha256=e796vtU5ol5ZG2yBX3E5YBXsONJuCrhyEX60YzGzFi0,50115
6
- pygeodesy/basics.py,sha256=tkHRN1-4wcZyniACBp9K5RJSrrFVIEmaVt9a0celNGk,27687
6
+ pygeodesy/basics.py,sha256=_BMYLzGKA6OIS3qv25HfHy7MIh0kAbmkzyScb59clUs,28160
7
7
  pygeodesy/booleans.py,sha256=HZbwoL-S7Ww9d4C2D1BVqXfmcuqqVpEVSU9_S0uyUyo,74204
8
8
  pygeodesy/cartesianBase.py,sha256=I3q29mRdBB3NCDmPoJsJ0QOFfLzkdMWc8X9zG4IwJyA,47264
9
9
  pygeodesy/clipy.py,sha256=VU3ynQ1IZ0v5hJlicqD48oW0imRgiL5_ZzRPrIjpfPw,27683
10
- pygeodesy/constants.py,sha256=0ooop-YabUzSvO1Z3Q3J6P--LT-ViR83om4aj6PlqOg,19144
10
+ pygeodesy/constants.py,sha256=ypxYsWB6tNAVgrD9QVpukJAPscaLgHtPLqPuot1gUfU,19174
11
11
  pygeodesy/css.py,sha256=sKXsahUiyruDcUk-tGjA6mxq-xzwBoBKxKo9_b2uBmY,25394
12
12
  pygeodesy/datums.py,sha256=gJZPgV4bELZvZ8Sj2zE3MBysVtsLxqsN8zm0xjOKvpo,33851
13
13
  pygeodesy/dms.py,sha256=op3MU-59CoJQRdybnu21aVM9wtocd_-XFNAZFqmozSo,44439
@@ -25,11 +25,11 @@ pygeodesy/elliptic.py,sha256=n2R-3H1ruMnwBewEtn75ahjpgeEU93irzcvFxarCyPs,42393
25
25
  pygeodesy/epsg.py,sha256=ldHoLWqJWR4FUiBVnDTtrI_e7TNjjtr9OkxDlri1g5E,8165
26
26
  pygeodesy/errors.py,sha256=fRGrdnb81Y9KB4Y1ua1nvB9zuuiv8IL6c6YN_cb3kBo,27280
27
27
  pygeodesy/etm.py,sha256=ADIMY8zxVu3cAiP9rUGj3Mq3LyO2C5w3ejdW2YY1eJ8,44583
28
- pygeodesy/fmath.py,sha256=z0YBdZW92NaLM0VEyx6ItTIjuf3I6PhRce27OUl_RnM,30360
28
+ pygeodesy/fmath.py,sha256=cmObX2axb8BfPyhaSDGFFVRs63kBP4ZWhWzGzPhl6OQ,30286
29
29
  pygeodesy/formy.py,sha256=Rces4Q5ecED0WVfuI9mQzeyHE54LDowYfI6faBHpyeA,74536
30
30
  pygeodesy/frechet.py,sha256=1tiLc6WZou8YVbizB0QisVhYH-VOP03pMZQ5GOAb9Dc,33527
31
31
  pygeodesy/fstats.py,sha256=X79Qd8pL4R-kUIYN0CZDuA7L1UArtBFvWqaFoHASNqs,25605
32
- pygeodesy/fsums.py,sha256=3Fdnk0ZJmg05qoJmUSLH8ra4mQQKuDFDTdD5L4jio5U,67166
32
+ pygeodesy/fsums.py,sha256=a2rhCX73eZbyiKYvk06T4q9l1QkGxYQ50T7N21oGRl8,68591
33
33
  pygeodesy/gars.py,sha256=fCiWBJ4kOJxPfNOxadX-OzBGDXj7C9g02NuGHiZa_88,11342
34
34
  pygeodesy/geodesicw.py,sha256=Rw1pmT8UabWsBKfecsd4VR5Ks3uMrhH8sufydJS_MMs,26901
35
35
  pygeodesy/geodsolve.py,sha256=J75l_bbPPsPr4NZXm1hCASl9TrXAS5DSeufXYkz2Qzs,21863
@@ -53,7 +53,7 @@ pygeodesy/nvectorBase.py,sha256=vpuGECs038I-8z_MDFJ9gZMY_6Een-EG2Y_llRLpl-o,2879
53
53
  pygeodesy/osgr.py,sha256=5JB6b_nvyZ6rFMlC7ZB45mvYtjc_aiLb4DPdeaui-KY,30995
54
54
  pygeodesy/points.py,sha256=8LiAH1dMiNN6mLmBZAisI0VsccD9PSy97XhDtJUfBuw,64441
55
55
  pygeodesy/props.py,sha256=ZnXRuSOkfkeJY3Of-iT06FNWZkTYgLZQBPBNF6aS0jE,21421
56
- pygeodesy/resections.py,sha256=ILXfcWkHH-WP2eczhYw6p5HANZXH_sttfhdJBM22l-k,43586
56
+ pygeodesy/resections.py,sha256=WGGsWgN9LCcJ9YCo-lbgtuA9VqEWnYvoe3OFOJ4nYFk,43619
57
57
  pygeodesy/simplify.py,sha256=FT3AzXiFjFfvwJ-X2q8iA9cQOUVdBT1vD_BIskM3gK8,25223
58
58
  pygeodesy/solveBase.py,sha256=cBe3QQWEaJlPWUlXoiYo1eFMh4z6GDZbQyt8ZiwMfos,16687
59
59
  pygeodesy/sphericalBase.py,sha256=sH2WwmY_RL0STyr1OhtCDKvGhO0Ks7o3V-MHLddpiHQ,31944
@@ -65,7 +65,7 @@ pygeodesy/triaxials.py,sha256=Yet0J9Fr4FbTEIL9_p3ziollRR3PzUN2-qt7Z9ZSODU,61537
65
65
  pygeodesy/units.py,sha256=ofFTYc7mF9wOpfvpYHN83dGszMM18rTdsxfEXVzaIko,38705
66
66
  pygeodesy/unitsBase.py,sha256=xRZlNlO7S47Mwy0FGBg_cefvB4fEtWFM5dAE-Nlw-NE,13111
67
67
  pygeodesy/ups.py,sha256=07AG-Rb1D4EvmN3g-xk3ZxKzyaNYvOzztcyYbx7t8jc,23229
68
- pygeodesy/utily.py,sha256=O9y9G02kRKtcvTyGJqxWaYDA10rv3mPhTub4VTQmrQI,36105
68
+ pygeodesy/utily.py,sha256=jP82RmCPCVghxTQv4ZRuIS89afmsD-roz2iafHfyHXA,36128
69
69
  pygeodesy/utm.py,sha256=0qF5Kjw8Wf9zxGWOaCF-LSweibat-EkHvvmy7dFO_KE,30981
70
70
  pygeodesy/utmups.py,sha256=u2hlWSnmZKwR-ApgKw16R9p75sW_yaqQAmbcBUE8Zfg,13007
71
71
  pygeodesy/utmupsBase.py,sha256=5mKmo2cGSCJM8Q5oRQ7tmGJgRTdIgRVncCwG_BqB030,18816
@@ -109,7 +109,7 @@ pygeodesy/rhumb/aux_.py,sha256=W4HkgoHAQz_aWaysfsmOA5010nCGvBfUs2Q-bymnHYU,16660
109
109
  pygeodesy/rhumb/bases.py,sha256=ZZ4Ba0Cl45tQdfEquyQPSQdH2vXgemF3tCw6UuzHphs,53893
110
110
  pygeodesy/rhumb/ekx.py,sha256=lF3tZ-ZY9fPJV8y1kgHW-7EOZCyb3gJr-kR-jj5Fbf8,23871
111
111
  pygeodesy/rhumb/solve.py,sha256=NZfwx7xv5UriQs7A0c7ZhoxxVUeAT15UwXK_jtwEXQw,27802
112
- PyGeodesy-24.4.2.dist-info/METADATA,sha256=hh9f4N7AItjpHOMK6dzULDBjunNb4dIoN4WIiVUxsDQ,19365
113
- PyGeodesy-24.4.2.dist-info/WHEEL,sha256=DZajD4pwLWue70CAfc7YaxT1wLUciNBvN_TTcvXpltE,110
114
- PyGeodesy-24.4.2.dist-info/top_level.txt,sha256=cEQPatCXzKZqrivpULC5V5fuy9_V_bAwaP_gUGid7pQ,10
115
- PyGeodesy-24.4.2.dist-info/RECORD,,
112
+ PyGeodesy-24.4.4.dist-info/METADATA,sha256=pWgDCMFWPNFawFtWPa1q4SpV_5XDSJx4r5MvNyFpXjE,19365
113
+ PyGeodesy-24.4.4.dist-info/WHEEL,sha256=DZajD4pwLWue70CAfc7YaxT1wLUciNBvN_TTcvXpltE,110
114
+ PyGeodesy-24.4.4.dist-info/top_level.txt,sha256=cEQPatCXzKZqrivpULC5V5fuy9_V_bAwaP_gUGid7pQ,10
115
+ PyGeodesy-24.4.4.dist-info/RECORD,,
pygeodesy/__init__.py CHANGED
@@ -133,7 +133,7 @@ U{geographiclib<https://PyPI.org/project/geographiclib>} 1.50, U{numpy<https://P
133
133
  1.16.6, U{scipy<https://PyPI.org/project/scipy>} 1.2.2, U{GeoConvert
134
134
  <https://GeographicLib.SourceForge.io/C++/doc/utilities.html>} 2.2, U{GeodSolve
135
135
  <https://GeographicLib.SourceForge.io/C++/doc/utilities.html>} 2.2 and U{RhumbSolve
136
- <https://GeographicLib.SourceForge.io/C++/doc/utilities.html>} 2.2), all on macOS 14.3.1 Sonoma and
136
+ <https://GeographicLib.SourceForge.io/C++/doc/utilities.html>} 2.2), all on macOS 14.4.1 Sonoma and
137
137
  in 64-bit only.
138
138
 
139
139
  All tests ran with and without C{lazy import} for Python 3 and with command line option C{-W default} and
@@ -164,7 +164,7 @@ Previously, the tests were run with Python 3.12.0-1, 3.11.2-4, 3.10.1-7, 3.9.1,
164
164
  1.16.5, 1.16.2, 1.15.2, 1.14.0, 1.13.1, 1.8.0rc1 or 1.6.2 and U{scipy<https://PyPI.org/project/scipy>} 1.5.0), U{PyPy
165
165
  <https://PyPy.org>} 7.3.0 (Python 2.7.13 and 3.6.9), U{PyPy<https://PyPy.org>} 6.0.0 (Python 2.7.13 and 3.5.3)
166
166
  and U{Intel-Python<https://software.Intel.com/en-us/distribution-for-python>} 3.5.3 (and U{numpy
167
- <https://PyPI.org/project/numpy>} 1.11.3) on macOS 14.0-2.1 Sonoma, 13.0-5.2 Ventura, 12.1-6 Monterey, 11.0-5.2-6.1
167
+ <https://PyPI.org/project/numpy>} 1.11.3) on macOS 14.0-3.1 Sonoma, 13.0-5.2 Ventura, 12.1-6 Monterey, 11.0-5.2-6.1
168
168
  Big Sur (aka 10.16), 10.15.3, 10.15.5-7 Catalina, 10.14 Mojave, 10.13.6 High Sierra and 10.12 Sierra, MacOS X
169
169
  10.11 El Capitan and/or MacOS X 10.10 Yosemite, with U{Pythonista<https://OMZ-Software.com/pythonista>}3.2 (with
170
170
  geographiclib 1.50 or 1.49 and numpy 1.8.0) on iOS 14.4.2, 11.4.1, 12.0-3 on iPad4, iPhone6, iPhone10 and/or
@@ -179,7 +179,7 @@ All Python source code has been statically U{checked<https://GitHub.com/ActiveSt
179
179
  Python/546532_PyChecker_postprocessor>} with U{PyChecker<https://PyPI.org/project/pychecker>}, U{PyFlakes
180
180
  <https://PyPI.org/project/pyflakes>}, U{PyCodeStyle<https://PyPI.org/project/pycodestyle>} (formerly Pep8) and
181
181
  U{McCabe<https://PyPI.org/project/mccabe>} using Python 2.7.18 and with U{Flake8<https://PyPI.org/project/flake8>}
182
- using Python 3.11.5, both in 64-bit on macOS 14.3.1 Sonoma.
182
+ using Python 3.11.5, both in 64-bit on macOS 14.4.1 Sonoma.
183
183
 
184
184
  For a summary of all I{Karney}-based functionality in C{pygeodesy}, see module U{karney
185
185
  <https://mrJean1.GitHub.io/PyGeodesy/docs/pygeodesy.karney-module.html>}.
@@ -586,7 +586,7 @@ else:
586
586
  _init__all__ = False
587
587
 
588
588
  from pygeodesy.interns import _DOT_, _version2 # PYCHOK import
589
- __version__ = '24.04.02'
589
+ __version__ = '24.04.04'
590
590
  # see setup.py for similar logic
591
591
  version = _DOT_(*_version2(__version__, n=3))
592
592
 
pygeodesy/basics.py CHANGED
@@ -14,21 +14,27 @@ if not division:
14
14
  raise ImportError('%s 1/2 == %s' % ('division', division))
15
15
  del division
16
16
 
17
+ # from pygeodesy.cartesianBase import CartesianBase # _MODS
18
+ # from pygeodesy.constants import isneg0, NEG0 # _MODS
17
19
  from pygeodesy.errors import _AttributeError, _ImportError, _NotImplementedError, \
18
20
  _TypeError, _TypesError, _ValueError, _xAssertionError, \
19
21
  _xkwds_get
20
22
  from pygeodesy.interns import MISSING, NN, _1_, _by_, _COMMA_, _DOT_, _DEPRECATED_, \
21
23
  _ELLIPSIS4_, _enquote, _EQUAL_, _in_, _invalid_, _N_A_, \
22
24
  _not_scalar_, _SPACE_, _UNDER_, _version_, _version_info
25
+ # from pygeodesy.latlonBase import LatLonBase # _MODS
23
26
  from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS, _FOR_DOCS, \
24
27
  _getenv, LazyImportError, _sys, _sys_version_info2
28
+ # from pygeodesy.named import classname, modulename # _MODS
29
+ # from pygeodesy.nvectorBase import NvectorBase # _MODS
30
+ # from pygeodesy.props import _update_all # _MODS
25
31
 
26
32
  from copy import copy as _copy, deepcopy as _deepcopy
27
33
  from math import copysign as _copysign
28
34
  import inspect as _inspect
29
35
 
30
36
  __all__ = _ALL_LAZY.basics
31
- __version__ = '24.04.01'
37
+ __version__ = '24.04.04'
32
38
 
33
39
  _0_0 = 0.0 # in .constants
34
40
  _below_ = 'below'
@@ -537,14 +543,19 @@ def neg_(*xs):
537
543
  return map(neg, xs)
538
544
 
539
545
 
540
- def _req_d_by(where, **name): # in .basics
546
+ def _neg0(x):
547
+ '''(INTERNAL) Return C{NEG0 if x < 0 else _0_0},
548
+ unlike C{_copysign_0_0} which returns C{_N_0_0}.
549
+ '''
550
+ return _MODS.constants.NEG0 if x < 0 else _0_0
551
+
552
+
553
+ def _req_d_by(where, name=NN): # in .basics
541
554
  '''(INTERNAL) Get the fully qualified name.
542
555
  '''
543
556
  m = _MODS.named.modulename(where, prefixed=True)
544
557
  if name:
545
- n = _xkwds_get(name, name=NN)
546
- if n:
547
- m = _DOT_(m, n)
558
+ m = _DOT_(m, name)
548
559
  return _SPACE_(_required_, _by_, m)
549
560
 
550
561
 
pygeodesy/constants.py CHANGED
@@ -23,7 +23,7 @@ except ImportError: # Python 2-
23
23
  _inf, _nan = float(_INF_), float(_NAN_)
24
24
 
25
25
  __all__ = _ALL_LAZY.constants
26
- __version__ = '24.03.15'
26
+ __version__ = '24.04.04'
27
27
 
28
28
 
29
29
  def _copysign_0_0(y):
@@ -137,7 +137,7 @@ def _over(p, q):
137
137
  '''(INTERNAL) Return C{B{p} / B{q}} avoiding C{ZeroDivisionError} exceptions.
138
138
  '''
139
139
  try:
140
- return (p / q)
140
+ return (p / q) # if p else _copysign_0_0(q)
141
141
  except ZeroDivisionError:
142
142
  return (_copysignINF(p) if isfinite(p) else NAN) if p else p
143
143
 
pygeodesy/fmath.py CHANGED
@@ -8,9 +8,9 @@ from __future__ import division as _; del _ # PYCHOK semicolon
8
8
 
9
9
  from pygeodesy.basics import _copysign, copysign0, isint, len2
10
10
  from pygeodesy.constants import EPS0, EPS02, EPS1, NAN, PI, PI_2, PI_4, \
11
- _0_0, _0_125, _0_25, _0_5, _1_0, _N_1_0, \
12
- _1_3rd, _1_5, _1_6th, _2_0, _2_3rd, _3_0, \
13
- _isfinite, isnear1, _over, remainder
11
+ _0_0, _0_125, _0_25, _0_5, _1_0, _1_3rd, \
12
+ _1_5, _1_6th, _2_0, _2_3rd, _3_0, \
13
+ _copysign_0_0, _isfinite, _over, remainder
14
14
  from pygeodesy.errors import _IsnotError, LenError, _TypeError, _ValueError, \
15
15
  _xError, _xkwds_get, _xkwds_pop2
16
16
  from pygeodesy.fsums import _2float, Fsum, _fsum, fsum, fsum1_, _pow_op_, \
@@ -25,7 +25,7 @@ from math import fabs, sqrt # pow
25
25
  import operator as _operator # in .datums, .trf, .utm
26
26
 
27
27
  __all__ = _ALL_LAZY.fmath
28
- __version__ = '24.03.31'
28
+ __version__ = '24.04.04'
29
29
 
30
30
  # sqrt(2) <https://WikiPedia.org/wiki/Square_root_of_2>
31
31
  _0_4142 = 0.41421356237309504880 # ... sqrt(2) - 1
@@ -116,8 +116,8 @@ class Fhypot(Fsum):
116
116
  self._fpow(r, _pow_op_)
117
117
  else:
118
118
  self._fset(_0_0)
119
- except Exception as X:
120
- raise self._ErrorX(X, xs, power=p)
119
+ except Exception as x:
120
+ raise self._ErrorXs(x, xs, power=p)
121
121
 
122
122
 
123
123
  class Fpolynomial(Fsum):
@@ -169,7 +169,7 @@ class Fpowers(Fsum):
169
169
  else:
170
170
  self._fset(_0_0)
171
171
  except Exception as x:
172
- raise self._ErrorX(x, xs, power=power)
172
+ raise self._ErrorXs(x, xs, power=power)
173
173
 
174
174
 
175
175
  class Fn_rt(Fsum):
@@ -193,7 +193,7 @@ class Fn_rt(Fsum):
193
193
  else:
194
194
  self._fset(_0_0)
195
195
  except Exception as x:
196
- raise self._ErrorX(x, xs, root=root)
196
+ raise self._ErrorXs(x, xs, root=root)
197
197
 
198
198
 
199
199
  class Fcbrt(Fn_rt):
@@ -359,7 +359,7 @@ def fatan1(x):
359
359
  # Eq (9): PI_4 * x - x * (abs(x) - 1) * (0.2447 + 0.0663 * abs(x)), for -1 < x < 1
360
360
  # PI_4 * x - (x**2 - x) * (0.2447 + 0.0663 * x), for 0 < x - 1
361
361
  # x * (1.0300981633974482 + x * (-0.1784 - x * 0.0663))
362
- H = Fhorner(x, _0_0, 1.0300982, -0.1784, -0.0663)
362
+ H = Fhorner(x, _0_0, 1.0300981634, -0.1784, -0.0663)
363
363
  return float(H)
364
364
 
365
365
 
@@ -716,7 +716,7 @@ if _sys_version_info2 < (3, 8): # PYCHOK no cover
716
716
  computed as M{hypot_(*((c1 - c2) for c1, c2 in zip(p1, p2)))},
717
717
  provided I{p1} and I{p2} have the same, non-zero length I{n}.
718
718
  '''
719
- h, x2 = _h_x2(xs)
719
+ h, x2 = _h_x2(xs, hypot_)
720
720
  return (h * sqrt(x2)) if x2 else _0_0
721
721
 
722
722
  elif _sys_version_info2 < (3, 10):
@@ -747,22 +747,23 @@ else:
747
747
  hypot_ = hypot
748
748
 
749
749
 
750
- def _h_x2(xs):
750
+ def _h_x2(xs, which):
751
751
  '''(INTERNAL) Helper for L{hypot_} and L{hypot2_}.
752
752
  '''
753
- if xs:
754
- n, xs = len2(xs)
755
- if n > 0:
756
- h = float(max(map(fabs, xs)))
757
- if h < EPS0:
758
- x2 = _0_0
759
- elif h in (_1_0, _N_1_0):
760
- x2 = _fsum(_1primed(x**2 for x in xs))
761
- else: # math.fsum
762
- x2 = _fsum(_1primed((x / h)**2 for x in xs))
763
- return h, x2
753
+ n, xs = len2(xs)
754
+ if n > 0:
755
+ h = float(max(map(fabs, xs)))
756
+ if h < EPS0:
757
+ x2 = _0_0
758
+ elif n > 1:
759
+ _h = (_1_0 / h) if h != _1_0 else _1_0
760
+ x2 = _fsum(_1primed((x * _h)**2 for x in xs))
761
+ else:
762
+ x2 = _1_0
763
+ return h, x2
764
764
 
765
- raise _ValueError(xs=xs, txt=_too_(_few_))
765
+ t = Fmt.PAREN(which.__name__, xs)
766
+ raise _ValueError(t, txt=_too_(_few_))
766
767
 
767
768
 
768
769
  def hypot1(x):
@@ -783,15 +784,12 @@ def hypot2(x, y):
783
784
 
784
785
  @return: C{B{x}**2 + B{y}**2} (C{float}).
785
786
  '''
787
+ if fabs(x) < fabs(y):
788
+ x, y = y, x
786
789
  if x:
790
+ h2 = x**2
787
791
  if y:
788
- if fabs(x) < fabs(y):
789
- x, y = y, x
790
- h2 = x**2 * ((y / x)**2 + _1_0)
791
- else:
792
- h2 = x**2
793
- elif y:
794
- h2 = y**2
792
+ h2 *= (y / x)**2 + _1_0
795
793
  else:
796
794
  h2 = _0_0
797
795
  return h2
@@ -810,7 +808,7 @@ def hypot2_(*xs):
810
808
 
811
809
  @see: Function L{hypot_}.
812
810
  '''
813
- h, x2 = _h_x2(xs)
811
+ h, x2 = _h_x2(xs, hypot2_)
814
812
  return (h**2 * x2) if x2 else _0_0
815
813
 
816
814
 
@@ -840,14 +838,15 @@ def norm2(x, y):
840
838
  @raise ValueError: Invalid B{C{x}} or B{C{y}}
841
839
  or zero norm.
842
840
  '''
843
- h = hypot(x, y)
844
- if not h:
845
- x = y = _0_0 # pass?
846
- elif not isnear1(h):
847
- try:
848
- x, y = x / h, y / h
849
- except Exception as e:
850
- raise _xError(e, x=x, y=y, h=h)
841
+ try:
842
+ h = hypot(x, y)
843
+ if h:
844
+ x, y = (x / h), (y / h)
845
+ else:
846
+ x = _copysign_0_0(x) # pass?
847
+ y = _copysign_0_0(y)
848
+ except Exception as e:
849
+ raise _xError(e, x=x, y=y, h=h)
851
850
  return x, y
852
851
 
853
852
 
@@ -861,16 +860,13 @@ def norm_(*xs):
861
860
  @raise ValueError: Invalid or insufficent B{C{xs}}
862
861
  or zero norm.
863
862
  '''
864
- h = hypot_(*xs)
865
- if h:
866
- try:
867
- for i, x in enumerate(xs):
868
- yield x / h
869
- except Exception as e:
870
- raise _xError(e, Fmt.SQUARE(xs=i), x, _h_, h)
871
- else:
872
- for _ in xs:
873
- yield _0_0
863
+ try:
864
+ h = hypot_(*xs)
865
+ _h = (_1_0 / h) if h else _0_0
866
+ for i, x in enumerate(xs):
867
+ yield x * _h
868
+ except Exception as e:
869
+ raise _xError(e, Fmt.SQUARE(xs=i), x, _h_, h)
874
870
 
875
871
 
876
872
  def _powers(x, n):
pygeodesy/fsums.py CHANGED
@@ -14,8 +14,8 @@ L{Fsum.__rpow__} return a (very long) C{int} if invoked with optional argument
14
14
  C{mod} set to C{None}. The C{residual} of an C{integer} L{Fsum} may be between
15
15
  C{-1.0} and C{+1.0}, including C{INT0} if considered to be I{exact}.
16
16
 
17
- Set env variable C{PYGEODESY_FSUM_PARTIALS} to an empty string (or anything
18
- other than C{"fsum"}) for backward compatible summation of L{Fsum} partials.
17
+ Set env variable C{PYGEODESY_FSUM_PARTIALS} to string C{"fsum"}) for summation
18
+ of L{Fsum} partials by Python function C{math.fsum}.
19
19
 
20
20
  Set env variable C{PYGEODESY_FSUM_RESIDUAL} to a C{float} string greater
21
21
  than C{"0.0"} as the threshold to throw a L{ResidualError} in division or
@@ -46,7 +46,7 @@ from pygeodesy.props import _allPropertiesOf_n, deprecated_property_RO, \
46
46
  from math import ceil as _ceil, fabs, floor as _floor # PYCHOK used! .ltp
47
47
 
48
48
  __all__ = _ALL_LAZY.fsums
49
- __version__ = '24.04.02'
49
+ __version__ = '24.04.04'
50
50
 
51
51
  _add_op_ = _PLUS_ # in .auxilats.auxAngle
52
52
  _eq_op_ = _EQUAL_ * 2 # _DEQUAL_
@@ -81,8 +81,8 @@ def _2float(index=None, **name_value): # in .fmath, .fstats
81
81
  if _isfinite(v):
82
82
  return v
83
83
  raise ValueError(_not_finite_)
84
- except Exception as e:
85
- raise _xError(e, Fmt.INDEX(n, index), v)
84
+ except Exception as X:
85
+ raise _xError(X, Fmt.INDEX(n, index), v)
86
86
 
87
87
 
88
88
  def _2floats(xs, origin=0, neg=False):
@@ -123,28 +123,28 @@ def _2ps(s, r):
123
123
 
124
124
 
125
125
  def _psum(ps): # PYCHOK used!
126
- '''(INTERNAL) Partials summation updating C{ps}, I{overridden below}.
126
+ '''(INTERNAL) Partials sum, updating C{ps}, I{overridden below}.
127
127
  '''
128
128
  # assert isinstance(ps, list)
129
- i = len(ps) - 1 # len(ps) > 2
130
- if i < 0:
131
- return _0_0
132
- s = ps[i]
129
+ i = len(ps) - 1
130
+ s = _0_0 if i < 0 else ps[i]
133
131
  _2s = _2sum
134
132
  while i > 0:
135
133
  i -= 1
136
134
  s, r = _2s(s, ps[i])
137
135
  if r: # sum(ps) became inexact
138
- ps[i:] = (s, r) if s else (r,)
139
- if i > 0:
140
- p = ps[i-1] # round half-even
141
- if (p > 0 and r > 0) or \
142
- (p < 0 and r < 0): # signs match
143
- r *= 2
144
- t = s + r
145
- if r == (t - s):
146
- s = t
147
- break
136
+ if s:
137
+ ps[i:] = r, s
138
+ if i > 0:
139
+ p = ps[i-1] # round half-even
140
+ if (p > 0 and r > 0) or \
141
+ (p < 0 and r < 0): # signs match
142
+ r *= 2
143
+ t = s + r
144
+ if r == (t - s):
145
+ s = t
146
+ break # return s
147
+ s = r # PYCHOK no cover
148
148
  ps[i:] = s,
149
149
  return s
150
150
 
@@ -168,24 +168,24 @@ def _2scalar(other, _raiser=None):
168
168
 
169
169
 
170
170
  def _strcomplex(s, *args):
171
- '''(INTERNAL) C{Complex} 2- or 3-arg C{pow} error C{str}.
171
+ '''(INTERNAL) C{Complex} 2- or 3-arg C{pow} error as C{str}.
172
172
  '''
173
- c = iscomplex.__name__[2:]
173
+ c = _strcomplex.__name__[4:]
174
174
  n = _DASH_(len(args), _arg_)
175
- t = _SPACE_(c, s, _from_, n, pow.__name__)
176
- return unstr(t, *args)
175
+ t = unstr(pow, *args)
176
+ return _SPACE_(c, s, _from_, n, t)
177
177
 
178
178
 
179
179
  def _stresidual(prefix, residual, **name_values):
180
- '''(INTERNAL) Residual error C{str}.
180
+ '''(INTERNAL) Residual error as C{str}.
181
181
  '''
182
- p = _SPACE_(prefix, Fsum.residual.name)
182
+ p = _stresidual.__name__[3:]
183
183
  t = Fmt.PARENSPACED(p, Fmt(residual))
184
184
  for n, v in itemsorted(name_values):
185
185
  n = n.replace(_UNDER_, _SPACE_)
186
186
  p = Fmt.PARENSPACED(n, Fmt(v))
187
187
  t = _COMMASPACE_(t, p)
188
- return t
188
+ return _SPACE_(prefix, t)
189
189
 
190
190
 
191
191
  def _2sum(a, b): # by .testFmath
@@ -219,8 +219,8 @@ def _2yield(xs, i, _X_ps, _x):
219
219
  raise ValueError(_not_finite_)
220
220
  yield f
221
221
  i += 1
222
- except Exception as e:
223
- raise _xError(e, Fmt.INDEX(xs=i), x)
222
+ except Exception as X:
223
+ raise _xError(X, Fmt.INDEX(xs=i), x)
224
224
 
225
225
 
226
226
  class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
@@ -756,14 +756,14 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
756
756
  def _cmp_0(self, other, op):
757
757
  '''(INTERNAL) Return C{scalar(self - B{other})} for 0-comparison.
758
758
  '''
759
- if isscalar(other):
759
+ if isinstance(other, Fsum):
760
+ s = _fsum(self._ps_1(*other._ps))
761
+ elif isscalar(other):
760
762
  if other:
761
763
  s = _fsum(self._ps_1(other))
762
764
  else:
763
765
  s, r = self._fprs2
764
766
  s = _signOf(s, -r)
765
- elif isinstance(other, Fsum):
766
- s = _fsum(self._ps_1(*other._ps))
767
767
  else:
768
768
  raise self._TypeError(op, other) # txt=_invalid_
769
769
  return s
@@ -816,13 +816,21 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
816
816
  f = self._copy_2(self.divmod)
817
817
  return f._fdivmod2(other, _divmod_op_)
818
818
 
819
- def _Error(self, op, other, Error, **txt):
819
+ def _Error(self, op, other, Error, **txt_cause):
820
820
  '''(INTERNAL) Format an B{C{Error}} for C{{self} B{op} B{other}}.
821
821
  '''
822
- return Error(_SPACE_(self.toRepr(), op, repr(other)), **txt)
822
+ return Error(_SPACE_(self.toRepr(), op, repr(other)), **txt_cause)
823
823
 
824
- def _ErrorX(self, X, xs, **kwds): # in .fmath
825
- '''(INTERNAL) Format a caught exception.
824
+ def _ErrorX(self, X, op, other, *mod):
825
+ '''(INTERNAL) Format the caught exception C{X}.
826
+ '''
827
+ E, t = _xError2(X)
828
+ if mod:
829
+ t = _COMMASPACE_(Fmt.PARENSPACED(mod=mod[0]), t)
830
+ return self._Error(op, other, E, txt=t, cause=X)
831
+
832
+ def _ErrorXs(self, X, xs, **kwds): # in .fmath
833
+ '''(INTERNAL) Format the caught exception C{X}.
826
834
  '''
827
835
  E, t = _xError2(X)
828
836
  n = unstr(self.named3, *xs[:3], _ELLIPSIS=len(xs) > 3, **kwds)
@@ -834,13 +842,14 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
834
842
  n, ps, _2s = 0, self._ps, _2sum
835
843
  for x in xs: # _iter()
836
844
  # assert isscalar(x) and isfinite(x)
837
- i = 0
838
- for p in ps:
839
- x, p = _2s(x, p)
840
- if p:
841
- ps[i] = p
842
- i += 1
843
- ps[i:] = x,
845
+ if x:
846
+ i = 0
847
+ for p in ps:
848
+ x, p = _2s(x, p)
849
+ if p:
850
+ ps[i] = p
851
+ i += 1
852
+ ps[i:] = (x,) if x else ()
844
853
  n += 1
845
854
  # assert self._ps is ps
846
855
  if n:
@@ -858,30 +867,29 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
858
867
  def _facc_power(self, power, xs, which): # in .fmath
859
868
  '''(INTERNAL) Add each C{xs} as C{float(x**power)}.
860
869
  '''
861
- if isinstance(power, Fsum):
862
- if power.is_exact:
863
- return self._facc_power(power._fprs, xs, which)
870
+ p = power
871
+ if isinstance(p, Fsum):
872
+ if p.is_exact:
873
+ return self._facc_power(p._fprs, xs, which)
864
874
  _Pow = Fsum._pow_any
865
- elif isint(power, both=True) and power >= 0:
866
- _Pow = Fsum._pow_int
867
- power = int(power)
875
+ elif isint(p, both=True) and p >= 0:
876
+ _Pow, p = Fsum._pow_int, int(p)
868
877
  else:
869
- _Pow = Fsum._pow_scalar
870
- power = _2float(power=power)
878
+ _Pow, p = Fsum._pow_scalar, _2float(power=p)
871
879
 
872
- if power:
880
+ if p:
873
881
  from math import pow as _pow
874
882
  op = which.__name__
875
883
 
876
884
  def _X(X):
877
- f = _Pow(X, power, power, op)
885
+ f = _Pow(X, p, power, op)
878
886
  try: # isinstance(f, Fsum)
879
887
  return f._ps
880
888
  except AttributeError: # scalar
881
889
  return f,
882
890
 
883
891
  def _x(x):
884
- return _pow(float(x), power)
892
+ return _pow(float(x), p)
885
893
 
886
894
  self._facc(_2yield(xs, 1, _X, _x)) # PYCHOK yield
887
895
  else:
@@ -985,7 +993,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
985
993
  '''
986
994
  if _isfinite(other):
987
995
  return other
988
- raise ValueError(_not_finite_) if not op else \
996
+ raise ValueError(_not_finite_) if op is None else \
989
997
  self._ValueError(op, other, txt=_not_finite_)
990
998
 
991
999
  def fint(self, raiser=True, **name):
@@ -1123,11 +1131,11 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1123
1131
  running sum and residual (L{Fsum2Tuple}).
1124
1132
  '''
1125
1133
  ps = self._ps
1126
- n = len(ps)
1127
- if n > 2: # len(ps) > 2
1134
+ n = len(ps) - 2
1135
+ if n > 0: # len(ps) > 2
1128
1136
  s = _psum(ps)
1129
1137
  r = _fsum(self._ps_1(s)) or INT0
1130
- elif n > 1: # len(ps) == 2
1138
+ elif n == 0: # len(ps) == 2
1131
1139
  ps[:] = _2ps(*_2sum(*ps))
1132
1140
  r, s = (INT0, ps[0]) if len(ps) != 2 else ps
1133
1141
  elif ps: # len(ps) == 1
@@ -1145,6 +1153,20 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1145
1153
  # _ = self._fprs
1146
1154
  # return self
1147
1155
 
1156
+ def fset_(self, *xs):
1157
+ '''Replace this instance' value with C{xs}.
1158
+
1159
+ @arg xs: Optional, new values (C{scalar} or L{Fsum}
1160
+ instances), all positional.
1161
+
1162
+ @return: This instance (C{Fsum}).
1163
+
1164
+ @see: Method L{Fsum.fadd} for further details.
1165
+ '''
1166
+ self._n = 0
1167
+ self._ps[:] = 0,
1168
+ return self.fadd(xs) if xs else self._update()
1169
+
1148
1170
  def _fset(self, other, asis=True, n=0):
1149
1171
  '''(INTERNAL) Overwrite this instance with an other or a C{scalar}.
1150
1172
  '''
@@ -1249,7 +1271,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1249
1271
  f = self._facc(_2floats(xs, origin=1)) if xs else self # PYCHOK yield
1250
1272
  return f._fprs
1251
1273
 
1252
- def fsum2(self, xs=(), **name):
1274
+ def fsum2(self, xs=(), name=NN):
1253
1275
  '''Add more C{scalar} or L{Fsum} instances and return the
1254
1276
  current precision running sum and the C{residual}.
1255
1277
 
@@ -1268,9 +1290,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1268
1290
  f = self._facc(_2floats(xs)) if xs else self # PYCHOK yield
1269
1291
  t = f._fprs2
1270
1292
  if name:
1271
- n = _xkwds_get(name, name=NN)
1272
- if n:
1273
- t = t.dup(name=n)
1293
+ t = t.dup(name=name)
1274
1294
  return t
1275
1295
 
1276
1296
  def fsum2_(self, *xs):
@@ -1306,16 +1326,16 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1306
1326
  '''
1307
1327
  n = _1_0
1308
1328
  if isinstance(other, Fsum):
1309
- if other is self or other._fprs2 == self._fprs2:
1329
+ if other is self or other == self:
1310
1330
  return self._fset(_1_0) # n=len(self)
1311
1331
  d, r = other._fprs2
1312
1332
  if r:
1313
- if not d: # PYCHOK no cover
1314
- d = r
1315
- elif self._raiser(r, d):
1316
- raise self._ResidualError(op, other, r)
1317
- else:
1333
+ if d:
1334
+ if self._raiser(r, d):
1335
+ raise self._ResidualError(op, other, r)
1318
1336
  d, n = other.as_integer_ratio()
1337
+ else: # PYCHOK no cover
1338
+ d = r
1319
1339
  elif isscalar(other):
1320
1340
  d = other
1321
1341
  else: # PYCHOK no cover
@@ -1323,9 +1343,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1323
1343
  try:
1324
1344
  s = 0 if isinf(d) else (
1325
1345
  d if isnan(d) else self._finite(n / d))
1326
- except Exception as x:
1327
- E, t = _xError2(x)
1328
- raise self._Error(op, other, E, txt=t)
1346
+ except Exception as X:
1347
+ raise self._ErrorX(X, op, other)
1329
1348
  f = self._mul_scalar(s, _mul_op_) # handles 0, NAN, etc.
1330
1349
  return self._fset(f, asis=False)
1331
1350
 
@@ -1444,7 +1463,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1444
1463
  def _pow_0_1(self, x, other):
1445
1464
  '''(INTERNAL) Return B{C{self}**1} or C{B{self}**0 == 1.0}.
1446
1465
  '''
1447
- return self if x else (1 if self.is_integer() and isint(other) else _1_0)
1466
+ return self if x else (1 if isint(other) and self.is_integer() else _1_0)
1448
1467
 
1449
1468
  def _pow_2(self, b, x, other, op):
1450
1469
  '''(INTERNAL) 2-arg C{pow(B{b}, scalar B{x})} embellishing errors.
@@ -1455,10 +1474,9 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1455
1474
  if not iscomplex(s):
1456
1475
  return self._finite(s) # 0**INF == 0.0, 1**INF==1.0
1457
1476
  # neg**frac == complex in Python 3+, but ValueError in 2-
1458
- E, t = _ValueError, _strcomplex(s, b, x) # PYCHOK no cover
1459
- except Exception as x:
1460
- E, t = _xError2(x)
1461
- raise self._Error(op, other, E, txt=t)
1477
+ raise ValueError(_strcomplex(s, b, x)) # PYCHOK no cover
1478
+ except Exception as X:
1479
+ raise self._ErrorX(X, op, other)
1462
1480
 
1463
1481
  def _pow_3(self, other, mod, op):
1464
1482
  '''(INTERNAL) 3-arg C{pow(B{self}, B{other}, int B{mod} or C{None})}.
@@ -1466,19 +1484,18 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1466
1484
  b, r = self._fprs2 if mod is None else self._fint2
1467
1485
  if r and self._raiser(r, b):
1468
1486
  t = _non_zero_ if mod is None else _integer_
1469
- E, t = ResidualError, _stresidual(t, r, mod=mod)
1470
- else:
1471
- try: # b, other, mod all C{int}, unless C{mod} is C{None}
1472
- x = _2scalar(other, _raiser=self._raiser)
1473
- s = pow(b, x, mod)
1474
- if not iscomplex(s):
1475
- return self._finite(s)
1476
- # neg**frac == complex in Python 3+, but ValueError in 2-
1477
- E, t = _ValueError, _strcomplex(s, b, x, mod) # PYCHOK no cover
1478
- except Exception as x:
1479
- E, t = _xError2(x)
1480
- t = _COMMASPACE_(Fmt.PARENSPACED(mod=mod), t)
1481
- raise self._Error(op, other, E, txt=t)
1487
+ t = _stresidual(t, r, mod=mod)
1488
+ raise self._Error(op, other, ResidualError, txt=t)
1489
+
1490
+ try: # b, other, mod all C{int}, unless C{mod} is C{None}
1491
+ x = _2scalar(other, _raiser=self._raiser)
1492
+ s = pow(b, x, mod)
1493
+ if not iscomplex(s):
1494
+ return self._finite(s)
1495
+ # neg**frac == complex in Python 3+, but ValueError in 2-
1496
+ raise ValueError(_strcomplex(s, b, x, mod)) # PYCHOK no cover
1497
+ except Exception as X:
1498
+ raise self._ErrorX(X, op, other, mod)
1482
1499
 
1483
1500
  def _pow_any(self, other, unused, op):
1484
1501
  '''Return C{B{self} ** B{other}}.
@@ -1569,14 +1586,15 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1569
1586
  def _ps_1(self, *less):
1570
1587
  '''(INTERNAL) Yield partials, 1-primed and subtract any C{less}.
1571
1588
  '''
1572
- yield _1_0
1589
+ n = len(self._ps) + len(less) - 4
1590
+ if n < 0:
1591
+ yield _1_0
1573
1592
  for p in self._ps:
1574
- if p:
1575
- yield p
1593
+ yield p
1576
1594
  for p in less:
1577
- if p:
1578
- yield -p
1579
- yield _N_1_0
1595
+ yield -p
1596
+ if n < 0:
1597
+ yield _N_1_0
1580
1598
 
1581
1599
  def _ps_mul(self, op, *factors): # see .fmath.Fhorner
1582
1600
  '''(INTERNAL) Yield all C{partials} times each B{C{factor}},
@@ -1636,12 +1654,12 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1636
1654
  C{PYGEODESY_FSUM_RESIDUAL} or if omitted, keep the
1637
1655
  current setting.
1638
1656
 
1639
- @return: The previous C{RESIDUAL} setting (C{float}).
1657
+ @return: The previous C{RESIDUAL} setting (C{float}), default C{0}.
1640
1658
 
1641
1659
  @raise ValueError: Negative B{C{threshold}}.
1642
1660
 
1643
1661
  @note: A L{ResidualError} is thrown if the non-zero I{ratio}
1644
- C{residual} / C{fsum} exceeds the B{C{threshold}}.
1662
+ C{residual / fsum} exceeds the B{C{threshold}}.
1645
1663
  '''
1646
1664
  r = self._RESIDUAL
1647
1665
  if threshold:
@@ -1659,7 +1677,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1659
1677
  '''(INTERNAL) Non-zero B{C{residual}} etc.
1660
1678
  '''
1661
1679
  t = _stresidual(_non_zero_, residual, ratio=self._ratio,
1662
- RESIDUAL=self._RESIDUAL)
1680
+ RESIDUAL=self._RESIDUAL)
1663
1681
  t = t.replace(_COMMASPACE_R_, _exceeds_R_)
1664
1682
  return self._Error(op, other, ResidualError, txt=t)
1665
1683
 
@@ -1721,7 +1739,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1721
1739
  # Fsum._fint2._update(self)
1722
1740
  # Fsum._fprs ._update(self)
1723
1741
  # Fsum._fprs2._update(self)
1724
- return self
1742
+ return self # for .fset_
1725
1743
 
1726
1744
  def _ValueError(self, op, other, **txt): # PYCHOK no cover
1727
1745
  '''(INTERNAL) Return a C{ValueError}.
@@ -1820,7 +1838,7 @@ try:
1820
1838
 
1821
1839
  Fsum._math_fsum = _sum = _fsum # PYCHOK exported
1822
1840
 
1823
- if _getenv('PYGEODESY_FSUM_PARTIALS', _fsum.__name__) == _fsum.__name__:
1841
+ if _getenv('PYGEODESY_FSUM_PARTIALS', NN) == _fsum.__name__:
1824
1842
  _psum = _fsum # PYCHOK re-def
1825
1843
 
1826
1844
  except ImportError:
@@ -1915,6 +1933,35 @@ def fsum1f_(*xs):
1915
1933
  return _fsum(_1primed(xs)) if xs else _0_0
1916
1934
 
1917
1935
 
1936
+ if __name__ == '__main__':
1937
+
1938
+ # usage: [env PYGEODESY_FSUM_PARTIALS=fsum] python3 -m pygeodesy.fsums
1939
+
1940
+ def _test(n):
1941
+ # copied from Hettinger, see L{Fsum} reference
1942
+ from pygeodesy import printf
1943
+ from random import gauss, random, shuffle
1944
+
1945
+ printf(_fsum.__name__, end=_COMMASPACE_)
1946
+ printf(_psum.__name__, end=_COMMASPACE_)
1947
+
1948
+ F = Fsum()
1949
+ if F.is_math_fsum:
1950
+ c = (7, 1e100, -7, -1e100, -9e-20, 8e-20) * 10
1951
+ for _ in range(n):
1952
+ t = list(c)
1953
+ s = 0
1954
+ for _ in range(n * 8):
1955
+ v = gauss(0, random())**7 - s
1956
+ t.append(v)
1957
+ s += v
1958
+ shuffle(t)
1959
+ assert float(F.fset_(*t)) == _fsum(t)
1960
+ printf(_DOT_, end=NN)
1961
+ printf(NN)
1962
+
1963
+ _test(128)
1964
+
1918
1965
  # **) MIT License
1919
1966
  #
1920
1967
  # Copyright (C) 2016-2024 -- mrJean1 at Gmail -- All Rights Reserved.
pygeodesy/resections.py CHANGED
@@ -13,9 +13,10 @@ L{triAngle}, L{triAngle5}, L{triSide}, L{triSide2} and L{triSide4}.
13
13
  from __future__ import division as _; del _ # PYCHOK semicolon
14
14
 
15
15
  from pygeodesy.basics import map1, map2, _zip, _ALL_LAZY
16
- from pygeodesy.constants import EPS, EPS0, EPS02, INT0, NEG0, PI, PI2, PI_2, PI_4, \
17
- _0_0, _0_5, _1_0, _N_1_0, _2_0, _N_2_0, _4_0, _16_0, \
18
- _180_0, _360_0, isnear0, _over, _umod_360
16
+ from pygeodesy.constants import EPS, EPS0, EPS02, INT0, PI, PI2, PI_2, PI_4, \
17
+ _0_0, _0_5, _1_0, _N_1_0, _2_0, _N_2_0, _4_0, \
18
+ _16_0, _180_0, _360_0, _copysign_0_0, isnear0, \
19
+ _over, _umod_360
19
20
  from pygeodesy.errors import _and, _or, TriangleError, _ValueError, _xcallable, \
20
21
  _xkwds, _xkwds_pop2
21
22
  from pygeodesy.fmath import favg, Fdot, fidw, fmean, hypot, hypot2_
@@ -33,7 +34,7 @@ from pygeodesy.vector3d import _otherV3d, Vector3d
33
34
  from math import cos, atan2, degrees, fabs, radians, sin, sqrt
34
35
 
35
36
  __all__ = _ALL_LAZY.resections
36
- __version__ = '24.03.26'
37
+ __version__ = '24.04.04'
37
38
 
38
39
  _concyclic_ = 'concyclic'
39
40
  _PA_ = 'PA'
@@ -457,7 +458,7 @@ def pierlotx(point1, point2, point3, alpha1, alpha2, alpha3, useZ=False,
457
458
 
458
459
  def _cot(s, c): # I{exact} cotangent
459
460
  try:
460
- return (c / s) if c else (NEG0 if s < 0 else _0_0)
461
+ return (c / s) if c else _copysign_0_0(s)
461
462
  except ZeroDivisionError:
462
463
  raise ValueError(_or(_coincident_, _colinear_))
463
464
 
pygeodesy/utily.py CHANGED
@@ -10,12 +10,14 @@ U{Vector-based geodesy<https://www.Movable-Type.co.UK/scripts/latlong-vectors.ht
10
10
  # make sure int/int division yields float quotient, see .basics
11
11
  from __future__ import division as _; del _ # PYCHOK semicolon
12
12
 
13
- from pygeodesy.basics import _copysign, isinstanceof, isint, isstr, neg, _passargs
14
- from pygeodesy.constants import EPS, EPS0, INF, NAN, NEG0, PI, PI2, PI_2, R_M, \
15
- _float as _F, _isfinite, isnan, isnear0, _over, \
16
- _umod_360, _umod_PI2, _M_KM, _M_NM, _M_SM, _0_0, \
17
- _1__90, _0_5, _1_0, _N_1_0, _2__PI, _10_0, _90_0, \
18
- _N_90_0, _180_0, _N_180_0, _360_0, _400_0
13
+ from pygeodesy.basics import _copysign, isinstanceof, isint, isstr, neg, \
14
+ _passargs
15
+ from pygeodesy.constants import EPS, EPS0, INF, NAN, PI, PI2, PI_2, R_M, \
16
+ _M_KM, _M_NM, _M_SM, _0_0, _1__90, _0_5, _1_0, \
17
+ _N_1_0, _2__PI, _10_0, _90_0, _N_90_0, _180_0, \
18
+ _N_180_0, _360_0, _400_0, _copysign_0_0, \
19
+ _float as _F, _isfinite, isnan, isnear0, \
20
+ _over, _umod_360, _umod_PI2
19
21
  from pygeodesy.errors import _ValueError, _xkwds, _xkwds_get, _ALL_LAZY, _MODS
20
22
  from pygeodesy.interns import _edge_, _radians_, _semi_circular_, _SPACE_
21
23
  # from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS # from .errors
@@ -25,7 +27,7 @@ from pygeodesy.units import Degrees, Degrees_, Feet, Float, Lam, Lam_, \
25
27
  from math import acos, asin, atan2, cos, degrees, fabs, radians, sin, tan # pow
26
28
 
27
29
  __all__ = _ALL_LAZY.utily
28
- __version__ = '24.01.12'
30
+ __version__ = '24.04.04'
29
31
 
30
32
  # read constant name "_M_Unit" as "meter per Unit"
31
33
  _M_CHAIN = _F( 20.1168) # yard2m(1) * 22
@@ -683,7 +685,7 @@ def _sin0cos2(q, r, sign):
683
685
  # else: # r == 0, testUtility failures
684
686
  # t = _0_0, _1_0, _0_0, _N_1_0, _0_0
685
687
  # q &= 3
686
- s = t[q] or (NEG0 if sign < 0 else _0_0)
688
+ s = t[q] or _copysign_0_0(sign)
687
689
  c = t[q + 1] or _0_0
688
690
  return s, c
689
691
 
@@ -691,8 +693,7 @@ def _sin0cos2(q, r, sign):
691
693
  def SinCos2(x):
692
694
  '''Get C{sin} and C{cos} of I{typed} angle.
693
695
 
694
- @arg x: Angle (L{Degrees}, L{Degrees_}, L{Radians}, L{Radians_}
695
- or scalar C{radians}).
696
+ @arg x: Angle (L{Degrees}, L{Radians} or scalar C{radians}).
696
697
 
697
698
  @return: 2-Tuple (C{sin(B{x})}, C{cos(B{x})}).
698
699
  '''