pygeodesy 24.9.9__py2.py3-none-any.whl → 24.9.29__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,197 +42,233 @@ 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, \
51
+ _xsError
39
52
  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_
53
+ from pygeodesy.interns import NN, _arg_, _COMMASPACE_, _DOT_, _from_, \
54
+ _not_finite_, _SPACE_, _std_, _UNDER_
44
55
  from pygeodesy.lazily import _ALL_LAZY, _getenv, _sys_version_info2
45
56
  from pygeodesy.named import _name__, _name2__, _Named, _NamedTuple, \
46
57
  _NotImplemented
47
- from pygeodesy.props import _allPropertiesOf_n, deprecated_property_RO, \
48
- Property, Property_RO, property_RO
58
+ from pygeodesy.props import _allPropertiesOf_n, deprecated_method, \
59
+ deprecated_property_RO, Property, \
60
+ Property_RO, property_RO
49
61
  from pygeodesy.streprs import Fmt, fstr, unstr
50
62
  # from pygeodesy.units import Float, Int # from .constants
51
63
 
52
- from math import ceil as _ceil, fabs, floor as _floor # PYCHOK used! .ltp
64
+ from math import fabs, isinf, isnan, \
65
+ ceil as _ceil, floor as _floor # PYCHOK used! .ltp
53
66
 
54
67
  __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
68
+ __version__ = '24.09.29'
69
+
70
+ from pygeodesy.interns import (
71
+ _PLUS_ as _add_op_, # in .auxilats.auxAngle
72
+ _EQUAL_ as _fset_op_,
73
+ _RANGLE_ as _gt_op_,
74
+ _LANGLE_ as _lt_op_,
75
+ _PERCENT_ as _mod_op_,
76
+ _STAR_ as _mul_op_,
77
+ _NOTEQUAL_ as _ne_op_,
78
+ _DASH_ as _sub_op_, # in .auxilats.auxAngle
79
+ _SLASH_ as _truediv_op_
80
+ )
81
+ _eq_op_ = _fset_op_ * 2 # _DEQUAL_
82
+ _floordiv_op_ = _truediv_op_ * 2 # _DSLASH_
83
+ _divmod_op_ = _floordiv_op_ + _mod_op_
84
+ _F2PRODUCT = _getenv('PYGEODESY_FSUM_F2PRODUCT', NN)
85
+ _ge_op_ = _gt_op_ + _fset_op_
86
+ _iadd_op_ = _add_op_ + _fset_op_ # in .auxilats.auxAngle, .fstats
65
87
  _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_
88
+ _isub_op_ = _sub_op_ + _fset_op_ # in .auxilats.auxAngle
89
+ _le_op_ = _lt_op_ + _fset_op_
90
+ _NONFINITEr = _0_0
91
+ _NONFINITES = _getenv('PYGEODESY_FSUM_NONFINITES', NN)
72
92
  _non_zero_ = 'non-zero'
73
- _pow_op_ = _STAR_ * 2 # _DSTAR_
93
+ _pow_op_ = _mul_op_ * 2 # _DSTAR_
94
+ _RESIDUAL_0_0 = _getenv('PYGEODESY_FSUM_RESIDUAL', _0_0)
74
95
  _significant_ = 'significant'
75
- _sub_op_ = _DASH_ # in .auxilats.auxAngle
76
96
  _threshold_ = 'threshold'
77
- _truediv_op_ = _SLASH_
78
- _divmod_op_ = _floordiv_op_ + _mod_op_
79
- _isub_op_ = _sub_op_ + _fset_op_ # in .auxilats.auxAngle
80
-
81
-
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
 
97
98
 
98
- def _2finite(x):
99
+ def _2finite(x): # in .fstats
99
100
  '''(INTERNAL) return C{float(x)} if finite.
100
101
  '''
101
- x = float(x)
102
- return x if _isfinite(x) else _2error(x)
102
+ return (float(x) if _isfinite(x) # and isscalar(x)
103
+ else _nfError(x))
103
104
 
104
105
 
105
- def _2float(index=None, **name_value): # in .fmath, .fstats
106
- '''(INTERNAL) Raise C{TypeError} or C{ValueError} if not scalar or infinite.
106
+ def _2float(index=None, _isfine=_isfinite, **name_x): # in .fmath, .fstats
107
+ '''(INTERNAL) Raise C{TypeError} or C{Overflow-/ValueError} if not finite.
107
108
  '''
108
- n, v = name_value.popitem() # _xkwds_item2(name_value)
109
+ n, x = name_x.popitem() # _xkwds_item2(name_x)
109
110
  try:
110
- return _2finite(v)
111
+ f = float(x)
112
+ return f if _isfine(f) else _nfError(x)
111
113
  except Exception as X:
112
- raise _xError(X, Fmt.INDEX(n, index), v)
114
+ raise _xError(X, Fmt.INDEX(n, index), x)
113
115
 
114
116
 
115
117
  def _X_ps(X): # for _2floats only
116
118
  return X._ps
117
119
 
118
120
 
119
- def _2floats(xs, origin=0, _X=_X_ps, _x=float):
121
+ def _2floats(xs, origin=0, _X=_X_ps, _x=float, _isfine=_isfinite):
120
122
  '''(INTERNAL) Yield each B{C{xs}} as a C{float}.
121
123
  '''
122
124
  try:
123
- i, x = origin, _X
124
- _fin = _isfinite
125
- _FsT = _Fsum_Fsum2Tuple_types
126
- _isa = isinstance
125
+ i, x = origin, xs
126
+ _FsT = _Fsum_2Tuple_types
127
127
  for x in _xiterable(xs):
128
- if _isa(x, _FsT):
128
+ if isinstance(x, _FsT):
129
129
  for p in _X(x._Fsum):
130
130
  yield p
131
131
  else:
132
132
  f = _x(x)
133
- yield f if _fin(f) else _2error(f)
133
+ yield f if _isfine(f) else _nfError(f)
134
134
  i += 1
135
135
  except Exception as X:
136
- raise _xError(X, xs=xs) if x is _X else \
137
- _xError(X, Fmt.INDEX(xs=i), x)
136
+ raise _xsError(X, xs, i, x)
138
137
 
139
138
 
140
- try: # MCCABE 14
139
+ try: # MCCABE 26
141
140
  from math import fma as _fma
142
141
 
143
- def _2products(x, ys, **unused):
142
+ def _2products(x, ys, *zs):
143
+ # yield(x * y for y in ys) + yield(z in zs)
144
144
  # TwoProductFMA U{Algorithm 3.5
145
145
  # <https://www.TUHH.De/ti3/paper/rump/OgRuOi05.pdf>}
146
146
  for y in ys:
147
147
  f = x * y
148
148
  yield f
149
149
  yield _fma(x, y, -f)
150
+ for z in zs:
151
+ yield z
150
152
 
151
- _2split3s = _passarg # NOP
153
+ # _2split3 = \
154
+ _2split3s = _passarg # in Fsum.is_math_fma
152
155
 
153
- except ImportError: # Python 3.12-
156
+ except ImportError: # PYCHOK DSPACE! Python 3.12-
154
157
 
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
158
+ if _F2PRODUCT and _F2PRODUCT != _std_:
159
+ # back to PyGeodesy 24.09.09, with _fmaX
165
160
 
166
- def _as_n_d(x):
161
+ def _fma(*a_b_c): # PYCHOK no cover
162
+ # mimick C{math.fma} from Python 3.13+,
163
+ # the same accuracy, but ~14x slower
164
+ (na, da), (nb, db), (nc, dc) = map(_2n_d, a_b_c)
165
+ n = na * nb * dc
166
+ n += da * db * nc
167
+ d = da * db * dc
167
168
  try:
168
- if _isfinite(x):
169
- # int.as_integer_ratio since 3.8
170
- return x.as_integer_ratio()
169
+ r = float(n / d)
170
+ except OverflowError: # "integer division result too large ..."
171
+ r = NINF if (_signOf(n, 0) * _signOf(d, 0)) < 0 else INF
172
+ return r if _isfinite(r) else _fmaX(r, *a_b_c) # "overflow in fma"
173
+
174
+ def _2n_d(x):
175
+ try: # int.as_integer_ratio in 3.8+
176
+ return x.as_integer_ratio()
171
177
  except (AttributeError, OverflowError, TypeError, ValueError):
172
- pass
173
- return float(x), 1
178
+ return (x if isint(x) else float(x)), 1
179
+ else:
174
180
 
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)
181
+ def _fma(a, b, c): # PYCHOK redef
182
+ # mimick C{math.fma} from Python 3.13+,
183
+ # the same accuracy, but ~13x slower
184
+ b3s = _2split3(b), # 1-tuple of 3-tuple
185
+ r = _fsum(_2products(a, b3s, c))
186
+ return r if _isfinite(r) else _fmaX(r, a, b, c)
187
+
188
+ _2n_d = None # redef
189
+
190
+ def _fmaX(r, *a_b_c): # like Python 3.13+ I{Modules/mathmodule.c}:
191
+ # raise a ValueError for a NAN result from non-NAN C{a_b_c}s or
192
+ # OverflowError for a non-NAN result from all finite C{a_b_c}s.
193
+ if isnan(r):
194
+ def _x(x):
195
+ return not isnan(x)
196
+ else:
197
+ _x = _isfinite
198
+ if all(map(_x, a_b_c)):
199
+ raise _nfError(r, unstr(_fma, *a_b_c))
200
+ return r
179
201
 
180
- def _2products(x, y3s, two=False): # PYCHOK redef
202
+ def _2products(x, y3s, *zs): # PYCHOK in Fsum._f2mul
203
+ # yield(x * y3 for y3 in y3s) + yield(z in zs)
181
204
  # TwoProduct U{Algorithm 3.3
182
205
  # <https://www.TUHH.De/ti3/paper/rump/OgRuOi05.pdf>}
206
+ # also in Python 3.13+ C{Modules/mathmodule.c} under
207
+ # #ifndef UNRELIABLE_FMA ... #else ... #endif
183
208
  _, a, b = _2split3(x)
184
209
  for y, c, d in y3s:
185
210
  y *= x
186
211
  yield y
187
- if two:
212
+ if False: # no cover
188
213
  yield b * d - (((y - a * c) - b * c) - a * d)
189
- # = b * d + (a * d - ((y - a * c) - b * c))
190
- # = b * d + (a * d + (b * c - (y - a * c)))
191
- # = b * d + (a * d + (b * c + (a * c - y)))
192
- else:
214
+ # = b * d + (a * d - ((y - a * c) - b * c))
215
+ # = b * d + (a * d + (b * c - (y - a * c)))
216
+ # = b * d + (a * d + (b * c + (a * c - y)))
217
+ elif a:
193
218
  yield a * c - y
194
219
  yield b * c
195
220
  if d:
196
221
  yield a * d
197
222
  yield b * d
223
+ else:
224
+ yield b * c - y
225
+ yield b * d
226
+ for z in zs:
227
+ yield z
198
228
 
199
- _2FACTOR = pow(2, (MANT_DIG + 1) // 2) + 1
229
+ _2FACTOR = pow(2, (MANT_DIG + 1) // 2) + _1_0 # 134217729 if MANT_DIG == 53
200
230
 
201
231
  def _2split3(x):
202
232
  # Split U{Algorithm 3.2
203
- # <ttps://www.TUHH.De/ti3/paper/rump/OgRuOi05.pdf>}
233
+ # <https://www.TUHH.De/ti3/paper/rump/OgRuOi05.pdf>}
204
234
  a = c = x * _2FACTOR
205
235
  a -= c - x
206
236
  b = x - a
207
237
  return x, a, b
208
238
 
209
- def _2split3s(xs): # PYCHOK redef
239
+ def _2split3s(xs): # in Fsum.is_math_fma
210
240
  return map(_2split3, xs)
211
241
 
212
- del MANT_DIG
213
242
 
243
+ def f2product(*two):
244
+ '''Turn accurate I{TwoProduct} multiplication on or off.
245
+
246
+ @arg two: If C{True}, turn I{TwoProduct} on, if C{False} off or
247
+ if C{None} or omitted, keep the current setting.
214
248
 
215
- def _Fsumf_(*xs): # floats=True, in .auxLat, ...
216
- '''(INTERNAL) An C{Fsum} of I{known scalars}.
249
+ @return: The previous setting (C{bool}).
250
+
251
+ @see: I{TwoProduct} multiplication is based on the I{TwoProductFMA}
252
+ U{Algorithm 3.5 <https://www.TUHH.De/ti3/paper/rump/OgRuOi05.pdf>}
253
+ using function C{math.fma} from Python 3.13 and later or an
254
+ equivalent, slower implementation when not available.
217
255
  '''
218
- return Fsum()._facc_scalar(xs, up=False)
256
+ t = Fsum._f2product
257
+ if two and two[0] is not None:
258
+ Fsum._f2product = bool(two[0])
259
+ return t
219
260
 
220
261
 
221
- def _Fsum1f_(*xs): # floats=True, in .albers, ...
222
- '''(INTERNAL) An C{Fsum} of I{known scalars}, 1-primed.
262
+ def _Fsumf_(*xs): # in .auxLat, .ltp, ...
263
+ '''(INTERNAL) An C{Fsum(xs)}, all C{scalar}, an L{Fsum} or L{Fsum2Tuple}.
223
264
  '''
224
- return Fsum()._facc_scalar(_1primed(xs), up=False)
265
+ return Fsum()._facc_scalarf(xs, up=False)
266
+
267
+
268
+ def _Fsum1f_(*xs): # in .albers
269
+ '''(INTERNAL) An C{Fsum(xs)}, all C{scalar}, an L{Fsum} or L{Fsum2Tuple}, 1-primed.
270
+ '''
271
+ return Fsum()._facc_scalarf(_1primed(xs), up=False)
225
272
 
226
273
 
227
274
  def _2halfeven(s, r, p):
@@ -242,16 +289,69 @@ def _isFsum(x): # in .fmath
242
289
  return isinstance(x, Fsum)
243
290
 
244
291
 
245
- def _isFsumTuple(x): # in .fmath
292
+ def _isFsum_2Tuple(x): # in .basics, .constants, .fmath, .fstats
246
293
  '''(INTERNAL) Is C{x} an C{Fsum} or C{Fsum2Tuple} instance?
247
294
  '''
248
- return isinstance(x, _Fsum_Fsum2Tuple_types)
295
+ return isinstance(x, _Fsum_2Tuple_types)
296
+
297
+
298
+ def _isOK(unused):
299
+ '''(INTERNAL) Helper for C{Fsum._fsum2} and C{Fsum.nonfinites}.
300
+ '''
301
+ return True
302
+
303
+
304
+ def _isOK_or_finite(x, _isfine=_isfinite):
305
+ '''(INTERNAL) Is C{x} finite or is I{non-finite} OK?.
306
+ '''
307
+ # assert _isfine in (_isOK, _isfinite)
308
+ return _isfine(x)
309
+
310
+
311
+ def _ixError(X, xs, i, x, origin=0, which=None):
312
+ '''(INTERNAL) Error for C{xs} or C{x}, item C{xs[i]}.
313
+ '''
314
+ t = _xsError(X, xs, i + origin, x)
315
+ if which:
316
+ t = _COMMASPACE_(unstr(which, _Cdot=Fsum), t)
317
+ return _xError(X, t, txt=None)
318
+
319
+
320
+ def _nfError(x, *args):
321
+ '''(INTERNAL) Throw a C{not-finite} exception.
322
+ '''
323
+ E = _NonfiniteError(x)
324
+ t = Fmt.PARENSPACED(_not_finite_, x)
325
+ if args: # in _fmaX, _2sum
326
+ return E(txt=t, *args)
327
+ raise E(t, txt=None)
328
+
329
+
330
+ def nonfiniterrors(*raiser):
331
+ '''Throw C{OverflowError} and C{ValueError} exceptions for or
332
+ handle I{non-finite} C{float}s as C{inf}, C{INF}, C{NINF},
333
+ C{nan} and C{NAN} in summations and multiplications.
334
+
335
+ @arg raiser: If C{True}, throw exceptions, if C{False} handle
336
+ I{non-finites} or if C{None} or omitted, leave
337
+ the setting unchanged.
338
+
339
+ @return: Previous setting (C{bool}).
340
+
341
+ @note: C{inf}, C{INF} and C{NINF} throw an C{OverflowError},
342
+ C{nan} and C{NAN} a C{ValueError}.
343
+ '''
344
+ d = Fsum._isfine
345
+ if raiser and raiser[0] is not None:
346
+ Fsum._isfine = {} if bool(raiser[0]) else Fsum._nonfinites_isfine_kwds[True]
347
+ return _xkwds_get1(d, _isfine=_isfinite) is _isfinite
249
348
 
250
349
 
251
- def _1_Over(x, op, **raiser_RESIDUAL): # vs _1_over
252
- '''(INTERNAL) Return C{Fsum(1) / B{x}}.
350
+ def _NonfiniteError(x):
351
+ '''(INTERNAL) Return the Error class for C{x}, I{non-finite}.
253
352
  '''
254
- return _Psum_(_1_0)._ftruediv(x, op, **raiser_RESIDUAL)
353
+ return _OverflowError if isinf(x) else (
354
+ _ValueError if isnan(x) else _AssertionError)
255
355
 
256
356
 
257
357
  def _1primed(xs): # in .fmath
@@ -264,16 +364,15 @@ def _1primed(xs): # in .fmath
264
364
  yield _N_1_0
265
365
 
266
366
 
267
- def _psum(ps): # PYCHOK used!
367
+ def _psum(ps, **_isfine): # PYCHOK used!
268
368
  '''(INTERNAL) Partials summation, updating C{ps}.
269
369
  '''
270
370
  # assert isinstance(ps, list)
271
- i = len(ps) - 1
272
- s = _0_0 if i < 0 else ps[i]
273
- _2s = _2sum
371
+ i = len(ps) - 1
372
+ s = _0_0 if i < 0 else ps[i]
274
373
  while i > 0:
275
374
  i -= 1
276
- s, r = _2s(s, ps[i])
375
+ s, r = _2sum(s, ps[i], **_isfine)
277
376
  if r: # sum(ps) became inexact
278
377
  if s:
279
378
  ps[i:] = r, s
@@ -281,31 +380,35 @@ def _psum(ps): # PYCHOK used!
281
380
  s = _2halfeven(s, r, ps[i-1])
282
381
  break # return s
283
382
  s = r # PYCHOK no cover
383
+ elif not _isfinite(s): # non-finite OK
384
+ i = 0 # collapse ps
385
+ if ps:
386
+ s += sum(ps)
284
387
  ps[i:] = s,
285
388
  return s
286
389
 
287
390
 
288
- def _Psum(ps, **name_RESIDUAL):
391
+ def _Psum(ps, **name_f2product_nonfinites_RESIDUAL):
289
392
  '''(INTERNAL) Return an C{Fsum} from I{ordered} partials C{ps}.
290
393
  '''
291
- f = Fsum(**name_RESIDUAL) if name_RESIDUAL else Fsum()
394
+ F = Fsum(**name_f2product_nonfinites_RESIDUAL)
292
395
  if ps:
293
- f._ps[:] = ps
294
- f._n = len(f._ps)
295
- return f
396
+ F._ps[:] = ps
397
+ F._n = len(F._ps)
398
+ return F
296
399
 
297
400
 
298
- def _Psum_(*ps, **name_RESIDUAL):
299
- '''(INTERNAL) Return an C{Fsum} from 1 or 2 known scalar(s) C{ps}.
401
+ def _Psum_(*ps, **name_f2product_nonfinites_RESIDUAL): # in .fmath
402
+ '''(INTERNAL) Return an C{Fsum} from I{known scalar} C{ps}.
300
403
  '''
301
- return _Psum(ps, **name_RESIDUAL)
404
+ return _Psum(ps, **name_f2product_nonfinites_RESIDUAL)
302
405
 
303
406
 
304
407
  def _2scalar2(other):
305
408
  '''(INTERNAL) Return 2-tuple C{(other, r)} with C{other} as C{int},
306
409
  C{float} or C{as-is} and C{r} the residual of C{as-is}.
307
410
  '''
308
- if _isFsumTuple(other):
411
+ if _isFsum_2Tuple(other):
309
412
  s, r = other._fint2
310
413
  if r:
311
414
  s, r = other._fprs2
@@ -322,7 +425,7 @@ def _2scalar2(other):
322
425
  def _s_r(s, r):
323
426
  '''(INTERNAL) Return C{(s, r)}, I{ordered}.
324
427
  '''
325
- if r:
428
+ if r and _isfinite(s):
326
429
  if fabs(s) < fabs(r):
327
430
  s, r = r, (s or INT0)
328
431
  else:
@@ -334,7 +437,7 @@ def _strcomplex(s, *args):
334
437
  '''(INTERNAL) C{Complex} 2- or 3-arg C{pow} error as C{str}.
335
438
  '''
336
439
  c = _strcomplex.__name__[4:]
337
- n = _DASH_(len(args), _arg_)
440
+ n = _sub_op_(len(args), _arg_)
338
441
  t = unstr(pow, *args)
339
442
  return _SPACE_(c, s, _from_, n, t)
340
443
 
@@ -350,9 +453,12 @@ def _stresidual(prefix, residual, R=0, **mod_ratio):
350
453
  return _SPACE_(prefix, t, Fmt.exceeds_R(R), _threshold_)
351
454
 
352
455
 
353
- def _2sum(a, b): # by .testFmath
354
- '''(INTERNAL) Return C{a + b} as 2-tuple (sum, residual).
456
+ def _2sum(a, b, _isfine=_isfinite): # in .testFmath
457
+ '''(INTERNAL) Return C{a + b} as 2-tuple C{(sum, residual)} with finite C{sum},
458
+ otherwise as 2-tuple C{(nonfinite, 0)} iff I{non-finites} are OK.
355
459
  '''
460
+ # FastTwoSum U{Algorithm 1.1<https://www.TUHH.De/ti3/paper/rump/OgRuOi05.pdf>}
461
+
356
462
  # Neumaier, A. U{Rundungsfehleranalyse einiger Verfahren zur Summation endlicher
357
463
  # Summen<https://OnlineLibrary.Wiley.com/doi/epdf/10.1002/zamm.19740540106>},
358
464
  # 1974, Zeitschrift für Angewandte Mathmatik und Mechanik, vol 51, nr 1, p 39-51
@@ -363,10 +469,12 @@ def _2sum(a, b): # by .testFmath
363
469
  r = (b - s) + a
364
470
  else:
365
471
  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)
472
+ elif _isfine(s):
473
+ r = _NONFINITEr
474
+ else: # non-finite and not OK
475
+ t = unstr(_2sum, a, b)
476
+ raise _nfError(s, t)
477
+ return s, r
370
478
 
371
479
 
372
480
  def _threshold(threshold=_0_0, **kwds):
@@ -374,29 +482,28 @@ def _threshold(threshold=_0_0, **kwds):
374
482
  optionally from single kwds C{B{RESIDUAL}=scalar}.
375
483
  '''
376
484
  if kwds:
377
- threshold, kwds = _xkwds_pop2(kwds, RESIDUAL=threshold)
378
- # threshold = kwds.pop('RESIDUAL', threshold)
379
- if kwds:
380
- raise _UnexpectedError(**kwds)
485
+ threshold = _xkwds_get1(kwds, RESIDUAL=threshold)
381
486
  try:
382
487
  return _2finite(threshold) # PYCHOK None
383
488
  except Exception as x:
384
489
  raise ResidualError(threshold=threshold, cause=x)
385
490
 
386
491
 
387
- class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
388
- '''Precision floating point summation and I{running} summation.
492
+ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase, .fstats, ...
493
+ '''Precision floating point summation, I{running} summation and accurate multiplication.
389
494
 
390
495
  Unlike Python's C{math.fsum}, this class accumulates values and provides intermediate,
391
496
  I{running}, precision floating point summations. Accumulation may continue after any
392
497
  intermediate, I{running} summuation.
393
498
 
394
499
  @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}.
500
+ i.e. any C{type} having method C{__float__}.
397
501
 
398
- @note: Handling of exceptions and C{inf}, C{INF}, C{nan} and C{NAN} differs from
399
- Python's C{math.fsum}.
502
+ @note: Handling of I{non-finites} as C{inf}, C{INF}, C{NINF}, C{nan} and C{NAN} is
503
+ determined by function L{nonfiniterrors<fsums.nonfiniterrors>} for the default
504
+ and by method L{nonfinites<Fsum.nonfinites>} for individual C{Fsum} instances,
505
+ overruling the default. For backward compatibility, I{non-finites} raise
506
+ exceptions by default.
400
507
 
401
508
  @see: U{Hettinger<https://GitHub.com/ActiveState/code/tree/master/recipes/Python/
402
509
  393090_Binary_floating_point_summatiaccurate_full/recipe-393090.py>},
@@ -404,42 +511,41 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
404
511
  <https://Link.Springer.com/article/10.1007/s00607-005-0139-x>}, Python 2.6+
405
512
  file I{Modules/mathmodule.c} and the issue log U{Full precision summation
406
513
  <https://Bugs.Python.org/issue2819>}.
514
+
515
+ @see: Method L{f2product<Fsum.f2product>} for details about accurate I{TwoProduct}
516
+ multiplication.
517
+
518
+ @see: Module L{fsums<pygeodesy.fsums>} for env variables C{PYGEODESY_FSUM_F2PRODUCT},
519
+ C{PYGEODESY_FSUM_NONFINITES} and C{PYGEODESY_FSUM_RESIDUAL}.
407
520
  '''
408
- _f2product = _2split3s is _passarg # True for 3.13+
409
- _math_fma = _fma if _f2product else None
410
- _math_fsum = None
521
+ _f2product = _sys_version_info2 > (3, 12) or bool(_F2PRODUCT)
522
+ _isfine = {} # == _isfinite
411
523
  _n = 0
412
524
  # _ps = [] # partial sums
413
- # _ps_max = 0 # max(Fsum._ps_max, len(Fsum._ps))
414
- _RESIDUAL = _threshold(_getenv('PYGEODESY_FSUM_RESIDUAL', _0_0))
525
+ # _ps_max = 0 # max(Fsum._ps_max, len(Fsum._ps)) # 41
526
+ _RESIDUAL = _threshold(_RESIDUAL_0_0)
415
527
 
416
- def __init__(self, *xs, **name_RESIDUAL):
417
- '''New L{Fsum} for I{running} precision floating point summation.
528
+ def __init__(self, *xs, **name_f2product_nonfinites_RESIDUAL):
529
+ '''New L{Fsum}.
418
530
 
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}).
531
+ @arg xs: No, one or more initial items to accumulate (each C{scalar}, an
532
+ L{Fsum} or L{Fsum2Tuple}), all positional.
533
+ @kwarg name_f2product_nonfinites_RESIDUAL: Optional C{B{name}=NN} (C{str})
534
+ and settings C{B{f2product}=None} (C{bool}), C{B{nonfinites}=None}
535
+ (C{bool}) and C{B{RESIDUAL}=0.0} threshold (C{scalar}) for this
536
+ L{Fsum}.
424
537
 
425
- @see: Methods L{Fsum.fadd} and L{Fsum.RESIDUAL}.
538
+ @see: Methods L{Fsum.f2product}, L{Fsum.nonfinites}, L{Fsum.RESIDUAL},
539
+ L{Fsum.fadd} and L{Fsum.fadd_}.
426
540
  '''
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
-
541
+ if name_f2product_nonfinites_RESIDUAL:
542
+ self._optionals(**name_f2product_nonfinites_RESIDUAL)
437
543
  self._ps = [] # [_0_0], see L{Fsum._fprs}
438
544
  if xs:
439
- self._facc_1(xs, up=False)
545
+ self._facc_args(xs, up=False)
440
546
 
441
547
  def __abs__(self):
442
- '''Return this instance' absolute value as an L{Fsum}.
548
+ '''Return C{abs(self)} as an L{Fsum}.
443
549
  '''
444
550
  s = self.signOf() # == self._cmp_0(0)
445
551
  return (-self) if s < 0 else self._copy_2(self.__abs__)
@@ -457,7 +563,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
457
563
  return f._fadd(other, _add_op_)
458
564
 
459
565
  def __bool__(self): # PYCHOK Python 3+
460
- '''Return C{True} if this instance is I{exactly} non-zero.
566
+ '''Return C{bool(B{self})}, C{True} iff C{residual} is zero.
461
567
  '''
462
568
  s, r = self._fprs2
463
569
  return bool(s or r) and s != -r # == self != 0
@@ -500,7 +606,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
500
606
  return f._fdivmod2(other, _divmod_op_, **raiser_RESIDUAL)
501
607
 
502
608
  def __eq__(self, other):
503
- '''Compare this with an other instance or C{scalar}.
609
+ '''Return C{(B{self} == B{other})} as C{bool} where B{C{other}}
610
+ is C{scalar}, an other L{Fsum} or L{Fsum2Tuple}.
504
611
  '''
505
612
  return self._cmp_0(other, _eq_op_) == 0
506
613
 
@@ -537,17 +644,17 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
537
644
  return _NotImplemented(self, *other)
538
645
 
539
646
  def __ge__(self, other):
540
- '''Compare this with an other instance or C{scalar}.
647
+ '''Return C{(B{self} >= B{other})}, see C{__eq__}.
541
648
  '''
542
649
  return self._cmp_0(other, _ge_op_) >= 0
543
650
 
544
651
  def __gt__(self, other):
545
- '''Compare this with an other instance or C{scalar}.
652
+ '''Return C{(B{self} > B{other})}, see C{__eq__}.
546
653
  '''
547
654
  return self._cmp_0(other, _gt_op_) > 0
548
655
 
549
656
  def __hash__(self): # PYCHOK no cover
550
- '''Return this instance' C{hash}.
657
+ '''Return C{hash(B{self})} as C{float}.
551
658
  '''
552
659
  # @see: U{Notes for type implementors<https://docs.Python.org/
553
660
  # 3/library/numbers.html#numbers.Rational>}
@@ -569,7 +676,9 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
569
676
  try:
570
677
  return self._fadd(other, _iadd_op_)
571
678
  except TypeError:
572
- return self._facc_inplace(other, _iadd_op_, self._facc)
679
+ pass
680
+ _xiterable(other)
681
+ return self._facc(other)
573
682
 
574
683
  def __ifloordiv__(self, other):
575
684
  '''Apply C{B{self} //= B{other}} to this instance.
@@ -583,7 +692,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
583
692
 
584
693
  @raise TypeError: Invalid B{C{other}} type.
585
694
 
586
- @raise ValueError: Invalid or non-finite B{C{other}}.
695
+ @raise ValueError: Invalid or I{non-finite} B{C{other}}.
587
696
 
588
697
  @raise ZeroDivisionError: Zero B{C{other}}.
589
698
 
@@ -617,7 +726,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
617
726
 
618
727
  @raise TypeError: Invalid B{C{other}} type.
619
728
 
620
- @raise ValueError: Invalid or non-finite B{C{other}}.
729
+ @raise ValueError: Invalid or I{non-finite} B{C{other}}.
621
730
  '''
622
731
  return self._fmul(other, _mul_op_ + _fset_op_)
623
732
 
@@ -638,7 +747,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
638
747
  def __ipow__(self, other, *mod, **raiser_RESIDUAL): # PYCHOK 2 vs 3 args
639
748
  '''Apply C{B{self} **= B{other}} to this instance.
640
749
 
641
- @arg other: The exponent (C{scalar}, L{Fsum} or L{Fsum2Tuple}).
750
+ @arg other: The exponent (C{scalar}, an L{Fsum} or L{Fsum2Tuple}).
642
751
  @arg mod: Optional modulus (C{int} or C{None}) for the 3-argument
643
752
  C{pow(B{self}, B{other}, B{mod})} version.
644
753
  @kwarg raiser_RESIDUAL: Use C{B{raiser}=False} to ignore
@@ -688,7 +797,9 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
688
797
  try:
689
798
  return self._fsub(other, _isub_op_)
690
799
  except TypeError:
691
- return self._facc_inplace(other, _isub_op_, self._facc_neg)
800
+ pass
801
+ _xiterable(other)
802
+ return self._facc_neg(other)
692
803
 
693
804
  def __iter__(self):
694
805
  '''Return an C{iter}ator over a C{partials} duplicate.
@@ -712,7 +823,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
712
823
 
713
824
  @raise TypeError: Invalid B{C{other}} type.
714
825
 
715
- @raise ValueError: Invalid or non-finite B{C{other}}.
826
+ @raise ValueError: Invalid or I{non-finite} B{C{other}}.
716
827
 
717
828
  @raise ZeroDivisionError: Zero B{C{other}}.
718
829
 
@@ -721,7 +832,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
721
832
  return self._ftruediv(other, _truediv_op_ + _fset_op_, **raiser_RESIDUAL)
722
833
 
723
834
  def __le__(self, other):
724
- '''Compare this with an other instance or C{scalar}.
835
+ '''Return C{(B{self} <= B{other})}, see C{__eq__}.
725
836
  '''
726
837
  return self._cmp_0(other, _le_op_) <= 0
727
838
 
@@ -731,7 +842,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
731
842
  return self._n
732
843
 
733
844
  def __lt__(self, other):
734
- '''Compare this with an other instance or C{scalar}.
845
+ '''Return C{(B{self} < B{other})}, see C{__eq__}.
735
846
  '''
736
847
  return self._cmp_0(other, _lt_op_) < 0
737
848
 
@@ -756,12 +867,12 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
756
867
  return f._fmul(other, _mul_op_)
757
868
 
758
869
  def __ne__(self, other):
759
- '''Compare this with an other instance or C{scalar}.
870
+ '''Return C{(B{self} != B{other})}, see C{__eq__}.
760
871
  '''
761
872
  return self._cmp_0(other, _ne_op_) != 0
762
873
 
763
874
  def __neg__(self):
764
- '''Return I{a copy of} this instance, I{negated}.
875
+ '''Return C{copy(B{self})}, I{negated}.
765
876
  '''
766
877
  f = self._copy_2(self.__neg__)
767
878
  return f._fset(self._neg)
@@ -909,11 +1020,13 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
909
1020
  def as_integer_ratio(self):
910
1021
  '''Return this instance as the ratio of 2 integers.
911
1022
 
912
- @return: 2-Tuple C{(numerator, denominator)} both C{int}
913
- with C{numerator} signed and C{denominator}
914
- non-zero, positive.
1023
+ @return: 2-Tuple C{(numerator, denominator)} both C{int} with
1024
+ C{numerator} signed and C{denominator} non-zero and
1025
+ positive. The C{numerator} is I{non-finite} if this
1026
+ instance is.
915
1027
 
916
- @see: Standard C{float.as_integer_ratio} in Python 2.7+.
1028
+ @see: Method L{Fsum.fint2} and C{float.as_integer_ratio} in
1029
+ Python 2.7+.
917
1030
  '''
918
1031
  n, r = self._fint2
919
1032
  if r:
@@ -926,8 +1039,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
926
1039
 
927
1040
  @property_RO
928
1041
  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}.
1042
+ '''Get this instance I{as-is} (L{Fsum} with C{non-zero residual},
1043
+ C{scalar} or I{non-finite}).
931
1044
  '''
932
1045
  s, r = self._fprs2
933
1046
  return self if r else s
@@ -953,7 +1066,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
953
1066
  def _cmp_0(self, other, op):
954
1067
  '''(INTERNAL) Return C{scalar(self - B{other})} for 0-comparison.
955
1068
  '''
956
- if _isFsumTuple(other):
1069
+ if _isFsum_2Tuple(other):
957
1070
  s = self._ps_1sum(*other._ps)
958
1071
  elif self._scalar(other, op):
959
1072
  s = self._ps_1sum(other)
@@ -996,19 +1109,14 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
996
1109
  return other._copy_2(which) if _isFsum(other) else \
997
1110
  self._copy_2(which)._fset(other)
998
1111
 
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
1112
  divmod = __divmod__
1007
1113
 
1008
1114
  def _Error(self, op, other, Error, **txt_cause):
1009
1115
  '''(INTERNAL) Format an B{C{Error}} for C{{self} B{op} B{other}}.
1010
1116
  '''
1011
- return Error(_SPACE_(self.as_iscalar, op, other), **txt_cause)
1117
+ # self.as_iscalar causes RecursionError for ._fprs2 errors
1118
+ s = _Psum(self._ps, nonfinites=True, name=self.name)
1119
+ return Error(_SPACE_(s.as_iscalar, op, other), **txt_cause)
1012
1120
 
1013
1121
  def _ErrorX(self, X, op, other, *mod):
1014
1122
  '''(INTERNAL) Format the caught exception C{X}.
@@ -1029,28 +1137,21 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1029
1137
  '''(INTERNAL) Accumulate more C{scalars} or L{Fsum}s.
1030
1138
  '''
1031
1139
  if xs:
1032
- _xs = _2floats(xs, **origin_X_x) # PYCHOK yield
1140
+ kwds = _xkwds(self._isfine, **origin_X_x)
1141
+ fs = _2floats(xs, **kwds) # PYCHOK yield
1033
1142
  ps = self._ps
1034
- ps[:] = self._ps_acc(list(ps), _xs, up=up)
1143
+ ps[:] = self._ps_acc(list(ps), fs, up=up)
1035
1144
  return self
1036
1145
 
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.
1146
+ def _facc_args(self, xs, **up):
1147
+ '''(INTERNAL) Accumulate 0, 1 or more C{xs}, all positional
1148
+ arguments in the caller of this method.
1040
1149
  '''
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)
1150
+ return self._facc(xs, origin=1, **up) if len(xs) != 1 else \
1151
+ self._fadd(xs[0], _add_op_, **up)
1051
1152
 
1052
1153
  def _facc_neg(self, xs, **up_origin):
1053
- '''(INTERNAL) Accumulate more C{scalars} or L{Fsum}s, negated.
1154
+ '''(INTERNAL) Accumulate more C{xs}, negated.
1054
1155
  '''
1055
1156
  def _N(X):
1056
1157
  return X._ps_neg
@@ -1065,7 +1166,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1065
1166
  '''
1066
1167
  def _Pow4(p):
1067
1168
  r = 0
1068
- if _isFsumTuple(p):
1169
+ if _isFsum_2Tuple(p):
1069
1170
  s, r = p._fprs2
1070
1171
  if r:
1071
1172
  m = Fsum._pow
@@ -1075,24 +1176,22 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1075
1176
  p = s = int(p)
1076
1177
  m = Fsum._pow_int
1077
1178
  else:
1078
- p = s = _2float(power=p)
1179
+ p = s = _2float(power=p, **self._isfine)
1079
1180
  m = Fsum._pow_scalar
1080
1181
  return m, p, s, r
1081
1182
 
1082
1183
  _Pow, p, s, r = _Pow4(power)
1083
1184
  if p: # and xs:
1084
1185
  op = which.__name__
1085
- _flt = float
1086
- _Fs = Fsum
1087
- _isa = isinstance
1186
+ _FsT = _Fsum_2Tuple_types
1088
1187
  _pow = self._pow_2_3
1089
1188
 
1090
1189
  def _P(X):
1091
1190
  f = _Pow(X, p, power, op, **raiser_RESIDUAL)
1092
- return f._ps if _isa(f, _Fs) else (f,)
1191
+ return f._ps if isinstance(f, _FsT) else (f,)
1093
1192
 
1094
1193
  def _p(x):
1095
- x = _flt(x)
1194
+ x = float(x)
1096
1195
  f = _pow(x, s, power, op, **raiser_RESIDUAL)
1097
1196
  if f and r:
1098
1197
  f *= _pow(x, r, power, op, **raiser_RESIDUAL)
@@ -1104,18 +1203,27 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1104
1203
  return f
1105
1204
 
1106
1205
  def _facc_scalar(self, xs, **up):
1107
- '''(INTERNAL) Accumulate all C{xs}, known to be scalar.
1206
+ '''(INTERNAL) Accumulate all C{xs}, each C{scalar}.
1108
1207
  '''
1109
1208
  if xs:
1110
1209
  _ = self._ps_acc(self._ps, xs, **up)
1111
1210
  return self
1112
1211
 
1113
1212
  def _facc_scalar_(self, *xs, **up):
1114
- '''(INTERNAL) Accumulate all positional C{xs}, known to be scalar.
1213
+ '''(INTERNAL) Accumulate all positional C{xs}, each C{scalar}.
1115
1214
  '''
1116
- if xs:
1117
- _ = self._ps_acc(self._ps, xs, **up)
1118
- return self
1215
+ return self._facc_scalar(xs, **up)
1216
+
1217
+ def _facc_scalarf(self, xs, **origin_which):
1218
+ '''(INTERNAL) Accumulate all C{xs}, each C{scalar}, an L{Fsum} or
1219
+ L{Fsum2Tuple}, like function C{_xsum}.
1220
+ '''
1221
+ i_x = [0, xs]
1222
+ try:
1223
+ nf = self.nonfinitesOK
1224
+ return self._facc_scalar(_xs(xs, i_x, nf))
1225
+ except (OverflowError, TypeError, ValueError) as X:
1226
+ raise _ixError(X, xs, *i_x, **origin_which)
1119
1227
 
1120
1228
  # def _facc_up(self, up=True):
1121
1229
  # '''(INTERNAL) Update the C{partials}, by removing
@@ -1143,12 +1251,13 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1143
1251
 
1144
1252
  @raise TypeError: An invalid B{C{xs}} item.
1145
1253
 
1146
- @raise ValueError: Invalid or non-finite B{C{xs}} value.
1254
+ @raise ValueError: Invalid or I{non-finite} B{C{xs}} value.
1147
1255
  '''
1148
- if _isFsumTuple(xs):
1256
+ if _isFsum_2Tuple(xs):
1149
1257
  self._facc_scalar(xs._ps)
1150
- elif isscalar(xs): # for backward compatibility
1151
- self._facc_scalar_(_2float(x=xs)) # PYCHOK no cover
1258
+ elif isscalar(xs): # for backward compatibility # PYCHOK no cover
1259
+ x = _2float(x=xs, **self._isfine)
1260
+ self._facc_scalar_(x)
1152
1261
  elif xs: # _xiterable(xs)
1153
1262
  self._facc(xs)
1154
1263
  return self
@@ -1161,17 +1270,21 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1161
1270
 
1162
1271
  @see: Method L{Fsum.fadd} for further details.
1163
1272
  '''
1164
- return self._facc_1(xs)
1273
+ return self._facc_args(xs)
1165
1274
 
1166
1275
  def _fadd(self, other, op, **up): # in .fmath.Fhorner
1167
1276
  '''(INTERNAL) Apply C{B{self} += B{other}}.
1168
1277
  '''
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)
1278
+ if _isFsum_2Tuple(other):
1279
+ if self._ps:
1280
+ self._facc_scalar(other._ps, **up)
1281
+ else:
1282
+ self._fset(other, op=op, **up)
1173
1283
  elif self._scalar(other, op):
1174
- self._facc_scalar_(other, **up)
1284
+ if self._ps:
1285
+ self._facc_scalar_(other, **up)
1286
+ else:
1287
+ self._fset(other, op=op, **up)
1175
1288
  return self
1176
1289
 
1177
1290
  fcopy = copy # for backward compatibility
@@ -1186,7 +1299,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1186
1299
  # but at least divmod(-3, 2) equals Cpython's result (-2, 1).
1187
1300
  q = self._truediv(other, op, **raiser_RESIDUAL).floor
1188
1301
  if q: # == float // other == floor(float / other)
1189
- self -= Fsum(q) * other # NOT other * q!
1302
+ self -= self._Fsum_as(q) * other # NOT other * q!
1190
1303
 
1191
1304
  s = signOf(other) # make signOf(self) == signOf(other)
1192
1305
  if s and self.signOf() == -s: # PYCHOK no cover
@@ -1203,12 +1316,12 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1203
1316
  else sum(... i=len(cs)-1..0)}.
1204
1317
  '''
1205
1318
  if _xiterablen(cs):
1206
- H = Fsum(name__=self._fhorner)
1207
- if _isFsumTuple(x):
1319
+ H = self._Fsum_as(name__=self._fhorner)
1320
+ if _isFsum_2Tuple(x):
1208
1321
  _mul = H._mul_Fsum
1209
1322
  else:
1210
1323
  _mul = H._mul_scalar
1211
- x = _2float(x=x)
1324
+ x = _2float(x=x, **self._isfine)
1212
1325
  if len(cs) > 1 and x:
1213
1326
  for c in (reversed(cs) if incx else cs):
1214
1327
  H._fset_ps(_mul(x, op))
@@ -1221,10 +1334,10 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1221
1334
  def _finite(self, other, op=None):
1222
1335
  '''(INTERNAL) Return B{C{other}} if C{finite}.
1223
1336
  '''
1224
- if _isfinite(other):
1337
+ if _isOK_or_finite(other, **self._isfine):
1225
1338
  return other
1226
- raise ValueError(_not_finite_) if op is None else \
1227
- self._Error(op, other, _ValueError, txt=_not_finite_)
1339
+ E = _NonfiniteError(other)
1340
+ raise self._Error(op, other, E, txt=_not_finite_)
1228
1341
 
1229
1342
  def fint(self, name=NN, **raiser_RESIDUAL):
1230
1343
  '''Return this instance' current running sum as C{integer}.
@@ -1248,7 +1361,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1248
1361
  if R:
1249
1362
  t = _stresidual(_integer_, r, **R)
1250
1363
  raise ResidualError(_integer_, i, txt=t)
1251
- return _Psum_(i, name=_name__(name, name__=self.fint))
1364
+ return self._Fsum_as(i, name=_name__(name, name__=self.fint))
1252
1365
 
1253
1366
  def fint2(self, **name):
1254
1367
  '''Return this instance' current running sum as C{int} and the
@@ -1259,6 +1372,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1259
1372
  @return: An L{Fsum2Tuple}C{(fsum, residual)} with C{fsum}
1260
1373
  an C{int} and I{integer} C{residual} a C{float} or
1261
1374
  C{INT0} if the C{fsum} is considered to be I{exact}.
1375
+ The C{fsum} is I{non-finite} if this instance is.
1262
1376
  '''
1263
1377
  return Fsum2Tuple(*self._fint2, **name)
1264
1378
 
@@ -1266,18 +1380,35 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1266
1380
  def _fint2(self): # see ._fset
1267
1381
  '''(INTERNAL) Get 2-tuple (C{int}, I{integer} residual).
1268
1382
  '''
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):
1383
+ s, _ = self._fprs2
1384
+ try:
1385
+ i = int(s)
1386
+ r = (self._ps_1sum(i) if len(self._ps) > 1 else
1387
+ float(s - i)) or INT0
1388
+ except (OverflowError, ValueError) as X:
1389
+ r = _NONFINITEr # INF, NAN, NINF
1390
+ i = self._fintX(X, sum(self._ps))
1391
+ return i, r # Fsum2Tuple?
1392
+
1393
+ @_fint2.setter_ # PYCHOK setter_UNDERscore!
1394
+ def _fint2(self, s): # in _fset
1277
1395
  '''(INTERNAL) Replace the C{_fint2} value.
1278
1396
  '''
1279
- i = int(s)
1280
- return i, ((s - i) or INT0)
1397
+ try:
1398
+ i = int(s)
1399
+ r = (s - i) or INT0
1400
+ except (OverflowError, ValueError) as X:
1401
+ r = _NONFINITEr # INF, NAN, NINF
1402
+ i = self._fintX(X, float(s))
1403
+ return i, r # like _fint2.getter
1404
+
1405
+ def _fintX(self, X, i): # PYCHOK X
1406
+ '''(INTERNAL) Handle I{non-finite} C{int}.
1407
+ '''
1408
+ # "cannot convert float infinity to integer"
1409
+ return i # ignore such Overflow-/ValueErrors
1410
+ # op = int.__name__
1411
+ # return self._nonfiniteX(X, op, i)
1281
1412
 
1282
1413
  @deprecated_property_RO
1283
1414
  def float_int(self): # PYCHOK no cover
@@ -1300,8 +1431,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1300
1431
  f -= 1
1301
1432
  return f # _floor(self._n_d)
1302
1433
 
1303
- # ffloordiv = __ifloordiv__ # for naming consistency
1304
- # floordiv = __floordiv__ # for naming consistency
1434
+ # ffloordiv = __ifloordiv__ # for naming consistency?
1435
+ # floordiv = __floordiv__ # for naming consistency?
1305
1436
 
1306
1437
  def _floordiv(self, other, op, **raiser_RESIDUAL): # rather _ffloordiv?
1307
1438
  '''Apply C{B{self} //= B{other}}.
@@ -1309,79 +1440,94 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1309
1440
  q = self._ftruediv(other, op, **raiser_RESIDUAL) # == self
1310
1441
  return self._fset(q.floor) # floor(q)
1311
1442
 
1312
- def fma(self, other1, other2): #
1443
+ def fma(self, other1, other2, **nonfinites): # in .fmath.fma
1313
1444
  '''Fused-multiply-add C{self *= B{other1}; self += B{other2}}.
1314
1445
 
1315
- @arg other1: A C{scalar}, an L{Fsum} or L{Fsum2Tuple} instance.
1316
- @arg other2: A C{scalar}, an L{Fsum} or L{Fsum2Tuple} instance.
1317
-
1318
- @note: Uses C{math.fma} in Python 3.13+, provided C{self},
1319
- B{C{other1}} and B{C{other2}} are all C{scalar}.
1446
+ @arg other1: Multiplier (C{scalar}, an L{Fsum} or L{Fsum2Tuple}).
1447
+ @arg other2: Addend (C{scalar}, an L{Fsum} or L{Fsum2Tuple}).
1448
+ @kwarg nonfinites: Use C{B{nonfinites}=True} or C{False}, to
1449
+ override L{nonfinites<Fsum.nonfinites>} and
1450
+ L{nonfiniterrors} default (C{bool}).
1320
1451
  '''
1321
- if len(self._ps) == 1 and isscalar(other1, both=True) \
1322
- and isscalar(other2, both=True):
1323
- p = _fma(self._ps[0], other1, other2)
1324
- self._ps[:] = self._finite(p, self.fma.__name__),
1325
- if other2:
1326
- self._n += 1
1327
- else:
1328
- self._f2mul(self.fma.__name__, other1)
1329
- self += other2
1330
- return self
1331
-
1332
- # def _fma_scalar(self, op, x, *ys): # in .karney
1333
- # '''(INTERNAL) Apply C{self.fma(B{x}, B{y}) for B{y} in B{ys}}
1334
- # for scalar C{x} and C{y}s.
1335
- # '''
1336
- # ps = self._ps
1337
- # if ps and ys:
1338
- # for y in ys:
1339
- # ps[:] = self._ps_acc(list(y), _2products(x, _2split3s(ps)))
1340
- # for p in (ps if op else()):
1341
- # self._finite(p, op)
1342
- # return self
1452
+ op = self.fma.__name__
1453
+ _fs = self._ps_other
1454
+ try:
1455
+ s, r = self._fprs2
1456
+ if r:
1457
+ f = self._f2mul(self.fma, other1, **nonfinites)
1458
+ f += other2
1459
+ else:
1460
+ fs = _2split3s(_fs(op, other1))
1461
+ fs = _2products(s, fs, *_fs(op, other2))
1462
+ f = _Psum(self._ps_acc([], fs, up=False), name=op)
1463
+ except TypeError as X:
1464
+ raise self._ErrorX(X, op, (other1, other2))
1465
+ except (OverflowError, ValueError) as X: # from math.fma
1466
+ f = self._mul_reduce(op, s, other1) # INF, NAN, NINF
1467
+ f = sum(_fs(op, f, other2))
1468
+ f = self._nonfiniteX(X, op, f, **nonfinites)
1469
+ return self._fset(f)
1343
1470
 
1344
1471
  fmul = __imul__
1345
1472
 
1346
1473
  def _fmul(self, other, op):
1347
1474
  '''(INTERNAL) Apply C{B{self} *= B{other}}.
1348
1475
  '''
1349
- if _isFsumTuple(other):
1476
+ if _isFsum_2Tuple(other):
1350
1477
  if len(self._ps) != 1:
1351
1478
  f = self._mul_Fsum(other, op)
1352
1479
  elif len(other._ps) != 1: # and len(self._ps) == 1
1353
- f = other._mul_scalar(self._ps[0], op)
1480
+ f = self._ps_mul(op, *other._ps) if other._ps else _0_0
1354
1481
  elif self._f2product: # len(other._ps) == 1
1355
1482
  f = self._mul_scalar(other._ps[0], op)
1356
1483
  else: # len(other._ps) == len(self._ps) == 1
1357
- f = self._finite(self._ps[0] * other._ps[0])
1484
+ f = self._finite(self._ps[0] * other._ps[0], op=op)
1358
1485
  else:
1359
1486
  s = self._scalar(other, op)
1360
1487
  f = self._mul_scalar(s, op)
1361
1488
  return self._fset(f) # n=len(self) + 1
1362
1489
 
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}.
1490
+ @deprecated_method
1491
+ def f2mul(self, *others, **raiser):
1492
+ '''DEPRECATED on 2024.09.13, use method L{f2mul_<Fsum.f2mul_>}.'''
1493
+ return self._fset(self.f2mul_(*others, **raiser))
1494
+
1495
+ def f2mul_(self, *others, **nonfinites): # in .fmath.f2mul
1496
+ '''Return C{B{self} * B{other} * B{other} ...} for all B{C{others}} using cascaded,
1497
+ accurate multiplication like with L{f2product<Fsum.f2product>} set to C{True}.
1498
+
1499
+ @arg others: Multipliers (each C{scalar}, an L{Fsum} or L{Fsum2Tuple}), all
1500
+ positional.
1501
+ @kwarg nonfinites: Use C{B{nonfinites}=True} or C{False}, to override both
1502
+ L{nonfinites<Fsum.nonfinites>} and the L{nonfiniterrors}
1503
+ default (C{bool}).
1504
+
1505
+ @return: The cascaded I{TwoProduct} (L{Fsum} or C{float}).
1367
1506
 
1368
1507
  @see: U{Equations 2.3<https://www.TUHH.De/ti3/paper/rump/OzOgRuOi06.pdf>}
1369
1508
  '''
1370
- return self._f2mul(self.f2mul.__name__, *others)
1509
+ return self._f2mul(self.f2mul_, *others, **nonfinites)
1371
1510
 
1372
- def _f2mul(self, op, *others):
1373
- '''(INTERNAL) See method C{f2mul}.
1511
+ def _f2mul(self, where, *others, **nonfinites_raiser):
1512
+ '''(INTERNAL) See methods C{fma} and C{f2mul_}.
1374
1513
  '''
1375
- P = _Psum(self._ps)
1376
- ps = P._ps
1514
+ f = self._copy_2(where)
1515
+ ps = f._ps
1377
1516
  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
1517
+ op = where.__name__
1518
+ try:
1519
+ for other in others: # to pinpoint errors
1520
+ for p in self._ps_other(op, other):
1521
+ pfs = _2products(p, _2split3s(ps))
1522
+ ps[:] = f._ps_acc([], pfs, up=False)
1523
+ f._update()
1524
+ except TypeError as X:
1525
+ raise self._ErrorX(X, op, other)
1526
+ except (OverflowError, ValueError) as X:
1527
+ r = self._mul_reduce(op, sum(ps), other) # INF, NAN, NINF
1528
+ r = self._nonfiniteX(X, op, r, **nonfinites_raiser)
1529
+ f._fset(r)
1530
+ return f
1385
1531
 
1386
1532
  def fover(self, over, **raiser_RESIDUAL):
1387
1533
  '''Apply C{B{self} /= B{over}} and summate.
@@ -1410,32 +1556,37 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1410
1556
  f = self._pow_2_3(self, other, other, op, *mod, **raiser_RESIDUAL)
1411
1557
  elif self.is_integer():
1412
1558
  # return an exact C{int} for C{int}**C{int}
1413
- i, _ = self._fint2 # assert _ == 0
1559
+ i, _ = self._fint2 # assert _ == 0
1414
1560
  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)
1561
+ f = self._Fsum_as(i)._pow_Fsum(other, op, **raiser_RESIDUAL) if r else \
1562
+ self._pow_2_3(i, x, other, op, **raiser_RESIDUAL)
1417
1563
  else: # mod[0] is None, power(self, other)
1418
- f = self._pow(other, other, op, **raiser_RESIDUAL)
1564
+ f = self._pow(other, other, op, **raiser_RESIDUAL)
1419
1565
  else: # pow(self, other)
1420
1566
  f = self._pow(other, other, op, **raiser_RESIDUAL)
1421
1567
  return self._fset(f) # n=max(len(self), 1)
1422
1568
 
1423
1569
  def f2product(self, *two):
1424
- '''Turn this instance' accurate I{TwoProduct} multiplication or or off.
1570
+ '''Get and set accurate I{TwoProduct} multiplication for this
1571
+ L{Fsum}, overriding the L{f2product} default.
1572
+
1573
+ @arg two: If omitted, leave the override unchanged, if C{True},
1574
+ turn I{TwoProduct} on, if C{False} off, if C{None}e
1575
+ remove th override (C{bool} or C{None}).
1425
1576
 
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.
1577
+ @return: The previous setting (C{bool} or C{None} if not set).
1428
1578
 
1429
- @return: The previous C{f2product} setting (C{bool}).
1579
+ @see: Function L{f2product<fsums.f2product>}.
1430
1580
 
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>}.
1581
+ @note: Use C{f.f2product() or f2product()} to determine whether
1582
+ multiplication is accurate for L{Fsum} C{f}.
1435
1583
  '''
1436
- t = self._f2product
1437
- if two and two[0] is not None:
1438
- self._f2product = bool(two[0])
1584
+ if two: # delattrof(self, _f2product=None)
1585
+ t = _xkwds_pop(self.__dict__, _f2product=None)
1586
+ if two[0] is not None:
1587
+ self._f2product = bool(two[0])
1588
+ else: # getattrof(self, _f2product=None)
1589
+ t = _xkwds_get(self.__dict__, _f2product=None)
1439
1590
  return t
1440
1591
 
1441
1592
  @Property
@@ -1449,7 +1600,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1449
1600
  s, _ = self._fprs2
1450
1601
  return s # ._fprs2.fsum
1451
1602
 
1452
- @_fprs.setter_ # PYCHOK setter_underscore!
1603
+ @_fprs.setter_ # PYCHOK setter_UNDERscore!
1453
1604
  def _fprs(self, s):
1454
1605
  '''(INTERNAL) Replace the C{_fprs} value.
1455
1606
  '''
@@ -1461,54 +1612,63 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1461
1612
  running sum and residual (L{Fsum2Tuple}).
1462
1613
  '''
1463
1614
  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,
1615
+ n = len(ps)
1616
+ try:
1617
+ if n > 2:
1618
+ s = _psum(ps, **self._isfine)
1619
+ if not _isfinite(s):
1620
+ ps[:] = s, # collapse ps
1621
+ return Fsum2Tuple(s, _NONFINITEr)
1622
+ n = len(ps)
1623
+ # Fsum._ps_max = max(Fsum._ps_max, n)
1624
+ if n > 2:
1625
+ r = self._ps_1sum(s)
1626
+ return Fsum2Tuple(*_s_r(s, r))
1627
+ if n > 1: # len(ps) == 2
1628
+ s, r = _s_r(*_2sum(*ps, **self._isfine))
1629
+ ps[:] = (r, s) if r else (s,)
1630
+ elif ps: # len(ps) == 1
1631
+ s = ps[0]
1632
+ r = INT0 if _isfinite(s) else _NONFINITEr
1633
+ else: # len(ps) == 0
1634
+ s, r = _0_0, INT0
1635
+ ps[:] = s,
1636
+ except (OverflowError, ValueError) as X:
1637
+ op = sum.__name__ # INF, NAN, NINF
1638
+ ps[:] = sum(ps), # collapse ps
1639
+ s = self._nonfiniteX(X, op, ps[0])
1640
+ r = _NONFINITEr
1479
1641
  # assert self._ps is ps
1480
1642
  return Fsum2Tuple(s, r)
1481
1643
 
1482
- @_fprs2.setter_ # PYCHOK setter_underscore!
1644
+ @_fprs2.setter_ # PYCHOK setter_UNDERscore!
1483
1645
  def _fprs2(self, s_r):
1484
1646
  '''(INTERNAL) Replace the C{_fprs2} value.
1485
1647
  '''
1486
1648
  return Fsum2Tuple(s_r)
1487
1649
 
1488
1650
  def fset_(self, *xs):
1489
- '''Replace this instance' value with all positional items.
1651
+ '''Apply C{B{self}.partials = Fsum(*B{xs}).partials}.
1490
1652
 
1491
1653
  @arg xs: Optional, new values (each C{scalar} or
1492
- an L{Fsum} or L{Fsum2Tuple} instance),
1493
- all positional.
1654
+ an L{Fsum} or L{Fsum2Tuple} instance), all
1655
+ positional.
1494
1656
 
1495
1657
  @return: This instance, replaced (C{Fsum}).
1496
1658
 
1497
1659
  @see: Method L{Fsum.fadd} for further details.
1498
1660
  '''
1499
- f = xs[0] if len(xs) == 1 else (
1500
- Fsum(*xs) if xs else _0_0)
1501
- return self._fset(f)
1661
+ f = self._Fsum_as(*xs)
1662
+ return self._fset(f, up=False, op=_fset_op_)
1502
1663
 
1503
1664
  def _fset(self, other, n=0, up=True, **op):
1504
1665
  '''(INTERNAL) Overwrite this instance with an other or a C{scalar}.
1505
1666
  '''
1506
1667
  if other is self:
1507
1668
  pass # from ._fmul, ._ftruediv and ._pow_0_1
1508
- elif _isFsumTuple(other):
1669
+ elif _isFsum_2Tuple(other):
1509
1670
  self._ps[:] = other._ps
1510
1671
  self._n = n or other._n
1511
- # self._copy_RESIDUAL(other)
1512
1672
  if up: # use or zap the C{Property_RO} values
1513
1673
  Fsum._fint2._update_from(self, other)
1514
1674
  Fsum._fprs ._update_from(self, other)
@@ -1524,13 +1684,13 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1524
1684
  self._fprs = s
1525
1685
  self._fprs2 = s, INT0
1526
1686
  # assert self._fprs is s
1527
- else: # PYCHOK no cover
1687
+ else:
1528
1688
  op = _xkwds_get1(op, op=_fset_op_)
1529
1689
  raise self._Error(op, other, _TypeError)
1530
1690
  return self
1531
1691
 
1532
- def _fset_ps(self, other): # in .fmath
1533
- '''(INTERNAL) Set partials from a known C{scalar}, L{Fsum} or L{Fsum2Tuple}.
1692
+ def _fset_ps(self, other): # in .fmath._Fsum__init__
1693
+ '''(INTERNAL) Set partials from a known C{other}.
1534
1694
  '''
1535
1695
  return self._fset(other, up=False)
1536
1696
 
@@ -1546,13 +1706,13 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1546
1706
 
1547
1707
  @see: Method L{Fsum.fadd_} for further details.
1548
1708
  '''
1549
- return self._fsub(xs[0], _sub_op_) if len(xs) == 1 else \
1550
- self._facc_neg(xs, origin=1)
1709
+ return self._facc_neg(xs, origin=1) if len(xs) != 1 else \
1710
+ self._fsub(xs[0], _sub_op_)
1551
1711
 
1552
1712
  def _fsub(self, other, op):
1553
1713
  '''(INTERNAL) Apply C{B{self} -= B{other}}.
1554
1714
  '''
1555
- if _isFsumTuple(other):
1715
+ if _isFsum_2Tuple(other):
1556
1716
  if other is self: # or other._fprs2 == self._fprs2:
1557
1717
  self._fset(_0_0, n=len(self) * 2)
1558
1718
  elif other._ps:
@@ -1562,8 +1722,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1562
1722
  return self
1563
1723
 
1564
1724
  def fsum(self, xs=()):
1565
- '''Add an iterable's items, summate and return the
1566
- current precision running sum.
1725
+ '''Add an iterable's items, summate and return the current
1726
+ precision running sum.
1567
1727
 
1568
1728
  @arg xs: Iterable of items to add (each item C{scalar}
1569
1729
  or an L{Fsum} or L{Fsum2Tuple} instance).
@@ -1577,8 +1737,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1577
1737
  return self._facc(xs)._fprs
1578
1738
 
1579
1739
  def fsum_(self, *xs):
1580
- '''Add any positional items, summate and return the
1581
- current precision running sum.
1740
+ '''Add any positional items, summate and return the current
1741
+ precision running sum.
1582
1742
 
1583
1743
  @arg xs: Items to add (each C{scalar} or an L{Fsum}
1584
1744
  or L{Fsum2Tuple} instance), all positional.
@@ -1587,11 +1747,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1587
1747
 
1588
1748
  @see: Methods L{Fsum.fsum}, L{Fsum.Fsum_} and L{Fsum.fsumf_}.
1589
1749
  '''
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
1750
+ return self._facc_args(xs)._fprs
1595
1751
 
1596
1752
  def Fsum_(self, *xs, **name):
1597
1753
  '''Like method L{Fsum.fsum_} but returning a named L{Fsum}.
@@ -1600,7 +1756,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1600
1756
 
1601
1757
  @return: Copy of this updated instance (L{Fsum}).
1602
1758
  '''
1603
- return self._facc_1(xs)._copy_2(self.Fsum_, **name)
1759
+ return self._facc_args(xs)._copy_2(self.Fsum_, **name)
1604
1760
 
1605
1761
  def Fsum2Tuple_(self, *xs, **name):
1606
1762
  '''Like method L{Fsum.fsum_} but returning a named L{Fsum2Tuple}.
@@ -1609,7 +1765,27 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1609
1765
 
1610
1766
  @return: Precision running sum (L{Fsum2Tuple}).
1611
1767
  '''
1612
- return Fsum2Tuple(self._facc_1(xs)._fprs2, **name)
1768
+ return Fsum2Tuple(self._facc_args(xs)._fprs2, **name)
1769
+
1770
+ @property_RO
1771
+ def _Fsum(self): # like L{Fsum2Tuple._Fsum}, for C{_2floats}, .fstats
1772
+ return self # NOT @Property_RO, see .copy and ._copy_2
1773
+
1774
+ def _Fsum_as(self, *xs, **name_f2product_nonfinites_RESIDUAL):
1775
+ '''(INTERNAL) Return an C{Fsum} with this C{Fsum}'s C{.f2product},
1776
+ C{.nonfinites} and C{.RESIDUAL} setting, optionally
1777
+ overridden with C{name_f2product_nonfinites_RESIDUAL} and
1778
+ with any C{xs} accumulated.
1779
+ '''
1780
+ kwds = _xkwds_not(None, Fsum._RESIDUAL, f2product =self.f2product(),
1781
+ nonfinites=self.nonfinites(),
1782
+ RESIDUAL =self.RESIDUAL())
1783
+ if name_f2product_nonfinites_RESIDUAL: # overwrites
1784
+ kwds.update(name_f2product_nonfinites_RESIDUAL)
1785
+ F = Fsum(**kwds)
1786
+ # assert all(v == self.__dict__[n] for n, v in F.__dict__.items())
1787
+ return F._fset(xs[0], op=_fset_op_) if len(xs) == 1 else (
1788
+ F._facc(xs, up=False) if xs else F)
1613
1789
 
1614
1790
  def fsum2(self, xs=(), **name):
1615
1791
  '''Add an iterable's items, summate and return the
@@ -1643,32 +1819,38 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1643
1819
 
1644
1820
  @see: Methods L{Fsum.fsum_} and L{Fsum.fsum}.
1645
1821
  '''
1646
- return self._fsum2(xs, self._facc_1)
1822
+ return self._fsum2(xs, self._facc_args)
1647
1823
 
1648
- def _fsum2(self, xs, _facc, **origin):
1824
+ def _fsum2(self, xs, _facc, **facc_kwds):
1649
1825
  '''(INTERNAL) Helper for L{Fsum.fsum2_} and L{Fsum.fsum2f_}.
1650
1826
  '''
1651
1827
  p, q = self._fprs2
1652
1828
  if xs:
1653
- s, r = _facc(xs, **origin)._fprs2
1654
- return s, _2delta(s - p, r - q) # _fsum(_1primed((s, -p, r, -q))
1829
+ s, r = _facc(xs, **facc_kwds)._fprs2
1830
+ if _isfinite(s): # _fsum(_1primed((s, -p, r, -q))
1831
+ d, r = _2sum(s - p, r - q, _isfine=_isOK)
1832
+ r, _ = _s_r(d, r)
1833
+ return s, (r if _isfinite(r) else _NONFINITEr)
1655
1834
  else:
1656
1835
  return p, _0_0
1657
1836
 
1658
1837
  def fsumf_(self, *xs):
1659
- '''Like method L{Fsum.fsum_} iff I{all} C{B{xs}} are I{known to be scalar}.
1838
+ '''Like method L{Fsum.fsum_} iff I{all} C{B{xs}}, each I{known to be}
1839
+ C{scalar}, an L{Fsum} or L{Fsum2Tuple}.
1660
1840
  '''
1661
- return self._facc_scalar(xs)._fprs
1841
+ return self._facc_scalarf(xs, origin=1, which=self.fsumf_)._fprs
1662
1842
 
1663
1843
  def Fsumf_(self, *xs):
1664
- '''Like method L{Fsum.Fsum_} iff I{all} C{B{xs}} are I{known to be scalar}.
1844
+ '''Like method L{Fsum.Fsum_} iff I{all} C{B{xs}}, each I{known to be}
1845
+ C{scalar}, an L{Fsum} or L{Fsum2Tuple}.
1665
1846
  '''
1666
- return self._facc_scalar(xs)._copy_2(self.Fsumf_)
1847
+ return self._facc_scalarf(xs, origin=1, which=self.Fsumf_)._copy_2(self.Fsumf_)
1667
1848
 
1668
1849
  def fsum2f_(self, *xs):
1669
- '''Like method L{Fsum.fsum2_} iff I{all} C{B{xs}} are I{known to be scalar}.
1850
+ '''Like method L{Fsum.fsum2_} iff I{all} C{B{xs}}, each I{known to be}
1851
+ C{scalar}, an L{Fsum} or L{Fsum2Tuple}.
1670
1852
  '''
1671
- return self._fsum2(xs, self._facc_scalar, origin=1)
1853
+ return self._fsum2(xs, self._facc_scalarf, origin=1, which=self.fsum2f_)
1672
1854
 
1673
1855
  # ftruediv = __itruediv__ # for naming consistency?
1674
1856
 
@@ -1676,7 +1858,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1676
1858
  '''(INTERNAL) Apply C{B{self} /= B{other}}.
1677
1859
  '''
1678
1860
  n = _1_0
1679
- if _isFsumTuple(other):
1861
+ if _isFsum_2Tuple(other):
1680
1862
  if other is self or self == other:
1681
1863
  return self._fset(n, n=len(self))
1682
1864
  d, r = other._fprs2
@@ -1709,15 +1891,14 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1709
1891
  L{ResidualError}s (C{bool}) and C{B{RESIDUAL}=scalar}
1710
1892
  to override the current L{RESIDUAL<Fsum.RESIDUAL>}.
1711
1893
 
1712
- @return: This C{integer} sum if this instance C{is_integer},
1713
- otherwise return the C{float} sum if the residual is
1714
- zero or not significant.
1894
+ @return: This C{int} sum if this instance C{is_integer}, otherwise
1895
+ the C{float} sum if the residual is zero or not significant.
1715
1896
 
1716
1897
  @raise ResidualError: Non-zero, significant residual or invalid
1717
1898
  B{C{RESIDUAL}}.
1718
1899
 
1719
- @see: Methods L{Fsum.fint}, L{Fsum.fint2}, L{Fsum.RESIDUAL} and
1720
- property L{Fsum.as_iscalar}.
1900
+ @see: Methods L{Fsum.fint}, L{Fsum.fint2}, L{Fsum.is_integer},
1901
+ L{Fsum.RESIDUAL} and property L{Fsum.as_iscalar}.
1721
1902
  '''
1722
1903
  s, r = self._fint2
1723
1904
  if r:
@@ -1736,27 +1917,38 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1736
1917
  '''
1737
1918
  return self.residual is INT0
1738
1919
 
1920
+ def is_finite(self): # in .constants
1921
+ '''Is this instance C{finite}? (C{bool}).
1922
+
1923
+ @see: Function L{isfinite<pygeodesy.isfinite>}.
1924
+ '''
1925
+ return _isfinite(sum(self._ps)) # == sum(self)
1926
+
1739
1927
  def is_integer(self):
1740
1928
  '''Is this instance' running sum C{integer}? (C{bool}).
1741
1929
 
1742
1930
  @see: Methods L{Fsum.fint}, L{Fsum.fint2} and L{Fsum.is_scalar}.
1743
1931
  '''
1744
- _, r = self._fint2
1745
- return False if r else True
1932
+ s, r = self._fint2
1933
+ return False if r else (_isfinite(s) and isint(s))
1934
+
1935
+ def is_math_fma(self):
1936
+ '''Is accurate L{f2product} multiplication based on Python's C{math.fma}?
1937
+
1938
+ @return: C{True} if accurate multiplication uses C{math.fma}, C{False}
1939
+ an C{fma} implementation as C{math.fma} or C{None}, a previous
1940
+ C{PyGeodesy} implementation.
1941
+ '''
1942
+ return (_2split3s is _passarg) or (False if _2n_d is None else None)
1746
1943
 
1747
1944
  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.
1945
+ '''Are the summation functions L{fsum}, L{fsum_}, L{fsumf_}, L{fsum1},
1946
+ L{fsum1_} and L{fsum1f_} based on Python's C{math.fsum}?
1751
1947
 
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.
1948
+ @return: C{True} if summation functions use C{math.fsum}, C{False}
1949
+ otherwise.
1757
1950
  '''
1758
- f = Fsum._math_fsum
1759
- return 2 if _psum is f else bool(f)
1951
+ return _sum is _fsum # _fsum.__module__ is fabs.__module__
1760
1952
 
1761
1953
  def is_scalar(self, **raiser_RESIDUAL):
1762
1954
  '''Is this instance' running sum C{scalar} without residual or with
@@ -1781,13 +1973,21 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1781
1973
  def _mul_Fsum(self, other, op=_mul_op_): # in .fmath.Fhorner
1782
1974
  '''(INTERNAL) Return C{B{self} * B{other}} as L{Fsum} or C{0}.
1783
1975
  '''
1784
- # assert _isFsumTuple(other)
1976
+ # assert _isFsum_2Tuple(other)
1785
1977
  if self._ps and other._ps:
1786
1978
  f = self._ps_mul(op, *other._ps) # NO .as_iscalar!
1787
1979
  else:
1788
1980
  f = _0_0
1789
1981
  return f
1790
1982
 
1983
+ def _mul_reduce(self, op, start, *others):
1984
+ '''(INTERNAL) Like fmath.freduce(_operator.mul, ...)
1985
+ for I{non-finite} C{start} and/or C{others}.
1986
+ '''
1987
+ for p in self._ps_other(op, *others):
1988
+ start *= p
1989
+ return start
1990
+
1791
1991
  def _mul_scalar(self, factor, op): # in .fmath.Fhorner
1792
1992
  '''(INTERNAL) Return C{B{self} * scalar B{factor}} as L{Fsum}, C{0.0} or C{self}.
1793
1993
  '''
@@ -1811,6 +2011,79 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1811
2011
  '''
1812
2012
  return _Psum(self._ps_neg) if self._ps else NEG0
1813
2013
 
2014
+ def nonfinites(self, *OK):
2015
+ '''Handle I{non-finite} C{float}s as C{inf}, C{INF}, C{NINF}, C{nan}
2016
+ and C{NAN} for this L{Fsum} or throw C{OverflowError} respectively
2017
+ C{ValueError} exceptions, overriding the L{nonfiniterrors} default.
2018
+
2019
+ @arg OK: If omitted, leave the override unchanged, if C{True},
2020
+ I{non-finites} are C{OK}, if C{False} throw exceptions
2021
+ or if C{None} remove the override (C{bool} or C{None}).
2022
+
2023
+ @return: The previous setting (C{bool} or C{None} if not set).
2024
+
2025
+ @see: Function L{nonfiniterrors<fsums.nonfiniterrors>}.
2026
+
2027
+ @note: Use property L{nonfinitesOK<Fsum.nonfinitesOK>} to determine
2028
+ whether I{non-finites} are C{OK} for this L{Fsum} and by the
2029
+ L{nonfiniterrors} default.
2030
+ '''
2031
+ _ks = Fsum._nonfinites_isfine_kwds
2032
+ if OK: # delattrof(self, _isfine=None)
2033
+ k = _xkwds_pop(self.__dict__, _isfine=None)
2034
+ if OK[0] is not None:
2035
+ self._isfine = _ks[bool(OK[0])]
2036
+ self._update()
2037
+ else: # getattrof(self, _isfine=None)
2038
+ k = _xkwds_get(self.__dict__, _isfine=None)
2039
+ # dict(map(reversed, _ks.items())).get(k, None)
2040
+ # raises a TypeError: unhashable type: 'dict'
2041
+ return True if k is _ks[True] else (
2042
+ False if k is _ks[False] else None)
2043
+
2044
+ _nonfinites_isfine_kwds = {True: dict(_isfine=_isOK),
2045
+ False: dict(_isfine=_isfinite)}
2046
+
2047
+ @property_RO
2048
+ def nonfinitesOK(self):
2049
+ '''Are I{non-finites} C{OK} for this L{Fsum} or by default? (C{bool}).
2050
+ '''
2051
+ nf = self.nonfinites()
2052
+ if nf is None:
2053
+ nf = not nonfiniterrors()
2054
+ return nf
2055
+
2056
+ def _nonfiniteX(self, X, op, f, nonfinites=None, raiser=None):
2057
+ '''(INTERNAL) Handle a I{non-finite} exception.
2058
+ '''
2059
+ if nonfinites is None:
2060
+ nonfinites = _isOK_or_finite(f, **self._isfine) if raiser is None else (not raiser)
2061
+ if not nonfinites:
2062
+ raise self._ErrorX(X, op, f)
2063
+ return f
2064
+
2065
+ def _optionals(self, f2product=None, nonfinites=None, **name_RESIDUAL):
2066
+ '''(INTERNAL) Re/set options from keyword arguments.
2067
+ '''
2068
+ if f2product is not None:
2069
+ self.f2product(f2product)
2070
+ if nonfinites is not None:
2071
+ self.nonfinites(nonfinites)
2072
+ if name_RESIDUAL: # MUST be last
2073
+ n, kwds = _name2__(**name_RESIDUAL)
2074
+ if kwds:
2075
+ R = Fsum._RESIDUAL
2076
+ t = _threshold(R, **kwds)
2077
+ if t != R:
2078
+ self._RESIDUAL = t
2079
+ if n:
2080
+ self.name = n # self.rename(n)
2081
+
2082
+ def _1_Over(self, x, op, **raiser_RESIDUAL): # vs _1_over
2083
+ '''(INTERNAL) Return C{Fsum(1) / B{x}}.
2084
+ '''
2085
+ return self._Fsum_as(_1_0)._ftruediv(x, op, **raiser_RESIDUAL)
2086
+
1814
2087
  @property_RO
1815
2088
  def partials(self):
1816
2089
  '''Get this instance' current, partial sums (C{tuple} of C{float}s).
@@ -1820,7 +2093,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1820
2093
  def pow(self, x, *mod, **raiser_RESIDUAL):
1821
2094
  '''Return C{B{self}**B{x}} as L{Fsum}.
1822
2095
 
1823
- @arg x: The exponent (C{scalar} or L{Fsum}).
2096
+ @arg x: The exponent (C{scalar}, an L{Fsum} or L{Fsum2Tuple}).
1824
2097
  @arg mod: Optional modulus (C{int} or C{None}) for the 3-argument
1825
2098
  C{pow(B{self}, B{other}, B{mod})} version.
1826
2099
  @kwarg raiser_RESIDUAL: Use C{B{raiser}=False} to ignore
@@ -1846,7 +2119,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1846
2119
  def _pow(self, other, unused, op, **raiser_RESIDUAL):
1847
2120
  '''Return C{B{self} ** B{other}}.
1848
2121
  '''
1849
- if _isFsumTuple(other):
2122
+ if _isFsum_2Tuple(other):
1850
2123
  f = self._pow_Fsum(other, op, **raiser_RESIDUAL)
1851
2124
  elif self._scalar(other, op):
1852
2125
  x = self._finite(other, op)
@@ -1867,7 +2140,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1867
2140
 
1868
2141
  if mod: # b, x, mod all C{int}, unless C{mod} is C{None}
1869
2142
  m = mod[0]
1870
- # assert _isFsumTuple(b)
2143
+ # assert _isFsum_2Tuple(b)
1871
2144
 
1872
2145
  def _s(s, r):
1873
2146
  R = self._raiser(r, s, **raiser_RESIDUAL)
@@ -1889,9 +2162,9 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1889
2162
  raise self._ErrorX(X, op, other, *mod)
1890
2163
 
1891
2164
  def _pow_Fsum(self, other, op, **raiser_RESIDUAL):
1892
- '''(INTERNAL) Return C{B{self} **= B{other}} for C{_isFsumTuple(other)}.
2165
+ '''(INTERNAL) Return C{B{self} **= B{other}} for C{_isFsum_2Tuple(other)}.
1893
2166
  '''
1894
- # assert _isFsumTuple(other)
2167
+ # assert _isFsum_2Tuple(other)
1895
2168
  x, r = other._fprs2
1896
2169
  f = self._pow_scalar(x, other, op, **raiser_RESIDUAL)
1897
2170
  if f and r:
@@ -1907,7 +2180,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1907
2180
  _mul_Fsum = Fsum._mul_Fsum
1908
2181
  if x > 4:
1909
2182
  p = self
1910
- f = self if (x & 1) else _Psum_(_1_0)
2183
+ f = self if (x & 1) else self._Fsum_as(_1_0)
1911
2184
  m = x >> 1 # // 2
1912
2185
  while m:
1913
2186
  p = _mul_Fsum(p, p, op) # p **= 2
@@ -1945,14 +2218,14 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1945
2218
  if _isFsum(f):
1946
2219
  s, r = f._fprs2
1947
2220
  if r:
1948
- return _1_Over(f, op, **raiser_RESIDUAL)
2221
+ return self._1_Over(f, op, **raiser_RESIDUAL)
1949
2222
  else: # scalar
1950
2223
  s = f
1951
2224
  # use s**(-1) to get the CPython
1952
2225
  # float_pow error iff s is zero
1953
2226
  x = -1
1954
2227
  elif x < 0: # self**(-1)
1955
- return _1_Over(self, op, **raiser_RESIDUAL) # 1 / self
2228
+ return self._1_Over(self, op, **raiser_RESIDUAL) # 1 / self
1956
2229
  else: # self**1 or self**0
1957
2230
  return self._pow_0_1(x, other) # self, 1 or 1.0
1958
2231
  else: # self**fractional
@@ -1971,12 +2244,13 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1971
2244
  '''
1972
2245
  n = 0
1973
2246
  _2s = _2sum
2247
+ _fi = self._isfine
1974
2248
  for x in (tuple(xs) if xs is ps else xs):
1975
- # assert isscalar(x) and _isfinite(x)
2249
+ # assert isscalar(x) and _isOK_or_finite(x, **self._isfine)
1976
2250
  if x:
1977
2251
  i = 0
1978
2252
  for p in ps:
1979
- x, p = _2s(x, p)
2253
+ x, p = _2s(x, p, **_fi)
1980
2254
  if p:
1981
2255
  ps[i] = p
1982
2256
  i += 1
@@ -1984,6 +2258,10 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1984
2258
  n += 1
1985
2259
  if n:
1986
2260
  self._n += n
2261
+ # if _fi: # collapse ps if non-finite
2262
+ # x = sum(ps)
2263
+ # if not _isfinite(x):
2264
+ # ps[:] = x,
1987
2265
  # Fsum._ps_max = max(Fsum._ps_max, len(ps))
1988
2266
  if up:
1989
2267
  self._update()
@@ -1993,22 +2271,25 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1993
2271
  '''(INTERNAL) Multiply this instance' C{partials} with
1994
2272
  each scalar C{factor} and accumulate into an C{Fsum}.
1995
2273
  '''
1996
- def _pfs(ps, fs):
2274
+ def _psfs(ps, fs, _isfine=_isfinite):
1997
2275
  if len(ps) < len(fs):
1998
2276
  ps, fs = fs, ps
1999
2277
  if self._f2product:
2000
- ps = tuple(_2split3s(ps))
2001
- _xys = _2products
2278
+ fs, p = _2split3s(fs), fs
2279
+ if len(ps) > 1 and fs is not p:
2280
+ fs = tuple(fs) # several ps
2281
+ _pfs = _2products
2002
2282
  else:
2003
- def _xys(x, ys):
2004
- return (x * y for y in ys)
2283
+ def _pfs(p, fs):
2284
+ return (p * f for f in fs)
2005
2285
 
2006
- _fin = _isfinite
2007
- for f in fs:
2008
- for p in _xys(f, ps):
2009
- yield p if _fin(p) else self._finite(p, op)
2286
+ for p in ps:
2287
+ for f in _pfs(p, fs):
2288
+ yield f if _isfine(f) else self._finite(f, op)
2010
2289
 
2011
- return Fsum()._facc_scalar(_pfs(self._ps, factors), up=False)
2290
+ fs = _psfs(self._ps, factors, **self._isfine)
2291
+ f = _Psum(self._ps_acc([], fs, up=False), name=op)
2292
+ return f
2012
2293
 
2013
2294
  @property_RO
2014
2295
  def _ps_neg(self):
@@ -2018,10 +2299,10 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
2018
2299
  yield -p
2019
2300
 
2020
2301
  def _ps_other(self, op, *others):
2021
- '''(INTERNAL) Yield the partials of all C{other}s.
2302
+ '''(INTERNAL) Yield all C{other}s as C{scalar}.
2022
2303
  '''
2023
2304
  for other in others:
2024
- if _isFsumTuple(other):
2305
+ if _isFsum_2Tuple(other):
2025
2306
  for p in other._ps:
2026
2307
  yield p
2027
2308
  else:
@@ -2030,7 +2311,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
2030
2311
  def _ps_1sum(self, *less):
2031
2312
  '''(INTERNAL) Return the partials sum, 1-primed C{less} some scalars.
2032
2313
  '''
2033
- def _1pls(ps, ls):
2314
+ def _1psls(ps, ls):
2034
2315
  yield _1_0
2035
2316
  for p in ps:
2036
2317
  yield p
@@ -2038,7 +2319,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
2038
2319
  yield -p
2039
2320
  yield _N_1_0
2040
2321
 
2041
- return _fsum(_1pls(self._ps, less))
2322
+ return _fsum(_1psls(self._ps, less))
2042
2323
 
2043
2324
  def _raiser(self, r, s, raiser=True, **RESIDUAL):
2044
2325
  '''(INTERNAL) Does ratio C{r / s} exceed the RESIDUAL threshold
@@ -2065,12 +2346,12 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
2065
2346
  and properties L{Fsum.ceil}, L{Fsum.floor},
2066
2347
  L{Fsum.imag} and L{Fsum.residual}.
2067
2348
  '''
2068
- return float(self._fprs)
2349
+ return float(self)
2069
2350
 
2070
2351
  @property_RO
2071
2352
  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}.
2353
+ '''Get this instance' residual or residue (C{float} or C{int}):
2354
+ the C{sum(partials)} less the precision running sum C{fsum}.
2074
2355
 
2075
2356
  @note: The C{residual is INT0} iff the precision running
2076
2357
  C{fsum} is considered to be I{exact}.
@@ -2085,7 +2366,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
2085
2366
 
2086
2367
  @arg threshold: If C{scalar}, the I{ratio} to exceed for raising
2087
2368
  L{ResidualError}s in division and exponention, if
2088
- C{None} restore the default set with env variable
2369
+ C{None}, restore the default set with env variable
2089
2370
  C{PYGEODESY_FSUM_RESIDUAL} or if omitted, keep the
2090
2371
  current setting.
2091
2372
 
@@ -2095,11 +2376,11 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
2095
2376
 
2096
2377
  @note: L{ResidualError}s may be thrown if (1) the non-zero I{ratio}
2097
2378
  C{residual / fsum} exceeds the given B{C{threshold}} and (2)
2098
- the C{residual} is non-zero and (3) I{significant} vs the
2379
+ the C{residual} is non-zero and (3) is I{significant} vs the
2099
2380
  C{fsum}, i.e. C{(fsum + residual) != fsum} and (4) optional
2100
2381
  keyword argument C{raiser=False} is missing. Specify a
2101
2382
  negative B{C{threshold}} for only non-zero C{residual}
2102
- testing without I{significant}.
2383
+ testing without the I{significant} case.
2103
2384
  '''
2104
2385
  r = self._RESIDUAL
2105
2386
  if threshold:
@@ -2122,9 +2403,9 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
2122
2403
  def root(self, root, **raiser_RESIDUAL):
2123
2404
  '''Return C{B{self}**(1 / B{root})} as L{Fsum}.
2124
2405
 
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}
2406
+ @arg root: Non-zero order (C{scalar}, an L{Fsum} or L{Fsum2Tuple}).
2407
+ @kwarg raiser_RESIDUAL: Use C{B{raiser}=False} to ignore any
2408
+ L{ResidualError}s (C{bool}) or C{B{RESIDUAL}=scalar}
2128
2409
  to override the current L{RESIDUAL<Fsum.RESIDUAL>}.
2129
2410
 
2130
2411
  @return: The C{self ** (1 / B{root})} result (L{Fsum}).
@@ -2134,12 +2415,12 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
2134
2415
 
2135
2416
  @see: Method L{Fsum.pow}.
2136
2417
  '''
2137
- x = _1_Over(root, _truediv_op_, **raiser_RESIDUAL)
2138
- f = self._copy_2(self.root)
2418
+ x = self._1_Over(root, _truediv_op_, **raiser_RESIDUAL)
2419
+ f = self._copy_2(self.root)
2139
2420
  return f._fpow(x, f.name, **raiser_RESIDUAL) # == pow(f, x)
2140
2421
 
2141
2422
  def _scalar(self, other, op, **txt):
2142
- '''(INTERNAL) Return scalar C{other}.
2423
+ '''(INTERNAL) Return scalar C{other} or throw a C{TypeError}.
2143
2424
  '''
2144
2425
  if isscalar(other):
2145
2426
  return other
@@ -2148,8 +2429,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
2148
2429
  def signOf(self, res=True):
2149
2430
  '''Determine the sign of this instance.
2150
2431
 
2151
- @kwarg res: If C{True}, consider, otherwise ignore
2152
- the residual (C{bool}).
2432
+ @kwarg res: If C{True}, consider the residual,
2433
+ otherwise ignore the latter (C{bool}).
2153
2434
 
2154
2435
  @return: The sign (C{int}, -1, 0 or +1).
2155
2436
  '''
@@ -2204,23 +2485,17 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
2204
2485
 
2205
2486
  _ROs = _allPropertiesOf_n(3, Fsum, Property_RO) # PYCHOK see Fsum._update
2206
2487
 
2488
+ if _NONFINITES == _std_: # PYCHOK no cover
2489
+ _ = nonfiniterrors(False)
2490
+
2207
2491
 
2208
2492
  def _Float_Int(arg, **name_Error):
2209
- '''(INTERNAL) Unit of L{Fsum2Tuple} items.
2493
+ '''(INTERNAL) L{DivMod2Tuple}, L{Fsum2Tuple} Unit.
2210
2494
  '''
2211
2495
  U = Int if isint(arg) else Float
2212
2496
  return U(arg, **name_Error)
2213
2497
 
2214
2498
 
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
2499
  class DivMod2Tuple(_NamedTuple):
2225
2500
  '''2-Tuple C{(div, mod)} with the quotient C{div} and remainder
2226
2501
  C{mod} results of a C{divmod} operation.
@@ -2228,7 +2503,7 @@ class DivMod2Tuple(_NamedTuple):
2228
2503
  @note: Quotient C{div} an C{int} in Python 3+ but a C{float}
2229
2504
  in Python 2-. Remainder C{mod} an L{Fsum} instance.
2230
2505
  '''
2231
- _Names_ = (_div_, _mod_)
2506
+ _Names_ = ('div', 'mod')
2232
2507
  _Units_ = (_Float_Int, Fsum)
2233
2508
 
2234
2509
 
@@ -2302,17 +2577,23 @@ class Fsum2Tuple(_NamedTuple): # in .fstats
2302
2577
  ps = (r, s) if r else (s,)
2303
2578
  return _Psum(ps, name=self.name)
2304
2579
 
2305
- def Fsum_(self, *xs, **name_RESIDUAL):
2580
+ def Fsum_(self, *xs, **name_f2product_nonfinites_RESIDUAL):
2306
2581
  '''Return this C{Fsum2Tuple} as an L{Fsum} plus some C{xs}.
2307
2582
  '''
2308
- f = _Psum(self._Fsum._ps, **name_RESIDUAL)
2309
- return f._facc_1(xs, up=False) if xs else f
2583
+ return Fsum(self, *xs, **name_f2product_nonfinites_RESIDUAL)
2310
2584
 
2311
2585
  def is_exact(self):
2312
2586
  '''Is this L{Fsum2Tuple} considered to be exact? (C{bool}).
2313
2587
  '''
2314
2588
  return self._Fsum.is_exact()
2315
2589
 
2590
+ def is_finite(self): # in .constants
2591
+ '''Is this L{Fsum2Tuple} C{finite}? (C{bool}).
2592
+
2593
+ @see: Function L{isfinite<pygeodesy.isfinite>}.
2594
+ '''
2595
+ return self._Fsum.is_finite()
2596
+
2316
2597
  def is_integer(self):
2317
2598
  '''Is this L{Fsum2Tuple} C{integer}? (C{bool}).
2318
2599
  '''
@@ -2351,7 +2632,7 @@ class Fsum2Tuple(_NamedTuple): # in .fstats
2351
2632
  '''
2352
2633
  return Fmt.PAREN(fstr(self, fmt=fmt, strepr=str, force=False, **prec_sep))
2353
2634
 
2354
- _Fsum_Fsum2Tuple_types = Fsum, Fsum2Tuple # PYCHOK lines
2635
+ _Fsum_2Tuple_types = Fsum, Fsum2Tuple # PYCHOK lines
2355
2636
 
2356
2637
 
2357
2638
  class ResidualError(_ValueError):
@@ -2373,105 +2654,132 @@ try:
2373
2654
  del _fsum # nope, remove _fsum ...
2374
2655
  raise ImportError() # ... use _fsum below
2375
2656
 
2376
- Fsum._math_fsum = _sum = _fsum # PYCHOK exported
2657
+ _sum = _fsum # in .elliptic
2377
2658
  except ImportError:
2378
- _sum = sum # Fsum(NAN) exception fall-back, in .elliptic
2659
+ _sum = sum # in .elliptic
2379
2660
 
2380
2661
  def _fsum(xs):
2381
2662
  '''(INTERNAL) Precision summation, Python 2.5-.
2382
2663
  '''
2383
- F = Fsum()
2384
- F.name = _fsum.__name__
2385
- return F._facc(xs, up=False)._fprs2.fsum
2664
+ F = Fsum(name=_fsum.name, f2product=False, nonfinites=True)
2665
+ return float(F._facc(xs, up=False))
2386
2666
 
2387
2667
 
2388
- def fsum(xs, floats=False):
2389
- '''Precision floating point summation based on/like Python's C{math.fsum}.
2668
+ def fsum(xs, nonfinites=None, **floats):
2669
+ '''Precision floating point summation from Python's C{math.fsum}.
2390
2670
 
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}).
2671
+ @arg xs: Iterable of items to add (each C{scalar}, an L{Fsum} or L{Fsum2Tuple}).
2672
+ @kwarg nonfinites: Use C{B{nonfinites}=True} if I{non-finites} are C{OK}, if
2673
+ C{False} I{non-finites} raise an Overflow-/ValueError or if
2674
+ C{None}, L{nonfiniterrors} applies (C{bool} or C{None}).
2675
+ @kwarg floats: DEPRECATED keyword argument C{B{floats}=False} (C{bool}), use
2676
+ keyword argument C{B{nonfinites}=False} instead.
2395
2677
 
2396
2678
  @return: Precision C{fsum} (C{float}).
2397
2679
 
2398
- @raise OverflowError: Partial C{2sum} overflow.
2399
-
2400
- @raise TypeError: Non-scalar B{C{xs}} item.
2680
+ @raise OverflowError: Infinite B{C{xs}} item or intermediate C{math.fsum} overflow.
2401
2681
 
2402
- @raise ValueError: Invalid or non-finite B{C{xs}} item.
2682
+ @raise TypeError: Invalid B{C{xs}} item.
2403
2683
 
2404
- @note: Exception and I{non-finite} handling may differ if not based
2405
- on Python's C{math.fsum}.
2684
+ @raise ValueError: Invalid or C{NAN} B{C{xs}} item.
2406
2685
 
2407
- @see: Class L{Fsum} and methods L{Fsum.fsum} and L{Fsum.fadd}.
2686
+ @see: Function L{nonfiniterrors}, class L{Fsum} and methods L{Fsum.nonfinites},
2687
+ L{Fsum.fsum}, L{Fsum.fadd} and L{Fsum.fadd_}.
2408
2688
  '''
2409
- return _fsum(xs if floats is True else _2floats(xs)) if xs else _0_0 # PYCHOK yield
2689
+ return _xsum(fsum, xs, nonfinites=nonfinites, **floats) if xs else _0_0
2410
2690
 
2411
2691
 
2412
- def fsum_(*xs, **floats):
2692
+ def fsum_(*xs, **nonfinites):
2413
2693
  '''Precision floating point summation of all positional items.
2414
2694
 
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}).
2695
+ @arg xs: Items to add (each C{scalar}, an L{Fsum} or L{Fsum2Tuple}), all positional.
2696
+ @kwarg nonfinites: Use C{B{nonfinites}=True} if I{non-finites} are C{OK} (C{bool}).
2419
2697
 
2420
2698
  @see: Function L{fsum<fsums.fsum>} for further details.
2421
2699
  '''
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
2700
+ return _xsum(fsum_, xs, origin=1, **nonfinites) if xs else _0_0
2424
2701
 
2425
2702
 
2426
2703
  def fsumf_(*xs):
2427
- '''Precision floating point summation iff I{all} C{B{xs}} items are I{known to be scalar}.
2704
+ '''Precision floating point summation of all positional items with I{non-finites} C{OK}.
2705
+
2706
+ @arg xs: Items to add (each C{scalar}, an L{Fsum} or L{Fsum2Tuple}),
2707
+ all positional.
2428
2708
 
2429
2709
  @see: Function L{fsum_<fsums.fsum_>} for further details.
2430
2710
  '''
2431
- return _fsum(xs) if xs else _0_0
2711
+ return _xsum(fsumf_, xs, nonfinites=True, origin=1) if xs else _0_0
2432
2712
 
2433
2713
 
2434
- def fsum1(xs, floats=False):
2714
+ def fsum1(xs, **nonfinites):
2435
2715
  '''Precision floating point summation, 1-primed.
2436
2716
 
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}).
2717
+ @arg xs: Iterable of items to add (each C{scalar}, an L{Fsum} or L{Fsum2Tuple}).
2718
+ @kwarg nonfinites: Use C{B{nonfinites}=True} if I{non-finites} are C{OK} (C{bool}).
2441
2719
 
2442
2720
  @see: Function L{fsum<fsums.fsum>} for further details.
2443
2721
  '''
2444
- return _fsum(_1primed(xs if floats is True else _2floats(xs))) if xs else _0_0 # PYCHOK yield
2722
+ return _xsum(fsum1, xs, primed=1, **nonfinites) if xs else _0_0
2445
2723
 
2446
2724
 
2447
- def fsum1_(*xs, **floats):
2448
- '''Precision floating point summation, 1-primed of all positional items.
2725
+ def fsum1_(*xs, **nonfinites):
2726
+ '''Precision floating point summation of all positional items, 1-primed.
2449
2727
 
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}).
2728
+ @arg xs: Items to add (each C{scalar}, an L{Fsum} or L{Fsum2Tuple}), all positional.
2729
+ @kwarg nonfinites: Use C{B{nonfinites}=True} if I{non-finites} are C{OK} (C{bool}).
2454
2730
 
2455
2731
  @see: Function L{fsum_<fsums.fsum_>} for further details.
2456
2732
  '''
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
2733
+ return _xsum(fsum1_, xs, origin=1, primed=1, **nonfinites) if xs else _0_0
2459
2734
 
2460
2735
 
2461
2736
  def fsum1f_(*xs):
2462
- '''Precision floating point summation iff I{all} C{B{xs}} items are I{known to be scalar}.
2737
+ '''Precision floating point summation of all positional items, 1-primed and
2738
+ with I{non-finites} C{OK}.
2463
2739
 
2464
2740
  @see: Function L{fsum_<fsums.fsum_>} for further details.
2465
2741
  '''
2466
- return _fsum(_1primed(xs)) if xs else _0_0
2742
+ return _xsum(fsum1f_, xs, nonfinites=True, primed=1) if xs else _0_0
2467
2743
 
2468
2744
 
2469
- if __name__ == '__main__':
2745
+ def _xs(xs, i_x, nfOK): # in Fsum._facc_scalarf
2746
+ '''(INTERNAL) Yield all C{xs} as C{scalar}.
2747
+ '''
2748
+ _x = _passarg if nfOK else _2finite
2749
+ for i, x in enumerate(xs):
2750
+ i_x[:] = i, x
2751
+ if _isFsum_2Tuple(x):
2752
+ for p in map(_x, x._ps):
2753
+ yield p
2754
+ else:
2755
+ yield _x(x)
2470
2756
 
2471
- # usage: [env _psum=fsum] python3 -m pygeodesy.fsums
2472
2757
 
2473
- if _getenv(_psum.__name__, NN) == _fsum.__name__:
2474
- _psum = _fsum
2758
+ def _xsum(which, xs, nonfinites=None, origin=0, primed=0, **floats):
2759
+ '''(INTERNAL) Precision summation of C{xs} with conditions.
2760
+ '''
2761
+ i_x = [0, xs]
2762
+ try:
2763
+ if floats: # for backward compatibility
2764
+ nonfinites = _xkwds_get1(floats, floats=nonfinites)
2765
+ elif nonfinites is None:
2766
+ nonfinites = not nonfiniterrors()
2767
+ fs = _xs(xs, i_x, nonfinites)
2768
+ return _fsum(_1primed(fs) if primed else fs)
2769
+ except (OverflowError, TypeError, ValueError) as X:
2770
+ origin -= 1 if primed else 0
2771
+ i_x += [origin, which]
2772
+ raise _ixError(X, xs, *i_x)
2773
+
2774
+
2775
+ # delete all decorators, etc.
2776
+ del _allPropertiesOf_n, deprecated_method, deprecated_property_RO, \
2777
+ Property, Property_RO, property_RO, _ALL_LAZY, _F2PRODUCT, \
2778
+ MANT_DIG, _NONFINITES, _RESIDUAL_0_0, _getenv, _std_
2779
+
2780
+ if __name__ == '__main__':
2781
+
2782
+ # usage: python3 -m pygeodesy.fsums
2475
2783
 
2476
2784
  def _test(n):
2477
2785
  # copied from Hettinger, see L{Fsum} reference