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