pygeodesy 24.9.9__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.
pygeodesy/fsums.py CHANGED
@@ -1,29 +1,40 @@
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
- Class L{Fsum} also supports accurate multiplication for Python 3.13 and
8
- later, but as an option for older Python versions. For more details, see
9
- method L{f2product<Fsum.f2product>}, class L{Fsum2product} and U{Accurate
10
- Sum and Dot Product<https://www.TUHH.De/ti3/paper/rump/OgRuOi05.pdf>}.
11
-
12
- Generally, an L{Fsum} instance is considered a C{float} plus a small or zero
13
- C{residual} value, see property L{Fsum.residual}. However, there are several
14
- C{integer} L{Fsum} cases, for example the result of C{ceil}, C{floor},
15
- C{Fsum.__floordiv__} and methods L{Fsum.fint} and L{Fsum.fint2}.
16
-
17
- Also, L{Fsum} methods L{Fsum.pow}, L{Fsum.__ipow__}, L{Fsum.__pow__} and
18
- L{Fsum.__rpow__} return a (very long) C{int} if invoked with optional argument
19
- C{mod} set to C{None}. The C{residual} of an C{integer} L{Fsum} may be between
20
- C{-1.0} and C{+1.0}, including C{INT0} if considered to be I{exact}.
21
-
22
- Set env variable C{PYGEODESY_FSUM_RESIDUAL} to a C{float} string greater than
23
- C{"0.0"} as the threshold to throw a L{ResidualError} for a division, power or
24
- 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
25
22
  the threshold. See methods L{Fsum.RESIDUAL}, L{Fsum.pow}, L{Fsum.__ipow__}
26
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.
27
38
  '''
28
39
  # make sure int/int division yields float quotient, see .basics
29
40
  from __future__ import division as _; del _ # PYCHOK semicolon
@@ -31,83 +42,73 @@ from __future__ import division as _; del _ # PYCHOK semicolon
31
42
  from pygeodesy.basics import isbool, iscomplex, isint, isscalar, \
32
43
  _signOf, itemsorted, signOf, _xiterable, \
33
44
  _xiterablen
34
- from pygeodesy.constants import INT0, _isfinite, MANT_DIG, NEG0, _pos_self, \
35
- _0_0, _1_0, _N_1_0, Float, Int
36
- from pygeodesy.errors import _OverflowError, _TypeError, _UnexpectedError, \
37
- _ValueError, _xError, _xError2, _xkwds_get1, \
38
- _xkwds_pop2
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
39
51
  from pygeodesy.internals import _enquote, _passarg
40
- from pygeodesy.interns import NN, _arg_, _COMMASPACE_, _DASH_, _DOT_, \
41
- _EQUAL_, _from_, _LANGLE_, _NOTEQUAL_, \
42
- _not_finite_, _PERCENT_, _PLUS_, \
43
- _RANGLE_, _SLASH_, _SPACE_, _STAR_, _UNDER_
52
+ from pygeodesy.interns import NN, _arg_, _COMMASPACE_, _DOT_, _from_, \
53
+ _not_finite_, _SPACE_, _std_, _UNDER_
44
54
  from pygeodesy.lazily import _ALL_LAZY, _getenv, _sys_version_info2
45
55
  from pygeodesy.named import _name__, _name2__, _Named, _NamedTuple, \
46
56
  _NotImplemented
47
- from pygeodesy.props import _allPropertiesOf_n, deprecated_property_RO, \
48
- Property, Property_RO, property_RO
57
+ from pygeodesy.props import _allPropertiesOf_n, deprecated_method, \
58
+ deprecated_property_RO, Property, \
59
+ Property_RO, property_RO
49
60
  from pygeodesy.streprs import Fmt, fstr, unstr
50
61
  # from pygeodesy.units import Float, Int # from .constants
51
62
 
52
- 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
53
65
 
54
66
  __all__ = _ALL_LAZY.fsums
55
- __version__ = '24.09.10'
56
-
57
- _add_op_ = _PLUS_ # in .auxilats.auxAngle
58
- _eq_op_ = _EQUAL_ * 2 # _DEQUAL_
59
- _div_ = 'div'
60
- _floordiv_op_ = _SLASH_ * 2 # _DSLASH_
61
- _fset_op_ = _EQUAL_
62
- _ge_op_ = _RANGLE_ + _EQUAL_
63
- _gt_op_ = _RANGLE_
64
- _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
65
86
  _integer_ = 'integer'
66
- _le_op_ = _LANGLE_ + _EQUAL_
67
- _lt_op_ = _LANGLE_
68
- _mod_ = 'mod'
69
- _mod_op_ = _PERCENT_
70
- _mul_op_ = _STAR_
71
- _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_
72
90
  _non_zero_ = 'non-zero'
73
- _pow_op_ = _STAR_ * 2 # _DSTAR_
91
+ _pow_op_ = _mul_op_ * 2 # _DSTAR_
92
+ _RESIDUAL_0_0 = _getenv('PYGEODESY_FSUM_RESIDUAL', _0_0)
74
93
  _significant_ = 'significant'
75
- _sub_op_ = _DASH_ # in .auxilats.auxAngle
94
+ _2split3s = _passarg
76
95
  _threshold_ = 'threshold'
77
- _truediv_op_ = _SLASH_
78
- _divmod_op_ = _floordiv_op_ + _mod_op_
79
- _isub_op_ = _sub_op_ + _fset_op_ # in .auxilats.auxAngle
80
96
 
81
97
 
82
- def _2delta(*ab):
83
- '''(INTERNAL) Helper for C{Fsum._fsum2}.
84
- '''
85
- try:
86
- a, b = _2sum(*ab)
87
- except _OverflowError:
88
- a, b = ab
89
- return float(a if fabs(a) > fabs(b) else b)
90
-
91
-
92
- def _2error(unused): # in .fstats
93
- '''(INTERNAL) Throw a C{not-finite} exception.
94
- '''
95
- raise ValueError(_not_finite_)
96
-
97
-
98
- def _2finite(x):
98
+ def _2finite(x): # in .fstats
99
99
  '''(INTERNAL) return C{float(x)} if finite.
100
100
  '''
101
- x = float(x)
102
- return x if _isfinite(x) else _2error(x)
101
+ return (float(x) if _isfinite(x) # and isscalar(x)
102
+ else _nfError(x))
103
103
 
104
104
 
105
- def _2float(index=None, **name_value): # in .fmath, .fstats
105
+ def _2float(index=None, _isfine=_isfinite, **name_value): # in .fmath, .fstats
106
106
  '''(INTERNAL) Raise C{TypeError} or C{ValueError} if not scalar or infinite.
107
107
  '''
108
108
  n, v = name_value.popitem() # _xkwds_item2(name_value)
109
109
  try:
110
- return _2finite(v)
110
+ f = float(v)
111
+ return f if _isfine(f) else _nfError(f)
111
112
  except Exception as X:
112
113
  raise _xError(X, Fmt.INDEX(n, index), v)
113
114
 
@@ -116,29 +117,82 @@ def _X_ps(X): # for _2floats only
116
117
  return X._ps
117
118
 
118
119
 
119
- def _2floats(xs, origin=0, _X=_X_ps, _x=float):
120
+ def _2floats(xs, origin=0, _X=_X_ps, _x=float, _isfine=_isfinite):
120
121
  '''(INTERNAL) Yield each B{C{xs}} as a C{float}.
121
122
  '''
122
123
  try:
123
- i, x = origin, _X
124
- _fin = _isfinite
124
+ i, x = origin, xs
125
125
  _FsT = _Fsum_Fsum2Tuple_types
126
- _isa = isinstance
127
126
  for x in _xiterable(xs):
128
- if _isa(x, _FsT):
127
+ if isinstance(x, _FsT):
129
128
  for p in _X(x._Fsum):
130
129
  yield p
131
130
  else:
132
131
  f = _x(x)
133
- yield f if _fin(f) else _2error(f)
132
+ yield f if _isfine(f) else _nfError(f)
134
133
  i += 1
135
134
  except Exception as X:
136
- raise _xError(X, xs=xs) if x is _X else \
137
- _xError(X, Fmt.INDEX(xs=i), x)
135
+ raise _xsError(X, xs, i, x)
138
136
 
139
137
 
140
- try: # MCCABE 14
138
+ try: # MCCABE 17
141
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
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)
151
+
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)
142
196
 
143
197
  def _2products(x, ys, **unused):
144
198
  # TwoProductFMA U{Algorithm 3.5
@@ -148,47 +202,22 @@ try: # MCCABE 14
148
202
  yield f
149
203
  yield _fma(x, y, -f)
150
204
 
151
- _2split3s = _passarg # NOP
152
-
153
- except ImportError: # Python 3.12-
154
-
155
- def _fma(*a_b_c): # in .fmath
156
- # mimick C{math.fma} from Python 3.13+
157
- # <https://MomentsInGraphics.De/FMA.html>
158
- # >>> a = 1.00000011920929
159
- # >>> b = 53400708
160
- # >>> c = -b
161
- # >>> _fma(a, b, c)
162
- # 6.365860485903399
163
- # >>> (a * b) + c
164
- # 6.3658604845404625
165
-
166
- def _as_n_d(x):
167
- try:
168
- if _isfinite(x):
169
- # int.as_integer_ratio since 3.8
170
- return x.as_integer_ratio()
171
- except (AttributeError, OverflowError, TypeError, ValueError):
172
- pass
173
- return float(x), 1
174
-
175
- (na, da), (nb, db), (nc, dc) = map(_as_n_d, a_b_c)
176
- n = na * nb * dc + da * db * nc
177
- d = da * db * dc
178
- return float(n / d)
205
+ else: # in _std_ _fma(a, b, c)
179
206
 
180
207
  def _2products(x, y3s, two=False): # PYCHOK redef
181
208
  # TwoProduct U{Algorithm 3.3
182
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
183
212
  _, a, b = _2split3(x)
184
213
  for y, c, d in y3s:
185
214
  y *= x
186
215
  yield y
187
- if two:
216
+ if two: # or not a:
188
217
  yield b * d - (((y - a * c) - b * c) - a * d)
189
- # = b * d + (a * d - ((y - a * c) - b * c))
190
- # = b * d + (a * d + (b * c - (y - a * c)))
191
- # = b * d + (a * d + (b * c + (a * c - y)))
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)))
192
221
  else:
193
222
  yield a * c - y
194
223
  yield b * c
@@ -196,29 +225,33 @@ except ImportError: # Python 3.12-
196
225
  yield a * d
197
226
  yield b * d
198
227
 
199
- _2FACTOR = pow(2, (MANT_DIG + 1) // 2) + 1
200
228
 
201
- def _2split3(x):
202
- # Split U{Algorithm 3.2
203
- # <ttps://www.TUHH.De/ti3/paper/rump/OgRuOi05.pdf>}
204
- a = c = x * _2FACTOR
205
- a -= c - x
206
- b = x - a
207
- return x, a, b
229
+ def f2product(*two):
230
+ '''Turn accurate I{TwoProduct} multiplication on or off.
208
231
 
209
- def _2split3s(xs): # PYCHOK redef
210
- return map(_2split3, xs)
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.
211
234
 
212
- del MANT_DIG
235
+ @return: The previous setting (C{bool}).
213
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
214
246
 
215
- def _Fsumf_(*xs): # floats=True, in .auxLat, ...
247
+
248
+ def _Fsumf_(*xs): # in .auxLat, .ltp, ...
216
249
  '''(INTERNAL) An C{Fsum} of I{known scalars}.
217
250
  '''
218
251
  return Fsum()._facc_scalar(xs, up=False)
219
252
 
220
253
 
221
- def _Fsum1f_(*xs): # floats=True, in .albers, ...
254
+ def _Fsum1f_(*xs): # in .albers
222
255
  '''(INTERNAL) An C{Fsum} of I{known scalars}, 1-primed.
223
256
  '''
224
257
  return Fsum()._facc_scalar(_1primed(xs), up=False)
@@ -242,16 +275,60 @@ def _isFsum(x): # in .fmath
242
275
  return isinstance(x, Fsum)
243
276
 
244
277
 
245
- def _isFsumTuple(x): # in .fmath
278
+ def _isFsumTuple(x): # in .basics, .constants, .fmath, .fstats
246
279
  '''(INTERNAL) Is C{x} an C{Fsum} or C{Fsum2Tuple} instance?
247
280
  '''
248
281
  return isinstance(x, _Fsum_Fsum2Tuple_types)
249
282
 
250
283
 
251
- def _1_Over(x, op, **raiser_RESIDUAL): # vs _1_over
252
- '''(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}.
253
320
  '''
254
- return _Psum_(_1_0)._ftruediv(x, op, **raiser_RESIDUAL)
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}.
329
+ '''
330
+ return _OverflowError if isinf(x) else (
331
+ _ValueError if isnan(x) else _AssertionError)
255
332
 
256
333
 
257
334
  def _1primed(xs): # in .fmath
@@ -264,16 +341,15 @@ def _1primed(xs): # in .fmath
264
341
  yield _N_1_0
265
342
 
266
343
 
267
- def _psum(ps): # PYCHOK used!
344
+ def _psum(ps, **_isfine): # PYCHOK used!
268
345
  '''(INTERNAL) Partials summation, updating C{ps}.
269
346
  '''
270
347
  # assert isinstance(ps, list)
271
- i = len(ps) - 1
272
- s = _0_0 if i < 0 else ps[i]
273
- _2s = _2sum
348
+ i = len(ps) - 1
349
+ s = _0_0 if i < 0 else ps[i]
274
350
  while i > 0:
275
351
  i -= 1
276
- s, r = _2s(s, ps[i])
352
+ s, r = _2sum(s, ps[i], **_isfine)
277
353
  if r: # sum(ps) became inexact
278
354
  if s:
279
355
  ps[i:] = r, s
@@ -281,24 +357,28 @@ def _psum(ps): # PYCHOK used!
281
357
  s = _2halfeven(s, r, ps[i-1])
282
358
  break # return s
283
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)
284
364
  ps[i:] = s,
285
365
  return s
286
366
 
287
367
 
288
- def _Psum(ps, **name_RESIDUAL):
368
+ def _Psum(ps, **name_f2product_nonfinites_RESIDUAL):
289
369
  '''(INTERNAL) Return an C{Fsum} from I{ordered} partials C{ps}.
290
370
  '''
291
- f = Fsum(**name_RESIDUAL) if name_RESIDUAL else Fsum()
371
+ F = Fsum(**name_f2product_nonfinites_RESIDUAL)
292
372
  if ps:
293
- f._ps[:] = ps
294
- f._n = len(f._ps)
295
- return f
373
+ F._ps[:] = ps
374
+ F._n = len(F._ps)
375
+ return F
296
376
 
297
377
 
298
- def _Psum_(*ps, **name_RESIDUAL):
299
- '''(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}.
300
380
  '''
301
- return _Psum(ps, **name_RESIDUAL)
381
+ return _Psum(ps, **name_f2product_nonfinites_RESIDUAL)
302
382
 
303
383
 
304
384
  def _2scalar2(other):
@@ -322,7 +402,7 @@ def _2scalar2(other):
322
402
  def _s_r(s, r):
323
403
  '''(INTERNAL) Return C{(s, r)}, I{ordered}.
324
404
  '''
325
- if r:
405
+ if r and _isfinite(s):
326
406
  if fabs(s) < fabs(r):
327
407
  s, r = r, (s or INT0)
328
408
  else:
@@ -334,7 +414,7 @@ def _strcomplex(s, *args):
334
414
  '''(INTERNAL) C{Complex} 2- or 3-arg C{pow} error as C{str}.
335
415
  '''
336
416
  c = _strcomplex.__name__[4:]
337
- n = _DASH_(len(args), _arg_)
417
+ n = _sub_op_(len(args), _arg_)
338
418
  t = unstr(pow, *args)
339
419
  return _SPACE_(c, s, _from_, n, t)
340
420
 
@@ -350,9 +430,12 @@ def _stresidual(prefix, residual, R=0, **mod_ratio):
350
430
  return _SPACE_(prefix, t, Fmt.exceeds_R(R), _threshold_)
351
431
 
352
432
 
353
- def _2sum(a, b): # by .testFmath
354
- '''(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.
355
436
  '''
437
+ # FastTwoSum U{Algorithm 1.1<https://www.TUHH.De/ti3/paper/rump/OgRuOi05.pdf>}
438
+
356
439
  # Neumaier, A. U{Rundungsfehleranalyse einiger Verfahren zur Summation endlicher
357
440
  # Summen<https://OnlineLibrary.Wiley.com/doi/epdf/10.1002/zamm.19740540106>},
358
441
  # 1974, Zeitschrift für Angewandte Mathmatik und Mechanik, vol 51, nr 1, p 39-51
@@ -363,10 +446,12 @@ def _2sum(a, b): # by .testFmath
363
446
  r = (b - s) + a
364
447
  else:
365
448
  r = (a - s) + b
366
- return s, r
367
- u = unstr(_2sum, a, b)
368
- t = Fmt.PARENSPACED(_not_finite_, s)
369
- 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
370
455
 
371
456
 
372
457
  def _threshold(threshold=_0_0, **kwds):
@@ -374,29 +459,28 @@ def _threshold(threshold=_0_0, **kwds):
374
459
  optionally from single kwds C{B{RESIDUAL}=scalar}.
375
460
  '''
376
461
  if kwds:
377
- threshold, kwds = _xkwds_pop2(kwds, RESIDUAL=threshold)
378
- # threshold = kwds.pop('RESIDUAL', threshold)
379
- if kwds:
380
- raise _UnexpectedError(**kwds)
462
+ threshold = _xkwds_get1(kwds, RESIDUAL=threshold)
381
463
  try:
382
464
  return _2finite(threshold) # PYCHOK None
383
465
  except Exception as x:
384
466
  raise ResidualError(threshold=threshold, cause=x)
385
467
 
386
468
 
387
- class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
388
- '''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.
389
471
 
390
472
  Unlike Python's C{math.fsum}, this class accumulates values and provides intermediate,
391
473
  I{running}, precision floating point summations. Accumulation may continue after any
392
474
  intermediate, I{running} summuation.
393
475
 
394
476
  @note: Values may be L{Fsum}, L{Fsum2Tuple}, C{int}, C{float} or C{scalar} instances,
395
- any C{type} having method C{__float__} to convert the C{scalar} to a single
396
- C{float}, except C{complex}.
477
+ i.e. any C{type} having method C{__float__}.
397
478
 
398
- @note: Handling of exceptions and C{inf}, C{INF}, C{nan} and C{NAN} differs from
399
- 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.
400
484
 
401
485
  @see: U{Hettinger<https://GitHub.com/ActiveState/code/tree/master/recipes/Python/
402
486
  393090_Binary_floating_point_summatiaccurate_full/recipe-393090.py>},
@@ -404,42 +488,41 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
404
488
  <https://Link.Springer.com/article/10.1007/s00607-005-0139-x>}, Python 2.6+
405
489
  file I{Modules/mathmodule.c} and the issue log U{Full precision summation
406
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}.
407
497
  '''
408
- _f2product = _2split3s is _passarg # True for 3.13+
409
- _math_fma = _fma if _f2product else None
410
- _math_fsum = None
498
+ _f2product = _sys_version_info2 > (3, 12) or bool(_F2PRODUCT)
499
+ _isfine = {} # == _isfinite
411
500
  _n = 0
412
501
  # _ps = [] # partial sums
413
502
  # _ps_max = 0 # max(Fsum._ps_max, len(Fsum._ps))
414
- _RESIDUAL = _threshold(_getenv('PYGEODESY_FSUM_RESIDUAL', _0_0))
503
+ _RESIDUAL = _threshold(_RESIDUAL_0_0)
415
504
 
416
- def __init__(self, *xs, **name_RESIDUAL):
417
- '''New L{Fsum} for I{running} precision floating point summation.
505
+ def __init__(self, *xs, **name_f2product_nonfinites_RESIDUAL):
506
+ '''New L{Fsum}.
418
507
 
419
- @arg xs: No, one or more initial items to add (each C{scalar} or
420
- an L{Fsum} or L{Fsum2Tuple} instance), all positional.
421
- @kwarg name_RESIDUAL: Optional C{B{name}=NN} (C{str}) for this
422
- L{Fsum} and the C{B{RESIDUAL}=0.0} threshold for
423
- 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}.
424
514
 
425
- @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_}.
426
517
  '''
427
- if name_RESIDUAL:
428
- n, kwds = _name2__(**name_RESIDUAL)
429
- if kwds:
430
- R = Fsum._RESIDUAL
431
- t = _threshold(R, **kwds)
432
- if t != R:
433
- self._RESIDUAL = t
434
- if n:
435
- self.name = n
436
-
518
+ if name_f2product_nonfinites_RESIDUAL:
519
+ self._optionals(**name_f2product_nonfinites_RESIDUAL)
437
520
  self._ps = [] # [_0_0], see L{Fsum._fprs}
438
521
  if xs:
439
- self._facc_1(xs, up=False)
522
+ self._facc_args(xs, up=False)
440
523
 
441
524
  def __abs__(self):
442
- '''Return this instance' absolute value as an L{Fsum}.
525
+ '''Return C{abs(self)} as an L{Fsum}.
443
526
  '''
444
527
  s = self.signOf() # == self._cmp_0(0)
445
528
  return (-self) if s < 0 else self._copy_2(self.__abs__)
@@ -457,7 +540,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
457
540
  return f._fadd(other, _add_op_)
458
541
 
459
542
  def __bool__(self): # PYCHOK Python 3+
460
- '''Return C{True} if this instance is I{exactly} non-zero.
543
+ '''Return C{bool(B{self})}, C{True} iff C{residual} is zero.
461
544
  '''
462
545
  s, r = self._fprs2
463
546
  return bool(s or r) and s != -r # == self != 0
@@ -500,7 +583,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
500
583
  return f._fdivmod2(other, _divmod_op_, **raiser_RESIDUAL)
501
584
 
502
585
  def __eq__(self, other):
503
- '''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}.
504
588
  '''
505
589
  return self._cmp_0(other, _eq_op_) == 0
506
590
 
@@ -537,17 +621,17 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
537
621
  return _NotImplemented(self, *other)
538
622
 
539
623
  def __ge__(self, other):
540
- '''Compare this with an other instance or C{scalar}.
624
+ '''Return C{(B{self} >= B{other})}, see C{__eq__}.
541
625
  '''
542
626
  return self._cmp_0(other, _ge_op_) >= 0
543
627
 
544
628
  def __gt__(self, other):
545
- '''Compare this with an other instance or C{scalar}.
629
+ '''Return C{(B{self} > B{other})}, see C{__eq__}.
546
630
  '''
547
631
  return self._cmp_0(other, _gt_op_) > 0
548
632
 
549
633
  def __hash__(self): # PYCHOK no cover
550
- '''Return this instance' C{hash}.
634
+ '''Return C{hash(B{self})} as C{float}.
551
635
  '''
552
636
  # @see: U{Notes for type implementors<https://docs.Python.org/
553
637
  # 3/library/numbers.html#numbers.Rational>}
@@ -569,7 +653,9 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
569
653
  try:
570
654
  return self._fadd(other, _iadd_op_)
571
655
  except TypeError:
572
- return self._facc_inplace(other, _iadd_op_, self._facc)
656
+ pass
657
+ _xiterable(other)
658
+ return self._facc(other)
573
659
 
574
660
  def __ifloordiv__(self, other):
575
661
  '''Apply C{B{self} //= B{other}} to this instance.
@@ -583,7 +669,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
583
669
 
584
670
  @raise TypeError: Invalid B{C{other}} type.
585
671
 
586
- @raise ValueError: Invalid or non-finite B{C{other}}.
672
+ @raise ValueError: Invalid or I{non-finite} B{C{other}}.
587
673
 
588
674
  @raise ZeroDivisionError: Zero B{C{other}}.
589
675
 
@@ -617,7 +703,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
617
703
 
618
704
  @raise TypeError: Invalid B{C{other}} type.
619
705
 
620
- @raise ValueError: Invalid or non-finite B{C{other}}.
706
+ @raise ValueError: Invalid or I{non-finite} B{C{other}}.
621
707
  '''
622
708
  return self._fmul(other, _mul_op_ + _fset_op_)
623
709
 
@@ -638,7 +724,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
638
724
  def __ipow__(self, other, *mod, **raiser_RESIDUAL): # PYCHOK 2 vs 3 args
639
725
  '''Apply C{B{self} **= B{other}} to this instance.
640
726
 
641
- @arg other: The exponent (C{scalar}, L{Fsum} or L{Fsum2Tuple}).
727
+ @arg other: The exponent (C{scalar}, an L{Fsum} or L{Fsum2Tuple}).
642
728
  @arg mod: Optional modulus (C{int} or C{None}) for the 3-argument
643
729
  C{pow(B{self}, B{other}, B{mod})} version.
644
730
  @kwarg raiser_RESIDUAL: Use C{B{raiser}=False} to ignore
@@ -688,7 +774,9 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
688
774
  try:
689
775
  return self._fsub(other, _isub_op_)
690
776
  except TypeError:
691
- return self._facc_inplace(other, _isub_op_, self._facc_neg)
777
+ pass
778
+ _xiterable(other)
779
+ return self._facc_neg(other)
692
780
 
693
781
  def __iter__(self):
694
782
  '''Return an C{iter}ator over a C{partials} duplicate.
@@ -712,7 +800,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
712
800
 
713
801
  @raise TypeError: Invalid B{C{other}} type.
714
802
 
715
- @raise ValueError: Invalid or non-finite B{C{other}}.
803
+ @raise ValueError: Invalid or I{non-finite} B{C{other}}.
716
804
 
717
805
  @raise ZeroDivisionError: Zero B{C{other}}.
718
806
 
@@ -721,7 +809,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
721
809
  return self._ftruediv(other, _truediv_op_ + _fset_op_, **raiser_RESIDUAL)
722
810
 
723
811
  def __le__(self, other):
724
- '''Compare this with an other instance or C{scalar}.
812
+ '''Return C{(B{self} <= B{other})}, see C{__eq__}.
725
813
  '''
726
814
  return self._cmp_0(other, _le_op_) <= 0
727
815
 
@@ -731,7 +819,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
731
819
  return self._n
732
820
 
733
821
  def __lt__(self, other):
734
- '''Compare this with an other instance or C{scalar}.
822
+ '''Return C{(B{self} < B{other})}, see C{__eq__}.
735
823
  '''
736
824
  return self._cmp_0(other, _lt_op_) < 0
737
825
 
@@ -756,12 +844,12 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
756
844
  return f._fmul(other, _mul_op_)
757
845
 
758
846
  def __ne__(self, other):
759
- '''Compare this with an other instance or C{scalar}.
847
+ '''Return C{(B{self} != B{other})}, see C{__eq__}.
760
848
  '''
761
849
  return self._cmp_0(other, _ne_op_) != 0
762
850
 
763
851
  def __neg__(self):
764
- '''Return I{a copy of} this instance, I{negated}.
852
+ '''Return C{copy(B{self})}, I{negated}.
765
853
  '''
766
854
  f = self._copy_2(self.__neg__)
767
855
  return f._fset(self._neg)
@@ -909,11 +997,13 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
909
997
  def as_integer_ratio(self):
910
998
  '''Return this instance as the ratio of 2 integers.
911
999
 
912
- @return: 2-Tuple C{(numerator, denominator)} both C{int}
913
- with C{numerator} signed and C{denominator}
914
- 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.
915
1004
 
916
- @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+.
917
1007
  '''
918
1008
  n, r = self._fint2
919
1009
  if r:
@@ -926,8 +1016,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
926
1016
 
927
1017
  @property_RO
928
1018
  def as_iscalar(self):
929
- '''Get this instance I{as-is} (L{Fsum} or C{scalar}), the
930
- 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}).
931
1021
  '''
932
1022
  s, r = self._fprs2
933
1023
  return self if r else s
@@ -996,13 +1086,6 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
996
1086
  return other._copy_2(which) if _isFsum(other) else \
997
1087
  self._copy_2(which)._fset(other)
998
1088
 
999
- # def _copy_RESIDUAL(self, other):
1000
- # '''(INTERNAL) Copy C{other._RESIDUAL}.
1001
- # '''
1002
- # R = other._RESIDUAL
1003
- # if R is not Fsum._RESIDUAL:
1004
- # self._RESIDUAL = R
1005
-
1006
1089
  divmod = __divmod__
1007
1090
 
1008
1091
  def _Error(self, op, other, Error, **txt_cause):
@@ -1029,28 +1112,21 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1029
1112
  '''(INTERNAL) Accumulate more C{scalars} or L{Fsum}s.
1030
1113
  '''
1031
1114
  if xs:
1032
- _xs = _2floats(xs, **origin_X_x) # PYCHOK yield
1115
+ kwds = _xkwds(self._isfine, **origin_X_x)
1116
+ _xs = _2floats(xs, **kwds) # PYCHOK yield
1033
1117
  ps = self._ps
1034
1118
  ps[:] = self._ps_acc(list(ps), _xs, up=up)
1035
1119
  return self
1036
1120
 
1037
- def _facc_1(self, xs, **up):
1038
- '''(INTERNAL) Accumulate 0, 1 or more C{scalars} or L{Fsum}s,
1039
- 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.
1040
1124
  '''
1041
- return self._fadd(xs[0], _add_op_, **up) if len(xs) == 1 else \
1042
- self._facc(xs, origin=1, **up)
1043
-
1044
- def _facc_inplace(self, other, op, _facc):
1045
- '''(INTERNAL) Accumulate from an iterable.
1046
- '''
1047
- try:
1048
- return _facc(other, origin=1) if _xiterable(other) else self
1049
- except Exception as X:
1050
- 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)
1051
1127
 
1052
1128
  def _facc_neg(self, xs, **up_origin):
1053
- '''(INTERNAL) Accumulate more C{scalars} or L{Fsum}s, negated.
1129
+ '''(INTERNAL) Accumulate more C{xs}, negated.
1054
1130
  '''
1055
1131
  def _N(X):
1056
1132
  return X._ps_neg
@@ -1075,24 +1151,22 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1075
1151
  p = s = int(p)
1076
1152
  m = Fsum._pow_int
1077
1153
  else:
1078
- p = s = _2float(power=p)
1154
+ p = s = _2float(power=p, **self._isfine)
1079
1155
  m = Fsum._pow_scalar
1080
1156
  return m, p, s, r
1081
1157
 
1082
1158
  _Pow, p, s, r = _Pow4(power)
1083
1159
  if p: # and xs:
1084
1160
  op = which.__name__
1085
- _flt = float
1086
- _Fs = Fsum
1087
- _isa = isinstance
1161
+ _FsT = _Fsum_Fsum2Tuple_types
1088
1162
  _pow = self._pow_2_3
1089
1163
 
1090
1164
  def _P(X):
1091
1165
  f = _Pow(X, p, power, op, **raiser_RESIDUAL)
1092
- return f._ps if _isa(f, _Fs) else (f,)
1166
+ return f._ps if isinstance(f, _FsT) else (f,)
1093
1167
 
1094
1168
  def _p(x):
1095
- x = _flt(x)
1169
+ x = float(x)
1096
1170
  f = _pow(x, s, power, op, **raiser_RESIDUAL)
1097
1171
  if f and r:
1098
1172
  f *= _pow(x, r, power, op, **raiser_RESIDUAL)
@@ -1143,12 +1217,13 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1143
1217
 
1144
1218
  @raise TypeError: An invalid B{C{xs}} item.
1145
1219
 
1146
- @raise ValueError: Invalid or non-finite B{C{xs}} value.
1220
+ @raise ValueError: Invalid or I{non-finite} B{C{xs}} value.
1147
1221
  '''
1148
1222
  if _isFsumTuple(xs):
1149
1223
  self._facc_scalar(xs._ps)
1150
- elif isscalar(xs): # for backward compatibility
1151
- 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)
1152
1227
  elif xs: # _xiterable(xs)
1153
1228
  self._facc(xs)
1154
1229
  return self
@@ -1161,17 +1236,21 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1161
1236
 
1162
1237
  @see: Method L{Fsum.fadd} for further details.
1163
1238
  '''
1164
- return self._facc_1(xs)
1239
+ return self._facc_args(xs)
1165
1240
 
1166
1241
  def _fadd(self, other, op, **up): # in .fmath.Fhorner
1167
1242
  '''(INTERNAL) Apply C{B{self} += B{other}}.
1168
1243
  '''
1169
- if not self._ps: # new Fsum(x)
1170
- self._fset(other, op=op, **up)
1171
- elif _isFsumTuple(other):
1172
- 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)
1173
1249
  elif self._scalar(other, op):
1174
- self._facc_scalar_(other, **up)
1250
+ if self._ps:
1251
+ self._facc_scalar_(other, **up)
1252
+ else:
1253
+ self._fset(other, op=op, **up)
1175
1254
  return self
1176
1255
 
1177
1256
  fcopy = copy # for backward compatibility
@@ -1186,7 +1265,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1186
1265
  # but at least divmod(-3, 2) equals Cpython's result (-2, 1).
1187
1266
  q = self._truediv(other, op, **raiser_RESIDUAL).floor
1188
1267
  if q: # == float // other == floor(float / other)
1189
- self -= Fsum(q) * other # NOT other * q!
1268
+ self -= self._Fsum_as(q) * other # NOT other * q!
1190
1269
 
1191
1270
  s = signOf(other) # make signOf(self) == signOf(other)
1192
1271
  if s and self.signOf() == -s: # PYCHOK no cover
@@ -1203,12 +1282,12 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1203
1282
  else sum(... i=len(cs)-1..0)}.
1204
1283
  '''
1205
1284
  if _xiterablen(cs):
1206
- H = Fsum(name__=self._fhorner)
1285
+ H = self._Fsum_as(name__=self._fhorner)
1207
1286
  if _isFsumTuple(x):
1208
1287
  _mul = H._mul_Fsum
1209
1288
  else:
1210
1289
  _mul = H._mul_scalar
1211
- x = _2float(x=x)
1290
+ x = _2float(x=x, **self._isfine)
1212
1291
  if len(cs) > 1 and x:
1213
1292
  for c in (reversed(cs) if incx else cs):
1214
1293
  H._fset_ps(_mul(x, op))
@@ -1221,10 +1300,10 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1221
1300
  def _finite(self, other, op=None):
1222
1301
  '''(INTERNAL) Return B{C{other}} if C{finite}.
1223
1302
  '''
1224
- if _isfinite(other):
1303
+ if _isOK_or_finite(other, **self._isfine):
1225
1304
  return other
1226
- raise ValueError(_not_finite_) if op is None else \
1227
- self._Error(op, other, _ValueError, txt=_not_finite_)
1305
+ E = _NonfiniteError(other)
1306
+ raise self._Error(op, other, E, txt=_not_finite_)
1228
1307
 
1229
1308
  def fint(self, name=NN, **raiser_RESIDUAL):
1230
1309
  '''Return this instance' current running sum as C{integer}.
@@ -1248,7 +1327,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1248
1327
  if R:
1249
1328
  t = _stresidual(_integer_, r, **R)
1250
1329
  raise ResidualError(_integer_, i, txt=t)
1251
- return _Psum_(i, name=_name__(name, name__=self.fint))
1330
+ return self._Fsum_as(i, name=_name__(name, name__=self.fint))
1252
1331
 
1253
1332
  def fint2(self, **name):
1254
1333
  '''Return this instance' current running sum as C{int} and the
@@ -1259,6 +1338,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1259
1338
  @return: An L{Fsum2Tuple}C{(fsum, residual)} with C{fsum}
1260
1339
  an C{int} and I{integer} C{residual} a C{float} or
1261
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.
1262
1342
  '''
1263
1343
  return Fsum2Tuple(*self._fint2, **name)
1264
1344
 
@@ -1266,18 +1346,35 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1266
1346
  def _fint2(self): # see ._fset
1267
1347
  '''(INTERNAL) Get 2-tuple (C{int}, I{integer} residual).
1268
1348
  '''
1269
- s, r = self._fprs2
1270
- i = int(s)
1271
- n = len(self._ps)
1272
- r = self._ps_1sum(i) if r and n > 1 else float(s - i)
1273
- return i, (r or INT0) # Fsum2Tuple?
1274
-
1275
- @_fint2.setter_ # PYCHOK setter_underscore!
1276
- 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
1277
1361
  '''(INTERNAL) Replace the C{_fint2} value.
1278
1362
  '''
1279
- i = int(s)
1280
- 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)
1281
1378
 
1282
1379
  @deprecated_property_RO
1283
1380
  def float_int(self): # PYCHOK no cover
@@ -1300,8 +1397,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1300
1397
  f -= 1
1301
1398
  return f # _floor(self._n_d)
1302
1399
 
1303
- # ffloordiv = __ifloordiv__ # for naming consistency
1304
- # floordiv = __floordiv__ # for naming consistency
1400
+ # ffloordiv = __ifloordiv__ # for naming consistency?
1401
+ # floordiv = __floordiv__ # for naming consistency?
1305
1402
 
1306
1403
  def _floordiv(self, other, op, **raiser_RESIDUAL): # rather _ffloordiv?
1307
1404
  '''Apply C{B{self} //= B{other}}.
@@ -1309,37 +1406,32 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1309
1406
  q = self._ftruediv(other, op, **raiser_RESIDUAL) # == self
1310
1407
  return self._fset(q.floor) # floor(q)
1311
1408
 
1312
- def fma(self, other1, other2): #
1409
+ def fma(self, other1, other2, raiser=False): # in .fmath.fma
1313
1410
  '''Fused-multiply-add C{self *= B{other1}; self += B{other2}}.
1314
1411
 
1315
- @arg other1: A C{scalar}, an L{Fsum} or L{Fsum2Tuple} instance.
1316
- @arg other2: A C{scalar}, an L{Fsum} or L{Fsum2Tuple} instance.
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}).
1317
1416
 
1318
1417
  @note: Uses C{math.fma} in Python 3.13+, provided C{self},
1319
1418
  B{C{other1}} and B{C{other2}} are all C{scalar}.
1320
1419
  '''
1321
- if len(self._ps) == 1 and isscalar(other1, both=True) \
1322
- and isscalar(other2, both=True):
1323
- p = _fma(self._ps[0], other1, other2)
1324
- self._ps[:] = self._finite(p, self.fma.__name__),
1325
- if other2:
1326
- self._n += 1
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)
1327
1431
  else:
1328
- self._f2mul(self.fma.__name__, other1)
1329
- self += other2
1330
- return self
1331
-
1332
- # def _fma_scalar(self, op, x, *ys): # in .karney
1333
- # '''(INTERNAL) Apply C{self.fma(B{x}, B{y}) for B{y} in B{ys}}
1334
- # for scalar C{x} and C{y}s.
1335
- # '''
1336
- # ps = self._ps
1337
- # if ps and ys:
1338
- # for y in ys:
1339
- # ps[:] = self._ps_acc(list(y), _2products(x, _2split3s(ps)))
1340
- # for p in (ps if op else()):
1341
- # self._finite(p, op)
1342
- # return self
1432
+ f = self._f2mul(self.fma, other1, raiser=raiser)
1433
+ f += other2
1434
+ return self._fset(f)
1343
1435
 
1344
1436
  fmul = __imul__
1345
1437
 
@@ -1350,38 +1442,55 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1350
1442
  if len(self._ps) != 1:
1351
1443
  f = self._mul_Fsum(other, op)
1352
1444
  elif len(other._ps) != 1: # and len(self._ps) == 1
1353
- f = other._mul_scalar(self._ps[0], op)
1445
+ f = self._ps_mul(op, *other._ps)
1354
1446
  elif self._f2product: # len(other._ps) == 1
1355
1447
  f = self._mul_scalar(other._ps[0], op)
1356
1448
  else: # len(other._ps) == len(self._ps) == 1
1357
- f = self._finite(self._ps[0] * other._ps[0])
1449
+ f = self._finite(self._ps[0] * other._ps[0], op=op)
1358
1450
  else:
1359
1451
  s = self._scalar(other, op)
1360
1452
  f = self._mul_scalar(s, op)
1361
1453
  return self._fset(f) # n=len(self) + 1
1362
1454
 
1363
- def f2mul(self, *others):
1364
- '''Apply C{B{self} *= B{other} for B{other} in B{others}} where each B{other}
1365
- is C{scalar}, an L{Fsum} or L{Fsum2Tuple} applying accurate multiplication
1366
- as if L{f2product<Fsum.f2product>}C{=True}.
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))
1367
1459
 
1368
- @see: U{Equations 2.3<https://www.TUHH.De/ti3/paper/rump/OzOgRuOi06.pdf>}
1369
- '''
1370
- return self._f2mul(self.f2mul.__name__, *others)
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}).
1371
1468
 
1372
- def _f2mul(self, op, *others):
1373
- '''(INTERNAL) See method C{f2mul}.
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>}
1374
1472
  '''
1375
- P = _Psum(self._ps)
1376
- ps = P._ps
1377
- if ps and others:
1378
- for p in self._ps_other(op, *others):
1379
- pfs = _2products(p, _2split3s(ps))
1380
- ps[:] = P._ps_acc([], pfs, up=False)
1381
- for p in ps:
1382
- self._finite(p, op)
1383
- self._fset(P, op=op)
1384
- return self
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
1385
1494
 
1386
1495
  def fover(self, over, **raiser_RESIDUAL):
1387
1496
  '''Apply C{B{self} /= B{over}} and summate.
@@ -1410,32 +1519,38 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1410
1519
  f = self._pow_2_3(self, other, other, op, *mod, **raiser_RESIDUAL)
1411
1520
  elif self.is_integer():
1412
1521
  # return an exact C{int} for C{int}**C{int}
1413
- i, _ = self._fint2 # assert _ == 0
1522
+ i, _ = self._fint2 # assert _ == 0
1414
1523
  x, r = _2scalar2(other) # C{int}, C{float} or other
1415
- f = _Psum_(i)._pow_Fsum(other, op, **raiser_RESIDUAL) if r else \
1416
- 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)
1417
1526
  else: # mod[0] is None, power(self, other)
1418
- f = self._pow(other, other, op, **raiser_RESIDUAL)
1527
+ f = self._pow(other, other, op, **raiser_RESIDUAL)
1419
1528
  else: # pow(self, other)
1420
1529
  f = self._pow(other, other, op, **raiser_RESIDUAL)
1421
1530
  return self._fset(f) # n=max(len(self), 1)
1422
1531
 
1423
1532
  def f2product(self, *two):
1424
- '''Turn this instance' accurate I{TwoProduct} multiplication or or off.
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>}.
1425
1536
 
1426
- @arg two: If C{True}, turn I{TwoProduct} on, if C{False} off or if
1427
- C{None} or if omitted, keep the current setting.
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}).
1428
1540
 
1429
- @return: The previous C{f2product} setting (C{bool}).
1541
+ @return: The previous setting (C{bool} or C{None} if not set).
1430
1542
 
1431
- @see: On Python 3.13 and later I{TwoProduct} is based on I{TwoProductFMA}
1432
- U{Algorithm 3.5<https://www.TUHH.De/ti3/paper/rump/OgRuOi05.pdf>}
1433
- otherwise on the slower I{TwoProduct} and I{Split} U{Algorithms
1434
- 3.3 and 3.2<https://www.TUHH.De/ti3/paper/rump/OgRuOi05.pdf>}.
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}.
1435
1547
  '''
1436
- t = self._f2product
1437
- if two and two[0] is not None:
1438
- self._f2product = bool(two[0])
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)
1439
1554
  return t
1440
1555
 
1441
1556
  @Property
@@ -1461,21 +1576,26 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1461
1576
  running sum and residual (L{Fsum2Tuple}).
1462
1577
  '''
1463
1578
  ps = self._ps
1464
- n = len(ps) - 2
1465
- if n > 0: # len(ps) > 2
1466
- s = _psum(ps)
1467
- n = len(ps) - 2
1468
- if n > 0:
1469
- r = self._ps_1sum(s)
1470
- return Fsum2Tuple(*_s_r(s, r))
1471
- if n == 0: # len(ps) == 2
1472
- s, r = _s_r(*_2sum(*ps))
1473
- ps[:] = (r, s) if r else (s,)
1474
- elif ps: # len(ps) == 1
1475
- s, r = ps[0], INT0
1476
- else: # len(ps) == 0
1477
- s, r = _0_0, INT0
1478
- 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
1479
1599
  # assert self._ps is ps
1480
1600
  return Fsum2Tuple(s, r)
1481
1601
 
@@ -1486,19 +1606,18 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1486
1606
  return Fsum2Tuple(s_r)
1487
1607
 
1488
1608
  def fset_(self, *xs):
1489
- '''Replace this instance' value with all positional items.
1609
+ '''Apply C{B{self}.partials = Fsum(*B{xs}).partials}.
1490
1610
 
1491
1611
  @arg xs: Optional, new values (each C{scalar} or
1492
- an L{Fsum} or L{Fsum2Tuple} instance),
1493
- all positional.
1612
+ an L{Fsum} or L{Fsum2Tuple} instance), all
1613
+ positional.
1494
1614
 
1495
1615
  @return: This instance, replaced (C{Fsum}).
1496
1616
 
1497
1617
  @see: Method L{Fsum.fadd} for further details.
1498
1618
  '''
1499
- f = xs[0] if len(xs) == 1 else (
1500
- Fsum(*xs) if xs else _0_0)
1501
- 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)
1502
1621
 
1503
1622
  def _fset(self, other, n=0, up=True, **op):
1504
1623
  '''(INTERNAL) Overwrite this instance with an other or a C{scalar}.
@@ -1508,7 +1627,6 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1508
1627
  elif _isFsumTuple(other):
1509
1628
  self._ps[:] = other._ps
1510
1629
  self._n = n or other._n
1511
- # self._copy_RESIDUAL(other)
1512
1630
  if up: # use or zap the C{Property_RO} values
1513
1631
  Fsum._fint2._update_from(self, other)
1514
1632
  Fsum._fprs ._update_from(self, other)
@@ -1524,13 +1642,13 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1524
1642
  self._fprs = s
1525
1643
  self._fprs2 = s, INT0
1526
1644
  # assert self._fprs is s
1527
- else: # PYCHOK no cover
1645
+ else:
1528
1646
  op = _xkwds_get1(op, op=_fset_op_)
1529
1647
  raise self._Error(op, other, _TypeError)
1530
1648
  return self
1531
1649
 
1532
- def _fset_ps(self, other): # in .fmath
1533
- '''(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}.
1534
1652
  '''
1535
1653
  return self._fset(other, up=False)
1536
1654
 
@@ -1546,8 +1664,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1546
1664
 
1547
1665
  @see: Method L{Fsum.fadd_} for further details.
1548
1666
  '''
1549
- return self._fsub(xs[0], _sub_op_) if len(xs) == 1 else \
1550
- 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_)
1551
1669
 
1552
1670
  def _fsub(self, other, op):
1553
1671
  '''(INTERNAL) Apply C{B{self} -= B{other}}.
@@ -1587,11 +1705,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1587
1705
 
1588
1706
  @see: Methods L{Fsum.fsum}, L{Fsum.Fsum_} and L{Fsum.fsumf_}.
1589
1707
  '''
1590
- return self._facc_1(xs)._fprs
1591
-
1592
- @property_RO
1593
- def _Fsum(self): # like L{Fsum2Tuple._Fsum}, for C{_2floats}, .fstats
1594
- return self # NOT @Property_RO, see .copy and ._copy_2
1708
+ return self._facc_args(xs)._fprs
1595
1709
 
1596
1710
  def Fsum_(self, *xs, **name):
1597
1711
  '''Like method L{Fsum.fsum_} but returning a named L{Fsum}.
@@ -1600,7 +1714,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1600
1714
 
1601
1715
  @return: Copy of this updated instance (L{Fsum}).
1602
1716
  '''
1603
- return self._facc_1(xs)._copy_2(self.Fsum_, **name)
1717
+ return self._facc_args(xs)._copy_2(self.Fsum_, **name)
1604
1718
 
1605
1719
  def Fsum2Tuple_(self, *xs, **name):
1606
1720
  '''Like method L{Fsum.fsum_} but returning a named L{Fsum2Tuple}.
@@ -1609,7 +1723,27 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1609
1723
 
1610
1724
  @return: Precision running sum (L{Fsum2Tuple}).
1611
1725
  '''
1612
- 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)
1613
1747
 
1614
1748
  def fsum2(self, xs=(), **name):
1615
1749
  '''Add an iterable's items, summate and return the
@@ -1643,7 +1777,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1643
1777
 
1644
1778
  @see: Methods L{Fsum.fsum_} and L{Fsum.fsum}.
1645
1779
  '''
1646
- return self._fsum2(xs, self._facc_1)
1780
+ return self._fsum2(xs, self._facc_args)
1647
1781
 
1648
1782
  def _fsum2(self, xs, _facc, **origin):
1649
1783
  '''(INTERNAL) Helper for L{Fsum.fsum2_} and L{Fsum.fsum2f_}.
@@ -1651,7 +1785,10 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1651
1785
  p, q = self._fprs2
1652
1786
  if xs:
1653
1787
  s, r = _facc(xs, **origin)._fprs2
1654
- 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)
1655
1792
  else:
1656
1793
  return p, _0_0
1657
1794
 
@@ -1716,8 +1853,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1716
1853
  @raise ResidualError: Non-zero, significant residual or invalid
1717
1854
  B{C{RESIDUAL}}.
1718
1855
 
1719
- @see: Methods L{Fsum.fint}, L{Fsum.fint2}, L{Fsum.RESIDUAL} and
1720
- 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}.
1721
1858
  '''
1722
1859
  s, r = self._fint2
1723
1860
  if r:
@@ -1736,27 +1873,38 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1736
1873
  '''
1737
1874
  return self.residual is INT0
1738
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
+
1739
1883
  def is_integer(self):
1740
1884
  '''Is this instance' running sum C{integer}? (C{bool}).
1741
1885
 
1742
1886
  @see: Methods L{Fsum.fint}, L{Fsum.fint2} and L{Fsum.is_scalar}.
1743
1887
  '''
1744
- _, r = self._fint2
1745
- 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
1746
1899
 
1747
1900
  def is_math_fsum(self):
1748
- '''Return whether functions L{fsum}, L{fsum_}, L{fsum1} and
1749
- L{fsum1_} plus partials summation are based on Python's
1750
- 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}?
1751
1903
 
1752
- @return: C{2} if all functions and partials summation
1753
- are based on C{math.fsum}, C{True} if only
1754
- the functions are based on C{math.fsum} (and
1755
- partials summation is not) or C{False} if
1756
- none are.
1904
+ @return: C{True} if summation functions use C{math.fsum}, C{False}
1905
+ otherwise.
1757
1906
  '''
1758
- f = Fsum._math_fsum
1759
- return 2 if _psum is f else bool(f)
1907
+ return _sum is _fsum # _fsum.__module__ is fabs.__module__
1760
1908
 
1761
1909
  def is_scalar(self, **raiser_RESIDUAL):
1762
1910
  '''Is this instance' running sum C{scalar} without residual or with
@@ -1788,6 +1936,14 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1788
1936
  f = _0_0
1789
1937
  return f
1790
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
+
1791
1947
  def _mul_scalar(self, factor, op): # in .fmath.Fhorner
1792
1948
  '''(INTERNAL) Return C{B{self} * scalar B{factor}} as L{Fsum}, C{0.0} or C{self}.
1793
1949
  '''
@@ -1811,6 +1967,67 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1811
1967
  '''
1812
1968
  return _Psum(self._ps_neg) if self._ps else NEG0
1813
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
+
1814
2031
  @property_RO
1815
2032
  def partials(self):
1816
2033
  '''Get this instance' current, partial sums (C{tuple} of C{float}s).
@@ -1820,7 +2037,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1820
2037
  def pow(self, x, *mod, **raiser_RESIDUAL):
1821
2038
  '''Return C{B{self}**B{x}} as L{Fsum}.
1822
2039
 
1823
- @arg x: The exponent (C{scalar} or L{Fsum}).
2040
+ @arg x: The exponent (C{scalar}, an L{Fsum} or L{Fsum2Tuple}).
1824
2041
  @arg mod: Optional modulus (C{int} or C{None}) for the 3-argument
1825
2042
  C{pow(B{self}, B{other}, B{mod})} version.
1826
2043
  @kwarg raiser_RESIDUAL: Use C{B{raiser}=False} to ignore
@@ -1907,7 +2124,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1907
2124
  _mul_Fsum = Fsum._mul_Fsum
1908
2125
  if x > 4:
1909
2126
  p = self
1910
- f = self if (x & 1) else _Psum_(_1_0)
2127
+ f = self if (x & 1) else self._Fsum_as(_1_0)
1911
2128
  m = x >> 1 # // 2
1912
2129
  while m:
1913
2130
  p = _mul_Fsum(p, p, op) # p **= 2
@@ -1945,14 +2162,14 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1945
2162
  if _isFsum(f):
1946
2163
  s, r = f._fprs2
1947
2164
  if r:
1948
- return _1_Over(f, op, **raiser_RESIDUAL)
2165
+ return self._1_Over(f, op, **raiser_RESIDUAL)
1949
2166
  else: # scalar
1950
2167
  s = f
1951
2168
  # use s**(-1) to get the CPython
1952
2169
  # float_pow error iff s is zero
1953
2170
  x = -1
1954
2171
  elif x < 0: # self**(-1)
1955
- return _1_Over(self, op, **raiser_RESIDUAL) # 1 / self
2172
+ return self._1_Over(self, op, **raiser_RESIDUAL) # 1 / self
1956
2173
  else: # self**1 or self**0
1957
2174
  return self._pow_0_1(x, other) # self, 1 or 1.0
1958
2175
  else: # self**fractional
@@ -1971,12 +2188,13 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1971
2188
  '''
1972
2189
  n = 0
1973
2190
  _2s = _2sum
2191
+ _fi = self._isfine
1974
2192
  for x in (tuple(xs) if xs is ps else xs):
1975
- # assert isscalar(x) and _isfinite(x)
2193
+ # assert isscalar(x) and _isOK_or_finite(x, **self._isfine)
1976
2194
  if x:
1977
2195
  i = 0
1978
2196
  for p in ps:
1979
- x, p = _2s(x, p)
2197
+ x, p = _2s(x, p, **_fi)
1980
2198
  if p:
1981
2199
  ps[i] = p
1982
2200
  i += 1
@@ -1984,6 +2202,10 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1984
2202
  n += 1
1985
2203
  if n:
1986
2204
  self._n += n
2205
+ # if _fi: # collapse ps if non-finite
2206
+ # x = _sum(ps)
2207
+ # if not _isfinite(x):
2208
+ # ps[:] = x,
1987
2209
  # Fsum._ps_max = max(Fsum._ps_max, len(ps))
1988
2210
  if up:
1989
2211
  self._update()
@@ -1993,22 +2215,23 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1993
2215
  '''(INTERNAL) Multiply this instance' C{partials} with
1994
2216
  each scalar C{factor} and accumulate into an C{Fsum}.
1995
2217
  '''
1996
- def _pfs(ps, fs):
2218
+ def _psfs(ps, fs, _isfine=_isfinite):
1997
2219
  if len(ps) < len(fs):
1998
2220
  ps, fs = fs, ps
1999
2221
  if self._f2product:
2000
- ps = tuple(_2split3s(ps))
2001
- _xys = _2products
2222
+ ps = _2split3s(ps)
2223
+ _fps = _2products
2002
2224
  else:
2003
- def _xys(x, ys):
2004
- return (x * y for y in ys)
2225
+ def _fps(f, ps):
2226
+ return (f * p for p in ps)
2005
2227
 
2006
- _fin = _isfinite
2007
2228
  for f in fs:
2008
- for p in _xys(f, ps):
2009
- 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)
2010
2231
 
2011
- 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)
2012
2235
 
2013
2236
  @property_RO
2014
2237
  def _ps_neg(self):
@@ -2018,7 +2241,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
2018
2241
  yield -p
2019
2242
 
2020
2243
  def _ps_other(self, op, *others):
2021
- '''(INTERNAL) Yield the partials of all C{other}s.
2244
+ '''(INTERNAL) Yield all C{other}s as C{scalar}.
2022
2245
  '''
2023
2246
  for other in others:
2024
2247
  if _isFsumTuple(other):
@@ -2030,7 +2253,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
2030
2253
  def _ps_1sum(self, *less):
2031
2254
  '''(INTERNAL) Return the partials sum, 1-primed C{less} some scalars.
2032
2255
  '''
2033
- def _1pls(ps, ls):
2256
+ def _1psls(ps, ls):
2034
2257
  yield _1_0
2035
2258
  for p in ps:
2036
2259
  yield p
@@ -2038,7 +2261,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
2038
2261
  yield -p
2039
2262
  yield _N_1_0
2040
2263
 
2041
- return _fsum(_1pls(self._ps, less))
2264
+ return _fsum(_1psls(self._ps, less))
2042
2265
 
2043
2266
  def _raiser(self, r, s, raiser=True, **RESIDUAL):
2044
2267
  '''(INTERNAL) Does ratio C{r / s} exceed the RESIDUAL threshold
@@ -2065,12 +2288,12 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
2065
2288
  and properties L{Fsum.ceil}, L{Fsum.floor},
2066
2289
  L{Fsum.imag} and L{Fsum.residual}.
2067
2290
  '''
2068
- return float(self._fprs)
2291
+ return float(self)
2069
2292
 
2070
2293
  @property_RO
2071
2294
  def residual(self):
2072
- '''Get this instance' residual (C{float} or C{int}): the
2073
- 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}.
2074
2297
 
2075
2298
  @note: The C{residual is INT0} iff the precision running
2076
2299
  C{fsum} is considered to be I{exact}.
@@ -2085,7 +2308,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
2085
2308
 
2086
2309
  @arg threshold: If C{scalar}, the I{ratio} to exceed for raising
2087
2310
  L{ResidualError}s in division and exponention, if
2088
- C{None} restore the default set with env variable
2311
+ C{None}, restore the default set with env variable
2089
2312
  C{PYGEODESY_FSUM_RESIDUAL} or if omitted, keep the
2090
2313
  current setting.
2091
2314
 
@@ -2095,11 +2318,11 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
2095
2318
 
2096
2319
  @note: L{ResidualError}s may be thrown if (1) the non-zero I{ratio}
2097
2320
  C{residual / fsum} exceeds the given B{C{threshold}} and (2)
2098
- 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
2099
2322
  C{fsum}, i.e. C{(fsum + residual) != fsum} and (4) optional
2100
2323
  keyword argument C{raiser=False} is missing. Specify a
2101
2324
  negative B{C{threshold}} for only non-zero C{residual}
2102
- testing without I{significant}.
2325
+ testing without the I{significant} case.
2103
2326
  '''
2104
2327
  r = self._RESIDUAL
2105
2328
  if threshold:
@@ -2122,9 +2345,9 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
2122
2345
  def root(self, root, **raiser_RESIDUAL):
2123
2346
  '''Return C{B{self}**(1 / B{root})} as L{Fsum}.
2124
2347
 
2125
- @arg root: The order (C{scalar} or L{Fsum}), non-zero.
2126
- @kwarg raiser_RESIDUAL: Use C{B{raiser}=False} to ignore
2127
- 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}
2128
2351
  to override the current L{RESIDUAL<Fsum.RESIDUAL>}.
2129
2352
 
2130
2353
  @return: The C{self ** (1 / B{root})} result (L{Fsum}).
@@ -2134,8 +2357,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
2134
2357
 
2135
2358
  @see: Method L{Fsum.pow}.
2136
2359
  '''
2137
- x = _1_Over(root, _truediv_op_, **raiser_RESIDUAL)
2138
- f = self._copy_2(self.root)
2360
+ x = self._1_Over(root, _truediv_op_, **raiser_RESIDUAL)
2361
+ f = self._copy_2(self.root)
2139
2362
  return f._fpow(x, f.name, **raiser_RESIDUAL) # == pow(f, x)
2140
2363
 
2141
2364
  def _scalar(self, other, op, **txt):
@@ -2204,23 +2427,17 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
2204
2427
 
2205
2428
  _ROs = _allPropertiesOf_n(3, Fsum, Property_RO) # PYCHOK see Fsum._update
2206
2429
 
2430
+ if _NONFINITES: # PYCHOK no cover
2431
+ _ = nonfiniterrors(False)
2432
+
2207
2433
 
2208
2434
  def _Float_Int(arg, **name_Error):
2209
- '''(INTERNAL) Unit of L{Fsum2Tuple} items.
2435
+ '''(INTERNAL) L{DivMod2Tuple}, L{Fsum2Tuple} Unit.
2210
2436
  '''
2211
2437
  U = Int if isint(arg) else Float
2212
2438
  return U(arg, **name_Error)
2213
2439
 
2214
2440
 
2215
- def Fsum2product(*xs, **name_RESIDUAL):
2216
- '''Return an L{Fsum} with L{f2product<Fsum.f2product>} accurate
2217
- multiplication I{turned on}.
2218
- '''
2219
- F = Fsum(*xs, **name_RESIDUAL)
2220
- F.f2product(True)
2221
- return F
2222
-
2223
-
2224
2441
  class DivMod2Tuple(_NamedTuple):
2225
2442
  '''2-Tuple C{(div, mod)} with the quotient C{div} and remainder
2226
2443
  C{mod} results of a C{divmod} operation.
@@ -2228,7 +2445,7 @@ class DivMod2Tuple(_NamedTuple):
2228
2445
  @note: Quotient C{div} an C{int} in Python 3+ but a C{float}
2229
2446
  in Python 2-. Remainder C{mod} an L{Fsum} instance.
2230
2447
  '''
2231
- _Names_ = (_div_, _mod_)
2448
+ _Names_ = ('div', 'mod')
2232
2449
  _Units_ = (_Float_Int, Fsum)
2233
2450
 
2234
2451
 
@@ -2302,17 +2519,23 @@ class Fsum2Tuple(_NamedTuple): # in .fstats
2302
2519
  ps = (r, s) if r else (s,)
2303
2520
  return _Psum(ps, name=self.name)
2304
2521
 
2305
- def Fsum_(self, *xs, **name_RESIDUAL):
2522
+ def Fsum_(self, *xs, **name_f2product_nonfinites_RESIDUAL):
2306
2523
  '''Return this C{Fsum2Tuple} as an L{Fsum} plus some C{xs}.
2307
2524
  '''
2308
- f = _Psum(self._Fsum._ps, **name_RESIDUAL)
2309
- return f._facc_1(xs, up=False) if xs else f
2525
+ return Fsum(self, *xs, **name_f2product_nonfinites_RESIDUAL)
2310
2526
 
2311
2527
  def is_exact(self):
2312
2528
  '''Is this L{Fsum2Tuple} considered to be exact? (C{bool}).
2313
2529
  '''
2314
2530
  return self._Fsum.is_exact()
2315
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
+
2316
2539
  def is_integer(self):
2317
2540
  '''Is this L{Fsum2Tuple} C{integer}? (C{bool}).
2318
2541
  '''
@@ -2373,105 +2596,142 @@ try:
2373
2596
  del _fsum # nope, remove _fsum ...
2374
2597
  raise ImportError() # ... use _fsum below
2375
2598
 
2376
- Fsum._math_fsum = _sum = _fsum # PYCHOK exported
2599
+ _sum = _fsum # in .elliptic
2377
2600
  except ImportError:
2378
- _sum = sum # Fsum(NAN) exception fall-back, in .elliptic
2601
+ _sum = sum # in .elliptic
2379
2602
 
2380
2603
  def _fsum(xs):
2381
2604
  '''(INTERNAL) Precision summation, Python 2.5-.
2382
2605
  '''
2383
- F = Fsum()
2384
- F.name = _fsum.__name__
2385
- return F._facc(xs, up=False)._fprs2.fsum
2606
+ F = Fsum(name=_fsum.name, nonfinites=True)
2607
+ return float(F._facc(xs, up=False))
2386
2608
 
2387
2609
 
2388
- def fsum(xs, floats=False):
2389
- '''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}.
2390
2612
 
2391
- @arg xs: Iterable of items to add (each C{scalar} or an L{Fsum} or L{Fsum2Tuple}
2392
- instance).
2393
- @kwarg floats: Use C{B{floats}=True} iff I{all} B{C{xs}} items are I{known to
2394
- 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.
2395
2620
 
2396
2621
  @return: Precision C{fsum} (C{float}).
2397
2622
 
2398
- @raise OverflowError: Partial C{2sum} overflow.
2399
-
2400
- @raise TypeError: Non-scalar B{C{xs}} item.
2623
+ @raise OverflowError: Infinite B{C{xs}} item or intermediate C{math.fsum} overflow.
2401
2624
 
2402
- @raise ValueError: Invalid or non-finite B{C{xs}} item.
2625
+ @raise TypeError: Invalid B{C{xs}} item.
2403
2626
 
2404
- @note: Exception and I{non-finite} handling may differ if not based
2405
- on Python's C{math.fsum}.
2627
+ @raise ValueError: Invalid or C{NAN} B{C{xs}} item.
2406
2628
 
2407
- @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_}.
2408
2631
  '''
2409
- 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
2410
2633
 
2411
2634
 
2412
- def fsum_(*xs, **floats):
2635
+ def fsum_(*xs, **nonfinites):
2413
2636
  '''Precision floating point summation of all positional items.
2414
2637
 
2415
- @arg xs: Items to add (each C{scalar} or an L{Fsum} or L{Fsum2Tuple} instance),
2416
- all positional.
2417
- @kwarg floats: Use C{B{floats}=True} iff I{all} B{C{xs}} items are I{known to
2418
- 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}).
2419
2640
 
2420
2641
  @see: Function L{fsum<fsums.fsum>} for further details.
2421
2642
  '''
2422
- return _fsum(xs if _xkwds_get1(floats, floats=False) is True else
2423
- _2floats(xs, origin=1)) if xs else _0_0 # PYCHOK yield
2643
+ return _xsum(fsum_, xs, origin=1, **nonfinites) if xs else _0_0
2424
2644
 
2425
2645
 
2426
2646
  def fsumf_(*xs):
2427
- '''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.
2428
2651
 
2429
2652
  @see: Function L{fsum_<fsums.fsum_>} for further details.
2430
2653
  '''
2431
- return _fsum(xs) if xs else _0_0
2654
+ return _xsum(fsumf_, xs, nonfinites=True, origin=1) if xs else _0_0
2432
2655
 
2433
2656
 
2434
- def fsum1(xs, floats=False):
2657
+ def fsum1(xs, **nonfinites):
2435
2658
  '''Precision floating point summation, 1-primed.
2436
2659
 
2437
- @arg xs: Iterable of items to add (each C{scalar} or an L{Fsum} or L{Fsum2Tuple}
2438
- instance).
2439
- @kwarg floats: Use C{B{floats}=True} iff I{all} B{C{xs}} items are I{known to
2440
- 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}).
2441
2662
 
2442
2663
  @see: Function L{fsum<fsums.fsum>} for further details.
2443
2664
  '''
2444
- 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
2445
2666
 
2446
2667
 
2447
- def fsum1_(*xs, **floats):
2448
- '''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.
2449
2670
 
2450
- @arg xs: Items to add (each C{scalar} or an L{Fsum} or L{Fsum2Tuple} instance),
2451
- all positional.
2452
- @kwarg floats: Use C{B{floats}=True} iff I{all} B{C{xs}} items are I{known to
2453
- 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}).
2454
2673
 
2455
2674
  @see: Function L{fsum_<fsums.fsum_>} for further details.
2456
2675
  '''
2457
- return _fsum(_1primed(xs if _xkwds_get1(floats, floats=False) is True else
2458
- _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
2459
2677
 
2460
2678
 
2461
2679
  def fsum1f_(*xs):
2462
- '''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}.
2463
2682
 
2464
2683
  @see: Function L{fsum_<fsums.fsum_>} for further details.
2465
2684
  '''
2466
- return _fsum(_1primed(xs)) if xs else _0_0
2685
+ return _xsum(fsum1f_, xs, nonfinites=True, primed=1) if xs else _0_0
2467
2686
 
2468
2687
 
2469
- 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))
2470
2706
 
2471
- # usage: [env _psum=fsum] python3 -m pygeodesy.fsums
2472
2707
 
2473
- if _getenv(_psum.__name__, NN) == _fsum.__name__:
2474
- _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
2475
2735
 
2476
2736
  def _test(n):
2477
2737
  # copied from Hettinger, see L{Fsum} reference