pygeodesy 24.4.24__py2.py3-none-any.whl → 24.5.2__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
@@ -14,28 +14,26 @@ L{Fsum.__rpow__} return a (very long) C{int} if invoked with optional argument
14
14
  C{mod} set to C{None}. The C{residual} of an C{integer} L{Fsum} may be between
15
15
  C{-1.0} and C{+1.0}, including C{INT0} if considered to be I{exact}.
16
16
 
17
- Set env variable C{PYGEODESY_FSUM_PARTIALS} to string C{"fsum"}) for summation
18
- of L{Fsum} partials by Python function C{math.fsum}.
19
-
20
17
  Set env variable C{PYGEODESY_FSUM_RESIDUAL} to a C{float} string greater than
21
- C{"0.0"} as the threshold to throw a L{ResidualError} in division or exponention
22
- of an L{Fsum} instance with a I{relative} C{residual} exceeding the threshold,
23
- see methods L{Fsum.RESIDUAL}, L{Fsum.pow}, L{Fsum.__ipow__} and L{Fsum.__itruediv__}.
18
+ C{"0.0"} as the threshold to throw a L{ResidualError} for a division, power or
19
+ root operation of an L{Fsum} instance with a C{residual} I{ratio} exceeding
20
+ the threshold. See methods L{Fsum.RESIDUAL}, L{Fsum.pow}, L{Fsum.__ipow__}
21
+ and L{Fsum.__itruediv__}.
24
22
  '''
25
23
  # make sure int/int division yields float quotient, see .basics
26
24
  from __future__ import division as _; del _ # PYCHOK semicolon
27
25
 
28
- from pygeodesy.basics import iscomplex, isint, isscalar, itemsorted, \
26
+ from pygeodesy.basics import isbool, iscomplex, isint, isscalar, itemsorted, \
29
27
  signOf, _signOf
30
28
  from pygeodesy.constants import INT0, _isfinite, NEG0, _pos_self, \
31
29
  _0_0, _1_0, _N_1_0, Float, Int
32
30
  from pygeodesy.errors import _AssertionError, _OverflowError, _TypeError, \
33
31
  _ValueError, _xError, _xError2, _xkwds_get, \
34
32
  _ZeroDivisionError
35
- from pygeodesy.interns import NN, _arg_, _COMMASPACE_, _DASH_, _DOT_, _EQUAL_, \
36
- _exceeds_, _from_, _iadd_op_, _LANGLE_, _negative_, \
37
- _NOTEQUAL_, _not_finite_, _PERCENT_, _PLUS_, _R_, \
38
- _RANGLE_, _SLASH_, _SPACE_, _STAR_, _UNDER_
33
+ from pygeodesy.interns import NN, _arg_, _COMMASPACE_, _DASH_, _DOT_, \
34
+ _EQUAL_, _from_, _LANGLE_, _NOTEQUAL_, \
35
+ _not_finite_, _PERCENT_, _PLUS_, _RANGLE_, \
36
+ _SLASH_, _SPACE_, _STAR_, _UNDER_
39
37
  from pygeodesy.lazily import _ALL_LAZY, _getenv, _sys_version_info2
40
38
  from pygeodesy.named import _Named, _NamedTuple, _NotImplemented, Fmt, unstr
41
39
  from pygeodesy.props import _allPropertiesOf_n, deprecated_property_RO, \
@@ -46,30 +44,32 @@ from pygeodesy.props import _allPropertiesOf_n, deprecated_property_RO, \
46
44
  from math import ceil as _ceil, fabs, floor as _floor # PYCHOK used! .ltp
47
45
 
48
46
  __all__ = _ALL_LAZY.fsums
49
- __version__ = '24.04.24'
50
-
51
- _add_op_ = _PLUS_ # in .auxilats.auxAngle
52
- _eq_op_ = _EQUAL_ * 2 # _DEQUAL_
53
- _COMMASPACE_R_ = _COMMASPACE_ + _R_
54
- _div_ = 'div'
55
- _exceeds_R_ = _SPACE_ + _exceeds_(_R_)
56
- _floordiv_op_ = _SLASH_ * 2 # _DSLASH_
57
- _fset_op_ = _EQUAL_
58
- _ge_op_ = _RANGLE_ + _EQUAL_
59
- _gt_op_ = _RANGLE_
60
- _integer_ = 'integer'
61
- _le_op_ = _LANGLE_ + _EQUAL_
62
- _lt_op_ = _LANGLE_
63
- _mod_ = 'mod'
64
- _mod_op_ = _PERCENT_
65
- _mul_op_ = _STAR_
66
- _ne_op_ = _NOTEQUAL_
67
- _non_zero_ = 'non-zero'
68
- _pow_op_ = _STAR_ * 2 # _DSTAR_, in .fmath
69
- _sub_op_ = _DASH_ # in .auxilats.auxAngle, .fsums
70
- _truediv_op_ = _SLASH_
71
- _divmod_op_ = _floordiv_op_ + _mod_op_
72
- _isub_op_ = _sub_op_ + _fset_op_ # in .auxilats.auxAngle, .fsums
47
+ __version__ = '24.05.02'
48
+
49
+ _add_op_ = _PLUS_ # in .auxilats.auxAngle
50
+ _eq_op_ = _EQUAL_ * 2 # _DEQUAL_
51
+ _div_ = 'div'
52
+ _floordiv_op_ = _SLASH_ * 2 # _DSLASH_
53
+ _fset_op_ = _EQUAL_
54
+ _ge_op_ = _RANGLE_ + _EQUAL_
55
+ _gt_op_ = _RANGLE_
56
+ _iadd_op_ = _add_op_ + _EQUAL_ # in .auxilats.auxAngle, .fstats
57
+ _integer_ = 'integer'
58
+ _isinstance = isinstance
59
+ _le_op_ = _LANGLE_ + _EQUAL_
60
+ _lt_op_ = _LANGLE_
61
+ _mod_ = 'mod'
62
+ _mod_op_ = _PERCENT_
63
+ _mul_op_ = _STAR_
64
+ _ne_op_ = _NOTEQUAL_
65
+ _non_zero_ = 'non-zero'
66
+ _pow_op_ = _STAR_ * 2 # _DSTAR_
67
+ _significant_ = 'significant'
68
+ _sub_op_ = _DASH_ # in .auxilats.auxAngle
69
+ _threshold_ = 'threshold'
70
+ _truediv_op_ = _SLASH_
71
+ _divmod_op_ = _floordiv_op_ + _mod_op_
72
+ _isub_op_ = _sub_op_ + _fset_op_ # in .auxilats.auxAngle
73
73
 
74
74
 
75
75
  def _2delta(*ab):
@@ -83,7 +83,7 @@ def _2delta(*ab):
83
83
 
84
84
 
85
85
  def _2error(unused):
86
- '''(INTERNAL) Throw a C{not finite} exception.
86
+ '''(INTERNAL) Throw a C{not-finite} exception.
87
87
  '''
88
88
  raise ValueError(_not_finite_)
89
89
 
@@ -109,10 +109,11 @@ def _2floats(xs, origin=0, _X=_X_ps, _x=float):
109
109
  try:
110
110
  i, x = origin, None
111
111
  _fin = _isfinite
112
- _Fs = Fsum
112
+ _FsT = _Fsum_Fsum2Tuple_types
113
+ _is = _isinstance
113
114
  for x in xs:
114
- if isinstance(x, _Fs):
115
- for p in _X(x):
115
+ if _is(x, _FsT):
116
+ for p in _X(x._Fsum):
116
117
  yield p
117
118
  else:
118
119
  f = _x(x)
@@ -146,10 +147,22 @@ def _2halfeven(s, r, p):
146
147
  return s
147
148
 
148
149
 
149
- def _1_over(x, op=_truediv_op_, **raiser):
150
- '''(INTERNAL) Return C{Fsum(1) /= B{x}}.
150
+ def _isFsum(x):
151
+ '''(INTERNAL) Is C{x} an C{Fsum} instance?
151
152
  '''
152
- return _Psum_(_1_0)._ftruediv(x, op, **raiser)
153
+ return _isinstance(x, Fsum)
154
+
155
+
156
+ def _isFsumTuple(x):
157
+ '''(INTERNAL) Is C{x} an C{Fsum} or C{Fsum2Tuple} instance?
158
+ '''
159
+ return _isinstance(x, _Fsum_Fsum2Tuple_types)
160
+
161
+
162
+ def _1_Over(x, op, **raiser_RESIDUAL): # vs _1_over
163
+ '''(INTERNAL) Return C{Fsum(1) / B{x}}.
164
+ '''
165
+ return _Psum_(_1_0)._ftruediv(x, op, **raiser_RESIDUAL)
153
166
 
154
167
 
155
168
  def _1primed(xs): # in .fmath
@@ -162,18 +175,10 @@ def _1primed(xs): # in .fmath
162
175
  yield _N_1_0
163
176
 
164
177
 
165
- def _2ps(s, r):
166
- '''(INTERNAL) Return an C{s} and C{r} pair, I{ps-ordered}.
167
- '''
168
- if fabs(s) < fabs(r):
169
- s, r = r, s
170
- return (r, s) if r else (s,) # PYCHOK types
171
-
172
-
173
178
  def _psum(ps): # PYCHOK used!
174
- '''(INTERNAL) Partials sum, updating C{ps}, I{overridden below}.
179
+ '''(INTERNAL) Partials summation, updating C{ps}.
175
180
  '''
176
- # assert isinstance(ps, list)
181
+ # assert _isinstance(ps, list)
177
182
  i = len(ps) - 1
178
183
  s = _0_0 if i < 0 else ps[i]
179
184
  _2s = _2sum
@@ -207,23 +212,33 @@ def _Psum_(*ps, **name):
207
212
  return _Psum(ps, **name)
208
213
 
209
214
 
210
- def _2scalar(other, _raiser=None, **mod):
211
- '''(INTERNAL) Return B{C{other}} as C{int}, C{float} or C{as-is}.
215
+ def _2scalar2(other):
216
+ '''(INTERNAL) Return 2-tuple C{(other, r)} with C{other} as C{int},
217
+ C{float} or C{as-is} and C{r} the residual of C{as-is}.
212
218
  '''
213
- if isinstance(other, Fsum):
219
+ if _isFsumTuple(other):
214
220
  s, r = other._fint2
215
221
  if r:
216
222
  s, r = other._fprs2
217
223
  if r: # PYCHOK no cover
218
- if _raiser and _raiser(r, s):
219
- t = _stresidual(_non_zero_, r, **mod)
220
- raise ResidualError(t, txt=None)
221
224
  s = other # L{Fsum} as-is
222
225
  else:
226
+ r = 0
223
227
  s = other # C{type} as-is
224
228
  if isint(s, both=True):
225
229
  s = int(s)
226
- return s
230
+ return s, r
231
+
232
+
233
+ def _s_r(s, r):
234
+ '''(INTERNAL) Return C{(s, r)}, I{ordered}.
235
+ '''
236
+ if r:
237
+ if fabs(s) < fabs(r):
238
+ s, r = r, (s or INT0)
239
+ else:
240
+ r = INT0
241
+ return s, r
227
242
 
228
243
 
229
244
  def _strcomplex(s, *args):
@@ -235,16 +250,15 @@ def _strcomplex(s, *args):
235
250
  return _SPACE_(c, s, _from_, n, t)
236
251
 
237
252
 
238
- def _stresidual(prefix, residual, **name_values):
239
- '''(INTERNAL) Residual error as C{str}.
253
+ def _stresidual(prefix, residual, R=0, **mod_ratio):
254
+ '''(INTERNAL) Residual error txt C{str}.
240
255
  '''
241
256
  p = _stresidual.__name__[3:]
242
257
  t = Fmt.PARENSPACED(p, Fmt(residual))
243
- for n, v in itemsorted(name_values):
244
- n = n.replace(_UNDER_, _SPACE_)
258
+ for n, v in itemsorted(mod_ratio):
245
259
  p = Fmt.PARENSPACED(n, Fmt(v))
246
260
  t = _COMMASPACE_(t, p)
247
- return _SPACE_(prefix, t)
261
+ return _SPACE_(prefix, t, Fmt.exceeds_R(R), _threshold_)
248
262
 
249
263
 
250
264
  def _2sum(a, b): # by .testFmath
@@ -260,6 +274,16 @@ def _2sum(a, b): # by .testFmath
260
274
  raise _OverflowError(u, txt=t)
261
275
 
262
276
 
277
+ def _threshold(threshold):
278
+ '''(INTERNAL) Get the L{ResidualError}s threshold.
279
+ '''
280
+ try:
281
+ t = float(threshold) or _0_0
282
+ return t if _isfinite(t) else _2error(t) # PYCHOK None
283
+ except Exception as x:
284
+ raise ResidualError(threshold=threshold, cause=x)
285
+
286
+
263
287
  class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
264
288
  '''Precision floating point summation and I{running} summation.
265
289
 
@@ -267,8 +291,9 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
267
291
  I{running}, precision floating point summations. Accumulation may continue after any
268
292
  intermediate, I{running} summuation.
269
293
 
270
- @note: Accumulated values may be L{Fsum} or C{scalar} instances, any C{type} having
271
- method C{__float__} to convert the C{scalar} to a single C{float}.
294
+ @note: Values may be L{Fsum}, L{Fsum2Tuple}, C{int}, C{float} or C{scalar} instances,
295
+ any C{type} having method C{__float__} to convert the C{scalar} to a single
296
+ C{float}, except C{complex}.
272
297
 
273
298
  @note: Handling of exceptions and C{inf}, C{INF}, C{nan} and C{NAN} differs from
274
299
  Python's C{math.fsum}.
@@ -281,44 +306,46 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
281
306
  <https://Bugs.Python.org/issue2819>}.
282
307
  '''
283
308
  _math_fsum = None
284
- _n = 0
309
+ _n = 0
285
310
  # _ps = [] # partial sums
286
- # _ps_max = 0 # max(Fsum._ps_max, len(Fsum._ps))
287
- _ratio = None # see method _raiser
288
- _recursive = bool(_getenv('PYGEODESY_FSUM_RECURSIVE', NN))
289
- _RESIDUAL = max(float(_getenv('PYGEODESY_FSUM_RESIDUAL', _0_0)), _0_0)
311
+ # _ps_max = 0 # max(Fsum._ps_max, len(Fsum._ps))
312
+ _RESIDUAL = _threshold(_getenv('PYGEODESY_FSUM_RESIDUAL', _0_0))
290
313
 
291
314
  def __init__(self, *xs, **name_RESIDUAL):
292
315
  '''New L{Fsum} for I{running} precision floating point summation.
293
316
 
294
- @arg xs: No, one or more initial values, all positional (each C{scalar}
295
- or an L{Fsum} instance).
317
+ @arg xs: No, one or more items to add (each C{scalar} or an L{Fsum}
318
+ or L{Fsum2Tuple} instance), all positional.
296
319
  @kwarg name_RESIDUAL: Optional C{B{name}=NN} for this L{Fsum} and
297
- C{B{RESIDUAL}=None} for the L{ResidualError} threshold.
320
+ the C{B{RESIDUAL}=0.0} threshold for L{ResidualError}s.
298
321
 
299
322
  @see: Methods L{Fsum.fadd} and L{Fsum.RESIDUAL}.
300
323
  '''
301
324
  if name_RESIDUAL:
302
- n = _xkwds_get(name_RESIDUAL, name=NN)
303
- if n: # set name before ...
304
- self.name = n
305
- r = _xkwds_get(name_RESIDUAL, RESIDUAL=None)
325
+
326
+ def _n_r(name=NN, RESIDUAL=None):
327
+ return name, RESIDUAL
328
+
329
+ n, r = _n_r(**name_RESIDUAL)
306
330
  if r is not None:
307
- self.RESIDUAL(r) # ... ResidualError
331
+ self.RESIDUAL(r)
332
+ if n:
333
+ self.name = n
334
+
308
335
  self._ps = [] # [_0_0], see L{Fsum._fprs}
309
336
  if xs:
310
- self._facc(xs, origin=1, up=False)
337
+ self._facc_1(xs, up=False)
311
338
 
312
339
  def __abs__(self):
313
340
  '''Return this instance' absolute value as an L{Fsum}.
314
341
  '''
315
- s = _fsum(self._ps_1primed()) # == self._cmp_0(0, ...)
342
+ s = self.signOf() # == self._cmp_0(0)
316
343
  return (-self) if s < 0 else self._copy_2(self.__abs__)
317
344
 
318
345
  def __add__(self, other):
319
346
  '''Return C{B{self} + B{other}} as an L{Fsum}.
320
347
 
321
- @arg other: An L{Fsum} or C{scalar}.
348
+ @arg other: An L{Fsum}, L{Fsum2Tuple} or C{scalar}.
322
349
 
323
350
  @return: The sum (L{Fsum}).
324
351
 
@@ -352,19 +379,23 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
352
379
  s = self._cmp_0(other, self.cmp.__name__)
353
380
  return _signOf(s, 0)
354
381
 
355
- cmp = __cmp__
356
-
357
- def __divmod__(self, other):
382
+ def __divmod__(self, other, **raiser_RESIDUAL):
358
383
  '''Return C{divmod(B{self}, B{other})} as a L{DivMod2Tuple}
359
384
  with quotient C{div} an C{int} in Python 3+ or C{float}
360
- in Python 2- and remainder C{mod} an L{Fsum}.
385
+ in Python 2- and remainder C{mod} an L{Fsum} instance.
386
+
387
+ @arg other: An L{Fsum}, L{Fsum2Tuple} or C{scalar} modulus.
388
+ @kwarg raiser_RESIDUAL: Use C{B{raiser}=False} (C{bool}) to
389
+ ignore L{ResidualError}s and C{B{RESIDUAL}=scalar}
390
+ to override the L{RESIDUAL<Fsum.RESIDUAL>}.
361
391
 
362
- @arg other: An L{Fsum} or C{scalar} modulus.
392
+ @raise ResidualError: Non-zero, significant residual or invalid
393
+ B{C{RESIDUAL}}.
363
394
 
364
395
  @see: Method L{Fsum.__itruediv__}.
365
396
  '''
366
397
  f = self._copy_2(self.__divmod__)
367
- return f._fdivmod2(other, _divmod_op_)
398
+ return f._fdivmod2(other, _divmod_op_, **raiser_RESIDUAL)
368
399
 
369
400
  def __eq__(self, other):
370
401
  '''Compare this with an other instance or C{scalar}.
@@ -390,7 +421,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
390
421
  def __floordiv__(self, other):
391
422
  '''Return C{B{self} // B{other}} as an L{Fsum}.
392
423
 
393
- @arg other: An L{Fsum} or C{scalar} divisor.
424
+ @arg other: An L{Fsum}, L{Fsum2Tuple} or C{scalar} divisor.
394
425
 
395
426
  @return: The C{floor} quotient (L{Fsum}).
396
427
 
@@ -421,7 +452,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
421
452
  def __iadd__(self, other):
422
453
  '''Apply C{B{self} += B{other}} to this instance.
423
454
 
424
- @arg other: An L{Fsum} or C{scalar} instance.
455
+ @arg other: An L{Fsum}, L{Fsum2Tuple} or C{scalar} instance.
425
456
 
426
457
  @return: This instance, updated (L{Fsum}).
427
458
 
@@ -435,11 +466,12 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
435
466
  def __ifloordiv__(self, other):
436
467
  '''Apply C{B{self} //= B{other}} to this instance.
437
468
 
438
- @arg other: An L{Fsum} or C{scalar} divisor.
469
+ @arg other: An L{Fsum}, L{Fsum2Tuple} or C{scalar} divisor.
439
470
 
440
471
  @return: This instance, updated (L{Fsum}).
441
472
 
442
- @raise ResidualError: Non-zero residual in B{C{other}}.
473
+ @raise ResidualError: Non-zero, significant residual
474
+ in B{C{other}}.
443
475
 
444
476
  @raise TypeError: Invalid B{C{other}} type.
445
477
 
@@ -458,7 +490,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
458
490
  def __imod__(self, other):
459
491
  '''Apply C{B{self} %= B{other}} to this instance.
460
492
 
461
- @arg other: An L{Fsum} or C{scalar} modulus.
493
+ @arg other: An L{Fsum}, L{Fsum2Tuple} or C{scalar} modulus.
462
494
 
463
495
  @return: This instance, updated (L{Fsum}).
464
496
 
@@ -469,7 +501,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
469
501
  def __imul__(self, other):
470
502
  '''Apply C{B{self} *= B{other}} to this instance.
471
503
 
472
- @arg other: An L{Fsum} or C{scalar} factor.
504
+ @arg other: An L{Fsum}, L{Fsum2Tuple} or C{scalar} factor.
473
505
 
474
506
  @return: This instance, updated (L{Fsum}).
475
507
 
@@ -496,49 +528,48 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
496
528
  # Luciano Ramalho, "Fluent Python", O'Reilly, 2nd Ed, 2022 p. 567
497
529
  return _NotImplemented(self)
498
530
 
499
- def __ipow__(self, other, *mod, **raiser): # PYCHOK 2 vs 3 args
531
+ def __ipow__(self, other, *mod, **raiser_RESIDUAL): # PYCHOK 2 vs 3 args
500
532
  '''Apply C{B{self} **= B{other}} to this instance.
501
533
 
502
- @arg other: The exponent (L{Fsum} or C{scalar}).
503
- @arg mod: Optional modulus (C{int} or C{None}) for the
504
- 3-argument C{pow(B{self}, B{other}, B{mod})}
505
- version.
506
- @kwarg raiser: Use C{B{raiser}=False} to ignore L{ResidualError}s
507
- (C{bool}), see also method L{RESIDUAL}.
534
+ @arg other: The exponent (C{scalar}, L{Fsum} or L{Fsum2Tuple}).
535
+ @arg mod: Optional modulus (C{int} or C{None}) for the 3-argument
536
+ C{pow(B{self}, B{other}, B{mod})} version.
537
+ @kwarg raiser_RESIDUAL: Use C{B{raiser}=False} (C{bool}) to
538
+ ignore L{ResidualError}s and C{B{RESIDUAL}=scalar}
539
+ to override the L{RESIDUAL<Fsum.RESIDUAL>}.
508
540
 
509
541
  @return: This instance, updated (L{Fsum}).
510
542
 
511
543
  @note: If B{C{mod}} is given, the result will be an C{integer}
512
544
  L{Fsum} in Python 3+ if this instance C{is_integer} or
513
- set to C{as_integer} if B{C{mod}} given as C{None}.
545
+ set to C{as_integer} and B{C{mod}} is given as C{None}.
514
546
 
515
547
  @raise OverflowError: Partial C{2sum} overflow.
516
548
 
517
- @raise ResidualError: Non-zero residual in B{C{other}} and
518
- env var C{PYGEODESY_FSUM_RESIDUAL}
519
- set or this instance has a non-zero
520
- residual and either B{C{mod}} is
521
- given and non-C{None} or B{C{other}}
522
- is a negative or fractional C{scalar}.
549
+ @raise ResidualError: Invalid B{C{RESIDUAL}} or the residual
550
+ is non-zero and significant and either
551
+ B{C{other}} is a fractional or negative
552
+ C{scalar} or B{C{mod}} is given and not
553
+ C{None}.
523
554
 
524
- @raise TypeError: Invalid B{C{other}} type or 3-argument
525
- C{pow} invocation failed.
555
+ @raise TypeError: Invalid B{C{other}} type or 3-argument C{pow}
556
+ invocation failed.
526
557
 
527
- @raise ValueError: If B{C{other}} is a negative C{scalar}
528
- and this instance is C{0} or B{C{other}}
529
- is a fractional C{scalar} and this
530
- instance is negative or has a non-zero
531
- residual or B{C{mod}} is given and C{0}.
558
+ @raise ValueError: If B{C{other}} is a negative C{scalar} and this
559
+ instance is C{0} or B{C{other}} is a fractional
560
+ C{scalar} and this instance is negative or has a
561
+ non-zero and significant residual or B{C{mod}}
562
+ is given as C{0}.
532
563
 
533
564
  @see: CPython function U{float_pow<https://GitHub.com/
534
565
  python/cpython/blob/main/Objects/floatobject.c>}.
535
566
  '''
536
- return self._fpow(other, _pow_op_ + _fset_op_, *mod, **raiser)
567
+ return self._fpow(other, _pow_op_ + _fset_op_, *mod, **raiser_RESIDUAL)
537
568
 
538
569
  def __isub__(self, other):
539
570
  '''Apply C{B{self} -= B{other}} to this instance.
540
571
 
541
- @arg other: An L{Fsum} or C{scalar}.
572
+ @arg other: An L{Fsum}, L{Fsum2Tuple} or C{scalar}.
542
573
 
543
574
  @return: This instance, updated (L{Fsum}).
544
575
 
@@ -553,19 +584,20 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
553
584
  '''
554
585
  return iter(self.partials)
555
586
 
556
- def __itruediv__(self, other, **raiser):
587
+ def __itruediv__(self, other, **raiser_RESIDUAL):
557
588
  '''Apply C{B{self} /= B{other}} to this instance.
558
589
 
559
- @arg other: An L{Fsum} or C{scalar} divisor.
560
- @kwarg raiser: Use C{B{raiser}=False} to ignore L{ResidualError}s
561
- (C{bool}), see also method L{RESIDUAL}.
590
+ @arg other: An L{Fsum}, L{Fsum2Tuple} or C{scalar} divisor.
591
+ @kwarg raiser_RESIDUAL: Use C{B{raiser}=False} (C{bool}) to
592
+ ignore L{ResidualError}s and C{B{RESIDUAL}=scalar}
593
+ to override the L{RESIDUAL<Fsum.RESIDUAL>}.
562
594
 
563
595
  @return: This instance, updated (L{Fsum}).
564
596
 
565
597
  @raise OverflowError: Partial C{2sum} overflow.
566
598
 
567
- @raise ResidualError: Non-zero residual in B{C{other}} and
568
- env var C{PYGEODESY_FSUM_RESIDUAL} set.
599
+ @raise ResidualError: Non-zero, significant residual or invalid
600
+ B{C{RESIDUAL}}.
569
601
 
570
602
  @raise TypeError: Invalid B{C{other}} type.
571
603
 
@@ -575,7 +607,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
575
607
 
576
608
  @see: Method L{Fsum.__ifloordiv__}.
577
609
  '''
578
- return self._ftruediv(other, _truediv_op_ + _fset_op_, **raiser)
610
+ return self._ftruediv(other, _truediv_op_ + _fset_op_, **raiser_RESIDUAL)
579
611
 
580
612
  def __le__(self, other):
581
613
  '''Compare this with an other instance or C{scalar}.
@@ -645,8 +677,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
645
677
  return f._fadd(self, _add_op_)
646
678
 
647
679
  def __rdivmod__(self, other):
648
- '''Return C{divmod(B{other}, B{self})} as 2-tuple C{(quotient,
649
- remainder)}.
680
+ '''Return C{divmod(B{other}, B{self})} as 2-tuple
681
+ C{(quotient, remainder)}.
650
682
 
651
683
  @see: Method L{Fsum.__divmod__}.
652
684
  '''
@@ -686,14 +718,14 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
686
718
  f = self._copy_2r(other, self.__rmul__)
687
719
  return f._fmul(self, _mul_op_)
688
720
 
689
- def __round__(self, *ndigits): # PYCHOK no cover
721
+ def __round__(self, *ndigits): # PYCHOK Python 3+
690
722
  '''Return C{round(B{self}, *B{ndigits}} as an L{Fsum}.
691
723
 
692
724
  @arg ndigits: Optional number of digits (C{int}).
693
725
  '''
726
+ f = self._copy_2(self.__round__)
694
727
  # <https://docs.Python.org/3.12/reference/datamodel.html?#object.__round__>
695
- return _Psum_(round(float(self), *ndigits), # can be C{int}
696
- name=self.__round__.__name__)
728
+ return f._fset(round(float(self), *ndigits)) # can be C{int}
697
729
 
698
730
  def __rpow__(self, other, *mod):
699
731
  '''Return C{B{other}**B{self}} as an L{Fsum}.
@@ -711,13 +743,13 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
711
743
  f = self._copy_2r(other, self.__rsub__)
712
744
  return f._fsub(self, _sub_op_)
713
745
 
714
- def __rtruediv__(self, other, **raiser):
746
+ def __rtruediv__(self, other, **raiser_RESIDUAL):
715
747
  '''Return C{B{other} / B{self}} as an L{Fsum}.
716
748
 
717
749
  @see: Method L{Fsum.__itruediv__}.
718
750
  '''
719
751
  f = self._copy_2r(other, self.__rtruediv__)
720
- return f._ftruediv(self, _truediv_op_, **raiser)
752
+ return f._ftruediv(self, _truediv_op_, **raiser_RESIDUAL)
721
753
 
722
754
  def __str__(self):
723
755
  '''Return the default C{str(self)}.
@@ -727,7 +759,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
727
759
  def __sub__(self, other):
728
760
  '''Return C{B{self} - B{other}} as an L{Fsum}.
729
761
 
730
- @arg other: An L{Fsum} or C{scalar}.
762
+ @arg other: An L{Fsum}, L{Fsum2Tuple} or C{scalar}.
731
763
 
732
764
  @return: The difference (L{Fsum}).
733
765
 
@@ -736,19 +768,23 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
736
768
  f = self._copy_2(self.__sub__)
737
769
  return f._fsub(other, _sub_op_)
738
770
 
739
- def __truediv__(self, other, **raiser):
771
+ def __truediv__(self, other, **raiser_RESIDUAL):
740
772
  '''Return C{B{self} / B{other}} as an L{Fsum}.
741
773
 
742
- @arg other: An L{Fsum} or C{scalar} divisor.
743
- @kwarg raiser: Use C{B{raiser}=False} to ignore L{ResidualError}s
744
- (C{bool}), see also method L{RESIDUAL}.
774
+ @arg other: An L{Fsum}, L{Fsum2Tuple} or C{scalar} divisor.
775
+ @kwarg raiser_RESIDUAL: Use C{B{raiser}=False} (C{bool}) to
776
+ ignore L{ResidualError}s and C{B{RESIDUAL}=scalar}
777
+ to override the L{RESIDUAL<Fsum.RESIDUAL>}.
745
778
 
746
779
  @return: The quotient (L{Fsum}).
747
780
 
781
+ @raise ResidualError: Non-zero, significant residual or invalid
782
+ B{C{RESIDUAL}}.
783
+
748
784
  @see: Method L{Fsum.__itruediv__}.
749
785
  '''
750
786
  f = self._copy_2(self.__truediv__)
751
- return f._ftruediv(other, _truediv_op_, **raiser)
787
+ return f._ftruediv(other, _truediv_op_, **raiser_RESIDUAL)
752
788
 
753
789
  __trunc__ = __int__
754
790
 
@@ -770,7 +806,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
770
806
  '''
771
807
  n, r = self._fint2
772
808
  if r:
773
- i, d = r.as_integer_ratio()
809
+ i, d = float(r).as_integer_ratio()
774
810
  n *= d
775
811
  n += i
776
812
  else: # PYCHOK no cover
@@ -779,7 +815,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
779
815
 
780
816
  @property_RO
781
817
  def as_iscalar(self):
782
- '''Get this instance I{as-is} (L{Fsum}) or C{scalar} iff scalar.
818
+ '''Get this instance I{as-is} (L{Fsum}) or as C{scalar} iff scalar.
783
819
  '''
784
820
  s, r = self._fprs2
785
821
  return self if r else s
@@ -800,16 +836,17 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
800
836
  c += 1
801
837
  return c
802
838
 
839
+ cmp = __cmp__
840
+
803
841
  def _cmp_0(self, other, op):
804
842
  '''(INTERNAL) Return C{scalar(self - B{other})} for 0-comparison.
805
843
  '''
806
- if isinstance(other, Fsum):
844
+ if _isFsumTuple(other):
807
845
  s = _fsum(self._ps_1primed(*other._ps))
808
846
  elif self._scalar(other, op):
809
847
  s = _fsum(self._ps_1primed(other))
810
848
  else:
811
- s, r = self._fprs2
812
- s = _signOf(s, -r)
849
+ s = self.signOf() # res=True
813
850
  return s
814
851
 
815
852
  def copy(self, deep=False, name=NN):
@@ -837,8 +874,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
837
874
  def _copy_2r(self, other, which):
838
875
  '''(INTERNAL) Copy for I{reverse-dyadic} operators.
839
876
  '''
840
- return other._copy_2(which) if isinstance(other, Fsum) else \
841
- Fsum(other, name=which.__name__)
877
+ return other._copy_2(which) if _isFsum(other) else \
878
+ self._copy_2(which)._fset(other)
842
879
 
843
880
  # def _copy_RESIDUAL(self, other):
844
881
  # '''(INTERNAL) Copy C{other._RESIDUAL}.
@@ -847,22 +884,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
847
884
  # if R is not Fsum._RESIDUAL:
848
885
  # self._RESIDUAL = R
849
886
 
850
- def divmod(self, other, **raiser):
851
- '''Return C{divmod(B{self}, B{other})} as 2-tuple C{(quotient,
852
- remainder)}.
853
-
854
- @arg other: An L{Fsum} or C{scalar} divisor.
855
- @kwarg raiser: Use C{B{raiser}=False} to ignore L{ResidualError}s
856
- (C{bool}), see also method L{RESIDUAL}.
857
-
858
- @return: A L{DivMod2Tuple}C{(div, mod)}, with quotient C{div}
859
- an C{int} in Python 3+ or C{float} in Python 2- and
860
- remainder C{mod} an L{Fsum} instance.
861
-
862
- @see: Method L{Fsum.__itruediv__}.
863
- '''
864
- f = self._copy_2(self.divmod)
865
- return f._fdivmod2(other, _divmod_op_, **raiser)
887
+ divmod = __divmod__
866
888
 
867
889
  def _Error(self, op, other, Error, **txt_cause):
868
890
  '''(INTERNAL) Format an B{C{Error}} for C{{self} B{op} B{other}}.
@@ -881,8 +903,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
881
903
  '''(INTERNAL) Format the caught exception C{X}.
882
904
  '''
883
905
  E, t = _xError2(X)
884
- n = unstr(self.named3, *xs[:3], _ELLIPSIS=len(xs) > 3, **kwds)
885
- return E(n, txt=t, cause=X)
906
+ u = unstr(self.named3, *xs[:3], _ELLIPSIS=len(xs) > 3, **kwds)
907
+ return E(u, txt=t, cause=X)
886
908
 
887
909
  def _facc(self, xs, up=True, **origin_X_x):
888
910
  '''(INTERNAL) Accumulate more C{scalars} or L{Fsum}s.
@@ -897,32 +919,31 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
897
919
  '''(INTERNAL) Accumulate 0, 1 or more C{scalars} or L{Fsum}s,
898
920
  all positional C{xs} in the caller of this method.
899
921
  '''
900
- # assert islistuple(xs)
901
- return self._fadd(xs[0], _add_op_) if len(xs) == 1 else \
922
+ return self._fadd(xs[0], _add_op_, **up) if len(xs) == 1 else \
902
923
  self._facc(xs, origin=1, **up)
903
924
 
904
- def _facc_neg(self, xs, up=True, **origin):
925
+ def _facc_neg(self, xs, **up_origin):
905
926
  '''(INTERNAL) Accumulate more C{scalars} or L{Fsum}s, negated.
906
927
  '''
907
- if xs:
908
- def _neg(x):
909
- return -x
928
+ def _N(X):
929
+ return X._ps_neg
910
930
 
911
- _x = _2floats(xs, **origin) # PYCHOK yield
912
- ps = self._ps
913
- ps[:] = self._ps_acc(list(ps), map(_neg, _x), up=up)
914
- return self
931
+ def _n(x):
932
+ return -float(x)
915
933
 
916
- def _facc_power(self, power, xs, which, **raiser): # in .fmath
934
+ return self._facc(xs, _X=_N, _x=_n, **up_origin)
935
+
936
+ def _facc_power(self, power, xs, which, **raiser_RESIDUAL): # in .fmath
917
937
  '''(INTERNAL) Add each C{xs} as C{float(x**power)}.
918
938
  '''
919
939
  def _Pow4(p):
920
940
  r = 0
921
- if isinstance(p, Fsum):
941
+ if _isFsumTuple(p):
922
942
  s, r = p._fprs2
923
- if r == 0:
943
+ if r:
944
+ m = Fsum._pow
945
+ else: # scalar
924
946
  return _Pow4(s)
925
- m = Fsum._pow
926
947
  elif isint(p, both=True) and int(p) >= 0:
927
948
  p = s = int(p)
928
949
  m = Fsum._pow_int
@@ -933,24 +954,22 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
933
954
 
934
955
  _Pow, p, s, r = _Pow4(power)
935
956
  if p: # and xs:
936
- _pow = Fsum._pow_2_3
937
- _Fs = Fsum
938
- _Ps = _Psum_ # ()._fset_ps_
939
957
  op = which.__name__
958
+ _isF = _isFsum
959
+ _pow = self._pow_2_3
940
960
 
941
- def _X(X):
942
- f = _Pow(X, p, power, op, **raiser)
943
- return f._ps if isinstance(f, _Fs) else (f,)
961
+ def _P(X):
962
+ f = _Pow(X, p, power, op, **raiser_RESIDUAL)
963
+ return f._ps if _isF(f) else (f,)
944
964
 
945
- def _x(x):
965
+ def _p(x):
946
966
  x = float(x)
947
- X = _Ps(x)
948
- f = _pow(X, x, s, power, op, **raiser)
949
- if r:
950
- f *= _pow(X, x, r, power, op, **raiser)
967
+ f = _pow(x, s, power, op, **raiser_RESIDUAL)
968
+ if f and r:
969
+ f *= _pow(x, r, power, op, **raiser_RESIDUAL)
951
970
  return f
952
971
 
953
- f = self._facc(xs, origin=1, _X=_X, _x=_x)
972
+ f = self._facc(xs, origin=1, _X=_P, _x=_p)
954
973
  else:
955
974
  f = self._facc_scalar_(float(len(xs))) # x**0 == 1
956
975
  return f
@@ -959,47 +978,46 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
959
978
  '''(INTERNAL) Accumulate all C{xs}, known to be scalar.
960
979
  '''
961
980
  if xs:
962
- self._ps_acc(self._ps, xs, **up)
981
+ _ = self._ps_acc(self._ps, xs, **up)
963
982
  return self
964
983
 
965
984
  def _facc_scalar_(self, *xs, **up):
966
985
  '''(INTERNAL) Accumulate all positional C{xs}, known to be scalar.
967
986
  '''
968
987
  if xs:
969
- self._ps_acc(self._ps, xs, **up)
988
+ _ = self._ps_acc(self._ps, xs, **up)
970
989
  return self
971
990
 
972
991
  # def _facc_up(self, up=True):
973
992
  # '''(INTERNAL) Update the C{partials}, by removing
974
993
  # and re-accumulating the final C{partial}.
975
994
  # '''
976
- # while len(self._ps) > 1:
977
- # p = self._ps.pop()
995
+ # ps = self._ps
996
+ # while len(ps) > 1:
997
+ # p = ps.pop()
978
998
  # if p:
979
999
  # n = self._n
980
- # self._facc_scalar_(p, up=False)
1000
+ # _ = self._ps_acc(ps, (p,), up=False)
981
1001
  # self._n = n
982
1002
  # break
983
- # return self._update() if up else self # ._fpsqz()
1003
+ # return self._update() if up else self
984
1004
 
985
1005
  def fadd(self, xs=()):
986
- '''Add an iterable of C{scalar} or L{Fsum} instances
987
- to this instance.
1006
+ '''Add all items from an iterable to this instance.
988
1007
 
989
- @arg xs: Iterable, list, tuple, etc. (C{scalar} or
990
- L{Fsum} instances).
1008
+ @arg xs: Iterable of items to add (each C{scalar}
1009
+ or an L{Fsum} or L{Fsum2Tuple} instance).
991
1010
 
992
1011
  @return: This instance (L{Fsum}).
993
1012
 
994
1013
  @raise OverflowError: Partial C{2sum} overflow.
995
1014
 
996
- @raise TypeError: An invalid B{C{xs}} type, not C{scalar}
997
- nor L{Fsum}.
1015
+ @raise TypeError: An invalid B{C{xs}} item.
998
1016
 
999
1017
  @raise ValueError: Invalid or non-finite B{C{xs}} value.
1000
1018
  '''
1001
- if isinstance(xs, Fsum):
1002
- self._facc_scalar(xs._ps) # tuple
1019
+ if _isFsumTuple(xs):
1020
+ self._facc_scalar(xs._ps) # tuple(xs._ps)
1003
1021
  elif isscalar(xs): # for backward compatibility
1004
1022
  self._facc_scalar_(_2float(x=xs)) # PYCHOK no cover
1005
1023
  elif xs: # assert isiterable(xs)
@@ -1007,27 +1025,19 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1007
1025
  return self
1008
1026
 
1009
1027
  def fadd_(self, *xs):
1010
- '''Add all positional C{scalar} or L{Fsum} instances
1011
- to this instance.
1012
-
1013
- @arg xs: Values to add (C{scalar} or L{Fsum} instances),
1014
- all positional.
1015
-
1016
- @return: This instance (L{Fsum}).
1017
-
1018
- @raise OverflowError: Partial C{2sum} overflow.
1028
+ '''Add all positional arguments to this instance.
1019
1029
 
1020
- @raise TypeError: An invalid B{C{xs}} type, not C{scalar}
1021
- nor L{Fsum}.
1030
+ @arg xs: Values to add (each C{scalar} or an L{Fsum}
1031
+ or L{Fsum2Tuple} instance), all positional.
1022
1032
 
1023
- @raise ValueError: Invalid or non-finite B{C{xs}} value.
1033
+ @see: Method L{Fsum.fadd} for further details.
1024
1034
  '''
1025
1035
  return self._facc_1(xs)
1026
1036
 
1027
1037
  def _fadd(self, other, op, **up): # in .fmath.Fhorner
1028
1038
  '''(INTERNAL) Apply C{B{self} += B{other}}.
1029
1039
  '''
1030
- if isinstance(other, Fsum):
1040
+ if _isFsumTuple(other):
1031
1041
  self._facc_scalar(other._ps, **up) # tuple
1032
1042
  elif self._scalar(other, op):
1033
1043
  self._facc_scalar_(other, **up)
@@ -1037,16 +1047,16 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1037
1047
  fdiv = __itruediv__ # for backward compatibility
1038
1048
  fdivmod = __divmod__ # for backward compatibility
1039
1049
 
1040
- def _fdivmod2(self, other, op, **raiser):
1050
+ def _fdivmod2(self, other, op, **raiser_RESIDUAL):
1041
1051
  '''(INTERNAL) Apply C{B{self} %= B{other}} and return a L{DivMod2Tuple}.
1042
1052
  '''
1043
1053
  # result mostly follows CPython function U{float_divmod
1044
1054
  # <https://GitHub.com/python/cpython/blob/main/Objects/floatobject.c>},
1045
1055
  # but at least divmod(-3, 2) equals Cpython's result (-2, 1).
1046
- f = self._copy_2(self._fdivmod2)
1047
- q = f._ftruediv(other, op, **raiser).floor
1056
+ q = self._copy_2(self._fdivmod2)
1057
+ q = q._ftruediv(other, op, **raiser_RESIDUAL).floor
1048
1058
  if q: # == float // other == floor(float / other)
1049
- self -= other * q
1059
+ self -= Fsum(other) * q # NO Fsum2Tuple.__mul__!
1050
1060
 
1051
1061
  s = signOf(other) # make signOf(self) == signOf(other)
1052
1062
  if s and self.signOf() == -s: # PYCHOK no cover
@@ -1065,33 +1075,34 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1065
1075
  raise ValueError(_not_finite_) if op is None else \
1066
1076
  self._ValueError(op, other, txt=_not_finite_)
1067
1077
 
1068
- def fint(self, raiser=True, name=NN, **RESIDUAL):
1078
+ def fint(self, name=NN, **raiser_RESIDUAL):
1069
1079
  '''Return this instance' current running sum as C{integer}.
1070
1080
 
1071
- @kwarg raiser: Use C{B{raiser}=False} to ignore L{ResidualError}s
1072
- (C{bool}), see also method L{RESIDUAL}.
1073
1081
  @kwarg name: Optional name (C{str}), overriding C{"fint"}.
1074
- @kwarg RESIDUAL: Optional threshold, overriding the current
1075
- L{RESIDUAL<Fsum.RESIDUAL>} (C{scalar}).
1082
+ @kwarg raiser_RESIDUAL: Use C{B{raiser}=False} (C{bool}) to
1083
+ ignore L{ResidualError}s and C{B{RESIDUAL}=scalar}
1084
+ to override the L{RESIDUAL<Fsum.RESIDUAL>}.
1076
1085
 
1077
- @return: The C{integer} sum (L{Fsum}) if this instance
1078
- C{is_integer} and the residual is zero or
1079
- insignificant or if C{B{raiser}=False}.
1086
+ @return: The C{integer} sum (L{Fsum}) if this instance C{is_integer}
1087
+ with a zero or insignificant I{integer} residual.
1080
1088
 
1081
- @raise ResidualError: Non-zero I{integer} residual.
1089
+ @raise ResidualError: Non-zero, significant residual or invalid
1090
+ B{C{RESIDUAL}}.
1082
1091
 
1083
1092
  @see: Methods L{Fsum.int_float} and L{Fsum.is_integer}.
1084
1093
  '''
1085
1094
  i, r = self._fint2
1086
- if r and raiser and self._raiser2sum(r, i, **RESIDUAL):
1087
- t = _stresidual(_integer_, r)
1088
- raise ResidualError(_integer_, i, txt=t)
1095
+ if r:
1096
+ R = self._raiser(r, i, **raiser_RESIDUAL)
1097
+ if R:
1098
+ t = _stresidual(_integer_, r, **R)
1099
+ raise ResidualError(_integer_, i, txt=t)
1089
1100
  f = self._copy_2(self.fint, name=name)
1090
1101
  return f._fset(i)
1091
1102
 
1092
1103
  def fint2(self, **name):
1093
- '''Return this instance' current running sum as C{int} and
1094
- the I{integer} residual.
1104
+ '''Return this instance' current running sum as C{int} and the
1105
+ I{integer} residual.
1095
1106
 
1096
1107
  @kwarg name: Optional name (C{str}).
1097
1108
 
@@ -1133,10 +1144,10 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1133
1144
 
1134
1145
  # floordiv = __floordiv__ # for naming consistency
1135
1146
 
1136
- def _floordiv(self, other, op, **raiser): # rather _ffloordiv?
1147
+ def _floordiv(self, other, op, **raiser_RESIDUAL): # rather _ffloordiv?
1137
1148
  '''Apply C{B{self} //= B{other}}.
1138
1149
  '''
1139
- q = self._ftruediv(other, op, **raiser) # == self
1150
+ q = self._ftruediv(other, op, **raiser_RESIDUAL) # == self
1140
1151
  return self._fset(q.floor) # floor(q)
1141
1152
 
1142
1153
  fmul = __imul__ # for backward compatibility
@@ -1144,7 +1155,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1144
1155
  def _fmul(self, other, op):
1145
1156
  '''(INTERNAL) Apply C{B{self} *= B{other}}.
1146
1157
  '''
1147
- if isinstance(other, Fsum):
1158
+ if _isFsumTuple(other):
1148
1159
  if len(self._ps) != 1:
1149
1160
  f = self._mul_Fsum(other, op)
1150
1161
  elif len(other._ps) != 1: # and len(self._ps) == 1
@@ -1156,37 +1167,41 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1156
1167
  f = self._mul_scalar(s, op)
1157
1168
  return self._fset(f) # n=len(self) + 1
1158
1169
 
1159
- def fover(self, over, **raiser):
1170
+ def fover(self, over, **raiser_RESIDUAL):
1160
1171
  '''Apply C{B{self} /= B{over}} and summate.
1161
1172
 
1162
1173
  @arg over: An L{Fsum} or C{scalar} denominator.
1163
- @kwarg raiser: Use C{B{raiser}=False} to ignore L{ResidualError}s
1164
- (C{bool}), see also method L{RESIDUAL}.
1174
+ @kwarg raiser_RESIDUAL: Use C{B{raiser}=False} (C{bool}) to
1175
+ ignore L{ResidualError}s and C{B{RESIDUAL}=scalar}
1176
+ to override the L{RESIDUAL<Fsum.RESIDUAL>}.
1165
1177
 
1166
1178
  @return: Precision running sum (C{float}).
1167
1179
 
1180
+ @raise ResidualError: Non-zero, significant residual or invalid
1181
+ B{C{RESIDUAL}}.
1182
+
1168
1183
  @see: Methods L{Fsum.fsum} and L{Fsum.__itruediv__}.
1169
1184
  '''
1170
- return float(self.fdiv(over, **raiser)._fprs)
1185
+ return float(self.fdiv(over, **raiser_RESIDUAL)._fprs)
1171
1186
 
1172
1187
  fpow = __ipow__ # for backward compatibility
1173
1188
 
1174
- def _fpow(self, other, op, *mod, **raiser):
1189
+ def _fpow(self, other, op, *mod, **raiser_RESIDUAL):
1175
1190
  '''Apply C{B{self} **= B{other}}, optional B{C{mod}} or C{None}.
1176
1191
  '''
1177
1192
  if mod:
1178
1193
  if mod[0] is not None: # == 3-arg C{pow}
1179
- f = self._pow_2_3(self, other, other, op, *mod, **raiser)
1194
+ f = self._pow_2_3(self, other, other, op, *mod, **raiser_RESIDUAL)
1180
1195
  elif self.is_integer():
1181
1196
  # return an exact C{int} for C{int}**C{int}
1182
1197
  i, _ = self._fint2 # assert _ == 0
1183
- x = _2scalar(other) # C{int}, C{float} or other
1184
- f = self._pow_2_3(i, x, other, op, **raiser) if isscalar(x) else \
1185
- _Psum_(i)._pow( x, other, op, **raiser) # x is Fsum
1198
+ x, r = _2scalar2(other) # C{int}, C{float} or other
1199
+ f = _Psum_(i)._pow_Fsum(other, op, **raiser_RESIDUAL) if r else \
1200
+ self._pow_2_3(i, x, other, op, **raiser_RESIDUAL)
1186
1201
  else: # mod[0] is None, power(self, other)
1187
- f = self._pow(other, other, op, **raiser)
1202
+ f = self._pow(other, other, op, **raiser_RESIDUAL)
1188
1203
  else: # pow(self, other)
1189
- f = self._pow(other, other, op, **raiser)
1204
+ f = self._pow(other, other, op, **raiser_RESIDUAL)
1190
1205
  return self._fset(f, asis=isint(f)) # n=max(len(self), 1)
1191
1206
 
1192
1207
  @Property_RO
@@ -1210,11 +1225,11 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1210
1225
  s = _psum(ps)
1211
1226
  n = len(ps) - 2
1212
1227
  if n > 0:
1213
- r = _fsum(self._ps_1primed(s)) or INT0
1214
- return Fsum2Tuple(s, r)
1228
+ r = _fsum(self._ps_1primed(s))
1229
+ return Fsum2Tuple(*_s_r(s, r))
1215
1230
  if n == 0: # len(ps) == 2
1216
- ps[:] = _2ps(*_2sum(*ps))
1217
- r, s = (INT0, ps[0]) if len(ps) != 2 else ps
1231
+ s, r = _s_r(*_2sum(*ps))
1232
+ ps[:] = (r, s) if r else (s,)
1218
1233
  elif ps: # len(ps) == 1
1219
1234
  s, r = ps[0], INT0
1220
1235
  else: # len(ps) == 0
@@ -1223,33 +1238,26 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1223
1238
  # assert self._ps is ps
1224
1239
  return Fsum2Tuple(s, r)
1225
1240
 
1226
- # def _fpsqz(self):
1227
- # '''(INTERNAL) Compress, squeeze the C{partials}.
1228
- # '''
1229
- # if len(self._ps) > 2:
1230
- # _ = self._fprs2
1231
- # return self
1232
-
1233
1241
  def fset_(self, *xs):
1234
1242
  '''Replace this instance' value with C{xs}.
1235
1243
 
1236
- @arg xs: Optional, new values (C{scalar} or L{Fsum}
1237
- instances), all positional.
1244
+ @arg xs: Optional, new values (each C{scalar} or
1245
+ an L{Fsum} or L{Fsum2Tuple} instance),
1246
+ all positional.
1238
1247
 
1239
1248
  @return: This instance (C{Fsum}).
1240
1249
 
1241
1250
  @see: Method L{Fsum.fadd} for further details.
1242
1251
  '''
1243
- self._ps[:] = 0,
1244
- self._n = 0
1245
- return self.fadd(xs) if xs else self._update()
1252
+ f = Fsum(*xs) if xs else _0_0
1253
+ return self._fset(f)
1246
1254
 
1247
1255
  def _fset(self, other, asis=True, n=0):
1248
1256
  '''(INTERNAL) Overwrite this instance with an other or a C{scalar}.
1249
1257
  '''
1250
1258
  if other is self:
1251
1259
  pass # from ._fmul, ._ftruediv and ._pow_0_1
1252
- elif isinstance(other, Fsum):
1260
+ elif _isFsumTuple(other):
1253
1261
  self._ps[:] = other._ps
1254
1262
  self._n = n or other._n
1255
1263
  # self._copy_RESIDUAL(other)
@@ -1271,9 +1279,9 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1271
1279
  return self
1272
1280
 
1273
1281
  def _fset_ps(self, other, n=0): # in .fmath
1274
- '''(INTERNAL) Set partials from a known C{Fsum} or C{scalar}.
1282
+ '''(INTERNAL) Set partials from a known C{scalar}, L{Fsum} or L{Fsum2Tuple}.
1275
1283
  '''
1276
- if isinstance(other, Fsum):
1284
+ if _isFsumTuple(other):
1277
1285
  self._ps[:] = other._ps
1278
1286
  self._n = n or other._n
1279
1287
  else: # assert isscalar(other)
@@ -1281,36 +1289,17 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1281
1289
  self._n = n or 1
1282
1290
  return self
1283
1291
 
1284
- # def _fset_ps_(self, *xs):
1285
- # '''(INTERNAL) Set partials to all known scalar C{xs}.
1286
- # '''
1287
- # self._ps[:] = xs
1288
- # self.n = len(xs)
1289
- # return self
1290
-
1291
1292
  def fsub(self, xs=()):
1292
- '''Subtract an iterable of C{scalar} or L{Fsum} instances from
1293
- this instance.
1293
+ '''Subtract all items of an iterable from this instance.
1294
1294
 
1295
- @arg xs: Iterable, list, tuple. etc. (C{scalar} or L{Fsum}
1296
- instances).
1297
-
1298
- @return: This instance, updated (L{Fsum}).
1299
-
1300
- @see: Method L{Fsum.fadd}.
1295
+ @see: Method L{Fsum.fadd} for further details.
1301
1296
  '''
1302
1297
  return self._facc_neg(xs)
1303
1298
 
1304
1299
  def fsub_(self, *xs):
1305
- '''Subtract all positional C{scalar} or L{Fsum} instances from
1306
- this instance.
1307
-
1308
- @arg xs: Values to subtract (C{scalar} or L{Fsum} instances),
1309
- all positional.
1310
-
1311
- @return: This instance, updated (L{Fsum}).
1300
+ '''Subtract all positional arguments from this instance.
1312
1301
 
1313
- @see: Method L{Fsum.fadd}.
1302
+ @see: Method L{Fsum.fadd_} for further details.
1314
1303
  '''
1315
1304
  return self._fsub(xs[0], _sub_op_) if len(xs) == 1 else \
1316
1305
  self._facc_neg(xs, origin=1)
@@ -1318,20 +1307,21 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1318
1307
  def _fsub(self, other, op):
1319
1308
  '''(INTERNAL) Apply C{B{self} -= B{other}}.
1320
1309
  '''
1321
- if isinstance(other, Fsum):
1310
+ if _isFsumTuple(other):
1322
1311
  if other is self: # or other._fprs2 == self._fprs2:
1323
- self._fset(_0_0) # n=len(self) * 2, self -= self
1312
+ self._fset(_0_0, n=len(self) * 2)
1324
1313
  elif other._ps:
1325
1314
  self._facc_scalar(other._ps_neg)
1326
1315
  elif self._scalar(other, op):
1327
- self._facc_scalar_(-self._finite(other, op))
1316
+ self._facc_scalar_(-other)
1328
1317
  return self
1329
1318
 
1330
1319
  def fsum(self, xs=()):
1331
- '''Add more C{scalar} or L{Fsum} instances and summate.
1320
+ '''Add more items from an iterable, summate and return
1321
+ the current precision running sum.
1332
1322
 
1333
- @kwarg xs: Iterable, list, tuple, etc. (C{scalar} or
1334
- L{Fsum} instances).
1323
+ @arg xs: Iterable of items to add (each item C{scalar}
1324
+ or an L{Fsum} or L{Fsum2Tuple} instance).
1335
1325
 
1336
1326
  @return: Precision running sum (C{float} or C{int}).
1337
1327
 
@@ -1342,10 +1332,11 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1342
1332
  return self._facc(xs)._fprs
1343
1333
 
1344
1334
  def fsum_(self, *xs):
1345
- '''Add all positional C{scalar} or L{Fsum} instances and summate.
1335
+ '''Add any positional arguments, summate and return the
1336
+ current precision running sum.
1346
1337
 
1347
- @arg xs: Values to add (C{scalar} or L{Fsum} instances), all
1348
- positional.
1338
+ @arg xs: Values to add (each C{scalar} or an L{Fsum}
1339
+ or L{Fsum2Tuple} instance), all positional.
1349
1340
 
1350
1341
  @return: Precision running sum (C{float} or C{int}).
1351
1342
 
@@ -1353,19 +1344,36 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1353
1344
  '''
1354
1345
  return self._facc_1(xs)._fprs
1355
1346
 
1356
- def Fsum_(self, *xs):
1357
- '''Like method L{Fsum.fsum_} but returning an L{Fsum}.
1347
+ @property_RO
1348
+ def _Fsum(self):
1349
+ '''(INTERNAL) Like L{Fsum2Tuple._Fsum}, for C{_2floats}.
1350
+ '''
1351
+ return self
1352
+
1353
+ def Fsum_(self, *xs, **name):
1354
+ '''Like method L{Fsum.fsum_} but returning a named L{Fsum}.
1355
+
1356
+ @kwarg name: Optional name (C{str}).
1358
1357
 
1359
1358
  @return: Current, precision running sum (L{Fsum}).
1360
1359
  '''
1361
- return self._facc_1(xs)._copy_2(self.Fsum_)
1360
+ return self._facc_1(xs)._copy_2(self.Fsum_, **name)
1361
+
1362
+ def Fsum2Tuple_(self, *xs, **name):
1363
+ '''Like method L{Fsum.fsum_} but returning a named L{Fsum2Tuple}.
1364
+
1365
+ @kwarg name: Optional name (C{str}).
1366
+
1367
+ @return: Current, precision running sum (L{Fsum2Tuple}).
1368
+ '''
1369
+ return Fsum2Tuple(self._facc_1(xs)._fprs2, **name)
1362
1370
 
1363
1371
  def fsum2(self, xs=(), name=NN):
1364
- '''Add more C{scalar} or L{Fsum} instances and return the
1365
- current precision running sum and the C{residual}.
1372
+ '''Add more items from an iterable, summate and return the
1373
+ current precision running sum I{and} the C{residual}.
1366
1374
 
1367
- @kwarg xs: Iterable, list, tuple, etc. (C{scalar} or L{Fsum}
1368
- instances).
1375
+ @arg xs: Iterable of items to add (each item C{scalar}
1376
+ or an L{Fsum} or L{Fsum2Tuple} instance).
1369
1377
  @kwarg name: Optional name (C{str}).
1370
1378
 
1371
1379
  @return: L{Fsum2Tuple}C{(fsum, residual)} with C{fsum} the
@@ -1380,11 +1388,11 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1380
1388
  return t.dup(name=name) if name else t
1381
1389
 
1382
1390
  def fsum2_(self, *xs):
1383
- '''Add any positional C{scalar} or L{Fsum} instances and return
1384
- the precision running sum and the C{differential}.
1391
+ '''Add any positional arguments, summate and return the current
1392
+ precision running sum I{and} the C{differential}.
1385
1393
 
1386
- @arg xs: Values to add (C{scalar} or L{Fsum} instances), all
1387
- positional.
1394
+ @arg xs: Values to add (each C{scalar} or an L{Fsum} or
1395
+ L{Fsum2Tuple} instance), all positional.
1388
1396
 
1389
1397
  @return: 2Tuple C{(fsum, delta)} with the current, precision
1390
1398
  running C{fsum} like method L{Fsum.fsum} and C{delta},
@@ -1405,37 +1413,35 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1405
1413
  return p, _0_0
1406
1414
 
1407
1415
  def fsumf_(self, *xs):
1408
- '''Like method L{Fsum.fsum_} but only for C{B{xs}}, I{known to be scalar}.
1416
+ '''Like method L{Fsum.fsum_} iff I{all} C{B{xs}} are I{known to be scalar}.
1409
1417
  '''
1410
1418
  return self._facc_scalar(xs)._fprs
1411
1419
 
1412
1420
  def Fsumf_(self, *xs):
1413
- '''Like method L{Fsum.Fsum_} but only for C{B{xs}}, I{known to be scalar}.
1421
+ '''Like method L{Fsum.Fsum_} iff I{all} C{B{xs}} are I{known to be scalar}.
1414
1422
  '''
1415
1423
  return self._facc_scalar(xs)._copy_2(self.Fsumf_)
1416
1424
 
1417
1425
  def fsum2f_(self, *xs):
1418
- '''Like method L{Fsum.fsum2_} but only for C{B{xs}}, I{known to be scalar}.
1426
+ '''Like method L{Fsum.fsum2_} iff I{all} C{B{xs}} are I{known to be scalar}.
1419
1427
  '''
1420
1428
  return self._fsum2(xs, self._facc_scalar, origin=1)
1421
1429
 
1422
1430
  # ftruediv = __itruediv__ # for naming consistency?
1423
1431
 
1424
- def _ftruediv(self, other, op, **raiser):
1432
+ def _ftruediv(self, other, op, **raiser_RESIDUAL):
1425
1433
  '''(INTERNAL) Apply C{B{self} /= B{other}}.
1426
1434
  '''
1427
1435
  n = _1_0
1428
- if isinstance(other, Fsum):
1429
- if other is self or other == self:
1436
+ if _isFsumTuple(other):
1437
+ if other is self or self == other:
1430
1438
  return self._fset(n) # n=len(self)
1431
1439
  d, r = other._fprs2
1432
1440
  if r:
1433
- if d:
1434
- if self._raiser(r, d, **raiser):
1435
- raise self._ResidualError(op, other, r)
1436
- d, n = other.as_integer_ratio()
1437
- else: # PYCHOK no cover
1438
- d = r
1441
+ R = self._raiser(r, d, **raiser_RESIDUAL)
1442
+ if R:
1443
+ raise self._ResidualError(op, other, r, **R)
1444
+ d, n = other.as_integer_ratio()
1439
1445
  else:
1440
1446
  d = self._scalar(other, op)
1441
1447
  try:
@@ -1453,28 +1459,31 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1453
1459
  '''
1454
1460
  return _0_0
1455
1461
 
1456
- def int_float(self, raiser=False, **RESIDUAL):
1462
+ def int_float(self, **raiser_RESIDUAL):
1457
1463
  '''Return this instance' current running sum as C{int} or C{float}.
1458
1464
 
1459
- @kwarg raiser: If C{True} throw a L{ResidualError} if the
1460
- residual exceeds the C{RESIDUAL} (C{bool}).
1461
- @kwarg RESIDUAL: Optional threshold, overriding the current
1462
- L{RESIDUAL<Fsum.RESIDUAL>} (C{scalar}).
1465
+ @kwarg raiser_RESIDUAL: Use C{B{raiser}=False} (C{bool}) to
1466
+ ignore L{ResidualError}s and C{B{RESIDUAL}=scalar}
1467
+ to override the L{RESIDUAL<Fsum.RESIDUAL>}.
1463
1468
 
1464
1469
  @return: This C{integer} sum if this instance C{is_integer},
1465
1470
  otherwise return the C{float} sum if the residual is
1466
- zero or insignificant or if C{B{raiser}=False}.
1471
+ zero or not significant.
1467
1472
 
1468
- @raise ResidualError: Non-zero residual and C{B{raiser}=True}.
1473
+ @raise ResidualError: Non-zero, significant residual or invalid
1474
+ B{C{RESIDUAL}}.
1469
1475
 
1470
- @see: Methods L{Fsum.fint} and L{Fsum.fint2} and property L{Fsum.as_iscalar}.
1476
+ @see: Methods L{Fsum.fint}, L{Fsum.fint2}, L{Fsum.RESIDUAL} and
1477
+ property L{Fsum.as_iscalar}.
1471
1478
  '''
1472
1479
  s, r = self._fint2
1473
1480
  if r:
1474
1481
  s, r = self._fprs2
1475
- if r and raiser and self._raiser2sum(r, s, **RESIDUAL): # PYCHOK no cover
1476
- t = _stresidual(_non_zero_, r)
1477
- raise ResidualError(int_float=s, txt=t)
1482
+ if r: # PYCHOK no cover
1483
+ R = self._raiser(r, s, **raiser_RESIDUAL)
1484
+ if R:
1485
+ t = _stresidual(_non_zero_, r, **R)
1486
+ raise ResidualError(int_float=s, txt=t)
1478
1487
  s = float(s) # redundant
1479
1488
  return s
1480
1489
 
@@ -1505,18 +1514,28 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1505
1514
  f = Fsum._math_fsum
1506
1515
  return 2 if _psum is f else bool(f)
1507
1516
 
1508
- def is_scalar(self):
1509
- '''Is this instance' running sum C{scalar}? (C{bool}).
1517
+ def is_scalar(self, **raiser_RESIDUAL):
1518
+ '''Is this instance' running sum C{scalar} with an insignificant
1519
+ residual and the residual I{ratio} not exceeding the RESIDUAL
1520
+ threshold? (C{bool}).
1521
+
1522
+ @kwarg raiser_RESIDUAL: Use C{B{raiser}=False} (C{bool}) to
1523
+ ignore L{ResidualError}s and C{B{RESIDUAL}=scalar}
1524
+ to override the L{RESIDUAL<Fsum.RESIDUAL>}.
1510
1525
 
1511
- @see: Method L{Fsum.is_integer} and property L{Fsum.as_iscalar}.
1526
+ @raise ResidualError: Non-zero, significant residual or invalid
1527
+ B{C{RESIDUAL}}.
1528
+
1529
+ @see: Method L{Fsum.RESIDUAL}, L{Fsum.is_integer} and property
1530
+ L{Fsum.as_iscalar}.
1512
1531
  '''
1513
- s, r = t = self._fprs2
1514
- return False if r and _2sum(s, r) != t else True
1532
+ s, r = self._fprs2
1533
+ return False if r and self._raiser(r, s, **raiser_RESIDUAL) else True
1515
1534
 
1516
1535
  def _mul_Fsum(self, other, op=_mul_op_): # in .fmath.Fhorner
1517
- '''(INTERNAL) Return C{B{self} * Fsum B{other}} as L{Fsum} or C{0}.
1536
+ '''(INTERNAL) Return C{B{self} * B{other}} as L{Fsum} or C{0}.
1518
1537
  '''
1519
- # assert isinstance(other, Fsum)
1538
+ # assert _isFsumTuple(other)
1520
1539
  if self._ps and other._ps:
1521
1540
  f = self._ps_mul(op, *other._ps) # NO .as_iscalar
1522
1541
  else:
@@ -1547,18 +1566,22 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1547
1566
  '''
1548
1567
  return tuple(self._ps)
1549
1568
 
1550
- def pow(self, x, *mod, **raiser):
1569
+ def pow(self, x, *mod, **raiser_RESIDUAL):
1551
1570
  '''Return C{B{self}**B{x}} as L{Fsum}.
1552
1571
 
1553
- @arg x: The exponent (L{Fsum} or C{scalar}).
1572
+ @arg x: The exponent (C{scalar} or L{Fsum}).
1554
1573
  @arg mod: Optional modulus (C{int} or C{None}) for the 3-argument
1555
1574
  C{pow(B{self}, B{other}, B{mod})} version.
1556
- @kwarg raiser: Use C{B{raiser}=False} to ignore L{ResidualError}s
1557
- (C{bool}), see also method L{RESIDUAL}.
1575
+ @kwarg raiser_RESIDUAL: Use C{B{raiser}=False} (C{bool}) to
1576
+ ignore L{ResidualError}s and C{B{RESIDUAL}=scalar}
1577
+ to override the L{RESIDUAL<Fsum.RESIDUAL>}.
1558
1578
 
1559
1579
  @return: The C{pow(self, B{x})} or C{pow(self, B{x}, *B{mod})}
1560
1580
  result (L{Fsum}).
1561
1581
 
1582
+ @raise ResidualError: Non-zero, significant residual or invalid
1583
+ B{C{RESIDUAL}}.
1584
+
1562
1585
  @note: If B{C{mod}} is given as C{None}, the result will be an
1563
1586
  C{integer} L{Fsum} provided this instance C{is_integer}
1564
1587
  or set to C{integer} by an L{Fsum.fint} call.
@@ -1567,21 +1590,16 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1567
1590
  and L{Fsum.root}.
1568
1591
  '''
1569
1592
  f = self._copy_2(self.pow)
1570
- return f._fpow(x, _pow_op_, *mod, **raiser) # f = pow(f, x, *mod)
1593
+ return f._fpow(x, _pow_op_, *mod, **raiser_RESIDUAL) # f = pow(f, x, *mod)
1571
1594
 
1572
- def _pow(self, other, unused, op, **raiser):
1595
+ def _pow(self, other, unused, op, **raiser_RESIDUAL):
1573
1596
  '''Return C{B{self} ** B{other}}.
1574
1597
  '''
1575
- if isinstance(other, Fsum):
1576
- x, r = other._fprs2
1577
- if r and self._raiser(r, x, **raiser):
1578
- raise self._ResidualError(op, other, r)
1579
- f = self._pow_scalar(x, other, op, **raiser)
1580
- if r:
1581
- f *= self._pow_scalar(r, other, op, **raiser)
1598
+ if _isFsumTuple(other):
1599
+ f = self._pow_Fsum(other, op, **raiser_RESIDUAL)
1582
1600
  elif self._scalar(other, op):
1583
1601
  x = self._finite(other, op)
1584
- f = self._pow_scalar(x, other, op, **raiser)
1602
+ f = self._pow_scalar(x, other, op, **raiser_RESIDUAL)
1585
1603
  else:
1586
1604
  f = self._pow_0_1(0, other)
1587
1605
  return f
@@ -1591,18 +1609,25 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1591
1609
  '''
1592
1610
  return self if x else (1 if isint(other) and self.is_integer() else _1_0)
1593
1611
 
1594
- def _pow_2_3(self, b, x, other, op, *mod, **raiser):
1612
+ def _pow_2_3(self, b, x, other, op, *mod, **raiser_RESIDUAL):
1595
1613
  '''(INTERNAL) 2-arg C{pow(B{b}, scalar B{x})} and 3-arg C{pow(B{b},
1596
1614
  B{x}, int B{mod} or C{None})}, embellishing errors.
1597
1615
  '''
1616
+
1617
+ if mod: # b, x, mod all C{int}, unless C{mod} is C{None}
1618
+ m = mod[0]
1619
+ # assert _isFsumTuple(b)
1620
+
1621
+ def _s(s, r):
1622
+ R = self._raiser(r, s, **raiser_RESIDUAL)
1623
+ if R:
1624
+ raise self._ResidualError(op, other, r, mod=m, **R)
1625
+ return s
1626
+
1627
+ b = _s(*(b._fprs2 if m is None else b._fint2))
1628
+ x = _s(*_2scalar2(x))
1629
+
1598
1630
  try:
1599
- if mod: # b, x, mod all C{int}, unless C{mod} is C{None}
1600
- m = mod[0]
1601
- b, r = b._fprs2 if m is None else b._fint2
1602
- if r and self._raiser(r, b, **raiser):
1603
- t = _non_zero_ if m is None else _integer_
1604
- raise ResidualError(_stresidual(t, r, mod=m), txt=None)
1605
- x = _2scalar(x, _raiser=self._raiser, mod=m)
1606
1631
  # 0**INF == 0.0, 1**INF == 1.0, -1**2.3 == -(1**2.3)
1607
1632
  s = pow(b, x, *mod)
1608
1633
  if iscomplex(s):
@@ -1612,7 +1637,17 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1612
1637
  except Exception as X:
1613
1638
  raise self._ErrorX(X, op, other, *mod)
1614
1639
 
1615
- def _pow_int(self, x, other, op, **raiser):
1640
+ def _pow_Fsum(self, other, op, **raiser_RESIDUAL):
1641
+ '''(INTERNAL) Return C{B{self} **= B{other}} for C{_isFsumTuple(other)}.
1642
+ '''
1643
+ # assert _isFsumTuple(other)
1644
+ x, r = other._fprs2
1645
+ f = self._pow_scalar(x, other, op, **raiser_RESIDUAL)
1646
+ if f and r:
1647
+ f *= self._pow_scalar(r, other, op, **raiser_RESIDUAL)
1648
+ return f
1649
+
1650
+ def _pow_int(self, x, other, op, **raiser_RESIDUAL):
1616
1651
  '''(INTERNAL) Return C{B{self} **= B{x}} for C{int B{x} >= 0}.
1617
1652
  '''
1618
1653
  # assert isint(x) and x >= 0
@@ -1636,49 +1671,52 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1636
1671
  else: # self**1 or self**0 == 1 or _1_0
1637
1672
  f = self._pow_0_1(x, other)
1638
1673
  elif ps: # self._ps[0]**x
1639
- f = self._pow_2_3(ps[0], x, other, op, **raiser)
1674
+ f = self._pow_2_3(ps[0], x, other, op, **raiser_RESIDUAL)
1640
1675
  else: # PYCHOK no cover
1641
1676
  # 0**pos_int == 0, but 0**0 == 1
1642
1677
  f = 0 if x else 1
1643
1678
  return f
1644
1679
 
1645
- def _pow_scalar(self, x, other, op, **raiser):
1680
+ def _pow_scalar(self, x, other, op, **raiser_RESIDUAL):
1646
1681
  '''(INTERNAL) Return C{self**B{x}} for C{scalar B{x}}.
1647
1682
  '''
1648
1683
  s, r = self._fprs2
1649
- if isint(x, both=True):
1650
- x = int(x) # Fsum**int
1651
- y = abs(x)
1652
- if y > 1:
1653
- if r:
1654
- f = self._pow_int(y, other, op, **raiser)
1655
- if x > 0: # > 1
1656
- return f
1657
- # assert x < 0 # < -1
1658
- s, r = f._fprs2 if isinstance(f, Fsum) else (f, 0)
1659
- if r:
1660
- return _1_over(f, op, **raiser) # PYCHOK 2 args
1661
- # use **= -1 for the CPython float_pow
1662
- # error if s is zero, and not s = 1 / s
1684
+ if r:
1685
+ # assert s != 0
1686
+ if isint(x, both=True): # self**int
1687
+ x = int(x)
1688
+ y = abs(x)
1689
+ if y > 1:
1690
+ f = self._pow_int(y, other, op, **raiser_RESIDUAL)
1691
+ if x > 0: # i.e. > 1
1692
+ return f # Fsum or scalar
1693
+ # assert x < 0 # i.e. < -1
1694
+ if _isFsum(f):
1695
+ s, r = f._fprs2
1696
+ if r:
1697
+ return _1_Over(f, op, **raiser_RESIDUAL)
1698
+ else: # scalar
1699
+ s = f
1700
+ # use s**(-1) to get the CPython
1701
+ # float_pow error iff s is zero
1663
1702
  x = -1
1664
- elif x < 0: # == -1: self**(-1) == 1 / self
1665
- if r:
1666
- return _1_over(self, op, **raiser) # PYCHOK 2 args
1667
- else: # self**1 or self**0
1668
- return self._pow_0_1(x, other) # self, 1 or 1.0
1669
- elif r: # non-zero residual**fractional
1670
- if s:
1703
+ elif x < 0: # self**(-1)
1704
+ return _1_Over(self, op, **raiser_RESIDUAL) # 1 / self
1705
+ else: # self**1 or self**0
1706
+ return self._pow_0_1(x, other) # self, 1 or 1.0
1707
+ else: # self**fractional
1708
+ R = self._raiser(r, s, **raiser_RESIDUAL)
1709
+ if R:
1710
+ raise self._ResidualError(op, other, r, **R)
1671
1711
  n, d = self.as_integer_ratio()
1672
1712
  if abs(n) > abs(d):
1673
1713
  n, d, x = d, n, (-x)
1674
1714
  s = n / d
1675
- else:
1676
- s = r
1677
1715
  # assert isscalar(s) and isscalar(x)
1678
- return self._pow_2_3(s, x, other, op, **raiser)
1716
+ return self._pow_2_3(s, x, other, op, **raiser_RESIDUAL)
1679
1717
 
1680
1718
  def _ps_acc(self, ps, xs, up=True, **unused):
1681
- '''(INTERNAL) Accumulate all scalar C{xs} into C{ps}.
1719
+ '''(INTERNAL) Accumulate all C{xs} scalars into list C{ps}.
1682
1720
  '''
1683
1721
  n = 0
1684
1722
  _2s = _2sum
@@ -1701,8 +1739,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1701
1739
  return ps
1702
1740
 
1703
1741
  def _ps_mul(self, op, *factors):
1704
- '''(INTERNAL) Multiply this instance' C{partials} with
1705
- each of the scalar B{C{factors}} and accumulate.
1742
+ '''(INTERNAL) Multiply this instance' C{partials} with each
1743
+ of the scalar B{C{factors}} and accumulate.
1706
1744
  '''
1707
1745
  def _pfs(ps, fs):
1708
1746
  if len(ps) < len(fs):
@@ -1713,7 +1751,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1713
1751
  p *= f
1714
1752
  yield p if _fin(p) else self._finite(p, op)
1715
1753
 
1716
- return _Psum(self._ps_acc([], _pfs(self._ps, factors)))
1754
+ return _Psum(self._ps_acc([], _pfs(self._ps, factors), up=False))
1717
1755
 
1718
1756
  @property_RO
1719
1757
  def _ps_neg(self):
@@ -1723,7 +1761,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1723
1761
  yield -p
1724
1762
 
1725
1763
  def _ps_1primed(self, *less):
1726
- '''(INTERNAL) Yield partials, 1-primed and subtract any C{less} scalars.
1764
+ '''(INTERNAL) Yield partials, 1-primed C{less} any scalars.
1727
1765
  '''
1728
1766
  yield _1_0
1729
1767
  for p in self._ps:
@@ -1733,21 +1771,19 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1733
1771
  yield _N_1_0
1734
1772
 
1735
1773
  def _raiser(self, r, s, raiser=True, **RESIDUAL):
1736
- '''(INTERNAL) Does ratio C{r / s} exceed the RESIDUAL threshold?
1774
+ '''(INTERNAL) Does ratio C{r / s} exceed the RESIDUAL threshold
1775
+ I{and} is residual C{r} I{non-zero} or I{significant} (for a
1776
+ negative respectively positive C{RESIDUAL} threshold)?
1737
1777
  '''
1738
- self._ratio = r = (r / s) if s else s # == 0.
1739
1778
  if r and raiser:
1740
- R = self._RESIDUAL
1779
+ t = self._RESIDUAL
1741
1780
  if RESIDUAL:
1742
- R = _xkwds_get(RESIDUAL, RESIDUAL=R)
1743
- return fabs(r) > R
1744
- return False
1745
-
1746
- def _raiser2sum(self, r, s, **raiser_RESIDUAL):
1747
- '''(INTERNAL) Does ratio C{r / s} exceed the RESIDUAL threshold
1748
- I{and} is the residual B{C{r}} significant vs sum B{C{s}}?
1749
- '''
1750
- return self._raiser(r, s, **raiser_RESIDUAL) and _2sum(s, r) != (s, r)
1781
+ t = _threshold(_xkwds_get(RESIDUAL, RESIDUAL=t))
1782
+ if t < 0 or (s + r) != s:
1783
+ q = (r / s) if s else s # == 0.
1784
+ if fabs(q) > fabs(t):
1785
+ return dict(ratio=q, R=t)
1786
+ return {}
1751
1787
 
1752
1788
  @property_RO
1753
1789
  def real(self):
@@ -1761,10 +1797,10 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1761
1797
 
1762
1798
  @property_RO
1763
1799
  def residual(self):
1764
- '''Get this instance' residual (C{float} or C{int}), the
1800
+ '''Get this instance' residual (C{float} or C{int}): the
1765
1801
  C{sum(partials)} less the precision running sum C{fsum}.
1766
1802
 
1767
- @note: If the C{residual is INT0}, the precision running
1803
+ @note: The C{residual is INT0} iff the precision running
1768
1804
  C{fsum} is considered to be I{exact}.
1769
1805
 
1770
1806
  @see: Methods L{Fsum.fsum}, L{Fsum.fsum2} and L{Fsum.is_exact}.
@@ -1783,45 +1819,52 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1783
1819
 
1784
1820
  @return: The previous C{RESIDUAL} setting (C{float}), default C{0.0}.
1785
1821
 
1786
- @raise ValueError: Negative B{C{threshold}}.
1822
+ @raise ResidualError: Invalid B{C{threshold}}.
1787
1823
 
1788
- @note: L{ResidualError}s will be thrown if the non-zero I{ratio}
1789
- C{residual / fsum} exceeds the B{C{threshold}}.
1824
+ @note: L{ResidualError}s may be thrown if the non-zero I{ratio}
1825
+ C{residual / fsum} exceeds the given B{C{threshold}} and
1826
+ if the C{residual} is non-zero and I{significant} vs the
1827
+ C{fsum}, i.e. C{(fsum + residual) != fsum} and if optional
1828
+ keyword argument C{raiser=False} is missing. Specify a
1829
+ negative B{C{threshold}} for only non-zero C{residual}
1830
+ testing without I{significant}.
1790
1831
  '''
1791
1832
  r = self._RESIDUAL
1792
1833
  if threshold:
1793
1834
  t = threshold[0]
1794
- t = Fsum._RESIDUAL if t is None else (
1795
- float(t) if isscalar(t) else ( # for backward ...
1796
- _0_0 if bool(t) else _1_0)) # ... compatibility
1797
- if t < 0:
1798
- u = _DOT_(self, unstr(self.RESIDUAL, *threshold))
1799
- raise _ValueError(u, RESIDUAL=t, txt=_negative_)
1800
- self._RESIDUAL = t
1835
+ self._RESIDUAL = Fsum._RESIDUAL if t is None else ( # for ...
1836
+ (_0_0 if t else _1_0) if isbool(t) else
1837
+ _threshold(t)) # ... backward compatibility
1801
1838
  return r
1802
1839
 
1803
- def _ResidualError(self, op, other, residual):
1840
+ def _ResidualError(self, op, other, residual, **mod_R):
1804
1841
  '''(INTERNAL) Non-zero B{C{residual}} etc.
1805
1842
  '''
1806
- t = _stresidual(_non_zero_, residual, ratio=self._ratio,
1807
- RESIDUAL=self._RESIDUAL)
1808
- t = t.replace(_COMMASPACE_R_, _exceeds_R_)
1843
+ def _p(mod=None, R=0, **unused): # ratio=0
1844
+ return (_non_zero_ if R < 0 else _significant_) \
1845
+ if mod is None else _integer_
1846
+
1847
+ t = _stresidual(_p(**mod_R), residual, **mod_R)
1809
1848
  return self._Error(op, other, ResidualError, txt=t)
1810
1849
 
1811
- def root(self, root, **raiser):
1850
+ def root(self, root, **raiser_RESIDUAL):
1812
1851
  '''Return C{B{self}**(1 / B{root})} as L{Fsum}.
1813
1852
 
1814
- @arg root: The order (C{scalar} or C{Fsum}), non-zero.
1815
- @kwarg raiser: Use C{B{raiser}=False} to ignore L{ResidualError}s
1816
- (C{bool}), see also method L{RESIDUAL}.
1853
+ @arg root: The order (C{scalar} or L{Fsum}), non-zero.
1854
+ @kwarg raiser_RESIDUAL: Use C{B{raiser}=False} (C{bool}) to
1855
+ ignore L{ResidualError}s and C{B{RESIDUAL}=scalar}
1856
+ to override the L{RESIDUAL<Fsum.RESIDUAL>}.
1817
1857
 
1818
1858
  @return: The C{self ** (1 / B{root})} result (L{Fsum}).
1819
1859
 
1860
+ @raise ResidualError: Non-zero, significant residual or invalid
1861
+ B{C{RESIDUAL}}.
1862
+
1820
1863
  @see: Method L{Fsum.pow}.
1821
1864
  '''
1822
- x = _1_over(root, **raiser)
1865
+ x = _1_Over(root, _truediv_op_, **raiser_RESIDUAL)
1823
1866
  f = self._copy_2(self.root)
1824
- return f._fpow(x, f.name, **raiser) # == pow(f, x)
1867
+ return f._fpow(x, f.name, **raiser_RESIDUAL) # == pow(f, x)
1825
1868
 
1826
1869
  def _scalar(self, other, op, **txt):
1827
1870
  '''(INTERNAL) Return scalar C{other}.
@@ -1839,7 +1882,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1839
1882
  @return: The sign (C{int}, -1, 0 or +1).
1840
1883
  '''
1841
1884
  s, r = self._fprs2
1842
- return _signOf(s, (-r) if res else 0)
1885
+ r = (-r) if res else 0
1886
+ return _signOf(s, r)
1843
1887
 
1844
1888
  def toRepr(self, **prec_sep_fmt_lenc): # PYCHOK signature
1845
1889
  '''Return this C{Fsum} instance as representation.
@@ -1868,10 +1912,13 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1868
1912
  def _toT(self, toT, fmt=Fmt.g, lenc=True, **kwds):
1869
1913
  '''(INTERNAL) Helper for C{toRepr} and C{toStr}.
1870
1914
  '''
1871
- n = self.named3
1915
+ p = self.classname
1872
1916
  if lenc:
1873
- n = Fmt.SQUARE(n, len(self))
1874
- return _SPACE_(n, toT(fmt=fmt, **kwds))
1917
+ p = Fmt.SQUARE(p, len(self))
1918
+ n = self.name
1919
+ if n:
1920
+ n = _UNDER_(*n.split())
1921
+ return NN(p, _SPACE_, n, toT(fmt=fmt, **kwds))
1875
1922
 
1876
1923
  def _TypeError(self, op, other, **txt): # PYCHOK no cover
1877
1924
  '''(INTERNAL) Return a C{TypeError}.
@@ -1900,7 +1947,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1900
1947
  '''
1901
1948
  return self._Error(op, other, _ZeroDivisionError, **txt)
1902
1949
 
1903
- _ROs = _allPropertiesOf_n(3, Fsum, Property_RO) # PYCHOK assert, see Fsum._fset, -._update
1950
+ _ROs = _allPropertiesOf_n(3, Fsum, Property_RO) # PYCHOK see Fsum._update
1904
1951
 
1905
1952
 
1906
1953
  def _Float_Int(arg, **name_Error):
@@ -1914,8 +1961,8 @@ class DivMod2Tuple(_NamedTuple):
1914
1961
  '''2-Tuple C{(div, mod)} with the quotient C{div} and remainder
1915
1962
  C{mod} results of a C{divmod} operation.
1916
1963
 
1917
- @note: Quotient C{div} an C{int} in Python 3+ or a C{float} in
1918
- Python 2-. Remainder C{mod} an L{Fsum} instance.
1964
+ @note: Quotient C{div} an C{int} in Python 3+ but a C{float}
1965
+ in Python 2-. Remainder C{mod} an L{Fsum} instance.
1919
1966
  '''
1920
1967
  _Names_ = (_div_, _mod_)
1921
1968
  _Units_ = (_Float_Int, Fsum)
@@ -1924,7 +1971,7 @@ class DivMod2Tuple(_NamedTuple):
1924
1971
  class Fsum2Tuple(_NamedTuple):
1925
1972
  '''2-Tuple C{(fsum, residual)} with the precision running C{fsum}
1926
1973
  and the C{residual}, the sum of the remaining partials. Each
1927
- item is either C{float} or C{int}.
1974
+ item is C{float} or C{int}.
1928
1975
 
1929
1976
  @note: If the C{residual is INT0}, the C{fsum} is considered
1930
1977
  to be I{exact}, see method L{Fsum2Tuple.is_exact}.
@@ -1932,27 +1979,61 @@ class Fsum2Tuple(_NamedTuple):
1932
1979
  _Names_ = ( Fsum.fsum.__name__, Fsum.residual.name)
1933
1980
  _Units_ = (_Float_Int, _Float_Int)
1934
1981
 
1982
+ def as_integer_ratio(self):
1983
+ '''Return this instance as the ratio of 2 integers.
1984
+
1985
+ @see: Method L{Fsum.as_integer_ratio} for further details.
1986
+ '''
1987
+ return self._Fsum.as_integer_ratio()
1988
+
1989
+ @property_RO
1990
+ def _fint2(self):
1991
+ return self._Fsum.fint2
1992
+
1993
+ @property_RO
1994
+ def _fprs2(self):
1995
+ return self._Fsum._fprs2
1996
+
1935
1997
  @Property_RO
1936
1998
  def _Fsum(self):
1937
- '''(INTERNAL) Get this L{Fsum2Tuple} as an L{Fsum}.
1938
- '''
1939
- s, r = map(float, self)
1940
- return _Psum(_2ps(s, r), name=self.name)
1999
+ return Fsum(*self)
1941
2000
 
1942
2001
  def is_exact(self):
1943
2002
  '''Is this L{Fsum2Tuple} considered to be exact? (C{bool}).
1944
2003
  '''
1945
- return self._Fsum.is_exact()
2004
+ _, r = _s_r(*self)
2005
+ return False if r else True
1946
2006
 
1947
2007
  def is_integer(self):
1948
2008
  '''Is this L{Fsum2Tuple} C{integer}? (C{bool}).
1949
2009
  '''
1950
- return self._Fsum.is_integer()
2010
+ s, r = _s_r(*self)
2011
+ return False if r else isint(s, both=True)
2012
+
2013
+ @property_RO
2014
+ def _n(self):
2015
+ return self._Fsum._n
2016
+
2017
+ @property_RO
2018
+ def _ps(self):
2019
+ return self._Fsum._ps
2020
+
2021
+ @property_RO
2022
+ def _ps_neg(self):
2023
+ return self._Fsum._ps_neg
2024
+
2025
+ def signOf(self, **res):
2026
+ '''Like L{Fsum.signOf}.
2027
+ '''
2028
+ return self._Fsum.signOf(**res)
2029
+
2030
+ _Fsum_Fsum2Tuple_types = Fsum, Fsum2Tuple # PYCHOK line
1951
2031
 
1952
2032
 
1953
2033
  class ResidualError(_ValueError):
1954
- '''Error raised for an operation involving a L{pygeodesy.sums.Fsum}
1955
- instance with a non-zero C{residual}, I{integer} or otherwise.
2034
+ '''Error raised for a division, power or root operation of
2035
+ an L{Fsum} instance with a C{residual} I{ratio} exceeding
2036
+ the L{RESIDUAL<Fsum.RESIDUAL>} threshold.
1956
2037
 
1957
2038
  @see: Module L{pygeodesy.fsums} and method L{Fsum.RESIDUAL}.
1958
2039
  '''
@@ -1969,64 +2050,59 @@ try:
1969
2050
  raise ImportError # ... use _fsum below
1970
2051
 
1971
2052
  Fsum._math_fsum = _sum = _fsum # PYCHOK exported
1972
-
1973
- if _getenv('PYGEODESY_FSUM_PARTIALS', NN) == _fsum.__name__:
1974
- _psum = _fsum # PYCHOK re-def
1975
-
1976
2053
  except ImportError:
1977
2054
  _sum = sum # Fsum(NAN) exception fall-back, in .elliptic
1978
2055
 
1979
2056
  def _fsum(xs):
1980
2057
  '''(INTERNAL) Precision summation, Python 2.5-.
1981
2058
  '''
1982
- f = Fsum()
1983
- f.name = _fsum.__name__
1984
- return f.fsum(xs)
2059
+ F = Fsum()
2060
+ F.name = _fsum.__name__
2061
+ return F._facc(xs, up=False)._fprs2.fsum
1985
2062
 
1986
2063
 
1987
2064
  def fsum(xs, floats=False):
1988
- '''Precision floating point summation based on or like Python's C{math.fsum}.
2065
+ '''Precision floating point summation based on/like Python's C{math.fsum}.
1989
2066
 
1990
- @arg xs: Iterable, list, tuple, etc. of values (C{scalar} or L{Fsum}
1991
- instances).
1992
- @kwarg floats: Use C{B{floats}=True} iff I{all} B{C{xs}} are known
1993
- to be C{float} scalars (C{bool}).
2067
+ @arg xs: Iterable of items to add (each C{scalar} or an L{Fsum} or
2068
+ L{Fsum2Tuple} instance).
2069
+ @kwarg floats: Use C{B{floats}=True} iff I{all} B{C{xs}} items are
2070
+ I{known to be scalar} (C{bool}).
1994
2071
 
1995
2072
  @return: Precision C{fsum} (C{float}).
1996
2073
 
1997
2074
  @raise OverflowError: Partial C{2sum} overflow.
1998
2075
 
1999
- @raise TypeError: Non-scalar B{C{xs}} value.
2076
+ @raise TypeError: Non-scalar B{C{xs}} item.
2000
2077
 
2001
- @raise ValueError: Invalid or non-finite B{C{xs}} value.
2078
+ @raise ValueError: Invalid or non-finite B{C{xs}} item.
2002
2079
 
2003
2080
  @note: Exception and I{non-finite} handling may differ if not based
2004
2081
  on Python's C{math.fsum}.
2005
2082
 
2006
2083
  @see: Class L{Fsum} and methods L{Fsum.fsum} and L{Fsum.fadd}.
2007
2084
  '''
2008
- return _fsum(xs if floats else _2floats(xs)) if xs else _0_0 # PYCHOK yield
2085
+ return _fsum(xs if floats is True else _2floats(xs)) if xs else _0_0 # PYCHOK yield
2009
2086
 
2010
2087
 
2011
2088
  def fsum_(*xs, **floats):
2012
2089
  '''Precision floating point summation of all positional arguments.
2013
2090
 
2014
- @arg xs: Values to be added (C{scalar} or L{Fsum} instances), all
2015
- positional.
2016
- @kwarg floats: Use C{B{floats}=True} iff I{all} B{C{xs}} are I{known
2017
- to be scalar} (C{bool}).
2018
-
2019
- @return: Precision C{fsum} (C{float}).
2091
+ @arg xs: Items to add (each C{scalar} or an L{Fsum} or L{Fsum2Tuple}
2092
+ instance), all positional.
2093
+ @kwarg floats: Use C{B{floats}=True} iff I{all} B{C{xs}} items are
2094
+ I{known to be scalar} (C{bool}).
2020
2095
 
2021
- @see: Function C{fsum}.
2096
+ @see: Function L{fsum<fsums.fsum>} for further details.
2022
2097
  '''
2023
- return _fsum(xs if _xkwds_get(floats, floats=False) else
2098
+ return _fsum(xs if _xkwds_get(floats, floats=False) is True else
2024
2099
  _2floats(xs, origin=1)) if xs else _0_0 # PYCHOK yield
2025
2100
 
2026
2101
 
2027
2102
  def fsumf_(*xs):
2028
- '''Precision floating point summation, L{fsum_}C{(*B{xs}, floats=True)},
2029
- but only for C{B{xs}} I{known to be scalar}.
2103
+ '''Precision floating point summation iff I{all} C{B{xs}} items are I{known to be scalar}.
2104
+
2105
+ @see: Function L{fsum_<fsums.fsum_>} for further details.
2030
2106
  '''
2031
2107
  return _fsum(xs) if xs else _0_0
2032
2108
 
@@ -2034,44 +2110,44 @@ def fsumf_(*xs):
2034
2110
  def fsum1(xs, floats=False):
2035
2111
  '''Precision floating point summation, 1-primed.
2036
2112
 
2037
- @arg xs: Iterable, list, tuple, etc. of values (C{scalar} or L{Fsum}
2038
- instances).
2039
- @kwarg floats: Use C{B{floats}=True} iff I{all} B{C{xs}} are known
2040
- to be C{float}.
2041
-
2042
- @return: Precision C{fsum} (C{float}).
2113
+ @arg xs: Iterable of items to add (each C{scalar} or an L{Fsum} or
2114
+ L{Fsum2Tuple} instance).
2115
+ @kwarg floats: Use C{B{floats}=True} iff I{all} B{C{xs}} items are
2116
+ I{known to be scalar} (C{bool}).
2043
2117
 
2044
- @see: Function C{fsum}.
2118
+ @see: Function L{fsum<fsums.fsum>} for further details.
2045
2119
  '''
2046
- return _fsum(_1primed(xs if floats else _2floats(xs))) if xs else _0_0 # PYCHOK yield
2120
+ return _fsum(_1primed(xs if floats is True else _2floats(xs))) if xs else _0_0 # PYCHOK yield
2047
2121
 
2048
2122
 
2049
2123
  def fsum1_(*xs, **floats):
2050
- '''Precision floating point summation, 1-primed.
2124
+ '''Precision floating point summation, 1-primed of all positional arguments.
2051
2125
 
2052
- @arg xs: Values to be added (C{scalar} or L{Fsum} instances), all
2053
- positional.
2054
- @kwarg floats: Use C{B{floats}=True} iff I{all} B{C{xs}} are I{known
2055
- to be scalar} (C{bool}).
2126
+ @arg xs: Items to add (each C{scalar} or an L{Fsum} or L{Fsum2Tuple}
2127
+ instance), all positional.
2128
+ @kwarg floats: Use C{B{floats}=True} iff I{all} B{C{xs}} items are
2129
+ I{known to be scalar} (C{bool}).
2056
2130
 
2057
- @return: Precision C{fsum} (C{float}).
2058
-
2059
- @see: Function C{fsum}
2131
+ @see: Function L{fsum_<fsums.fsum_>} for further details.
2060
2132
  '''
2061
- return _fsum(_1primed(xs if _xkwds_get(floats, floats=False) else
2133
+ return _fsum(_1primed(xs if _xkwds_get(floats, floats=False) is True else
2062
2134
  _2floats(xs, origin=1))) if xs else _0_0 # PYCHOK yield
2063
2135
 
2064
2136
 
2065
2137
  def fsum1f_(*xs):
2066
- '''Precision floating point summation, L{fsum1_}C{(*B{xs}, floats=True)},
2067
- but only for C{B{xs}} I{known to be scalar}.
2138
+ '''Precision floating point summation iff I{all} C{B{xs}} are I{known to be scalar}.
2139
+
2140
+ @see: Function L{fsum_<fsums.fsum_>} for further details.
2068
2141
  '''
2069
2142
  return _fsum(_1primed(xs)) if xs else _0_0
2070
2143
 
2071
2144
 
2072
2145
  if __name__ == '__main__':
2073
2146
 
2074
- # usage: [env PYGEODESY_FSUM_PARTIALS=fsum] python3 -m pygeodesy.fsums
2147
+ # usage: [env _psum=fsum] python3 -m pygeodesy.fsums
2148
+
2149
+ if _getenv(_psum.__name__, NN) == _fsum.__name__:
2150
+ _psum = _fsum
2075
2151
 
2076
2152
  def _test(n):
2077
2153
  # copied from Hettinger, see L{Fsum} reference