pygeodesy 24.8.24__py2.py3-none-any.whl → 24.9.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 (49) hide show
  1. {PyGeodesy-24.8.24.dist-info → PyGeodesy-24.9.24.dist-info}/METADATA +7 -7
  2. {PyGeodesy-24.8.24.dist-info → PyGeodesy-24.9.24.dist-info}/RECORD +49 -48
  3. pygeodesy/__init__.py +7 -5
  4. pygeodesy/__main__.py +46 -47
  5. pygeodesy/auxilats/_CX_4.py +104 -181
  6. pygeodesy/auxilats/_CX_6.py +152 -277
  7. pygeodesy/auxilats/_CX_8.py +211 -438
  8. pygeodesy/auxilats/_CX_Rs.py +222 -0
  9. pygeodesy/auxilats/__init__.py +2 -2
  10. pygeodesy/auxilats/__main__.py +30 -38
  11. pygeodesy/auxilats/auxLat.py +28 -36
  12. pygeodesy/auxilats/auxily.py +30 -50
  13. pygeodesy/basics.py +24 -14
  14. pygeodesy/booleans.py +13 -14
  15. pygeodesy/clipy.py +7 -7
  16. pygeodesy/constants.py +44 -31
  17. pygeodesy/deprecated/__init__.py +1 -1
  18. pygeodesy/deprecated/functions.py +9 -1
  19. pygeodesy/elliptic.py +154 -88
  20. pygeodesy/errors.py +32 -5
  21. pygeodesy/etm.py +71 -59
  22. pygeodesy/fmath.py +125 -96
  23. pygeodesy/fstats.py +8 -12
  24. pygeodesy/fsums.py +802 -355
  25. pygeodesy/geodesici.py +6 -5
  26. pygeodesy/geodesicx/_C4_24.py +1 -3
  27. pygeodesy/geodesicx/_C4_27.py +1 -3
  28. pygeodesy/geodesicx/_C4_30.py +1 -3
  29. pygeodesy/geodesicx/__init__.py +1 -1
  30. pygeodesy/geodesicx/__main__.py +44 -46
  31. pygeodesy/geodesicx/gxarea.py +3 -3
  32. pygeodesy/geodesicx/gxbases.py +32 -18
  33. pygeodesy/internals.py +50 -9
  34. pygeodesy/interns.py +3 -2
  35. pygeodesy/karney.py +79 -60
  36. pygeodesy/ktm.py +4 -4
  37. pygeodesy/lazily.py +10 -5
  38. pygeodesy/mgrs.py +47 -42
  39. pygeodesy/named.py +4 -1
  40. pygeodesy/points.py +3 -3
  41. pygeodesy/props.py +7 -6
  42. pygeodesy/resections.py +2 -2
  43. pygeodesy/rhumb/__init__.py +1 -1
  44. pygeodesy/rhumb/aux_.py +42 -60
  45. pygeodesy/sphericalNvector.py +4 -4
  46. pygeodesy/sphericalTrigonometry.py +2 -2
  47. pygeodesy/triaxials.py +3 -3
  48. {PyGeodesy-24.8.24.dist-info → PyGeodesy-24.9.24.dist-info}/WHEEL +0 -0
  49. {PyGeodesy-24.8.24.dist-info → PyGeodesy-24.9.24.dist-info}/top_level.txt +0 -0
pygeodesy/fsums.py CHANGED
@@ -1,108 +1,114 @@
1
1
 
2
2
  # -*- coding: utf-8 -*-
3
3
 
4
- u'''Class L{Fsum} for precision floating point summation and I{running}
5
- summation based on, respectively similar to Python's C{math.fsum}.
6
-
7
- Generally, an L{Fsum} instance is considered a C{float} plus a small or zero
8
- C{residual} value, see property L{Fsum.residual}. However, there are several
9
- C{integer} L{Fsum} cases, for example the result of C{ceil}, C{floor},
10
- C{Fsum.__floordiv__} and methods L{Fsum.fint} and L{Fsum.fint2}.
11
-
12
- Also, L{Fsum} methods L{Fsum.pow}, L{Fsum.__ipow__}, L{Fsum.__pow__} and
13
- L{Fsum.__rpow__} return a (very long) C{int} if invoked with optional argument
14
- C{mod} set to C{None}. The C{residual} of an C{integer} L{Fsum} may be between
15
- C{-1.0} and C{+1.0}, including C{INT0} if considered to be I{exact}.
16
-
17
- Set env variable C{PYGEODESY_FSUM_RESIDUAL} to a C{float} string greater than
18
- C{"0.0"} as the threshold to throw a L{ResidualError} for a division, power or
19
- root operation of an L{Fsum} instance with a C{residual} I{ratio} exceeding
4
+ u'''Class L{Fsum} for precision floating point summation similar to
5
+ Python's C{math.fsum} enhanced with I{running} summation and as an
6
+ option, accurate I{TwoProduct} multiplication.
7
+
8
+ Accurate multiplication is based on the C{math.fma} function for
9
+ Python 3.13 and newer or one of two equivalent C{fma} implementations
10
+ for Python 3.12 and older. To enable accurate multiplication, set
11
+ env variable C{PYGEODESY_FSUM_F2PRODUCT} to C{"std"} or any non-empty
12
+ string or invoke function C{pygeodesy.f2product(True)} or set. With
13
+ C{"std"} the C{fma} implemention follows the C{math.fma} function,
14
+ otherwise the C{PyGeodesy 24.09.09} release.
15
+
16
+ Generally, an L{Fsum} instance is considered a C{float} plus a small or
17
+ zero C{residue} aka C{residual} value, see property L{Fsum.residual}.
18
+
19
+ Set env variable C{PYGEODESY_FSUM_RESIDUAL} to a C{float} string greater
20
+ than C{"0.0"} as the threshold to throw a L{ResidualError} for a division,
21
+ power or root operation of an L{Fsum} with a C{residual} I{ratio} exceeding
20
22
  the threshold. See methods L{Fsum.RESIDUAL}, L{Fsum.pow}, L{Fsum.__ipow__}
21
23
  and L{Fsum.__itruediv__}.
24
+
25
+ There are several C{integer} L{Fsum} cases, for example the result from
26
+ functions C{ceil}, C{floor}, C{Fsum.__floordiv__} and methods L{Fsum.fint},
27
+ L{Fsum.fint2} and L{Fsum.is_integer}. Also, L{Fsum} methods L{Fsum.pow},
28
+ L{Fsum.__ipow__}, L{Fsum.__pow__} and L{Fsum.__rpow__} return a (very long)
29
+ C{int} if invoked with optional argument C{mod} set to C{None}. The
30
+ C{residual} of an C{integer} L{Fsum} is between C{-1.0} and C{+1.0} and
31
+ will be C{INT0} if that is considered to be I{exact}.
32
+
33
+ Set env variable C{PYGEODESY_FSUM_NONFINITES} to C{"std"} or use function
34
+ C{pygeodesy.nonfiniterrors(False)} to allow I{non-finite} C{float}s like
35
+ C{inf}, C{INF}, C{NINF}, C{nan} and C{NAN} and to ignore C{OverflowError}
36
+ respectively C{ValueError} exceptions. However, in that case I{non-finite}
37
+ results may differ from Python's C{math.fsum} results.
22
38
  '''
23
39
  # make sure int/int division yields float quotient, see .basics
24
40
  from __future__ import division as _; del _ # PYCHOK semicolon
25
41
 
26
42
  from pygeodesy.basics import isbool, iscomplex, isint, isscalar, \
27
43
  _signOf, itemsorted, signOf, _xiterable, \
28
- _xiterablen, _enquote
29
- from pygeodesy.constants import INT0, _isfinite, NEG0, _pos_self, \
30
- _0_0, _1_0, _N_1_0, Float, Int
31
- from pygeodesy.errors import _OverflowError, _TypeError, _UnexpectedError, \
32
- _ValueError, _xError, _xError2, _xkwds_get1, \
33
- _xkwds_pop2
34
- # from pygeodesy.internals import _enquote # from .basics
35
- from pygeodesy.interns import NN, _arg_, _COMMASPACE_, _DASH_, _DOT_, \
36
- _EQUAL_, _from_, _LANGLE_, _NOTEQUAL_, \
37
- _not_finite_, _PERCENT_, _PLUS_, \
38
- _RANGLE_, _SLASH_, _SPACE_, _STAR_, _UNDER_
44
+ _xiterablen
45
+ from pygeodesy.constants import INF, INT0, MANT_DIG, NEG0, NINF, _0_0, \
46
+ _1_0, _N_1_0, _isfinite, _pos_self, \
47
+ Float, Int
48
+ from pygeodesy.errors import _AssertionError, _OverflowError, _TypeError, \
49
+ _ValueError, _xError, _xError2, _xkwds_get, \
50
+ _xkwds, _xkwds_get1, _xkwds_not, _xkwds_pop
51
+ from pygeodesy.internals import _enquote, _passarg
52
+ from pygeodesy.interns import NN, _arg_, _COMMASPACE_, _DOT_, _from_, \
53
+ _not_finite_, _SPACE_, _std_, _UNDER_
39
54
  from pygeodesy.lazily import _ALL_LAZY, _getenv, _sys_version_info2
40
55
  from pygeodesy.named import _name__, _name2__, _Named, _NamedTuple, \
41
56
  _NotImplemented
42
- from pygeodesy.props import _allPropertiesOf_n, deprecated_property_RO, \
43
- Property, Property_RO, property_RO
57
+ from pygeodesy.props import _allPropertiesOf_n, deprecated_method, \
58
+ deprecated_property_RO, Property, \
59
+ Property_RO, property_RO
44
60
  from pygeodesy.streprs import Fmt, fstr, unstr
45
61
  # from pygeodesy.units import Float, Int # from .constants
46
62
 
47
- from math import ceil as _ceil, fabs, floor as _floor # PYCHOK used! .ltp
63
+ from math import fabs, isinf, isnan, \
64
+ ceil as _ceil, floor as _floor # PYCHOK used! .ltp
48
65
 
49
66
  __all__ = _ALL_LAZY.fsums
50
- __version__ = '24.08.13'
51
-
52
- _add_op_ = _PLUS_ # in .auxilats.auxAngle
53
- _eq_op_ = _EQUAL_ * 2 # _DEQUAL_
54
- _div_ = 'div'
55
- _floordiv_op_ = _SLASH_ * 2 # _DSLASH_
56
- _fset_op_ = _EQUAL_
57
- _ge_op_ = _RANGLE_ + _EQUAL_
58
- _gt_op_ = _RANGLE_
59
- _iadd_op_ = _add_op_ + _EQUAL_ # in .auxilats.auxAngle, .fstats
67
+ __version__ = '24.09.25'
68
+
69
+ from pygeodesy.interns import (
70
+ _PLUS_ as _add_op_, # in .auxilats.auxAngle
71
+ _EQUAL_ as _fset_op_,
72
+ _RANGLE_ as _gt_op_,
73
+ _LANGLE_ as _lt_op_,
74
+ _PERCENT_ as _mod_op_,
75
+ _STAR_ as _mul_op_,
76
+ _NOTEQUAL_ as _ne_op_,
77
+ _DASH_ as _sub_op_, # in .auxilats.auxAngle
78
+ _SLASH_ as _truediv_op_
79
+ )
80
+ _eq_op_ = _fset_op_ * 2 # _DEQUAL_
81
+ _floordiv_op_ = _truediv_op_ * 2 # _DSLASH_
82
+ _divmod_op_ = _floordiv_op_ + _mod_op_
83
+ _F2PRODUCT = _getenv('PYGEODESY_FSUM_F2PRODUCT', NN)
84
+ _ge_op_ = _gt_op_ + _fset_op_
85
+ _iadd_op_ = _add_op_ + _fset_op_ # in .auxilats.auxAngle, .fstats
60
86
  _integer_ = 'integer'
61
- _le_op_ = _LANGLE_ + _EQUAL_
62
- _lt_op_ = _LANGLE_
63
- _mod_ = 'mod'
64
- _mod_op_ = _PERCENT_
65
- _mul_op_ = _STAR_
66
- _ne_op_ = _NOTEQUAL_
87
+ _isub_op_ = _sub_op_ + _fset_op_ # in .auxilats.auxAngle
88
+ _le_op_ = _lt_op_ + _fset_op_
89
+ _NONFINITES = _getenv('PYGEODESY_FSUM_NONFINITES', NN) == _std_
67
90
  _non_zero_ = 'non-zero'
68
- _pow_op_ = _STAR_ * 2 # _DSTAR_
91
+ _pow_op_ = _mul_op_ * 2 # _DSTAR_
92
+ _RESIDUAL_0_0 = _getenv('PYGEODESY_FSUM_RESIDUAL', _0_0)
69
93
  _significant_ = 'significant'
70
- _sub_op_ = _DASH_ # in .auxilats.auxAngle
94
+ _2split3s = _passarg
71
95
  _threshold_ = 'threshold'
72
- _truediv_op_ = _SLASH_
73
- _divmod_op_ = _floordiv_op_ + _mod_op_
74
- _isub_op_ = _sub_op_ + _fset_op_ # in .auxilats.auxAngle
75
96
 
76
97
 
77
- def _2delta(*ab):
78
- '''(INTERNAL) Helper for C{Fsum._fsum2}.
79
- '''
80
- try:
81
- a, b = _2sum(*ab)
82
- except _OverflowError:
83
- a, b = ab
84
- return float(a if fabs(a) > fabs(b) else b)
85
-
86
-
87
- def _2error(unused): # in .fstats
88
- '''(INTERNAL) Throw a C{not-finite} exception.
89
- '''
90
- raise ValueError(_not_finite_)
91
-
92
-
93
- def _2finite(x):
98
+ def _2finite(x): # in .fstats
94
99
  '''(INTERNAL) return C{float(x)} if finite.
95
100
  '''
96
- x = float(x)
97
- return x if _isfinite(x) else _2error(x)
101
+ return (float(x) if _isfinite(x) # and isscalar(x)
102
+ else _nfError(x))
98
103
 
99
104
 
100
- def _2float(index=None, **name_value): # in .fmath, .fstats
105
+ def _2float(index=None, _isfine=_isfinite, **name_value): # in .fmath, .fstats
101
106
  '''(INTERNAL) Raise C{TypeError} or C{ValueError} if not scalar or infinite.
102
107
  '''
103
108
  n, v = name_value.popitem() # _xkwds_item2(name_value)
104
109
  try:
105
- return _2finite(v)
110
+ f = float(v)
111
+ return f if _isfine(f) else _nfError(f)
106
112
  except Exception as X:
107
113
  raise _xError(X, Fmt.INDEX(n, index), v)
108
114
 
@@ -111,34 +117,141 @@ def _X_ps(X): # for _2floats only
111
117
  return X._ps
112
118
 
113
119
 
114
- def _2floats(xs, origin=0, _X=_X_ps, _x=float):
120
+ def _2floats(xs, origin=0, _X=_X_ps, _x=float, _isfine=_isfinite):
115
121
  '''(INTERNAL) Yield each B{C{xs}} as a C{float}.
116
122
  '''
117
123
  try:
118
- i, x = origin, _X
119
- _fin = _isfinite
124
+ i, x = origin, xs
120
125
  _FsT = _Fsum_Fsum2Tuple_types
121
- _isa = isinstance
122
126
  for x in _xiterable(xs):
123
- if _isa(x, _FsT):
127
+ if isinstance(x, _FsT):
124
128
  for p in _X(x._Fsum):
125
129
  yield p
126
130
  else:
127
131
  f = _x(x)
128
- yield f if _fin(f) else _2error(f)
132
+ yield f if _isfine(f) else _nfError(f)
129
133
  i += 1
130
134
  except Exception as X:
131
- raise _xError(X, xs=xs) if x is _X else \
132
- _xError(X, Fmt.INDEX(xs=i), x)
135
+ raise _xsError(X, xs, i, x)
136
+
137
+
138
+ try: # MCCABE 17
139
+ from math import fma as _fma
140
+ except ImportError: # Python 3.12-
141
+
142
+ if _F2PRODUCT == _std_:
143
+ _2FACTOR = pow(2, (MANT_DIG + 1) // 2) + 1
133
144
 
145
+ def _fma(a, b, c):
146
+ # mimick C{math.fma} from Python 3.13+,
147
+ # the same accuracy, but ~13x slower
148
+ b3s = _2split3(b),
149
+ r = fsumf_(c, *_2products(a, b3s)) # two=True
150
+ return r if _isfinite(r) else _fmaX(r, a, b, c)
134
151
 
135
- def _Fsumf_(*xs): # floats=True, in .auxLat, ...
152
+ def _2split3(x):
153
+ # Split U{Algorithm 3.2
154
+ # <https://www.TUHH.De/ti3/paper/rump/OgRuOi05.pdf>}
155
+ a = c = x * _2FACTOR
156
+ a -= c - x
157
+ b = x - a
158
+ return x, a, b
159
+
160
+ def _2split3s(xs): # overwrites
161
+ return tuple(map(_2split3, xs))
162
+
163
+ else:
164
+ def _fma(*a_b_c): # PYCHOK no cover
165
+ # mimick C{math.fma} from Python 3.13+,
166
+ # the same accuracy, but ~14x slower
167
+ (na, da), (nb, db), (nc, dc) = map(_2n_d, a_b_c)
168
+ n = na * nb * dc
169
+ n += da * db * nc
170
+ d = da * db * dc
171
+ try:
172
+ r = float(n / d)
173
+ except OverflowError: # "integer division result too large ..."
174
+ r = NINF if (_signOf(n, 0) * _signOf(d, 0)) < 0 else INF
175
+ return r if _isfinite(r) else _fmaX(r, *a_b_c) # "overflow in fma"
176
+
177
+ def _2n_d(x):
178
+ try: # int.as_integer_ratio in 3.8+
179
+ return x.as_integer_ratio()
180
+ except (AttributeError, OverflowError, TypeError, ValueError):
181
+ return (x if isint(x) else float(x)), 1
182
+
183
+ def _fmaX(r, *a_b_c): # like Python 3.13+ I{Modules/mathmodule.c}:
184
+ # raise a ValueError for a NAN result from non-NAN C{a_b_c}s or
185
+ # OverflowError for a non-NAN result from all finite C{a_b_c}s.
186
+ if isnan(r):
187
+ def _is(x):
188
+ return not isnan(x)
189
+ else:
190
+ _is = _isfinite
191
+ if all(map(_is, a_b_c)):
192
+ raise _nfError(r, unstr(_fma, *a_b_c))
193
+ return r
194
+
195
+ if _2split3s is _passarg: # math._fma or _fma(*a_b_c)
196
+
197
+ def _2products(x, ys, **unused):
198
+ # TwoProductFMA U{Algorithm 3.5
199
+ # <https://www.TUHH.De/ti3/paper/rump/OgRuOi05.pdf>}
200
+ for y in ys:
201
+ f = x * y
202
+ yield f
203
+ yield _fma(x, y, -f)
204
+
205
+ else: # in _std_ _fma(a, b, c)
206
+
207
+ def _2products(x, y3s, two=False): # PYCHOK redef
208
+ # TwoProduct U{Algorithm 3.3
209
+ # <https://www.TUHH.De/ti3/paper/rump/OgRuOi05.pdf>}
210
+ # also in Python 3.13+ C{Modules/marhmodule.c} under
211
+ # #ifndef UNRELIABLE_FMA ... #else ... #endif
212
+ _, a, b = _2split3(x)
213
+ for y, c, d in y3s:
214
+ y *= x
215
+ yield y
216
+ if two: # or not a:
217
+ yield b * d - (((y - a * c) - b * c) - a * d)
218
+ # = b * d + (a * d - ((y - a * c) - b * c))
219
+ # = b * d + (a * d + (b * c - (y - a * c)))
220
+ # = b * d + (a * d + (b * c + (a * c - y)))
221
+ else:
222
+ yield a * c - y
223
+ yield b * c
224
+ if d:
225
+ yield a * d
226
+ yield b * d
227
+
228
+
229
+ def f2product(*two):
230
+ '''Turn accurate I{TwoProduct} multiplication on or off.
231
+
232
+ @arg two: If C{True}, turn I{TwoProduct} on, if C{False} off or
233
+ if C{None} or omitted, keep the current setting.
234
+
235
+ @return: The previous setting (C{bool}).
236
+
237
+ @see: I{TwoProduct} multiplication is based on the I{TwoProductFMA}
238
+ U{Algorithm 3.5 <https://www.TUHH.De/ti3/paper/rump/OgRuOi05.pdf>}
239
+ using function C{math.fma} from Python 3.13 and later or an
240
+ equivalent, slower implementation when not available.
241
+ '''
242
+ t = Fsum._f2product
243
+ if two and two[0] is not None:
244
+ Fsum._f2product = bool(two[0])
245
+ return t
246
+
247
+
248
+ def _Fsumf_(*xs): # in .auxLat, .ltp, ...
136
249
  '''(INTERNAL) An C{Fsum} of I{known scalars}.
137
250
  '''
138
251
  return Fsum()._facc_scalar(xs, up=False)
139
252
 
140
253
 
141
- def _Fsum1f_(*xs): # floats=True, in .albers, ...
254
+ def _Fsum1f_(*xs): # in .albers
142
255
  '''(INTERNAL) An C{Fsum} of I{known scalars}, 1-primed.
143
256
  '''
144
257
  return Fsum()._facc_scalar(_1primed(xs), up=False)
@@ -162,16 +275,60 @@ def _isFsum(x): # in .fmath
162
275
  return isinstance(x, Fsum)
163
276
 
164
277
 
165
- def _isFsumTuple(x): # in .fmath
278
+ def _isFsumTuple(x): # in .basics, .constants, .fmath, .fstats
166
279
  '''(INTERNAL) Is C{x} an C{Fsum} or C{Fsum2Tuple} instance?
167
280
  '''
168
281
  return isinstance(x, _Fsum_Fsum2Tuple_types)
169
282
 
170
283
 
171
- def _1_Over(x, op, **raiser_RESIDUAL): # vs _1_over
172
- '''(INTERNAL) Return C{Fsum(1) / B{x}}.
284
+ def _isOK(unused):
285
+ '''(INTERNAL) Helper for C{nonfiniterrors} and C{Fsum.nonfinites}.
286
+ '''
287
+ return True
288
+
289
+
290
+ def _isOK_or_finite(x, _isfine=_isfinite):
291
+ '''(INTERNAL) Is C{x} finite or is I{non-finite} OK?.
292
+ '''
293
+ # assert _isfine in (_isOK, _isfinite)
294
+ return _isfine(x)
295
+
296
+
297
+ def _nfError(x, *args):
298
+ '''(INTERNAL) Throw a C{not-finite} exception.
299
+ '''
300
+ E = _NonfiniteError(x)
301
+ t = Fmt.PARENSPACED(_not_finite_, x)
302
+ if args: # in _fma, _2sum
303
+ return E(txt=t, *args)
304
+ raise E(t, txt=None)
305
+
306
+
307
+ def nonfiniterrors(*raiser):
308
+ '''Throw C{OverflowError} and C{ValueError} exceptions for or
309
+ handle I{non-finite} C{float}s as C{inf}, C{INF}, C{NINF},
310
+ C{nan} and C{NAN} in summations and multiplications.
311
+
312
+ @arg raiser: If C{True}, throw exceptions, if C{False} handle
313
+ I{non-finites} or if C{None} or omitted, leave
314
+ the setting unchanged.
315
+
316
+ @return: Previous setting (C{bool}).
317
+
318
+ @note: C{inf}, C{INF} and C{NINF} throw an C{OverflowError},
319
+ C{nan} and C{NAN} a C{ValueError}.
320
+ '''
321
+ d = Fsum._isfine
322
+ if raiser and raiser[0] is not None:
323
+ Fsum._isfine = {} if bool(raiser[0]) else Fsum._nonfinites_isfine_kwds[True]
324
+ return _xkwds_get1(d, _isfine=_isfinite) is _isfinite
325
+
326
+
327
+ def _NonfiniteError(x):
328
+ '''(INTERNAL) Return the Error class for C{x}, I{non-finite}.
173
329
  '''
174
- return _Psum_(_1_0)._ftruediv(x, op, **raiser_RESIDUAL)
330
+ return _OverflowError if isinf(x) else (
331
+ _ValueError if isnan(x) else _AssertionError)
175
332
 
176
333
 
177
334
  def _1primed(xs): # in .fmath
@@ -184,16 +341,15 @@ def _1primed(xs): # in .fmath
184
341
  yield _N_1_0
185
342
 
186
343
 
187
- def _psum(ps): # PYCHOK used!
344
+ def _psum(ps, **_isfine): # PYCHOK used!
188
345
  '''(INTERNAL) Partials summation, updating C{ps}.
189
346
  '''
190
347
  # assert isinstance(ps, list)
191
- i = len(ps) - 1
192
- s = _0_0 if i < 0 else ps[i]
193
- _2s = _2sum
348
+ i = len(ps) - 1
349
+ s = _0_0 if i < 0 else ps[i]
194
350
  while i > 0:
195
351
  i -= 1
196
- s, r = _2s(s, ps[i])
352
+ s, r = _2sum(s, ps[i], **_isfine)
197
353
  if r: # sum(ps) became inexact
198
354
  if s:
199
355
  ps[i:] = r, s
@@ -201,24 +357,28 @@ def _psum(ps): # PYCHOK used!
201
357
  s = _2halfeven(s, r, ps[i-1])
202
358
  break # return s
203
359
  s = r # PYCHOK no cover
360
+ elif not _isfinite(s): # non-finite OK
361
+ i = 0 # collapse ps
362
+ if ps:
363
+ s += _sum(ps) # _fsum(ps)
204
364
  ps[i:] = s,
205
365
  return s
206
366
 
207
367
 
208
- def _Psum(ps, **name_RESIDUAL):
368
+ def _Psum(ps, **name_f2product_nonfinites_RESIDUAL):
209
369
  '''(INTERNAL) Return an C{Fsum} from I{ordered} partials C{ps}.
210
370
  '''
211
- f = Fsum(**name_RESIDUAL) if name_RESIDUAL else Fsum()
371
+ F = Fsum(**name_f2product_nonfinites_RESIDUAL)
212
372
  if ps:
213
- f._ps[:] = ps
214
- f._n = len(f._ps)
215
- return f
373
+ F._ps[:] = ps
374
+ F._n = len(F._ps)
375
+ return F
216
376
 
217
377
 
218
- def _Psum_(*ps, **name_RESIDUAL):
219
- '''(INTERNAL) Return an C{Fsum} from 1 or 2 known scalar(s) C{ps}.
378
+ def _Psum_(*ps, **name_f2product_nonfinites_RESIDUAL): # in .fmath
379
+ '''(INTERNAL) Return an C{Fsum} from I{known scalar} C{ps}.
220
380
  '''
221
- return _Psum(ps, **name_RESIDUAL)
381
+ return _Psum(ps, **name_f2product_nonfinites_RESIDUAL)
222
382
 
223
383
 
224
384
  def _2scalar2(other):
@@ -242,7 +402,7 @@ def _2scalar2(other):
242
402
  def _s_r(s, r):
243
403
  '''(INTERNAL) Return C{(s, r)}, I{ordered}.
244
404
  '''
245
- if r:
405
+ if r and _isfinite(s):
246
406
  if fabs(s) < fabs(r):
247
407
  s, r = r, (s or INT0)
248
408
  else:
@@ -254,7 +414,7 @@ def _strcomplex(s, *args):
254
414
  '''(INTERNAL) C{Complex} 2- or 3-arg C{pow} error as C{str}.
255
415
  '''
256
416
  c = _strcomplex.__name__[4:]
257
- n = _DASH_(len(args), _arg_)
417
+ n = _sub_op_(len(args), _arg_)
258
418
  t = unstr(pow, *args)
259
419
  return _SPACE_(c, s, _from_, n, t)
260
420
 
@@ -270,9 +430,12 @@ def _stresidual(prefix, residual, R=0, **mod_ratio):
270
430
  return _SPACE_(prefix, t, Fmt.exceeds_R(R), _threshold_)
271
431
 
272
432
 
273
- def _2sum(a, b): # by .testFmath
274
- '''(INTERNAL) Return C{a + b} as 2-tuple (sum, residual).
433
+ def _2sum(a, b, _isfine=_isfinite): # in .testFmath
434
+ '''(INTERNAL) Return C{a + b} as 2-tuple C{(sum, residual)} with finite C{sum},
435
+ otherwise as 2-tuple C{(nonfinite, 0)} iff I{non-finites} are OK.
275
436
  '''
437
+ # FastTwoSum U{Algorithm 1.1<https://www.TUHH.De/ti3/paper/rump/OgRuOi05.pdf>}
438
+
276
439
  # Neumaier, A. U{Rundungsfehleranalyse einiger Verfahren zur Summation endlicher
277
440
  # Summen<https://OnlineLibrary.Wiley.com/doi/epdf/10.1002/zamm.19740540106>},
278
441
  # 1974, Zeitschrift für Angewandte Mathmatik und Mechanik, vol 51, nr 1, p 39-51
@@ -283,10 +446,12 @@ def _2sum(a, b): # by .testFmath
283
446
  r = (b - s) + a
284
447
  else:
285
448
  r = (a - s) + b
286
- return s, r
287
- u = unstr(_2sum, a, b)
288
- t = Fmt.PARENSPACED(_not_finite_, s)
289
- raise _OverflowError(u, txt=t)
449
+ elif _isfine(s):
450
+ r = 0
451
+ else: # non-finite and not OK
452
+ t = unstr(_2sum, a, b)
453
+ raise _nfError(s, t)
454
+ return s, r
290
455
 
291
456
 
292
457
  def _threshold(threshold=_0_0, **kwds):
@@ -294,29 +459,28 @@ def _threshold(threshold=_0_0, **kwds):
294
459
  optionally from single kwds C{B{RESIDUAL}=scalar}.
295
460
  '''
296
461
  if kwds:
297
- threshold, kwds = _xkwds_pop2(kwds, RESIDUAL=threshold)
298
- # threshold = kwds.pop('RESIDUAL', threshold)
299
- if kwds:
300
- raise _UnexpectedError(**kwds)
462
+ threshold = _xkwds_get1(kwds, RESIDUAL=threshold)
301
463
  try:
302
464
  return _2finite(threshold) # PYCHOK None
303
465
  except Exception as x:
304
466
  raise ResidualError(threshold=threshold, cause=x)
305
467
 
306
468
 
307
- class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
308
- '''Precision floating point summation and I{running} summation.
469
+ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase, .fstats, ...
470
+ '''Precision floating point summation, I{running} summation and accurate multiplication.
309
471
 
310
472
  Unlike Python's C{math.fsum}, this class accumulates values and provides intermediate,
311
473
  I{running}, precision floating point summations. Accumulation may continue after any
312
474
  intermediate, I{running} summuation.
313
475
 
314
476
  @note: Values may be L{Fsum}, L{Fsum2Tuple}, C{int}, C{float} or C{scalar} instances,
315
- any C{type} having method C{__float__} to convert the C{scalar} to a single
316
- C{float}, except C{complex}.
477
+ i.e. any C{type} having method C{__float__}.
317
478
 
318
- @note: Handling of exceptions and C{inf}, C{INF}, C{nan} and C{NAN} differs from
319
- Python's C{math.fsum}.
479
+ @note: Handling of I{non-finites} as C{inf}, C{INF}, C{NINF}, C{nan} and C{NAN} is
480
+ determined globally by function L{nonfiniterrors<fsums.nonfiniterrors>} and
481
+ by method L{nonfinites<Fsum.nonfinites>} for individual C{Fsum} instances,
482
+ overruling the global setting. By default and for backward compatibility,
483
+ I{non-finites} raise exceptions.
320
484
 
321
485
  @see: U{Hettinger<https://GitHub.com/ActiveState/code/tree/master/recipes/Python/
322
486
  393090_Binary_floating_point_summatiaccurate_full/recipe-393090.py>},
@@ -324,40 +488,41 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
324
488
  <https://Link.Springer.com/article/10.1007/s00607-005-0139-x>}, Python 2.6+
325
489
  file I{Modules/mathmodule.c} and the issue log U{Full precision summation
326
490
  <https://Bugs.Python.org/issue2819>}.
491
+
492
+ @see: Method L{f2product<Fsum.f2product>} for details about accurate I{TwoProduct}
493
+ multiplication.
494
+
495
+ @see: Module L{fsums<pygeodesy.fsums>} for env variables C{PYGEODESY_FSUM_F2PRODUCT},
496
+ C{PYGEODESY_FSUM_NONFINITES} and C{PYGEODESY_FSUM_RESIDUAL}.
327
497
  '''
328
- _math_fsum = None
498
+ _f2product = _sys_version_info2 > (3, 12) or bool(_F2PRODUCT)
499
+ _isfine = {} # == _isfinite
329
500
  _n = 0
330
501
  # _ps = [] # partial sums
331
502
  # _ps_max = 0 # max(Fsum._ps_max, len(Fsum._ps))
332
- _RESIDUAL = _threshold(_getenv('PYGEODESY_FSUM_RESIDUAL', _0_0))
503
+ _RESIDUAL = _threshold(_RESIDUAL_0_0)
333
504
 
334
- def __init__(self, *xs, **name_RESIDUAL):
335
- '''New L{Fsum} for I{running} precision floating point summation.
505
+ def __init__(self, *xs, **name_f2product_nonfinites_RESIDUAL):
506
+ '''New L{Fsum}.
336
507
 
337
- @arg xs: No, one or more initial items to add (each C{scalar} or
338
- an L{Fsum} or L{Fsum2Tuple} instance), all positional.
339
- @kwarg name_RESIDUAL: Optional C{B{name}=NN} (C{str}) for this
340
- L{Fsum} and the C{B{RESIDUAL}=0.0} threshold for
341
- L{ResidualError}s (C{scalar}).
508
+ @arg xs: No, one or more initial items to accumulate (each C{scalar}, an
509
+ L{Fsum} or L{Fsum2Tuple}), all positional.
510
+ @kwarg name_f2product_nonfinites_RESIDUAL: Optional C{B{name}=NN} (C{str})
511
+ and settings C{B{f2product}=None} (C{bool}), C{B{nonfinites}=None}
512
+ (C{bool}) and C{B{RESIDUAL}=0.0} threshold (C{scalar}) for this
513
+ L{Fsum}.
342
514
 
343
- @see: Methods L{Fsum.fadd} and L{Fsum.RESIDUAL}.
515
+ @see: Methods L{Fsum.f2product}, L{Fsum.nonfinites}, L{Fsum.RESIDUAL},
516
+ L{Fsum.fadd} and L{Fsum.fadd_}.
344
517
  '''
345
- if name_RESIDUAL:
346
- n, kwds = _name2__(**name_RESIDUAL)
347
- if kwds:
348
- R = Fsum._RESIDUAL
349
- t = _threshold(R, **kwds)
350
- if t != R:
351
- self._RESIDUAL = t
352
- if n:
353
- self.name = n
354
-
518
+ if name_f2product_nonfinites_RESIDUAL:
519
+ self._optionals(**name_f2product_nonfinites_RESIDUAL)
355
520
  self._ps = [] # [_0_0], see L{Fsum._fprs}
356
521
  if xs:
357
- self._facc_1(xs, up=False)
522
+ self._facc_args(xs, up=False)
358
523
 
359
524
  def __abs__(self):
360
- '''Return this instance' absolute value as an L{Fsum}.
525
+ '''Return C{abs(self)} as an L{Fsum}.
361
526
  '''
362
527
  s = self.signOf() # == self._cmp_0(0)
363
528
  return (-self) if s < 0 else self._copy_2(self.__abs__)
@@ -375,7 +540,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
375
540
  return f._fadd(other, _add_op_)
376
541
 
377
542
  def __bool__(self): # PYCHOK Python 3+
378
- '''Return C{True} if this instance is I{exactly} non-zero.
543
+ '''Return C{bool(B{self})}, C{True} iff C{residual} is zero.
379
544
  '''
380
545
  s, r = self._fprs2
381
546
  return bool(s or r) and s != -r # == self != 0
@@ -418,7 +583,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
418
583
  return f._fdivmod2(other, _divmod_op_, **raiser_RESIDUAL)
419
584
 
420
585
  def __eq__(self, other):
421
- '''Compare this with an other instance or C{scalar}.
586
+ '''Return C{(B{self} == B{other})} as C{bool} where B{C{other}}
587
+ is C{scalar}, an other L{Fsum} or L{Fsum2Tuple}.
422
588
  '''
423
589
  return self._cmp_0(other, _eq_op_) == 0
424
590
 
@@ -455,19 +621,21 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
455
621
  return _NotImplemented(self, *other)
456
622
 
457
623
  def __ge__(self, other):
458
- '''Compare this with an other instance or C{scalar}.
624
+ '''Return C{(B{self} >= B{other})}, see C{__eq__}.
459
625
  '''
460
626
  return self._cmp_0(other, _ge_op_) >= 0
461
627
 
462
628
  def __gt__(self, other):
463
- '''Compare this with an other instance or C{scalar}.
629
+ '''Return C{(B{self} > B{other})}, see C{__eq__}.
464
630
  '''
465
631
  return self._cmp_0(other, _gt_op_) > 0
466
632
 
467
633
  def __hash__(self): # PYCHOK no cover
468
- '''Return this instance' C{hash}.
634
+ '''Return C{hash(B{self})} as C{float}.
469
635
  '''
470
- return hash(self._ps) # XXX id(self)?
636
+ # @see: U{Notes for type implementors<https://docs.Python.org/
637
+ # 3/library/numbers.html#numbers.Rational>}
638
+ return hash(self.partials) # tuple.__hash__()
471
639
 
472
640
  def __iadd__(self, other):
473
641
  '''Apply C{B{self} += B{other}} to this instance.
@@ -485,7 +653,9 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
485
653
  try:
486
654
  return self._fadd(other, _iadd_op_)
487
655
  except TypeError:
488
- return self._facc_inplace(other, _iadd_op_, self._facc)
656
+ pass
657
+ _xiterable(other)
658
+ return self._facc(other)
489
659
 
490
660
  def __ifloordiv__(self, other):
491
661
  '''Apply C{B{self} //= B{other}} to this instance.
@@ -499,7 +669,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
499
669
 
500
670
  @raise TypeError: Invalid B{C{other}} type.
501
671
 
502
- @raise ValueError: Invalid or non-finite B{C{other}}.
672
+ @raise ValueError: Invalid or I{non-finite} B{C{other}}.
503
673
 
504
674
  @raise ZeroDivisionError: Zero B{C{other}}.
505
675
 
@@ -533,7 +703,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
533
703
 
534
704
  @raise TypeError: Invalid B{C{other}} type.
535
705
 
536
- @raise ValueError: Invalid or non-finite B{C{other}}.
706
+ @raise ValueError: Invalid or I{non-finite} B{C{other}}.
537
707
  '''
538
708
  return self._fmul(other, _mul_op_ + _fset_op_)
539
709
 
@@ -554,7 +724,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
554
724
  def __ipow__(self, other, *mod, **raiser_RESIDUAL): # PYCHOK 2 vs 3 args
555
725
  '''Apply C{B{self} **= B{other}} to this instance.
556
726
 
557
- @arg other: The exponent (C{scalar}, L{Fsum} or L{Fsum2Tuple}).
727
+ @arg other: The exponent (C{scalar}, an L{Fsum} or L{Fsum2Tuple}).
558
728
  @arg mod: Optional modulus (C{int} or C{None}) for the 3-argument
559
729
  C{pow(B{self}, B{other}, B{mod})} version.
560
730
  @kwarg raiser_RESIDUAL: Use C{B{raiser}=False} to ignore
@@ -604,7 +774,9 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
604
774
  try:
605
775
  return self._fsub(other, _isub_op_)
606
776
  except TypeError:
607
- return self._facc_inplace(other, _isub_op_, self._facc_neg)
777
+ pass
778
+ _xiterable(other)
779
+ return self._facc_neg(other)
608
780
 
609
781
  def __iter__(self):
610
782
  '''Return an C{iter}ator over a C{partials} duplicate.
@@ -628,7 +800,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
628
800
 
629
801
  @raise TypeError: Invalid B{C{other}} type.
630
802
 
631
- @raise ValueError: Invalid or non-finite B{C{other}}.
803
+ @raise ValueError: Invalid or I{non-finite} B{C{other}}.
632
804
 
633
805
  @raise ZeroDivisionError: Zero B{C{other}}.
634
806
 
@@ -637,7 +809,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
637
809
  return self._ftruediv(other, _truediv_op_ + _fset_op_, **raiser_RESIDUAL)
638
810
 
639
811
  def __le__(self, other):
640
- '''Compare this with an other instance or C{scalar}.
812
+ '''Return C{(B{self} <= B{other})}, see C{__eq__}.
641
813
  '''
642
814
  return self._cmp_0(other, _le_op_) <= 0
643
815
 
@@ -647,7 +819,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
647
819
  return self._n
648
820
 
649
821
  def __lt__(self, other):
650
- '''Compare this with an other instance or C{scalar}.
822
+ '''Return C{(B{self} < B{other})}, see C{__eq__}.
651
823
  '''
652
824
  return self._cmp_0(other, _lt_op_) < 0
653
825
 
@@ -672,12 +844,12 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
672
844
  return f._fmul(other, _mul_op_)
673
845
 
674
846
  def __ne__(self, other):
675
- '''Compare this with an other instance or C{scalar}.
847
+ '''Return C{(B{self} != B{other})}, see C{__eq__}.
676
848
  '''
677
849
  return self._cmp_0(other, _ne_op_) != 0
678
850
 
679
851
  def __neg__(self):
680
- '''Return I{a copy of} this instance, I{negated}.
852
+ '''Return C{copy(B{self})}, I{negated}.
681
853
  '''
682
854
  f = self._copy_2(self.__neg__)
683
855
  return f._fset(self._neg)
@@ -825,11 +997,13 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
825
997
  def as_integer_ratio(self):
826
998
  '''Return this instance as the ratio of 2 integers.
827
999
 
828
- @return: 2-Tuple C{(numerator, denominator)} both C{int}
829
- with C{numerator} signed and C{denominator}
830
- non-zero, positive.
1000
+ @return: 2-Tuple C{(numerator, denominator)} both C{int} with
1001
+ C{numerator} signed and C{denominator} non-zero and
1002
+ positive. The C{numerator} is I{non-finite} if this
1003
+ instance is.
831
1004
 
832
- @see: Standard C{float.as_integer_ratio} in Python 2.7+.
1005
+ @see: Method L{Fsum.fint2} and C{float.as_integer_ratio} in
1006
+ Python 2.7+.
833
1007
  '''
834
1008
  n, r = self._fint2
835
1009
  if r:
@@ -842,8 +1016,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
842
1016
 
843
1017
  @property_RO
844
1018
  def as_iscalar(self):
845
- '''Get this instance I{as-is} (L{Fsum} or C{scalar}), the
846
- latter only if the C{residual} equals C{zero}.
1019
+ '''Get this instance I{as-is} (L{Fsum} with C{non-zero residual},
1020
+ C{scalar} or I{non-finite}).
847
1021
  '''
848
1022
  s, r = self._fprs2
849
1023
  return self if r else s
@@ -890,6 +1064,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
890
1064
  f._ps = list(self._ps) # separate list
891
1065
  if not deep:
892
1066
  f._n = 1
1067
+ # assert f._f2product == self._f2product
893
1068
  # assert f._Fsum is f
894
1069
  return f
895
1070
 
@@ -901,6 +1076,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
901
1076
  f = _Named.copy(self, deep=False, name=n)
902
1077
  f._ps = list(self._ps) # separate list
903
1078
  # assert f._n == self._n
1079
+ # assert f._f2product == self._f2product
904
1080
  # assert f._Fsum is f
905
1081
  return f
906
1082
 
@@ -910,13 +1086,6 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
910
1086
  return other._copy_2(which) if _isFsum(other) else \
911
1087
  self._copy_2(which)._fset(other)
912
1088
 
913
- # def _copy_RESIDUAL(self, other):
914
- # '''(INTERNAL) Copy C{other._RESIDUAL}.
915
- # '''
916
- # R = other._RESIDUAL
917
- # if R is not Fsum._RESIDUAL:
918
- # self._RESIDUAL = R
919
-
920
1089
  divmod = __divmod__
921
1090
 
922
1091
  def _Error(self, op, other, Error, **txt_cause):
@@ -943,28 +1112,21 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
943
1112
  '''(INTERNAL) Accumulate more C{scalars} or L{Fsum}s.
944
1113
  '''
945
1114
  if xs:
946
- _xs = _2floats(xs, **origin_X_x) # PYCHOK yield
1115
+ kwds = _xkwds(self._isfine, **origin_X_x)
1116
+ _xs = _2floats(xs, **kwds) # PYCHOK yield
947
1117
  ps = self._ps
948
1118
  ps[:] = self._ps_acc(list(ps), _xs, up=up)
949
1119
  return self
950
1120
 
951
- def _facc_1(self, xs, **up):
952
- '''(INTERNAL) Accumulate 0, 1 or more C{scalars} or L{Fsum}s,
953
- all positional C{xs} in the caller of this method.
1121
+ def _facc_args(self, xs, **up):
1122
+ '''(INTERNAL) Accumulate 0, 1 or more C{xs}, all positional
1123
+ arguments in the caller of this method.
954
1124
  '''
955
- return self._fadd(xs[0], _add_op_, **up) if len(xs) == 1 else \
956
- self._facc(xs, origin=1, **up)
957
-
958
- def _facc_inplace(self, other, op, _facc):
959
- '''(INTERNAL) Accumulate from an iterable.
960
- '''
961
- try:
962
- return _facc(other, origin=1) if _xiterable(other) else self
963
- except Exception as X:
964
- raise self._ErrorX(X, op, other)
1125
+ return self._facc(xs, origin=1, **up) if len(xs) != 1 else \
1126
+ self._fadd(xs[0], _add_op_, **up)
965
1127
 
966
1128
  def _facc_neg(self, xs, **up_origin):
967
- '''(INTERNAL) Accumulate more C{scalars} or L{Fsum}s, negated.
1129
+ '''(INTERNAL) Accumulate more C{xs}, negated.
968
1130
  '''
969
1131
  def _N(X):
970
1132
  return X._ps_neg
@@ -989,24 +1151,22 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
989
1151
  p = s = int(p)
990
1152
  m = Fsum._pow_int
991
1153
  else:
992
- p = s = _2float(power=p)
1154
+ p = s = _2float(power=p, **self._isfine)
993
1155
  m = Fsum._pow_scalar
994
1156
  return m, p, s, r
995
1157
 
996
1158
  _Pow, p, s, r = _Pow4(power)
997
1159
  if p: # and xs:
998
1160
  op = which.__name__
999
- _flt = float
1000
- _Fs = Fsum
1001
- _isa = isinstance
1161
+ _FsT = _Fsum_Fsum2Tuple_types
1002
1162
  _pow = self._pow_2_3
1003
1163
 
1004
1164
  def _P(X):
1005
1165
  f = _Pow(X, p, power, op, **raiser_RESIDUAL)
1006
- return f._ps if _isa(f, _Fs) else (f,)
1166
+ return f._ps if isinstance(f, _FsT) else (f,)
1007
1167
 
1008
1168
  def _p(x):
1009
- x = _flt(x)
1169
+ x = float(x)
1010
1170
  f = _pow(x, s, power, op, **raiser_RESIDUAL)
1011
1171
  if f and r:
1012
1172
  f *= _pow(x, r, power, op, **raiser_RESIDUAL)
@@ -1057,12 +1217,13 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1057
1217
 
1058
1218
  @raise TypeError: An invalid B{C{xs}} item.
1059
1219
 
1060
- @raise ValueError: Invalid or non-finite B{C{xs}} value.
1220
+ @raise ValueError: Invalid or I{non-finite} B{C{xs}} value.
1061
1221
  '''
1062
1222
  if _isFsumTuple(xs):
1063
1223
  self._facc_scalar(xs._ps)
1064
- elif isscalar(xs): # for backward compatibility
1065
- self._facc_scalar_(_2float(x=xs)) # PYCHOK no cover
1224
+ elif isscalar(xs): # for backward compatibility # PYCHOK no cover
1225
+ x = _2float(x=xs, **self._isfine)
1226
+ self._facc_scalar_(x)
1066
1227
  elif xs: # _xiterable(xs)
1067
1228
  self._facc(xs)
1068
1229
  return self
@@ -1075,17 +1236,21 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1075
1236
 
1076
1237
  @see: Method L{Fsum.fadd} for further details.
1077
1238
  '''
1078
- return self._facc_1(xs)
1239
+ return self._facc_args(xs)
1079
1240
 
1080
1241
  def _fadd(self, other, op, **up): # in .fmath.Fhorner
1081
1242
  '''(INTERNAL) Apply C{B{self} += B{other}}.
1082
1243
  '''
1083
- if not self._ps: # new Fsum(x)
1084
- self._fset(other, op=op, **up)
1085
- elif _isFsumTuple(other):
1086
- self._facc_scalar(other._ps, **up)
1244
+ if _isFsumTuple(other):
1245
+ if self._ps:
1246
+ self._facc_scalar(other._ps, **up)
1247
+ else:
1248
+ self._fset(other, op=op, **up)
1087
1249
  elif self._scalar(other, op):
1088
- self._facc_scalar_(other, **up)
1250
+ if self._ps:
1251
+ self._facc_scalar_(other, **up)
1252
+ else:
1253
+ self._fset(other, op=op, **up)
1089
1254
  return self
1090
1255
 
1091
1256
  fcopy = copy # for backward compatibility
@@ -1100,7 +1265,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1100
1265
  # but at least divmod(-3, 2) equals Cpython's result (-2, 1).
1101
1266
  q = self._truediv(other, op, **raiser_RESIDUAL).floor
1102
1267
  if q: # == float // other == floor(float / other)
1103
- self -= Fsum(q) * other # NOT other * q!
1268
+ self -= self._Fsum_as(q) * other # NOT other * q!
1104
1269
 
1105
1270
  s = signOf(other) # make signOf(self) == signOf(other)
1106
1271
  if s and self.signOf() == -s: # PYCHOK no cover
@@ -1111,33 +1276,34 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1111
1276
  # raise self._Error(op, other, _AssertionError, txt__=signOf)
1112
1277
  return DivMod2Tuple(q, self) # q is C{int} in Python 3+, but C{float} in Python 2-
1113
1278
 
1114
- def _fhorner(self, x, cs, op): # in .fmath
1279
+ def _fhorner(self, x, cs, op, incx=True): # in .fmath
1115
1280
  '''(INTERNAL) Add an L{Fhorner} evaluation of polynomial
1116
- M{sum(cs[i] * x**i for i=0..len(cs)-1)}.
1281
+ C{sum(cs[i] * B{x}**i for i=0..len(cs)-1) if B{incx}
1282
+ else sum(... i=len(cs)-1..0)}.
1117
1283
  '''
1118
1284
  if _xiterablen(cs):
1119
- H = Fsum(name__=self._fhorner)
1285
+ H = self._Fsum_as(name__=self._fhorner)
1120
1286
  if _isFsumTuple(x):
1121
1287
  _mul = H._mul_Fsum
1122
1288
  else:
1123
1289
  _mul = H._mul_scalar
1124
- x = _2float(x=x)
1290
+ x = _2float(x=x, **self._isfine)
1125
1291
  if len(cs) > 1 and x:
1126
- for c in reversed(cs):
1292
+ for c in (reversed(cs) if incx else cs):
1127
1293
  H._fset_ps(_mul(x, op))
1128
1294
  H._fadd(c, op, up=False)
1129
1295
  else: # x == 0
1130
- H = cs[0]
1296
+ H = cs[0] if cs else _0_0
1131
1297
  self._fadd(H, op)
1132
1298
  return self
1133
1299
 
1134
1300
  def _finite(self, other, op=None):
1135
1301
  '''(INTERNAL) Return B{C{other}} if C{finite}.
1136
1302
  '''
1137
- if _isfinite(other):
1303
+ if _isOK_or_finite(other, **self._isfine):
1138
1304
  return other
1139
- raise ValueError(_not_finite_) if op is None else \
1140
- self._Error(op, other, _ValueError, txt=_not_finite_)
1305
+ E = _NonfiniteError(other)
1306
+ raise self._Error(op, other, E, txt=_not_finite_)
1141
1307
 
1142
1308
  def fint(self, name=NN, **raiser_RESIDUAL):
1143
1309
  '''Return this instance' current running sum as C{integer}.
@@ -1161,7 +1327,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1161
1327
  if R:
1162
1328
  t = _stresidual(_integer_, r, **R)
1163
1329
  raise ResidualError(_integer_, i, txt=t)
1164
- return _Psum_(i, name=_name__(name, name__=self.fint))
1330
+ return self._Fsum_as(i, name=_name__(name, name__=self.fint))
1165
1331
 
1166
1332
  def fint2(self, **name):
1167
1333
  '''Return this instance' current running sum as C{int} and the
@@ -1172,6 +1338,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1172
1338
  @return: An L{Fsum2Tuple}C{(fsum, residual)} with C{fsum}
1173
1339
  an C{int} and I{integer} C{residual} a C{float} or
1174
1340
  C{INT0} if the C{fsum} is considered to be I{exact}.
1341
+ The C{fsum} is I{non-finite} if this instance is.
1175
1342
  '''
1176
1343
  return Fsum2Tuple(*self._fint2, **name)
1177
1344
 
@@ -1179,18 +1346,35 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1179
1346
  def _fint2(self): # see ._fset
1180
1347
  '''(INTERNAL) Get 2-tuple (C{int}, I{integer} residual).
1181
1348
  '''
1182
- s, r = self._fprs2
1183
- i = int(s)
1184
- n = len(self._ps)
1185
- r = self._ps_1sum(i) if r and n > 1 else float(s - i)
1186
- return i, (r or INT0) # Fsum2Tuple?
1187
-
1188
- @_fint2.setter_ # PYCHOK setter_underscore!
1189
- def _fint2(self, s):
1349
+ s, _ = self._fprs2
1350
+ try:
1351
+ i = int(s)
1352
+ r = (self._ps_1sum(i) if len(self._ps) > 1 else
1353
+ float(s - i)) or INT0
1354
+ except (OverflowError, ValueError) as X:
1355
+ r = 0 # INF, NAN, NINF
1356
+ i = self._fintX(X, sum(self._ps))
1357
+ return i, r # Fsum2Tuple?
1358
+
1359
+ @_fint2.setter_ # PYCHOK setter_UNDERscore!
1360
+ def _fint2(self, s): # in _fset
1190
1361
  '''(INTERNAL) Replace the C{_fint2} value.
1191
1362
  '''
1192
- i = int(s)
1193
- return i, ((s - i) or INT0)
1363
+ try:
1364
+ i = int(s)
1365
+ r = (s - i) or INT0
1366
+ except (OverflowError, ValueError) as X:
1367
+ r = 0 # INF, NAN, NINF
1368
+ i = self._fintX(X, float(s))
1369
+ return i, r # like _fint2.getter
1370
+
1371
+ def _fintX(self, X, i): # PYCHOK X
1372
+ '''(INTERNAL) Handle I{non-finite} C{int}.
1373
+ '''
1374
+ # "cannot convert float infinity to integer"
1375
+ return i # ignore such Overflow-/ValueErrors
1376
+ # op = int.__name__
1377
+ # return self._nonfiniteX(X, op, i)
1194
1378
 
1195
1379
  @deprecated_property_RO
1196
1380
  def float_int(self): # PYCHOK no cover
@@ -1213,8 +1397,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1213
1397
  f -= 1
1214
1398
  return f # _floor(self._n_d)
1215
1399
 
1216
- # ffloordiv = __ifloordiv__ # for naming consistency
1217
- # floordiv = __floordiv__ # for naming consistency
1400
+ # ffloordiv = __ifloordiv__ # for naming consistency?
1401
+ # floordiv = __floordiv__ # for naming consistency?
1218
1402
 
1219
1403
  def _floordiv(self, other, op, **raiser_RESIDUAL): # rather _ffloordiv?
1220
1404
  '''Apply C{B{self} //= B{other}}.
@@ -1222,6 +1406,33 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1222
1406
  q = self._ftruediv(other, op, **raiser_RESIDUAL) # == self
1223
1407
  return self._fset(q.floor) # floor(q)
1224
1408
 
1409
+ def fma(self, other1, other2, raiser=False): # in .fmath.fma
1410
+ '''Fused-multiply-add C{self *= B{other1}; self += B{other2}}.
1411
+
1412
+ @arg other1: Multiplier (C{scalar}, an L{Fsum} or L{Fsum2Tuple}).
1413
+ @arg other2: Addend (C{scalar}, an L{Fsum} or L{Fsum2Tuple}).
1414
+ @kwarg raiser: If C{True}, throw an exception, otherwise pass
1415
+ the I{non-finite} result (C{bool}).
1416
+
1417
+ @note: Uses C{math.fma} in Python 3.13+, provided C{self},
1418
+ B{C{other1}} and B{C{other2}} are all C{scalar}.
1419
+ '''
1420
+ f, r = self._fprs2
1421
+ if r == 0 and isscalar(other1, both=True) \
1422
+ and isscalar(other2, both=True):
1423
+ try:
1424
+ f = _fma(f, other1, other2)
1425
+ except (OverflowError, TypeError, ValueError) as X: # from math.fma
1426
+ op = self.fma.__name__ # INF, NAN, NINF
1427
+ f = self._mul_reduce(op, f, other1)
1428
+ f = _sum(self._ps_other(op, f, other2))
1429
+ if raiser:
1430
+ f = self._nonfiniteX(X, op, f)
1431
+ else:
1432
+ f = self._f2mul(self.fma, other1, raiser=raiser)
1433
+ f += other2
1434
+ return self._fset(f)
1435
+
1225
1436
  fmul = __imul__
1226
1437
 
1227
1438
  def _fmul(self, other, op):
@@ -1231,14 +1442,56 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1231
1442
  if len(self._ps) != 1:
1232
1443
  f = self._mul_Fsum(other, op)
1233
1444
  elif len(other._ps) != 1: # and len(self._ps) == 1
1234
- f = other._mul_scalar(self._ps[0], op)
1445
+ f = self._ps_mul(op, *other._ps)
1446
+ elif self._f2product: # len(other._ps) == 1
1447
+ f = self._mul_scalar(other._ps[0], op)
1235
1448
  else: # len(other._ps) == len(self._ps) == 1
1236
- f = self._finite(self._ps[0] * other._ps[0])
1449
+ f = self._finite(self._ps[0] * other._ps[0], op=op)
1237
1450
  else:
1238
1451
  s = self._scalar(other, op)
1239
1452
  f = self._mul_scalar(s, op)
1240
1453
  return self._fset(f) # n=len(self) + 1
1241
1454
 
1455
+ @deprecated_method
1456
+ def f2mul(self, *others, **raiser):
1457
+ '''DEPRECATED on 2024.09.13, use method L{f2mul_<Fsum.f2mul_>}.'''
1458
+ return self._fset(self.f2mul_(*others, **raiser))
1459
+
1460
+ def f2mul_(self, *others, **raiser): # in .fmath.f2mul
1461
+ '''Return C{B{self} * B{other} * B{other} ...} for all B{C{others}} using cascaded,
1462
+ accurate multiplication like with L{f2product<Fsum.f2product>} set to C{True}.
1463
+
1464
+ @arg others: Multipliers (each C{scalar}, an L{Fsum} or L{Fsum2Tuple}), all
1465
+ positional.
1466
+ @kwarg raiser: Keyword argument C{B{raiser}=False}, if C{True}, throw an exception,
1467
+ otherwise pass the I{non-finite} result (C{bool}).
1468
+
1469
+ @return: The cascaded I{TwoProduct} (L{Fsum} or C{float}).
1470
+
1471
+ @see: U{Equations 2.3<https://www.TUHH.De/ti3/paper/rump/OzOgRuOi06.pdf>}
1472
+ '''
1473
+ return self._f2mul(self.f2mul_, *others, **raiser)
1474
+
1475
+ def _f2mul(self, where, *others, **raiser):
1476
+ '''(INTERNAL) See methods C{fma} and C{f2mul_}.
1477
+ '''
1478
+ f = self._copy_2(where)
1479
+ if others:
1480
+ op = where.__name__
1481
+ ps = f._ps
1482
+ if ps:
1483
+ try:
1484
+ for p in self._ps_other(op, *others):
1485
+ pfs = _2products(p, _2split3s(ps))
1486
+ ps[:] = f._ps_acc([], pfs, up=False)
1487
+ f._update()
1488
+ except (OverflowError, TypeError, ValueError) as X:
1489
+ r = self._mul_reduce(op, _sum(ps), *others) # INF, NAN, NINF
1490
+ if _xkwds_get1(raiser, raiser=False):
1491
+ r = self._nonfiniteX(X, op, r)
1492
+ f._fset(r)
1493
+ return f
1494
+
1242
1495
  def fover(self, over, **raiser_RESIDUAL):
1243
1496
  '''Apply C{B{self} /= B{over}} and summate.
1244
1497
 
@@ -1266,16 +1519,40 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1266
1519
  f = self._pow_2_3(self, other, other, op, *mod, **raiser_RESIDUAL)
1267
1520
  elif self.is_integer():
1268
1521
  # return an exact C{int} for C{int}**C{int}
1269
- i, _ = self._fint2 # assert _ == 0
1522
+ i, _ = self._fint2 # assert _ == 0
1270
1523
  x, r = _2scalar2(other) # C{int}, C{float} or other
1271
- f = _Psum_(i)._pow_Fsum(other, op, **raiser_RESIDUAL) if r else \
1272
- self._pow_2_3(i, x, other, op, **raiser_RESIDUAL)
1524
+ f = self._Fsum_as(i)._pow_Fsum(other, op, **raiser_RESIDUAL) if r else \
1525
+ self._pow_2_3(i, x, other, op, **raiser_RESIDUAL)
1273
1526
  else: # mod[0] is None, power(self, other)
1274
- f = self._pow(other, other, op, **raiser_RESIDUAL)
1527
+ f = self._pow(other, other, op, **raiser_RESIDUAL)
1275
1528
  else: # pow(self, other)
1276
1529
  f = self._pow(other, other, op, **raiser_RESIDUAL)
1277
1530
  return self._fset(f) # n=max(len(self), 1)
1278
1531
 
1532
+ def f2product(self, *two):
1533
+ '''Get and set accurate I{TwoProduct} multiplication for this
1534
+ L{Fsum}, I{overriding the global setting} from function
1535
+ L{f2product<fsums.f2product>}.
1536
+
1537
+ @arg two: If omitted, leave the override unchanged, if C{True},
1538
+ turn I{TwoProduct} on, if C{False} off, if C{None}e
1539
+ remove th override (C{bool} or C{None}).
1540
+
1541
+ @return: The previous setting (C{bool} or C{None} if not set).
1542
+
1543
+ @see: Function L{f2product<fsums.f2product>}.
1544
+
1545
+ @note: Use C{f.f2product() or f2product()} to determine whether
1546
+ multiplication is accurate for L{Fsum} C{f}.
1547
+ '''
1548
+ if two: # delattrof(self, _f2product=None)
1549
+ t = _xkwds_pop(self.__dict__, _f2product=None)
1550
+ if two[0] is not None:
1551
+ self._f2product = bool(two[0])
1552
+ else: # getattrof(self, _f2product=None)
1553
+ t = _xkwds_get(self.__dict__, _f2product=None)
1554
+ return t
1555
+
1279
1556
  @Property
1280
1557
  def _fprs(self):
1281
1558
  '''(INTERNAL) Get and cache this instance' precision
@@ -1299,21 +1576,26 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1299
1576
  running sum and residual (L{Fsum2Tuple}).
1300
1577
  '''
1301
1578
  ps = self._ps
1302
- n = len(ps) - 2
1303
- if n > 0: # len(ps) > 2
1304
- s = _psum(ps)
1305
- n = len(ps) - 2
1306
- if n > 0:
1307
- r = self._ps_1sum(s)
1308
- return Fsum2Tuple(*_s_r(s, r))
1309
- if n == 0: # len(ps) == 2
1310
- s, r = _s_r(*_2sum(*ps))
1311
- ps[:] = (r, s) if r else (s,)
1312
- elif ps: # len(ps) == 1
1313
- s, r = ps[0], INT0
1314
- else: # len(ps) == 0
1315
- s, r = _0_0, INT0
1316
- ps[:] = s,
1579
+ try:
1580
+ n = len(ps) - 2
1581
+ if n > 0: # len(ps) > 2
1582
+ s = _psum(ps, **self._isfine)
1583
+ n = len(ps) - 2
1584
+ if n > 0:
1585
+ r = self._ps_1sum(s)
1586
+ return Fsum2Tuple(*_s_r(s, r))
1587
+ if n == 0: # len(ps) == 2
1588
+ s, r = _s_r(*_2sum(*ps, **self._isfine))
1589
+ ps[:] = (r, s) if r else (s,)
1590
+ elif ps: # len(ps) == 1
1591
+ s, r = ps[0], INT0
1592
+ else: # len(ps) == 0
1593
+ s, r = _0_0, INT0
1594
+ ps[:] = s,
1595
+ except (OverflowError, ValueError) as X:
1596
+ op = _sum.__name__ # INF, NAN, NINF
1597
+ s = self._nonfiniteX(X, op, _sum(self._ps))
1598
+ r = _0_0
1317
1599
  # assert self._ps is ps
1318
1600
  return Fsum2Tuple(s, r)
1319
1601
 
@@ -1324,19 +1606,18 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1324
1606
  return Fsum2Tuple(s_r)
1325
1607
 
1326
1608
  def fset_(self, *xs):
1327
- '''Replace this instance' value with all positional items.
1609
+ '''Apply C{B{self}.partials = Fsum(*B{xs}).partials}.
1328
1610
 
1329
1611
  @arg xs: Optional, new values (each C{scalar} or
1330
- an L{Fsum} or L{Fsum2Tuple} instance),
1331
- all positional.
1612
+ an L{Fsum} or L{Fsum2Tuple} instance), all
1613
+ positional.
1332
1614
 
1333
1615
  @return: This instance, replaced (C{Fsum}).
1334
1616
 
1335
1617
  @see: Method L{Fsum.fadd} for further details.
1336
1618
  '''
1337
- f = xs[0] if len(xs) == 1 else (
1338
- Fsum(*xs) if xs else _0_0)
1339
- return self._fset(f)
1619
+ return self._fset(xs[0], op=_fset_op_) if len(xs) == 1 else \
1620
+ self._fset(_0_0)._facc_args(xs)
1340
1621
 
1341
1622
  def _fset(self, other, n=0, up=True, **op):
1342
1623
  '''(INTERNAL) Overwrite this instance with an other or a C{scalar}.
@@ -1346,7 +1627,6 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1346
1627
  elif _isFsumTuple(other):
1347
1628
  self._ps[:] = other._ps
1348
1629
  self._n = n or other._n
1349
- # self._copy_RESIDUAL(other)
1350
1630
  if up: # use or zap the C{Property_RO} values
1351
1631
  Fsum._fint2._update_from(self, other)
1352
1632
  Fsum._fprs ._update_from(self, other)
@@ -1362,13 +1642,13 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1362
1642
  self._fprs = s
1363
1643
  self._fprs2 = s, INT0
1364
1644
  # assert self._fprs is s
1365
- else: # PYCHOK no cover
1645
+ else:
1366
1646
  op = _xkwds_get1(op, op=_fset_op_)
1367
1647
  raise self._Error(op, other, _TypeError)
1368
1648
  return self
1369
1649
 
1370
- def _fset_ps(self, other): # in .fmath
1371
- '''(INTERNAL) Set partials from a known C{scalar}, L{Fsum} or L{Fsum2Tuple}.
1650
+ def _fset_ps(self, other): # in .fmath._Fsum__init__
1651
+ '''(INTERNAL) Set partials from a known C{other}.
1372
1652
  '''
1373
1653
  return self._fset(other, up=False)
1374
1654
 
@@ -1384,8 +1664,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1384
1664
 
1385
1665
  @see: Method L{Fsum.fadd_} for further details.
1386
1666
  '''
1387
- return self._fsub(xs[0], _sub_op_) if len(xs) == 1 else \
1388
- self._facc_neg(xs, origin=1)
1667
+ return self._facc_neg(xs, origin=1) if len(xs) != 1 else \
1668
+ self._fsub(xs[0], _sub_op_)
1389
1669
 
1390
1670
  def _fsub(self, other, op):
1391
1671
  '''(INTERNAL) Apply C{B{self} -= B{other}}.
@@ -1425,11 +1705,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1425
1705
 
1426
1706
  @see: Methods L{Fsum.fsum}, L{Fsum.Fsum_} and L{Fsum.fsumf_}.
1427
1707
  '''
1428
- return self._facc_1(xs)._fprs
1429
-
1430
- @property_RO
1431
- def _Fsum(self): # like L{Fsum2Tuple._Fsum}, for C{_2floats}, .fstats
1432
- return self # NOT @Property_RO, see .copy and ._copy_2
1708
+ return self._facc_args(xs)._fprs
1433
1709
 
1434
1710
  def Fsum_(self, *xs, **name):
1435
1711
  '''Like method L{Fsum.fsum_} but returning a named L{Fsum}.
@@ -1438,7 +1714,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1438
1714
 
1439
1715
  @return: Copy of this updated instance (L{Fsum}).
1440
1716
  '''
1441
- return self._facc_1(xs)._copy_2(self.Fsum_, **name)
1717
+ return self._facc_args(xs)._copy_2(self.Fsum_, **name)
1442
1718
 
1443
1719
  def Fsum2Tuple_(self, *xs, **name):
1444
1720
  '''Like method L{Fsum.fsum_} but returning a named L{Fsum2Tuple}.
@@ -1447,7 +1723,27 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1447
1723
 
1448
1724
  @return: Precision running sum (L{Fsum2Tuple}).
1449
1725
  '''
1450
- return Fsum2Tuple(self._facc_1(xs)._fprs2, **name)
1726
+ return Fsum2Tuple(self._facc_args(xs)._fprs2, **name)
1727
+
1728
+ @property_RO
1729
+ def _Fsum(self): # like L{Fsum2Tuple._Fsum}, for C{_2floats}, .fstats
1730
+ return self # NOT @Property_RO, see .copy and ._copy_2
1731
+
1732
+ def _Fsum_as(self, *xs, **name_f2product_nonfinites_RESIDUAL):
1733
+ '''(INTERNAL) Return an C{Fsum} with this C{Fsum}'s C{.f2product},
1734
+ C{.nonfinites} and C{.RESIDUAL} setting, optionally
1735
+ overridden with C{name_f2product_nonfinites_RESIDUAL} and
1736
+ with any C{xs} accumulated.
1737
+ '''
1738
+ kwds = _xkwds_not(None, Fsum._RESIDUAL, f2product =self.f2product(),
1739
+ nonfinites=self.nonfinites(),
1740
+ RESIDUAL =self.RESIDUAL())
1741
+ if name_f2product_nonfinites_RESIDUAL: # overwrites
1742
+ kwds.update(name_f2product_nonfinites_RESIDUAL)
1743
+ F = Fsum(**kwds)
1744
+ # assert all(v == self.__dict__[n] for n, v in F.__dict__.items())
1745
+ return F._fset(xs[0]) if len(xs) == 1 else (
1746
+ F._facc(xs, up=False) if xs else F)
1451
1747
 
1452
1748
  def fsum2(self, xs=(), **name):
1453
1749
  '''Add an iterable's items, summate and return the
@@ -1481,7 +1777,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1481
1777
 
1482
1778
  @see: Methods L{Fsum.fsum_} and L{Fsum.fsum}.
1483
1779
  '''
1484
- return self._fsum2(xs, self._facc_1)
1780
+ return self._fsum2(xs, self._facc_args)
1485
1781
 
1486
1782
  def _fsum2(self, xs, _facc, **origin):
1487
1783
  '''(INTERNAL) Helper for L{Fsum.fsum2_} and L{Fsum.fsum2f_}.
@@ -1489,7 +1785,10 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1489
1785
  p, q = self._fprs2
1490
1786
  if xs:
1491
1787
  s, r = _facc(xs, **origin)._fprs2
1492
- return s, _2delta(s - p, r - q) # _fsum(_1primed((s, -p, r, -q))
1788
+ if _isfinite(s): # _fsum(_1primed((s, -p, r, -q))
1789
+ d, r = _2sum(s - p, r - q, _isfine=_isOK)
1790
+ r, _ = _s_r(d, r)
1791
+ return s, (r if _isfinite(r) else _0_0)
1493
1792
  else:
1494
1793
  return p, _0_0
1495
1794
 
@@ -1554,8 +1853,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1554
1853
  @raise ResidualError: Non-zero, significant residual or invalid
1555
1854
  B{C{RESIDUAL}}.
1556
1855
 
1557
- @see: Methods L{Fsum.fint}, L{Fsum.fint2}, L{Fsum.RESIDUAL} and
1558
- property L{Fsum.as_iscalar}.
1856
+ @see: Methods L{Fsum.fint}, L{Fsum.fint2}, L{Fsum.is_integer},
1857
+ L{Fsum.RESIDUAL} and property L{Fsum.as_iscalar}.
1559
1858
  '''
1560
1859
  s, r = self._fint2
1561
1860
  if r:
@@ -1574,27 +1873,38 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1574
1873
  '''
1575
1874
  return self.residual is INT0
1576
1875
 
1876
+ def is_finite(self): # in .constants
1877
+ '''Is this instance C{finite}? (C{bool}).
1878
+
1879
+ @see: Function L{isfinite<pygeodesy.isfinite>}.
1880
+ '''
1881
+ return _isfinite(_sum(self._ps)) # _sum(self)
1882
+
1577
1883
  def is_integer(self):
1578
1884
  '''Is this instance' running sum C{integer}? (C{bool}).
1579
1885
 
1580
1886
  @see: Methods L{Fsum.fint}, L{Fsum.fint2} and L{Fsum.is_scalar}.
1581
1887
  '''
1582
- _, r = self._fint2
1583
- return False if r else True
1888
+ s, r = self._fint2
1889
+ return False if r else (_isfinite(s) and isint(s))
1890
+
1891
+ def is_math_fma(self):
1892
+ '''Is accurate L{f2product} multiplication based on Python's C{math.fma}?
1893
+
1894
+ @return: C{True} if accurate multiplication uses C{math.fma}, C{False}
1895
+ an C{fma} implementation as C{math.fma} or C{None}, a previous
1896
+ C{PyGeodesy} implementation.
1897
+ '''
1898
+ return (_fma.__module__ is fabs.__module__ or None) if _2split3s is _passarg else False
1584
1899
 
1585
1900
  def is_math_fsum(self):
1586
- '''Return whether functions L{fsum}, L{fsum_}, L{fsum1} and
1587
- L{fsum1_} plus partials summation are based on Python's
1588
- C{math.fsum} or not.
1901
+ '''Are the summation functions L{fsum}, L{fsum_}, L{fsumf_}, L{fsum1},
1902
+ L{fsum1_} and L{fsum1f_} based on Python's C{math.fsum}?
1589
1903
 
1590
- @return: C{2} if all functions and partials summation
1591
- are based on C{math.fsum}, C{True} if only
1592
- the functions are based on C{math.fsum} (and
1593
- partials summation is not) or C{False} if
1594
- none are.
1904
+ @return: C{True} if summation functions use C{math.fsum}, C{False}
1905
+ otherwise.
1595
1906
  '''
1596
- f = Fsum._math_fsum
1597
- return 2 if _psum is f else bool(f)
1907
+ return _sum is _fsum # _fsum.__module__ is fabs.__module__
1598
1908
 
1599
1909
  def is_scalar(self, **raiser_RESIDUAL):
1600
1910
  '''Is this instance' running sum C{scalar} without residual or with
@@ -1626,6 +1936,14 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1626
1936
  f = _0_0
1627
1937
  return f
1628
1938
 
1939
+ def _mul_reduce(self, op, start, *others):
1940
+ '''(INTERNAL) Like fmath.freduce(_operator.mul, ...)
1941
+ for I{non-finite} C{start} and/or C{others}.
1942
+ '''
1943
+ for p in self._ps_other(op, *others):
1944
+ start *= p
1945
+ return start
1946
+
1629
1947
  def _mul_scalar(self, factor, op): # in .fmath.Fhorner
1630
1948
  '''(INTERNAL) Return C{B{self} * scalar B{factor}} as L{Fsum}, C{0.0} or C{self}.
1631
1949
  '''
@@ -1649,6 +1967,67 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1649
1967
  '''
1650
1968
  return _Psum(self._ps_neg) if self._ps else NEG0
1651
1969
 
1970
+ def nonfinites(self, *OK):
1971
+ '''Handle I{non-finite} C{float}s as C{inf}, C{INF}, C{NINF}, C{nan}
1972
+ and C{NAN} for this L{Fsum} or throw C{OverflowError} respectively
1973
+ C{ValueError} exceptions, I{overriding the global setting} from
1974
+ function L{nonfiniterrors<fsums.nonfiniterrors>}.
1975
+
1976
+ @arg OK: If omitted, leave the override unchanged, if C{True},
1977
+ I{non-finites} are C{OK}, if C{False} throw exceptions
1978
+ or if C{None} remove the override (C{bool} or C{None}).
1979
+
1980
+ @return: The previous setting (C{bool} or C{None} if not set).
1981
+
1982
+ @see: Function L{nonfiniterrors<fsums.nonfiniterrors>}.
1983
+
1984
+ @note: Use C{f.nonfinites() or not nonfiniterrors()} to determine
1985
+ whether L{Fsum} C{f} handles I{non-finites}.
1986
+ '''
1987
+ _ks = Fsum._nonfinites_isfine_kwds
1988
+ if OK: # delattrof(self, _isfine=None)
1989
+ k = _xkwds_pop(self.__dict__, _isfine=None)
1990
+ if OK[0] is not None:
1991
+ self._isfine = _ks[bool(OK[0])]
1992
+ else: # getattrof(self, _isfine=None)
1993
+ k = _xkwds_get(self.__dict__, _isfine=None)
1994
+ # dict(map(reversed, _ks.items())).get(k, None)
1995
+ # raises a TypeError: unhashable type: 'dict'
1996
+ return True if k is _ks[True] else (
1997
+ False if k is _ks[False] else None)
1998
+
1999
+ _nonfinites_isfine_kwds = {True: dict(_isfine=_isOK),
2000
+ False: dict(_isfine=_isfinite)}
2001
+
2002
+ def _nonfiniteX(self, X, op, f):
2003
+ '''(INTERNAL) Handle a I{non-finite} exception.
2004
+ '''
2005
+ if not _isOK_or_finite(f, **self._isfine):
2006
+ raise self._ErrorX(X, op, f)
2007
+ return f
2008
+
2009
+ def _optionals(self, f2product=None, nonfinites=None, **name_RESIDUAL):
2010
+ '''(INTERNAL) Re/set options from keyword arguments.
2011
+ '''
2012
+ if f2product is not None:
2013
+ self.f2product(f2product)
2014
+ if nonfinites is not None:
2015
+ self.nonfinites(nonfinites)
2016
+ if name_RESIDUAL: # MUST be last
2017
+ n, kwds = _name2__(**name_RESIDUAL)
2018
+ if kwds:
2019
+ R = Fsum._RESIDUAL
2020
+ t = _threshold(R, **kwds)
2021
+ if t != R:
2022
+ self._RESIDUAL = t
2023
+ if n:
2024
+ self.name = n # self.rename(n)
2025
+
2026
+ def _1_Over(self, x, op, **raiser_RESIDUAL): # vs _1_over
2027
+ '''(INTERNAL) Return C{Fsum(1) / B{x}}.
2028
+ '''
2029
+ return self._Fsum_as(_1_0)._ftruediv(x, op, **raiser_RESIDUAL)
2030
+
1652
2031
  @property_RO
1653
2032
  def partials(self):
1654
2033
  '''Get this instance' current, partial sums (C{tuple} of C{float}s).
@@ -1658,7 +2037,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1658
2037
  def pow(self, x, *mod, **raiser_RESIDUAL):
1659
2038
  '''Return C{B{self}**B{x}} as L{Fsum}.
1660
2039
 
1661
- @arg x: The exponent (C{scalar} or L{Fsum}).
2040
+ @arg x: The exponent (C{scalar}, an L{Fsum} or L{Fsum2Tuple}).
1662
2041
  @arg mod: Optional modulus (C{int} or C{None}) for the 3-argument
1663
2042
  C{pow(B{self}, B{other}, B{mod})} version.
1664
2043
  @kwarg raiser_RESIDUAL: Use C{B{raiser}=False} to ignore
@@ -1745,7 +2124,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1745
2124
  _mul_Fsum = Fsum._mul_Fsum
1746
2125
  if x > 4:
1747
2126
  p = self
1748
- f = self if (x & 1) else _Psum_(_1_0)
2127
+ f = self if (x & 1) else self._Fsum_as(_1_0)
1749
2128
  m = x >> 1 # // 2
1750
2129
  while m:
1751
2130
  p = _mul_Fsum(p, p, op) # p **= 2
@@ -1783,14 +2162,14 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1783
2162
  if _isFsum(f):
1784
2163
  s, r = f._fprs2
1785
2164
  if r:
1786
- return _1_Over(f, op, **raiser_RESIDUAL)
2165
+ return self._1_Over(f, op, **raiser_RESIDUAL)
1787
2166
  else: # scalar
1788
2167
  s = f
1789
2168
  # use s**(-1) to get the CPython
1790
2169
  # float_pow error iff s is zero
1791
2170
  x = -1
1792
2171
  elif x < 0: # self**(-1)
1793
- return _1_Over(self, op, **raiser_RESIDUAL) # 1 / self
2172
+ return self._1_Over(self, op, **raiser_RESIDUAL) # 1 / self
1794
2173
  else: # self**1 or self**0
1795
2174
  return self._pow_0_1(x, other) # self, 1 or 1.0
1796
2175
  else: # self**fractional
@@ -1809,12 +2188,13 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1809
2188
  '''
1810
2189
  n = 0
1811
2190
  _2s = _2sum
2191
+ _fi = self._isfine
1812
2192
  for x in (tuple(xs) if xs is ps else xs):
1813
- # assert isscalar(x) and _isfinite(x)
2193
+ # assert isscalar(x) and _isOK_or_finite(x, **self._isfine)
1814
2194
  if x:
1815
2195
  i = 0
1816
2196
  for p in ps:
1817
- x, p = _2s(x, p)
2197
+ x, p = _2s(x, p, **_fi)
1818
2198
  if p:
1819
2199
  ps[i] = p
1820
2200
  i += 1
@@ -1822,6 +2202,10 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1822
2202
  n += 1
1823
2203
  if n:
1824
2204
  self._n += n
2205
+ # if _fi: # collapse ps if non-finite
2206
+ # x = _sum(ps)
2207
+ # if not _isfinite(x):
2208
+ # ps[:] = x,
1825
2209
  # Fsum._ps_max = max(Fsum._ps_max, len(ps))
1826
2210
  if up:
1827
2211
  self._update()
@@ -1831,16 +2215,23 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1831
2215
  '''(INTERNAL) Multiply this instance' C{partials} with
1832
2216
  each scalar C{factor} and accumulate into an C{Fsum}.
1833
2217
  '''
1834
- def _pfs(ps, fs):
2218
+ def _psfs(ps, fs, _isfine=_isfinite):
1835
2219
  if len(ps) < len(fs):
1836
2220
  ps, fs = fs, ps
1837
- _fin = _isfinite
2221
+ if self._f2product:
2222
+ ps = _2split3s(ps)
2223
+ _fps = _2products
2224
+ else:
2225
+ def _fps(f, ps):
2226
+ return (f * p for p in ps)
2227
+
1838
2228
  for f in fs:
1839
- for p in ps:
1840
- p *= f
1841
- yield p if _fin(p) else self._finite(p, op)
2229
+ for p in _fps(f, ps):
2230
+ yield p if _isfine(p) else self._finite(p, op)
1842
2231
 
1843
- return Fsum()._facc_scalar(_pfs(self._ps, factors), up=False)
2232
+ F = self._Fsum_as(name=op) # assert F._ps is not self._ps
2233
+ _s = _psfs(self._ps, factors, **self._isfine)
2234
+ return F._facc_scalar(_s, up=False)
1844
2235
 
1845
2236
  @property_RO
1846
2237
  def _ps_neg(self):
@@ -1849,10 +2240,20 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1849
2240
  for p in self._ps:
1850
2241
  yield -p
1851
2242
 
2243
+ def _ps_other(self, op, *others):
2244
+ '''(INTERNAL) Yield all C{other}s as C{scalar}.
2245
+ '''
2246
+ for other in others:
2247
+ if _isFsumTuple(other):
2248
+ for p in other._ps:
2249
+ yield p
2250
+ else:
2251
+ yield self._scalar(other, op)
2252
+
1852
2253
  def _ps_1sum(self, *less):
1853
2254
  '''(INTERNAL) Return the partials sum, 1-primed C{less} some scalars.
1854
2255
  '''
1855
- def _1pls(ps, ls):
2256
+ def _1psls(ps, ls):
1856
2257
  yield _1_0
1857
2258
  for p in ps:
1858
2259
  yield p
@@ -1860,7 +2261,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1860
2261
  yield -p
1861
2262
  yield _N_1_0
1862
2263
 
1863
- return _fsum(_1pls(self._ps, less))
2264
+ return _fsum(_1psls(self._ps, less))
1864
2265
 
1865
2266
  def _raiser(self, r, s, raiser=True, **RESIDUAL):
1866
2267
  '''(INTERNAL) Does ratio C{r / s} exceed the RESIDUAL threshold
@@ -1887,12 +2288,12 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1887
2288
  and properties L{Fsum.ceil}, L{Fsum.floor},
1888
2289
  L{Fsum.imag} and L{Fsum.residual}.
1889
2290
  '''
1890
- return float(self._fprs)
2291
+ return float(self)
1891
2292
 
1892
2293
  @property_RO
1893
2294
  def residual(self):
1894
- '''Get this instance' residual (C{float} or C{int}): the
1895
- C{sum(partials)} less the precision running sum C{fsum}.
2295
+ '''Get this instance' residual or residue (C{float} or C{int}):
2296
+ the C{sum(partials)} less the precision running sum C{fsum}.
1896
2297
 
1897
2298
  @note: The C{residual is INT0} iff the precision running
1898
2299
  C{fsum} is considered to be I{exact}.
@@ -1907,7 +2308,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1907
2308
 
1908
2309
  @arg threshold: If C{scalar}, the I{ratio} to exceed for raising
1909
2310
  L{ResidualError}s in division and exponention, if
1910
- C{None} restore the default set with env variable
2311
+ C{None}, restore the default set with env variable
1911
2312
  C{PYGEODESY_FSUM_RESIDUAL} or if omitted, keep the
1912
2313
  current setting.
1913
2314
 
@@ -1917,11 +2318,11 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1917
2318
 
1918
2319
  @note: L{ResidualError}s may be thrown if (1) the non-zero I{ratio}
1919
2320
  C{residual / fsum} exceeds the given B{C{threshold}} and (2)
1920
- the C{residual} is non-zero and (3) I{significant} vs the
2321
+ the C{residual} is non-zero and (3) is I{significant} vs the
1921
2322
  C{fsum}, i.e. C{(fsum + residual) != fsum} and (4) optional
1922
2323
  keyword argument C{raiser=False} is missing. Specify a
1923
2324
  negative B{C{threshold}} for only non-zero C{residual}
1924
- testing without I{significant}.
2325
+ testing without the I{significant} case.
1925
2326
  '''
1926
2327
  r = self._RESIDUAL
1927
2328
  if threshold:
@@ -1944,9 +2345,9 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1944
2345
  def root(self, root, **raiser_RESIDUAL):
1945
2346
  '''Return C{B{self}**(1 / B{root})} as L{Fsum}.
1946
2347
 
1947
- @arg root: The order (C{scalar} or L{Fsum}), non-zero.
1948
- @kwarg raiser_RESIDUAL: Use C{B{raiser}=False} to ignore
1949
- L{ResidualError}s (C{bool}) and C{B{RESIDUAL}=scalar}
2348
+ @arg root: The order (C{scalar}, L{Fsum} or L{Fsum2Tuple}), non-zero.
2349
+ @kwarg raiser_RESIDUAL: Use C{B{raiser}=False} to ignore any
2350
+ L{ResidualError}s (C{bool}) or C{B{RESIDUAL}=scalar}
1950
2351
  to override the current L{RESIDUAL<Fsum.RESIDUAL>}.
1951
2352
 
1952
2353
  @return: The C{self ** (1 / B{root})} result (L{Fsum}).
@@ -1956,8 +2357,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1956
2357
 
1957
2358
  @see: Method L{Fsum.pow}.
1958
2359
  '''
1959
- x = _1_Over(root, _truediv_op_, **raiser_RESIDUAL)
1960
- f = self._copy_2(self.root)
2360
+ x = self._1_Over(root, _truediv_op_, **raiser_RESIDUAL)
2361
+ f = self._copy_2(self.root)
1961
2362
  return f._fpow(x, f.name, **raiser_RESIDUAL) # == pow(f, x)
1962
2363
 
1963
2364
  def _scalar(self, other, op, **txt):
@@ -2026,9 +2427,12 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
2026
2427
 
2027
2428
  _ROs = _allPropertiesOf_n(3, Fsum, Property_RO) # PYCHOK see Fsum._update
2028
2429
 
2430
+ if _NONFINITES: # PYCHOK no cover
2431
+ _ = nonfiniterrors(False)
2432
+
2029
2433
 
2030
2434
  def _Float_Int(arg, **name_Error):
2031
- '''(INTERNAL) Unit of L{Fsum2Tuple} items.
2435
+ '''(INTERNAL) L{DivMod2Tuple}, L{Fsum2Tuple} Unit.
2032
2436
  '''
2033
2437
  U = Int if isint(arg) else Float
2034
2438
  return U(arg, **name_Error)
@@ -2041,7 +2445,7 @@ class DivMod2Tuple(_NamedTuple):
2041
2445
  @note: Quotient C{div} an C{int} in Python 3+ but a C{float}
2042
2446
  in Python 2-. Remainder C{mod} an L{Fsum} instance.
2043
2447
  '''
2044
- _Names_ = (_div_, _mod_)
2448
+ _Names_ = ('div', 'mod')
2045
2449
  _Units_ = (_Float_Int, Fsum)
2046
2450
 
2047
2451
 
@@ -2115,17 +2519,23 @@ class Fsum2Tuple(_NamedTuple): # in .fstats
2115
2519
  ps = (r, s) if r else (s,)
2116
2520
  return _Psum(ps, name=self.name)
2117
2521
 
2118
- def Fsum_(self, *xs, **name_RESIDUAL):
2522
+ def Fsum_(self, *xs, **name_f2product_nonfinites_RESIDUAL):
2119
2523
  '''Return this C{Fsum2Tuple} as an L{Fsum} plus some C{xs}.
2120
2524
  '''
2121
- f = _Psum(self._Fsum._ps, **name_RESIDUAL)
2122
- return f._facc_1(xs, up=False) if xs else f
2525
+ return Fsum(self, *xs, **name_f2product_nonfinites_RESIDUAL)
2123
2526
 
2124
2527
  def is_exact(self):
2125
2528
  '''Is this L{Fsum2Tuple} considered to be exact? (C{bool}).
2126
2529
  '''
2127
2530
  return self._Fsum.is_exact()
2128
2531
 
2532
+ def is_finite(self): # in .constants
2533
+ '''Is this L{Fsum2Tuple} C{finite}? (C{bool}).
2534
+
2535
+ @see: Function L{isfinite<pygeodesy.isfinite>}.
2536
+ '''
2537
+ return self._Fsum.is_finite()
2538
+
2129
2539
  def is_integer(self):
2130
2540
  '''Is this L{Fsum2Tuple} C{integer}? (C{bool}).
2131
2541
  '''
@@ -2186,105 +2596,142 @@ try:
2186
2596
  del _fsum # nope, remove _fsum ...
2187
2597
  raise ImportError() # ... use _fsum below
2188
2598
 
2189
- Fsum._math_fsum = _sum = _fsum # PYCHOK exported
2599
+ _sum = _fsum # in .elliptic
2190
2600
  except ImportError:
2191
- _sum = sum # Fsum(NAN) exception fall-back, in .elliptic
2601
+ _sum = sum # in .elliptic
2192
2602
 
2193
2603
  def _fsum(xs):
2194
2604
  '''(INTERNAL) Precision summation, Python 2.5-.
2195
2605
  '''
2196
- F = Fsum()
2197
- F.name = _fsum.__name__
2198
- return F._facc(xs, up=False)._fprs2.fsum
2606
+ F = Fsum(name=_fsum.name, nonfinites=True)
2607
+ return float(F._facc(xs, up=False))
2199
2608
 
2200
2609
 
2201
- def fsum(xs, floats=False):
2202
- '''Precision floating point summation based on/like Python's C{math.fsum}.
2610
+ def fsum(xs, nonfinites=None, **floats):
2611
+ '''Precision floating point summation from Python's C{math.fsum}.
2203
2612
 
2204
- @arg xs: Iterable of items to add (each C{scalar} or an L{Fsum} or L{Fsum2Tuple}
2205
- instance).
2206
- @kwarg floats: Use C{B{floats}=True} iff I{all} B{C{xs}} items are I{known to
2207
- be scalar} (C{bool}).
2613
+ @arg xs: Iterable of items to add (each C{scalar}, an L{Fsum} or L{Fsum2Tuple}).
2614
+ @kwarg nonfinites: Use C{B{nonfinites}=True} if I{non-finites} are C{OK}, if
2615
+ C{False} I{non-finites} raise an Overflow-/ValueError or if
2616
+ C{None}, apply C{B{nonfinites}=not }L{nonfiniterrors()}
2617
+ (C{bool} or C{None}).
2618
+ @kwarg floats: DEPRECATED keyword argument C{B{floats}=False} (C{bool}), use
2619
+ keyword argument C{B{nonfinites}=False} instead.
2208
2620
 
2209
2621
  @return: Precision C{fsum} (C{float}).
2210
2622
 
2211
- @raise OverflowError: Partial C{2sum} overflow.
2212
-
2213
- @raise TypeError: Non-scalar B{C{xs}} item.
2623
+ @raise OverflowError: Infinite B{C{xs}} item or intermediate C{math.fsum} overflow.
2214
2624
 
2215
- @raise ValueError: Invalid or non-finite B{C{xs}} item.
2625
+ @raise TypeError: Invalid B{C{xs}} item.
2216
2626
 
2217
- @note: Exception and I{non-finite} handling may differ if not based
2218
- on Python's C{math.fsum}.
2627
+ @raise ValueError: Invalid or C{NAN} B{C{xs}} item.
2219
2628
 
2220
- @see: Class L{Fsum} and methods L{Fsum.fsum} and L{Fsum.fadd}.
2629
+ @see: Function L{nonfiniterrors}, class L{Fsum} and methods L{Fsum.nonfinites},
2630
+ L{Fsum.fsum}, L{Fsum.fadd} and L{Fsum.fadd_}.
2221
2631
  '''
2222
- return _fsum(xs if floats is True else _2floats(xs)) if xs else _0_0 # PYCHOK yield
2632
+ return _xsum(fsum, xs, nonfinites=nonfinites, **floats) if xs else _0_0
2223
2633
 
2224
2634
 
2225
- def fsum_(*xs, **floats):
2635
+ def fsum_(*xs, **nonfinites):
2226
2636
  '''Precision floating point summation of all positional items.
2227
2637
 
2228
- @arg xs: Items to add (each C{scalar} or an L{Fsum} or L{Fsum2Tuple} instance),
2229
- all positional.
2230
- @kwarg floats: Use C{B{floats}=True} iff I{all} B{C{xs}} items are I{known to
2231
- be scalar} (C{bool}).
2638
+ @arg xs: Items to add (each C{scalar}, an L{Fsum} or L{Fsum2Tuple}), all positional.
2639
+ @kwarg nonfinites: Use C{B{nonfinites}=True} if I{non-finites} are C{OK} (C{bool}).
2232
2640
 
2233
2641
  @see: Function L{fsum<fsums.fsum>} for further details.
2234
2642
  '''
2235
- return _fsum(xs if _xkwds_get1(floats, floats=False) is True else
2236
- _2floats(xs, origin=1)) if xs else _0_0 # PYCHOK yield
2643
+ return _xsum(fsum_, xs, origin=1, **nonfinites) if xs else _0_0
2237
2644
 
2238
2645
 
2239
2646
  def fsumf_(*xs):
2240
- '''Precision floating point summation iff I{all} C{B{xs}} items are I{known to be scalar}.
2647
+ '''Precision floating point summation of all positional items with I{non-finites} C{OK}.
2648
+
2649
+ @arg xs: Items to add (each C{scalar}, an L{Fsum} or L{Fsum2Tuple}),
2650
+ all positional.
2241
2651
 
2242
2652
  @see: Function L{fsum_<fsums.fsum_>} for further details.
2243
2653
  '''
2244
- return _fsum(xs) if xs else _0_0
2654
+ return _xsum(fsumf_, xs, nonfinites=True, origin=1) if xs else _0_0
2245
2655
 
2246
2656
 
2247
- def fsum1(xs, floats=False):
2657
+ def fsum1(xs, **nonfinites):
2248
2658
  '''Precision floating point summation, 1-primed.
2249
2659
 
2250
- @arg xs: Iterable of items to add (each C{scalar} or an L{Fsum} or L{Fsum2Tuple}
2251
- instance).
2252
- @kwarg floats: Use C{B{floats}=True} iff I{all} B{C{xs}} items are I{known to
2253
- be scalar} (C{bool}).
2660
+ @arg xs: Iterable of items to add (each C{scalar}, an L{Fsum} or L{Fsum2Tuple}).
2661
+ @kwarg nonfinites: Use C{B{nonfinites}=True} if I{non-finites} are C{OK} (C{bool}).
2254
2662
 
2255
2663
  @see: Function L{fsum<fsums.fsum>} for further details.
2256
2664
  '''
2257
- return _fsum(_1primed(xs if floats is True else _2floats(xs))) if xs else _0_0 # PYCHOK yield
2665
+ return _xsum(fsum1, xs, primed=1, **nonfinites) if xs else _0_0
2258
2666
 
2259
2667
 
2260
- def fsum1_(*xs, **floats):
2261
- '''Precision floating point summation, 1-primed of all positional items.
2668
+ def fsum1_(*xs, **nonfinites):
2669
+ '''Precision floating point summation of all positional items, 1-primed.
2262
2670
 
2263
- @arg xs: Items to add (each C{scalar} or an L{Fsum} or L{Fsum2Tuple} instance),
2264
- all positional.
2265
- @kwarg floats: Use C{B{floats}=True} iff I{all} B{C{xs}} items are I{known to
2266
- be scalar} (C{bool}).
2671
+ @arg xs: Items to add (each C{scalar}, an L{Fsum} or L{Fsum2Tuple}), all positional.
2672
+ @kwarg nonfinites: Use C{B{nonfinites}=True} if I{non-finites} are C{OK} (C{bool}).
2267
2673
 
2268
2674
  @see: Function L{fsum_<fsums.fsum_>} for further details.
2269
2675
  '''
2270
- return _fsum(_1primed(xs if _xkwds_get1(floats, floats=False) is True else
2271
- _2floats(xs, origin=1))) if xs else _0_0 # PYCHOK yield
2676
+ return _xsum(fsum1_, xs, origin=1, primed=1, **nonfinites) if xs else _0_0
2272
2677
 
2273
2678
 
2274
2679
  def fsum1f_(*xs):
2275
- '''Precision floating point summation iff I{all} C{B{xs}} items are I{known to be scalar}.
2680
+ '''Precision floating point summation of all positional items, 1-primed and
2681
+ with I{non-finites} C{OK}.
2276
2682
 
2277
2683
  @see: Function L{fsum_<fsums.fsum_>} for further details.
2278
2684
  '''
2279
- return _fsum(_1primed(xs)) if xs else _0_0
2685
+ return _xsum(fsum1f_, xs, nonfinites=True, primed=1) if xs else _0_0
2280
2686
 
2281
2687
 
2282
- if __name__ == '__main__':
2688
+ def _xs(xs, _x, i_x):
2689
+ '''(INTERNAL) Yield all C{xs} as C{scalar}.
2690
+ '''
2691
+ for i, x in enumerate(xs):
2692
+ i_x[:] = i, x
2693
+ if _isFsumTuple(x):
2694
+ for p in map(_x, x._ps):
2695
+ yield p
2696
+ else:
2697
+ yield _x(x)
2698
+
2699
+
2700
+ def _xsError(X, xs, i, x, *n): # in _2floats, ._fstats
2701
+ '''(INTERNAL) Error for C{xs} or C{x}, item C{xs[i]}.
2702
+ '''
2703
+ return ((_xError(X, n[0], xs) if n else
2704
+ _xError(X, xs=xs)) if x is xs else
2705
+ _xError(X, Fmt.INDEX(xs=i), x))
2283
2706
 
2284
- # usage: [env _psum=fsum] python3 -m pygeodesy.fsums
2285
2707
 
2286
- if _getenv(_psum.__name__, NN) == _fsum.__name__:
2287
- _psum = _fsum
2708
+ def _xsum(which, xs, nonfinites=None, origin=0, primed=0, **floats):
2709
+ '''(INTERNAL) Precision summation of C{xs} with conditions.
2710
+ '''
2711
+ i_x = [0, xs]
2712
+ try:
2713
+ if nonfinites is None:
2714
+ nonfinites = not nonfiniterrors()
2715
+ elif floats:
2716
+ nonfinites = _xkwds_get1(floats, floats=nonfinites)
2717
+ fs = _xs(xs, (_passarg if nonfinites else _2finite), i_x)
2718
+ return _fsum(_1primed(fs) if primed else fs)
2719
+ except (OverflowError, TypeError, ValueError) as X:
2720
+ i, x = i_x
2721
+ i += origin - (1 if primed else 0)
2722
+ t = _xsError(X, xs, i, x)
2723
+ t = _COMMASPACE_(unstr(which), t)
2724
+ raise _xError(X, t, txt=None)
2725
+
2726
+
2727
+ # delete all decorators, etc.
2728
+ del _allPropertiesOf_n, deprecated_method, deprecated_property_RO, \
2729
+ Property, Property_RO, property_RO, _ALL_LAZY, _F2PRODUCT, \
2730
+ MANT_DIG, _NONFINITES, _RESIDUAL_0_0, _getenv, _std_
2731
+
2732
+ if __name__ == '__main__':
2733
+
2734
+ # usage: python3 -m pygeodesy.fsums
2288
2735
 
2289
2736
  def _test(n):
2290
2737
  # copied from Hettinger, see L{Fsum} reference