pygeodesy 24.4.12__py2.py3-none-any.whl → 24.4.24__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pygeodesy/fsums.py CHANGED
@@ -27,14 +27,15 @@ from __future__ import division as _; del _ # PYCHOK semicolon
27
27
 
28
28
  from pygeodesy.basics import iscomplex, isint, isscalar, itemsorted, \
29
29
  signOf, _signOf
30
- from pygeodesy.constants import INT0, _isfinite, isinf, isnan, NEG0, _pos_self, \
30
+ from pygeodesy.constants import INT0, _isfinite, NEG0, _pos_self, \
31
31
  _0_0, _1_0, _N_1_0, Float, Int
32
- from pygeodesy.errors import _OverflowError, _TypeError, _ValueError, _xError, \
33
- _xError2, _xkwds_get, _ZeroDivisionError
32
+ from pygeodesy.errors import _AssertionError, _OverflowError, _TypeError, \
33
+ _ValueError, _xError, _xError2, _xkwds_get, \
34
+ _ZeroDivisionError
34
35
  from pygeodesy.interns import NN, _arg_, _COMMASPACE_, _DASH_, _DOT_, _EQUAL_, \
35
36
  _exceeds_, _from_, _iadd_op_, _LANGLE_, _negative_, \
36
- _NOTEQUAL_, _not_finite_, _not_scalar_, _PERCENT_, \
37
- _PLUS_, _R_, _RANGLE_, _SLASH_, _SPACE_, _STAR_, _UNDER_
37
+ _NOTEQUAL_, _not_finite_, _PERCENT_, _PLUS_, _R_, \
38
+ _RANGLE_, _SLASH_, _SPACE_, _STAR_, _UNDER_
38
39
  from pygeodesy.lazily import _ALL_LAZY, _getenv, _sys_version_info2
39
40
  from pygeodesy.named import _Named, _NamedTuple, _NotImplemented, Fmt, unstr
40
41
  from pygeodesy.props import _allPropertiesOf_n, deprecated_property_RO, \
@@ -45,7 +46,7 @@ from pygeodesy.props import _allPropertiesOf_n, deprecated_property_RO, \
45
46
  from math import ceil as _ceil, fabs, floor as _floor # PYCHOK used! .ltp
46
47
 
47
48
  __all__ = _ALL_LAZY.fsums
48
- __version__ = '24.04.09'
49
+ __version__ = '24.04.24'
49
50
 
50
51
  _add_op_ = _PLUS_ # in .auxilats.auxAngle
51
52
  _eq_op_ = _EQUAL_ * 2 # _DEQUAL_
@@ -72,7 +73,7 @@ _isub_op_ = _sub_op_ + _fset_op_ # in .auxilats.auxAngle, .fsums
72
73
 
73
74
 
74
75
  def _2delta(*ab):
75
- '''(INTERNAL) Helper for C{Fsum.fsum2f_}.
76
+ '''(INTERNAL) Helper for C{Fsum._fsum2}.
76
77
  '''
77
78
  try:
78
79
  a, b = _2sum(*ab)
@@ -121,6 +122,18 @@ def _2floats(xs, origin=0, _X=_X_ps, _x=float):
121
122
  raise _xError(X, Fmt.INDEX(xs=i), x)
122
123
 
123
124
 
125
+ def _Fsumf_(*xs): # floats=True, in .auxLat, ...
126
+ '''(INTERNAL) An C{Fsum} of I{known scalars}.
127
+ '''
128
+ return Fsum()._facc_scalar(xs, up=False)
129
+
130
+
131
+ def _Fsum1f_(*xs): # floats=True, in .albers, ...
132
+ '''(INTERNAL) An C{Fsum} of I{known scalars}, 1-primed.
133
+ '''
134
+ return Fsum()._facc_scalar(_1primed(xs), up=False)
135
+
136
+
124
137
  def _2halfeven(s, r, p):
125
138
  '''(INTERNAL) Round half-even.
126
139
  '''
@@ -133,6 +146,12 @@ def _2halfeven(s, r, p):
133
146
  return s
134
147
 
135
148
 
149
+ def _1_over(x, op=_truediv_op_, **raiser):
150
+ '''(INTERNAL) Return C{Fsum(1) /= B{x}}.
151
+ '''
152
+ return _Psum_(_1_0)._ftruediv(x, op, **raiser)
153
+
154
+
136
155
  def _1primed(xs): # in .fmath
137
156
  '''(INTERNAL) 1-Primed summation of iterable C{xs}
138
157
  items, all I{known} to be C{finite float}.
@@ -144,9 +163,11 @@ def _1primed(xs): # in .fmath
144
163
 
145
164
 
146
165
  def _2ps(s, r):
147
- '''(INTERNAL) Return a C{s} and C{r} pair, I{ps-ordered}.
166
+ '''(INTERNAL) Return an C{s} and C{r} pair, I{ps-ordered}.
148
167
  '''
149
- return (s, r) if fabs(s) < fabs(r) else (r, s)
168
+ if fabs(s) < fabs(r):
169
+ s, r = r, s
170
+ return (r, s) if r else (s,) # PYCHOK types
150
171
 
151
172
 
152
173
  def _psum(ps): # PYCHOK used!
@@ -173,20 +194,17 @@ def _psum(ps): # PYCHOK used!
173
194
  def _Psum(ps, **name):
174
195
  '''(INTERNAL) Return an C{Fsum} from I{ordered} partials C{ps}.
175
196
  '''
176
- f = Fsum(**name) if name else Fsum()
197
+ F = Fsum(**name) if name else Fsum()
177
198
  if ps:
178
- f._ps[:] = ps
179
- f._n = len(f._ps)
180
- return f
199
+ F._ps[:] = ps
200
+ F._n = len(F._ps)
201
+ return F
181
202
 
182
203
 
183
- def _Psum_1(p=_1_0, **name):
184
- '''(INTERNAL) Return an C{Fsum} from a single partial C{p}.
204
+ def _Psum_(*ps, **name):
205
+ '''(INTERNAL) Return an C{Fsum} from 1 or 2 known scalar(s) C{ps}.
185
206
  '''
186
- f = Fsum(**name) if name else Fsum()
187
- f._ps[:] = p,
188
- f._n = 1 # len(f._ps)
189
- return f
207
+ return _Psum(ps, **name)
190
208
 
191
209
 
192
210
  def _2scalar(other, _raiser=None, **mod):
@@ -233,13 +251,13 @@ def _2sum(a, b): # by .testFmath
233
251
  '''(INTERNAL) Return C{a + b} as 2-tuple (sum, residual).
234
252
  '''
235
253
  s = a + b
236
- if not _isfinite(s):
237
- u = unstr(_2sum, a, b)
238
- t = Fmt.PARENSPACED(_not_finite_, s)
239
- raise _OverflowError(u, txt=t)
240
- if fabs(a) < fabs(b):
241
- a, b = b, a
242
- return s, (b - (s - a))
254
+ if _isfinite(s):
255
+ if fabs(a) < fabs(b):
256
+ b, a = a, b
257
+ return s, (b - (s - a))
258
+ u = unstr(_2sum, a, b)
259
+ t = Fmt.PARENSPACED(_not_finite_, s)
260
+ raise _OverflowError(u, txt=t)
243
261
 
244
262
 
245
263
  class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
@@ -255,9 +273,9 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
255
273
  @note: Handling of exceptions and C{inf}, C{INF}, C{nan} and C{NAN} differs from
256
274
  Python's C{math.fsum}.
257
275
 
258
- @see: U{Hettinger<https://GitHub.com/ActiveState/code/blob/master/recipes/Python/
259
- 393090_Binary_floating_point_summatiaccurate_full/recipe-393090.py>}, U{Kahan
260
- <https://WikiPedia.org/wiki/Kahan_summation_algorithm>}, U{Klein
276
+ @see: U{Hettinger<https://GitHub.com/ActiveState/code/tree/master/recipes/Python/
277
+ 393090_Binary_floating_point_summatiaccurate_full/recipe-393090.py>},
278
+ U{Kahan<https://WikiPedia.org/wiki/Kahan_summation_algorithm>}, U{Klein
261
279
  <https://Link.Springer.com/article/10.1007/s00607-005-0139-x>}, Python 2.6+
262
280
  file I{Modules/mathmodule.c} and the issue log U{Full precision summation
263
281
  <https://Bugs.Python.org/issue2819>}.
@@ -266,7 +284,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
266
284
  _n = 0
267
285
  # _ps = [] # partial sums
268
286
  # _ps_max = 0 # max(Fsum._ps_max, len(Fsum._ps))
269
- _ratio = None
287
+ _ratio = None # see method _raiser
270
288
  _recursive = bool(_getenv('PYGEODESY_FSUM_RECURSIVE', NN))
271
289
  _RESIDUAL = max(float(_getenv('PYGEODESY_FSUM_RESIDUAL', _0_0)), _0_0)
272
290
 
@@ -289,12 +307,12 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
289
307
  self.RESIDUAL(r) # ... ResidualError
290
308
  self._ps = [] # [_0_0], see L{Fsum._fprs}
291
309
  if xs:
292
- self._facc_any(xs, origin=1, up=False)
310
+ self._facc(xs, origin=1, up=False)
293
311
 
294
312
  def __abs__(self):
295
313
  '''Return this instance' absolute value as an L{Fsum}.
296
314
  '''
297
- s = _fsum(self._ps_1()) # == self._cmp_0(0, ...)
315
+ s = _fsum(self._ps_1primed()) # == self._cmp_0(0, ...)
298
316
  return (-self) if s < 0 else self._copy_2(self.__abs__)
299
317
 
300
318
  def __add__(self, other):
@@ -324,8 +342,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
324
342
  '''
325
343
  return self.ceil
326
344
 
327
- def __cmp__(self, other): # Python 2-
328
- '''Compare this with an other instance or C{scalar}.
345
+ def __cmp__(self, other): # PYCHOK no cover
346
+ '''Compare this with an other instance or C{scalar}, Python 2-.
329
347
 
330
348
  @return: -1, 0 or +1 (C{int}).
331
349
 
@@ -478,13 +496,15 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
478
496
  # Luciano Ramalho, "Fluent Python", O'Reilly, 2nd Ed, 2022 p. 567
479
497
  return _NotImplemented(self)
480
498
 
481
- def __ipow__(self, other, *mod): # PYCHOK 2 vs 3 args
499
+ def __ipow__(self, other, *mod, **raiser): # PYCHOK 2 vs 3 args
482
500
  '''Apply C{B{self} **= B{other}} to this instance.
483
501
 
484
502
  @arg other: The exponent (L{Fsum} or C{scalar}).
485
503
  @arg mod: Optional modulus (C{int} or C{None}) for the
486
504
  3-argument C{pow(B{self}, B{other}, B{mod})}
487
505
  version.
506
+ @kwarg raiser: Use C{B{raiser}=False} to ignore L{ResidualError}s
507
+ (C{bool}), see also method L{RESIDUAL}.
488
508
 
489
509
  @return: This instance, updated (L{Fsum}).
490
510
 
@@ -513,7 +533,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
513
533
  @see: CPython function U{float_pow<https://GitHub.com/
514
534
  python/cpython/blob/main/Objects/floatobject.c>}.
515
535
  '''
516
- return self._fpow(other, _pow_op_ + _fset_op_, *mod)
536
+ return self._fpow(other, _pow_op_ + _fset_op_, *mod, **raiser)
517
537
 
518
538
  def __isub__(self, other):
519
539
  '''Apply C{B{self} -= B{other}} to this instance.
@@ -533,10 +553,12 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
533
553
  '''
534
554
  return iter(self.partials)
535
555
 
536
- def __itruediv__(self, other):
556
+ def __itruediv__(self, other, **raiser):
537
557
  '''Apply C{B{self} /= B{other}} to this instance.
538
558
 
539
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}.
540
562
 
541
563
  @return: This instance, updated (L{Fsum}).
542
564
 
@@ -553,7 +575,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
553
575
 
554
576
  @see: Method L{Fsum.__ifloordiv__}.
555
577
  '''
556
- return self._ftruediv(other, _truediv_op_ + _fset_op_)
578
+ return self._ftruediv(other, _truediv_op_ + _fset_op_, **raiser)
557
579
 
558
580
  def __le__(self, other):
559
581
  '''Compare this with an other instance or C{scalar}.
@@ -670,8 +692,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
670
692
  @arg ndigits: Optional number of digits (C{int}).
671
693
  '''
672
694
  # <https://docs.Python.org/3.12/reference/datamodel.html?#object.__round__>
673
- return _Psum_1(round(float(self), *ndigits), # can be C{int}
674
- name=self.__round__.__name__)
695
+ return _Psum_(round(float(self), *ndigits), # can be C{int}
696
+ name=self.__round__.__name__)
675
697
 
676
698
  def __rpow__(self, other, *mod):
677
699
  '''Return C{B{other}**B{self}} as an L{Fsum}.
@@ -689,13 +711,13 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
689
711
  f = self._copy_2r(other, self.__rsub__)
690
712
  return f._fsub(self, _sub_op_)
691
713
 
692
- def __rtruediv__(self, other):
714
+ def __rtruediv__(self, other, **raiser):
693
715
  '''Return C{B{other} / B{self}} as an L{Fsum}.
694
716
 
695
717
  @see: Method L{Fsum.__itruediv__}.
696
718
  '''
697
719
  f = self._copy_2r(other, self.__rtruediv__)
698
- return f._ftruediv(self, _truediv_op_)
720
+ return f._ftruediv(self, _truediv_op_, **raiser)
699
721
 
700
722
  def __str__(self):
701
723
  '''Return the default C{str(self)}.
@@ -714,17 +736,19 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
714
736
  f = self._copy_2(self.__sub__)
715
737
  return f._fsub(other, _sub_op_)
716
738
 
717
- def __truediv__(self, other):
739
+ def __truediv__(self, other, **raiser):
718
740
  '''Return C{B{self} / B{other}} as an L{Fsum}.
719
741
 
720
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}.
721
745
 
722
746
  @return: The quotient (L{Fsum}).
723
747
 
724
748
  @see: Method L{Fsum.__itruediv__}.
725
749
  '''
726
750
  f = self._copy_2(self.__truediv__)
727
- return f._ftruediv(other, _truediv_op_)
751
+ return f._ftruediv(other, _truediv_op_, **raiser)
728
752
 
729
753
  __trunc__ = __int__
730
754
 
@@ -753,6 +777,13 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
753
777
  d = 1
754
778
  return n, d
755
779
 
780
+ @property_RO
781
+ def as_iscalar(self):
782
+ '''Get this instance I{as-is} (L{Fsum}) or C{scalar} iff scalar.
783
+ '''
784
+ s, r = self._fprs2
785
+ return self if r else s
786
+
756
787
  @property_RO
757
788
  def ceil(self):
758
789
  '''Get this instance' C{ceil} value (C{int} in Python 3+,
@@ -773,15 +804,12 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
773
804
  '''(INTERNAL) Return C{scalar(self - B{other})} for 0-comparison.
774
805
  '''
775
806
  if isinstance(other, Fsum):
776
- s = _fsum(self._ps_1(*other._ps))
777
- elif isscalar(other):
778
- if other:
779
- s = _fsum(self._ps_1(other))
780
- else:
781
- s, r = self._fprs2
782
- s = _signOf(s, -r)
807
+ s = _fsum(self._ps_1primed(*other._ps))
808
+ elif self._scalar(other, op):
809
+ s = _fsum(self._ps_1primed(other))
783
810
  else:
784
- raise self._TypeError(op, other) # txt=_invalid_
811
+ s, r = self._fprs2
812
+ s = _signOf(s, -r)
785
813
  return s
786
814
 
787
815
  def copy(self, deep=False, name=NN):
@@ -790,8 +818,10 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
790
818
  @return: The copy (L{Fsum}).
791
819
  '''
792
820
  f = _Named.copy(self, deep=deep, name=name)
793
- f._ps = list(self._ps) # separate list
794
- f._n = self._n if deep else 1
821
+ if f._ps is self._ps:
822
+ f._ps = list(self._ps) # separate list
823
+ if not deep:
824
+ f._n = 1
795
825
  return f
796
826
 
797
827
  def _copy_2(self, which, name=NN):
@@ -817,11 +847,13 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
817
847
  # if R is not Fsum._RESIDUAL:
818
848
  # self._RESIDUAL = R
819
849
 
820
- def divmod(self, other):
850
+ def divmod(self, other, **raiser):
821
851
  '''Return C{divmod(B{self}, B{other})} as 2-tuple C{(quotient,
822
852
  remainder)}.
823
853
 
824
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}.
825
857
 
826
858
  @return: A L{DivMod2Tuple}C{(div, mod)}, with quotient C{div}
827
859
  an C{int} in Python 3+ or C{float} in Python 2- and
@@ -830,12 +862,12 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
830
862
  @see: Method L{Fsum.__itruediv__}.
831
863
  '''
832
864
  f = self._copy_2(self.divmod)
833
- return f._fdivmod2(other, _divmod_op_)
865
+ return f._fdivmod2(other, _divmod_op_, **raiser)
834
866
 
835
867
  def _Error(self, op, other, Error, **txt_cause):
836
868
  '''(INTERNAL) Format an B{C{Error}} for C{{self} B{op} B{other}}.
837
869
  '''
838
- return Error(_SPACE_(self.toStr(), op, other), **txt_cause)
870
+ return Error(_SPACE_(self.as_iscalar, op, other), **txt_cause)
839
871
 
840
872
  def _ErrorX(self, X, op, other, *mod):
841
873
  '''(INTERNAL) Format the caught exception C{X}.
@@ -852,66 +884,91 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
852
884
  n = unstr(self.named3, *xs[:3], _ELLIPSIS=len(xs) > 3, **kwds)
853
885
  return E(n, txt=t, cause=X)
854
886
 
855
- def _facc(self, xs, **up):
856
- '''(INTERNAL) Accumulate all C{xs}, known to be scalar.
857
- '''
858
- self._ps_acc(self._ps, xs, **up)
859
- return self
860
-
861
- def _facc_(self, *xs, **up):
862
- '''(INTERNAL) Accumulate all positional C{xs}, known to be scalar.
887
+ def _facc(self, xs, up=True, **origin_X_x):
888
+ '''(INTERNAL) Accumulate more C{scalars} or L{Fsum}s.
863
889
  '''
864
890
  if xs:
865
- self._ps_acc(self._ps, xs, **up)
891
+ _xs = _2floats(xs, **origin_X_x) # PYCHOK yield
892
+ ps = self._ps
893
+ ps[:] = self._ps_acc(list(ps), _xs, up=up)
866
894
  return self
867
895
 
868
- def _facc_any(self, xs, up=True, **origin_X_x):
869
- '''(INTERNAL) Accumulate more C{scalars} or L{Fsum}s.
896
+ def _facc_1(self, xs, **up):
897
+ '''(INTERNAL) Accumulate 0, 1 or more C{scalars} or L{Fsum}s,
898
+ all positional C{xs} in the caller of this method.
870
899
  '''
871
- self._ps[:] = self._ps_acc(list(self._ps),
872
- _2floats(xs, **origin_X_x), up=up) # PYCHOK yield
873
- return self
900
+ # assert islistuple(xs)
901
+ return self._fadd(xs[0], _add_op_) if len(xs) == 1 else \
902
+ self._facc(xs, origin=1, **up)
874
903
 
875
- def _facc_any_neg(self, xs, up=True, **origin):
904
+ def _facc_neg(self, xs, up=True, **origin):
876
905
  '''(INTERNAL) Accumulate more C{scalars} or L{Fsum}s, negated.
877
906
  '''
878
- def _neg(x):
879
- return -x
907
+ if xs:
908
+ def _neg(x):
909
+ return -x
880
910
 
881
- self._ps[:] = self._ps_acc(list(self._ps), map(_neg,
882
- _2floats(xs, **origin)), up=up) # PYCHOK yield
911
+ _x = _2floats(xs, **origin) # PYCHOK yield
912
+ ps = self._ps
913
+ ps[:] = self._ps_acc(list(ps), map(_neg, _x), up=up)
883
914
  return self
884
915
 
885
- def _facc_power(self, power, xs, which): # in .fmath
916
+ def _facc_power(self, power, xs, which, **raiser): # in .fmath
886
917
  '''(INTERNAL) Add each C{xs} as C{float(x**power)}.
887
918
  '''
888
- p = power
889
- if isinstance(p, Fsum):
890
- if p.is_exact:
891
- return self._facc_power(p._fprs, xs, which)
892
- _Pow = Fsum._pow_any
893
- elif isint(p, both=True) and p >= 0:
894
- _Pow, p = Fsum._pow_int, int(p)
895
- else:
896
- _Pow, p = Fsum._pow_scalar, _2float(power=p)
919
+ def _Pow4(p):
920
+ r = 0
921
+ if isinstance(p, Fsum):
922
+ s, r = p._fprs2
923
+ if r == 0:
924
+ return _Pow4(s)
925
+ m = Fsum._pow
926
+ elif isint(p, both=True) and int(p) >= 0:
927
+ p = s = int(p)
928
+ m = Fsum._pow_int
929
+ else:
930
+ p = s = _2float(power=p)
931
+ m = Fsum._pow_scalar
932
+ return m, p, s, r
897
933
 
898
- if p:
899
- from math import pow as _pow
900
- op = which.__name__
901
- _Fs = Fsum
934
+ _Pow, p, s, r = _Pow4(power)
935
+ if p: # and xs:
936
+ _pow = Fsum._pow_2_3
937
+ _Fs = Fsum
938
+ _Ps = _Psum_ # ()._fset_ps_
939
+ op = which.__name__
902
940
 
903
941
  def _X(X):
904
- f = _Pow(X, p, power, op)
942
+ f = _Pow(X, p, power, op, **raiser)
905
943
  return f._ps if isinstance(f, _Fs) else (f,)
906
944
 
907
945
  def _x(x):
908
- return _pow(float(x), p)
946
+ 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)
951
+ return f
909
952
 
910
- f = self._facc_any(xs, origin=1, _X=_X, _x=_x)
953
+ f = self._facc(xs, origin=1, _X=_X, _x=_x)
911
954
  else:
912
- f = self._facc_(float(len(xs))) # x**0 == 1
955
+ f = self._facc_scalar_(float(len(xs))) # x**0 == 1
913
956
  return f
914
957
 
958
+ def _facc_scalar(self, xs, **up):
959
+ '''(INTERNAL) Accumulate all C{xs}, known to be scalar.
960
+ '''
961
+ if xs:
962
+ self._ps_acc(self._ps, xs, **up)
963
+ return self
964
+
965
+ def _facc_scalar_(self, *xs, **up):
966
+ '''(INTERNAL) Accumulate all positional C{xs}, known to be scalar.
967
+ '''
968
+ if xs:
969
+ self._ps_acc(self._ps, xs, **up)
970
+ return self
971
+
915
972
  # def _facc_up(self, up=True):
916
973
  # '''(INTERNAL) Update the C{partials}, by removing
917
974
  # and re-accumulating the final C{partial}.
@@ -920,7 +977,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
920
977
  # p = self._ps.pop()
921
978
  # if p:
922
979
  # n = self._n
923
- # self._facc_(p, up=False)
980
+ # self._facc_scalar_(p, up=False)
924
981
  # self._n = n
925
982
  # break
926
983
  # return self._update() if up else self # ._fpsqz()
@@ -942,11 +999,11 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
942
999
  @raise ValueError: Invalid or non-finite B{C{xs}} value.
943
1000
  '''
944
1001
  if isinstance(xs, Fsum):
945
- self._facc(xs._ps) # tuple
1002
+ self._facc_scalar(xs._ps) # tuple
946
1003
  elif isscalar(xs): # for backward compatibility
947
- self._facc_(_2float(x=xs)) # PYCHOK no cover
948
- elif xs:
949
- self._facc_any(xs)
1004
+ self._facc_scalar_(_2float(x=xs)) # PYCHOK no cover
1005
+ elif xs: # assert isiterable(xs)
1006
+ self._facc(xs)
950
1007
  return self
951
1008
 
952
1009
  def fadd_(self, *xs):
@@ -965,30 +1022,29 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
965
1022
 
966
1023
  @raise ValueError: Invalid or non-finite B{C{xs}} value.
967
1024
  '''
968
- return self._facc_any(xs, origin=1)
1025
+ return self._facc_1(xs)
969
1026
 
970
1027
  def _fadd(self, other, op, **up): # in .fmath.Fhorner
971
1028
  '''(INTERNAL) Apply C{B{self} += B{other}}.
972
1029
  '''
973
1030
  if isinstance(other, Fsum):
974
- self._facc(other._ps, **up) # tuple
975
- elif not isscalar(other):
976
- raise self._TypeError(op, other) # txt=_invalid_
977
- elif other:
978
- self._facc_(other, **up)
1031
+ self._facc_scalar(other._ps, **up) # tuple
1032
+ elif self._scalar(other, op):
1033
+ self._facc_scalar_(other, **up)
979
1034
  return self
980
1035
 
981
1036
  fcopy = copy # for backward compatibility
982
1037
  fdiv = __itruediv__ # for backward compatibility
983
1038
  fdivmod = __divmod__ # for backward compatibility
984
1039
 
985
- def _fdivmod2(self, other, op):
1040
+ def _fdivmod2(self, other, op, **raiser):
986
1041
  '''(INTERNAL) Apply C{B{self} %= B{other}} and return a L{DivMod2Tuple}.
987
1042
  '''
988
1043
  # result mostly follows CPython function U{float_divmod
989
1044
  # <https://GitHub.com/python/cpython/blob/main/Objects/floatobject.c>},
990
1045
  # but at least divmod(-3, 2) equals Cpython's result (-2, 1).
991
- q = self._copy_2(self._fdivmod2)._ftruediv(other, op).floor
1046
+ f = self._copy_2(self._fdivmod2)
1047
+ q = f._ftruediv(other, op, **raiser).floor
992
1048
  if q: # == float // other == floor(float / other)
993
1049
  self -= other * q
994
1050
 
@@ -1009,24 +1065,28 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1009
1065
  raise ValueError(_not_finite_) if op is None else \
1010
1066
  self._ValueError(op, other, txt=_not_finite_)
1011
1067
 
1012
- def fint(self, raiser=True, **name):
1068
+ def fint(self, raiser=True, name=NN, **RESIDUAL):
1013
1069
  '''Return this instance' current running sum as C{integer}.
1014
1070
 
1015
- @kwarg raiser: If C{True} throw a L{ResidualError} if the
1016
- I{integer} residual is non-zero (C{bool}).
1071
+ @kwarg raiser: Use C{B{raiser}=False} to ignore L{ResidualError}s
1072
+ (C{bool}), see also method L{RESIDUAL}.
1017
1073
  @kwarg name: Optional name (C{str}), overriding C{"fint"}.
1074
+ @kwarg RESIDUAL: Optional threshold, overriding the current
1075
+ L{RESIDUAL<Fsum.RESIDUAL>} (C{scalar}).
1018
1076
 
1019
- @return: The C{integer} (L{Fsum}).
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}.
1020
1080
 
1021
1081
  @raise ResidualError: Non-zero I{integer} residual.
1022
1082
 
1023
1083
  @see: Methods L{Fsum.int_float} and L{Fsum.is_integer}.
1024
1084
  '''
1025
1085
  i, r = self._fint2
1026
- if r and raiser:
1086
+ if r and raiser and self._raiser2sum(r, i, **RESIDUAL):
1027
1087
  t = _stresidual(_integer_, r)
1028
1088
  raise ResidualError(_integer_, i, txt=t)
1029
- f = self._copy_2(self.fint, **name)
1089
+ f = self._copy_2(self.fint, name=name)
1030
1090
  return f._fset(i)
1031
1091
 
1032
1092
  def fint2(self, **name):
@@ -1047,7 +1107,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1047
1107
  '''
1048
1108
  s, r = self._fprs2
1049
1109
  i = int(s)
1050
- r = _fsum(self._ps_1(i)) if r else float(s - i)
1110
+ r = _fsum(self._ps_1primed(i)) if r else float(s - i)
1051
1111
  return i, (r or INT0) # Fsum2Tuple?
1052
1112
 
1053
1113
  @deprecated_property_RO
@@ -1073,10 +1133,10 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1073
1133
 
1074
1134
  # floordiv = __floordiv__ # for naming consistency
1075
1135
 
1076
- def _floordiv(self, other, op): # rather _ffloordiv?
1136
+ def _floordiv(self, other, op, **raiser): # rather _ffloordiv?
1077
1137
  '''Apply C{B{self} //= B{other}}.
1078
1138
  '''
1079
- q = self._ftruediv(other, op) # == self
1139
+ q = self._ftruediv(other, op, **raiser) # == self
1080
1140
  return self._fset(q.floor) # floor(q)
1081
1141
 
1082
1142
  fmul = __imul__ # for backward compatibility
@@ -1091,41 +1151,42 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1091
1151
  f = other._mul_scalar(self._ps[0], op)
1092
1152
  else: # len(other._ps) == len(self._ps) == 1
1093
1153
  f = self._finite(self._ps[0] * other._ps[0])
1094
- elif isscalar(other):
1095
- f = self._mul_scalar(other, op) if other != _1_0 else self
1096
1154
  else:
1097
- raise self._TypeError(op, other) # txt=_invalid_
1155
+ s = self._scalar(other, op)
1156
+ f = self._mul_scalar(s, op)
1098
1157
  return self._fset(f) # n=len(self) + 1
1099
1158
 
1100
- def fover(self, over):
1159
+ def fover(self, over, **raiser):
1101
1160
  '''Apply C{B{self} /= B{over}} and summate.
1102
1161
 
1103
1162
  @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}.
1104
1165
 
1105
1166
  @return: Precision running sum (C{float}).
1106
1167
 
1107
1168
  @see: Methods L{Fsum.fsum} and L{Fsum.__itruediv__}.
1108
1169
  '''
1109
- return float(self.fdiv(over)._fprs)
1170
+ return float(self.fdiv(over, **raiser)._fprs)
1110
1171
 
1111
1172
  fpow = __ipow__ # for backward compatibility
1112
1173
 
1113
- def _fpow(self, other, op, *mod):
1174
+ def _fpow(self, other, op, *mod, **raiser):
1114
1175
  '''Apply C{B{self} **= B{other}}, optional B{C{mod}} or C{None}.
1115
1176
  '''
1116
1177
  if mod:
1117
1178
  if mod[0] is not None: # == 3-arg C{pow}
1118
- f = self._pow_2_3(self, other, other, op, *mod)
1179
+ f = self._pow_2_3(self, other, other, op, *mod, **raiser)
1119
1180
  elif self.is_integer():
1120
1181
  # return an exact C{int} for C{int}**C{int}
1121
1182
  i, _ = self._fint2 # assert _ == 0
1122
1183
  x = _2scalar(other) # C{int}, C{float} or other
1123
- f = self._pow_2_3(i, x, other, op) if isscalar(x) else \
1124
- _Psum_1(i)._pow_any(x, other, op)
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
1125
1186
  else: # mod[0] is None, power(self, other)
1126
- f = self._pow_any(other, other, op)
1127
- else: # pow(self, other) == pow(self, other, None)
1128
- f = self._pow_any(other, other, op)
1187
+ f = self._pow(other, other, op, **raiser)
1188
+ else: # pow(self, other)
1189
+ f = self._pow(other, other, op, **raiser)
1129
1190
  return self._fset(f, asis=isint(f)) # n=max(len(self), 1)
1130
1191
 
1131
1192
  @Property_RO
@@ -1149,7 +1210,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1149
1210
  s = _psum(ps)
1150
1211
  n = len(ps) - 2
1151
1212
  if n > 0:
1152
- r = _fsum(self._ps_1(s)) or INT0
1213
+ r = _fsum(self._ps_1primed(s)) or INT0
1153
1214
  return Fsum2Tuple(s, r)
1154
1215
  if n == 0: # len(ps) == 2
1155
1216
  ps[:] = _2ps(*_2sum(*ps))
@@ -1187,7 +1248,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1187
1248
  '''(INTERNAL) Overwrite this instance with an other or a C{scalar}.
1188
1249
  '''
1189
1250
  if other is self:
1190
- pass # from ._fmul, ._ftruediv and ._pow_scalar
1251
+ pass # from ._fmul, ._ftruediv and ._pow_0_1
1191
1252
  elif isinstance(other, Fsum):
1192
1253
  self._ps[:] = other._ps
1193
1254
  self._n = n or other._n
@@ -1206,7 +1267,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1206
1267
  # Property's _fset zaps the value just set by the @setter
1207
1268
  self.__dict__.update(_fint2=t, _fprs=s, _fprs2=Fsum2Tuple(s, INT0))
1208
1269
  else: # PYCHOK no cover
1209
- raise self._TypeError(_fset_op_, other) # txt=_invalid_
1270
+ raise self._Error(_fset_op_, other, _AssertionError)
1210
1271
  return self
1211
1272
 
1212
1273
  def _fset_ps(self, other, n=0): # in .fmath
@@ -1220,6 +1281,13 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1220
1281
  self._n = n or 1
1221
1282
  return self
1222
1283
 
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
+
1223
1291
  def fsub(self, xs=()):
1224
1292
  '''Subtract an iterable of C{scalar} or L{Fsum} instances from
1225
1293
  this instance.
@@ -1231,7 +1299,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1231
1299
 
1232
1300
  @see: Method L{Fsum.fadd}.
1233
1301
  '''
1234
- return self._facc_any_neg(xs) if xs else self
1302
+ return self._facc_neg(xs)
1235
1303
 
1236
1304
  def fsub_(self, *xs):
1237
1305
  '''Subtract all positional C{scalar} or L{Fsum} instances from
@@ -1244,7 +1312,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1244
1312
 
1245
1313
  @see: Method L{Fsum.fadd}.
1246
1314
  '''
1247
- return self._facc_any_neg(xs, origin=1) if xs else self
1315
+ return self._fsub(xs[0], _sub_op_) if len(xs) == 1 else \
1316
+ self._facc_neg(xs, origin=1)
1248
1317
 
1249
1318
  def _fsub(self, other, op):
1250
1319
  '''(INTERNAL) Apply C{B{self} -= B{other}}.
@@ -1253,11 +1322,9 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1253
1322
  if other is self: # or other._fprs2 == self._fprs2:
1254
1323
  self._fset(_0_0) # n=len(self) * 2, self -= self
1255
1324
  elif other._ps:
1256
- self._facc(other._ps_neg)
1257
- elif not isscalar(other):
1258
- raise self._TypeError(op, other) # txt=_invalid_
1259
- elif self._finite(other, op):
1260
- self._facc_(-other)
1325
+ self._facc_scalar(other._ps_neg)
1326
+ elif self._scalar(other, op):
1327
+ self._facc_scalar_(-self._finite(other, op))
1261
1328
  return self
1262
1329
 
1263
1330
  def fsum(self, xs=()):
@@ -1272,8 +1339,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1272
1339
 
1273
1340
  @note: Accumulation can continue after summation.
1274
1341
  '''
1275
- f = self._facc_any(xs) if xs else self
1276
- return f._fprs
1342
+ return self._facc(xs)._fprs
1277
1343
 
1278
1344
  def fsum_(self, *xs):
1279
1345
  '''Add all positional C{scalar} or L{Fsum} instances and summate.
@@ -1285,15 +1351,14 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1285
1351
 
1286
1352
  @see: Methods L{Fsum.fsum}, L{Fsum.Fsum_} and L{Fsum.fsumf_}.
1287
1353
  '''
1288
- f = self._facc_any(xs, origin=1) if xs else self
1289
- return f._fprs
1354
+ return self._facc_1(xs)._fprs
1290
1355
 
1291
1356
  def Fsum_(self, *xs):
1292
1357
  '''Like method L{Fsum.fsum_} but returning an L{Fsum}.
1293
1358
 
1294
1359
  @return: Current, precision running sum (L{Fsum}).
1295
1360
  '''
1296
- return self._facc_any(xs, origin=1)._copy_2(self.Fsum_)
1361
+ return self._facc_1(xs)._copy_2(self.Fsum_)
1297
1362
 
1298
1363
  def fsum2(self, xs=(), name=NN):
1299
1364
  '''Add more C{scalar} or L{Fsum} instances and return the
@@ -1311,11 +1376,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1311
1376
 
1312
1377
  @see: Methods L{Fsum.fint2}, L{Fsum.fsum} and L{Fsum.fsum2_}
1313
1378
  '''
1314
- f = self._facc_any(xs) if xs else self
1315
- t = f._fprs2
1316
- if name:
1317
- t = t.dup(name=name)
1318
- return t
1379
+ t = self._facc(xs)._fprs2
1380
+ return t.dup(name=name) if name else t
1319
1381
 
1320
1382
  def fsum2_(self, *xs):
1321
1383
  '''Add any positional C{scalar} or L{Fsum} instances and return
@@ -1330,62 +1392,58 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1330
1392
 
1331
1393
  @see: Methods L{Fsum.fsum_} and L{Fsum.fsum}.
1332
1394
  '''
1333
- return self._fsum2f_any(xs, self._facc_any, origin=1)
1395
+ return self._fsum2(xs, self._facc_1)
1396
+
1397
+ def _fsum2(self, xs, _f, **origin):
1398
+ '''(INTERNAL) Helper for L{Fsum.fsum2_} and L{Fsum.fsum2f_}.
1399
+ '''
1400
+ p, q = self._fprs2
1401
+ if xs:
1402
+ s, r = _f(xs, **origin)._fprs2
1403
+ return s, _2delta(s - p, r - q) # _fsum(_1primed((s, -p, r, -q))
1404
+ else:
1405
+ return p, _0_0
1334
1406
 
1335
1407
  def fsumf_(self, *xs):
1336
- '''Like method L{Fsum.fsum_} but only for I{known} C{float B{xs}}.
1408
+ '''Like method L{Fsum.fsum_} but only for C{B{xs}}, I{known to be scalar}.
1337
1409
  '''
1338
- f = self._facc(xs) if xs else self
1339
- return f._fprs
1410
+ return self._facc_scalar(xs)._fprs
1340
1411
 
1341
1412
  def Fsumf_(self, *xs):
1342
- '''Like method L{Fsum.Fsum_} but only for I{known} C{float B{xs}}.
1413
+ '''Like method L{Fsum.Fsum_} but only for C{B{xs}}, I{known to be scalar}.
1343
1414
  '''
1344
- return self._facc(xs)._copy_2(self.Fsumf_)
1415
+ return self._facc_scalar(xs)._copy_2(self.Fsumf_)
1345
1416
 
1346
1417
  def fsum2f_(self, *xs):
1347
- '''Like method L{Fsum.fsum2_} but only for I{known} C{float B{xs}}.
1348
- '''
1349
- return self._fsum2f_any(xs, self._facc)
1350
-
1351
- def _fsum2f_any(self, xs, _facc, **origin):
1352
- '''(INTERNAL) Helper for L{Fsum.fsum2_} and L{Fsum.fsum2f_}.
1418
+ '''Like method L{Fsum.fsum2_} but only for C{B{xs}}, I{known to be scalar}.
1353
1419
  '''
1354
- p, q = self._fprs2
1355
- if xs:
1356
- s, r = _facc(xs, **origin)._fprs2
1357
- return s, _2delta(s - p, r - q) # _fsum(_1primed((s, -p, r, -q))
1358
- else:
1359
- return p, _0_0
1420
+ return self._fsum2(xs, self._facc_scalar, origin=1)
1360
1421
 
1361
1422
  # ftruediv = __itruediv__ # for naming consistency?
1362
1423
 
1363
- def _ftruediv(self, other, op):
1424
+ def _ftruediv(self, other, op, **raiser):
1364
1425
  '''(INTERNAL) Apply C{B{self} /= B{other}}.
1365
1426
  '''
1366
1427
  n = _1_0
1367
1428
  if isinstance(other, Fsum):
1368
1429
  if other is self or other == self:
1369
- return self._fset(_1_0) # n=len(self)
1430
+ return self._fset(n) # n=len(self)
1370
1431
  d, r = other._fprs2
1371
1432
  if r:
1372
1433
  if d:
1373
- if self._raiser(r, d):
1434
+ if self._raiser(r, d, **raiser):
1374
1435
  raise self._ResidualError(op, other, r)
1375
1436
  d, n = other.as_integer_ratio()
1376
1437
  else: # PYCHOK no cover
1377
1438
  d = r
1378
- elif isscalar(other):
1379
- d = other
1380
- else: # PYCHOK no cover
1381
- raise self._TypeError(op, other) # txt=_invalid_
1439
+ else:
1440
+ d = self._scalar(other, op)
1382
1441
  try:
1383
- s = 0 if isinf(d) else (
1384
- d if isnan(d) else self._finite(n / d))
1442
+ s = n / d
1385
1443
  except Exception as X:
1386
1444
  raise self._ErrorX(X, op, other)
1387
- f = self._mul_scalar(s, _mul_op_) # handles 0, NAN, etc.
1388
- return self._fset(f, asis=False)
1445
+ f = self._mul_scalar(s, _mul_op_) # handles 0, INF, NAN
1446
+ return self._fset(f) # asis=False
1389
1447
 
1390
1448
  @property_RO
1391
1449
  def imag(self):
@@ -1395,24 +1453,26 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1395
1453
  '''
1396
1454
  return _0_0
1397
1455
 
1398
- def int_float(self, raiser=False):
1456
+ def int_float(self, raiser=False, **RESIDUAL):
1399
1457
  '''Return this instance' current running sum as C{int} or C{float}.
1400
1458
 
1401
1459
  @kwarg raiser: If C{True} throw a L{ResidualError} if the
1402
- residual is non-zero.
1460
+ residual exceeds the C{RESIDUAL} (C{bool}).
1461
+ @kwarg RESIDUAL: Optional threshold, overriding the current
1462
+ L{RESIDUAL<Fsum.RESIDUAL>} (C{scalar}).
1403
1463
 
1404
1464
  @return: This C{integer} sum if this instance C{is_integer},
1405
- otherwise return the C{float} sum if the residual
1406
- is zero or if C{B{raiser}=False}.
1465
+ otherwise return the C{float} sum if the residual is
1466
+ zero or insignificant or if C{B{raiser}=False}.
1407
1467
 
1408
1468
  @raise ResidualError: Non-zero residual and C{B{raiser}=True}.
1409
1469
 
1410
- @see: Methods L{Fsum.fint} and L{Fsum.fint2}.
1470
+ @see: Methods L{Fsum.fint} and L{Fsum.fint2} and property L{Fsum.as_iscalar}.
1411
1471
  '''
1412
1472
  s, r = self._fint2
1413
1473
  if r:
1414
1474
  s, r = self._fprs2
1415
- if r and raiser: # PYCHOK no cover
1475
+ if r and raiser and self._raiser2sum(r, s, **RESIDUAL): # PYCHOK no cover
1416
1476
  t = _stresidual(_non_zero_, r)
1417
1477
  raise ResidualError(int_float=s, txt=t)
1418
1478
  s = float(s) # redundant
@@ -1426,7 +1486,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1426
1486
  def is_integer(self):
1427
1487
  '''Is this instance' running sum C{integer}? (C{bool}).
1428
1488
 
1429
- @see: Methods L{Fsum.fint} and L{Fsum.fint2}.
1489
+ @see: Methods L{Fsum.fint}, L{Fsum.fint2} and L{Fsum.is_scalar}.
1430
1490
  '''
1431
1491
  _, r = self._fint2
1432
1492
  return False if r else True
@@ -1445,12 +1505,20 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1445
1505
  f = Fsum._math_fsum
1446
1506
  return 2 if _psum is f else bool(f)
1447
1507
 
1508
+ def is_scalar(self):
1509
+ '''Is this instance' running sum C{scalar}? (C{bool}).
1510
+
1511
+ @see: Method L{Fsum.is_integer} and property L{Fsum.as_iscalar}.
1512
+ '''
1513
+ s, r = t = self._fprs2
1514
+ return False if r and _2sum(s, r) != t else True
1515
+
1448
1516
  def _mul_Fsum(self, other, op=_mul_op_): # in .fmath.Fhorner
1449
1517
  '''(INTERNAL) Return C{B{self} * Fsum B{other}} as L{Fsum} or C{0}.
1450
1518
  '''
1451
1519
  # assert isinstance(other, Fsum)
1452
1520
  if self._ps and other._ps:
1453
- f = self._ps_mul(op, *other._ps) # NO ._2scalar
1521
+ f = self._ps_mul(op, *other._ps) # NO .as_iscalar
1454
1522
  else:
1455
1523
  f = _0_0
1456
1524
  return f
@@ -1462,7 +1530,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1462
1530
  if self._ps and self._finite(factor, op):
1463
1531
  f = self if factor == _1_0 else (
1464
1532
  self._neg if factor == _N_1_0 else
1465
- self._ps_mul(op, factor)._2scalar)
1533
+ self._ps_mul(op, factor).as_iscalar)
1466
1534
  else:
1467
1535
  f = _0_0
1468
1536
  return f
@@ -1479,12 +1547,14 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1479
1547
  '''
1480
1548
  return tuple(self._ps)
1481
1549
 
1482
- def pow(self, x, *mod):
1550
+ def pow(self, x, *mod, **raiser):
1483
1551
  '''Return C{B{self}**B{x}} as L{Fsum}.
1484
1552
 
1485
1553
  @arg x: The exponent (L{Fsum} or C{scalar}).
1486
1554
  @arg mod: Optional modulus (C{int} or C{None}) for the 3-argument
1487
1555
  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}.
1488
1558
 
1489
1559
  @return: The C{pow(self, B{x})} or C{pow(self, B{x}, *B{mod})}
1490
1560
  result (L{Fsum}).
@@ -1493,17 +1563,35 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1493
1563
  C{integer} L{Fsum} provided this instance C{is_integer}
1494
1564
  or set to C{integer} by an L{Fsum.fint} call.
1495
1565
 
1496
- @see: Methods L{Fsum.__ipow__}, L{Fsum.fint} and L{Fsum.is_integer}.
1566
+ @see: Methods L{Fsum.__ipow__}, L{Fsum.fint}, L{Fsum.is_integer}
1567
+ and L{Fsum.root}.
1497
1568
  '''
1498
1569
  f = self._copy_2(self.pow)
1499
- return f._fpow(x, _pow_op_, *mod) # f = pow(f, x, *mod)
1570
+ return f._fpow(x, _pow_op_, *mod, **raiser) # f = pow(f, x, *mod)
1571
+
1572
+ def _pow(self, other, unused, op, **raiser):
1573
+ '''Return C{B{self} ** B{other}}.
1574
+ '''
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)
1582
+ elif self._scalar(other, op):
1583
+ x = self._finite(other, op)
1584
+ f = self._pow_scalar(x, other, op, **raiser)
1585
+ else:
1586
+ f = self._pow_0_1(0, other)
1587
+ return f
1500
1588
 
1501
1589
  def _pow_0_1(self, x, other):
1502
1590
  '''(INTERNAL) Return B{C{self}**1} or C{B{self}**0 == 1.0}.
1503
1591
  '''
1504
1592
  return self if x else (1 if isint(other) and self.is_integer() else _1_0)
1505
1593
 
1506
- def _pow_2_3(self, b, x, other, op, *mod):
1594
+ def _pow_2_3(self, b, x, other, op, *mod, **raiser):
1507
1595
  '''(INTERNAL) 2-arg C{pow(B{b}, scalar B{x})} and 3-arg C{pow(B{b},
1508
1596
  B{x}, int B{mod} or C{None})}, embellishing errors.
1509
1597
  '''
@@ -1511,7 +1599,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1511
1599
  if mod: # b, x, mod all C{int}, unless C{mod} is C{None}
1512
1600
  m = mod[0]
1513
1601
  b, r = b._fprs2 if m is None else b._fint2
1514
- if r and self._raiser(r, b):
1602
+ if r and self._raiser(r, b, **raiser):
1515
1603
  t = _non_zero_ if m is None else _integer_
1516
1604
  raise ResidualError(_stresidual(t, r, mod=m), txt=None)
1517
1605
  x = _2scalar(x, _raiser=self._raiser, mod=m)
@@ -1524,24 +1612,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1524
1612
  except Exception as X:
1525
1613
  raise self._ErrorX(X, op, other, *mod)
1526
1614
 
1527
- def _pow_any(self, other, unused, op):
1528
- '''Return C{B{self} ** B{other}}.
1529
- '''
1530
- if isinstance(other, Fsum):
1531
- x, r = other._fprs2
1532
- if r and self._raiser(r, x):
1533
- raise self._ResidualError(op, other, r)
1534
- f = self._pow_scalar(x, other, op)
1535
- if r:
1536
- f *= self._pow_scalar(r, other, op)
1537
- elif isscalar(other):
1538
- x = self._finite(other, op)
1539
- f = self._pow_scalar(x, other, op)
1540
- else:
1541
- raise self._TypeError(op, other) # txt=_invalid_
1542
- return f
1543
-
1544
- def _pow_int(self, x, other, op):
1615
+ def _pow_int(self, x, other, op, **raiser):
1545
1616
  '''(INTERNAL) Return C{B{self} **= B{x}} for C{int B{x} >= 0}.
1546
1617
  '''
1547
1618
  # assert isint(x) and x >= 0
@@ -1550,7 +1621,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1550
1621
  _mul_Fsum = Fsum._mul_Fsum
1551
1622
  if x > 4:
1552
1623
  p = self
1553
- f = self if (x & 1) else _Psum_1()
1624
+ f = self if (x & 1) else _Psum_(_1_0)
1554
1625
  m = x >> 1 # // 2
1555
1626
  while m:
1556
1627
  p = _mul_Fsum(p, p, op) # p **= 2
@@ -1561,17 +1632,17 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1561
1632
  f = _mul_Fsum(self, self, op)
1562
1633
  if x > 2: # self**3 or 4
1563
1634
  p = self if x < 4 else f
1564
- f = _mul_Fsum(f, p, op)._2scalar
1635
+ f = _mul_Fsum(f, p, op).as_iscalar
1565
1636
  else: # self**1 or self**0 == 1 or _1_0
1566
1637
  f = self._pow_0_1(x, other)
1567
1638
  elif ps: # self._ps[0]**x
1568
- f = self._pow_2_3(ps[0], x, other, op)
1639
+ f = self._pow_2_3(ps[0], x, other, op, **raiser)
1569
1640
  else: # PYCHOK no cover
1570
1641
  # 0**pos_int == 0, but 0**0 == 1
1571
1642
  f = 0 if x else 1
1572
1643
  return f
1573
1644
 
1574
- def _pow_scalar(self, x, other, op):
1645
+ def _pow_scalar(self, x, other, op, **raiser):
1575
1646
  '''(INTERNAL) Return C{self**B{x}} for C{scalar B{x}}.
1576
1647
  '''
1577
1648
  s, r = self._fprs2
@@ -1580,41 +1651,33 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1580
1651
  y = abs(x)
1581
1652
  if y > 1:
1582
1653
  if r:
1583
- f = self._pow_int(y, other, op)
1654
+ f = self._pow_int(y, other, op, **raiser)
1584
1655
  if x > 0: # > 1
1585
1656
  return f
1586
1657
  # assert x < 0 # < -1
1587
1658
  s, r = f._fprs2 if isinstance(f, Fsum) else (f, 0)
1588
1659
  if r:
1589
- return _Psum_1()._ftruediv(f, op)
1660
+ return _1_over(f, op, **raiser) # PYCHOK 2 args
1590
1661
  # use **= -1 for the CPython float_pow
1591
1662
  # error if s is zero, and not s = 1 / s
1592
1663
  x = -1
1593
1664
  elif x < 0: # == -1: self**(-1) == 1 / self
1594
1665
  if r:
1595
- return _Psum_1()._ftruediv(self, op)
1666
+ return _1_over(self, op, **raiser) # PYCHOK 2 args
1596
1667
  else: # self**1 or self**0
1597
1668
  return self._pow_0_1(x, other) # self, 1 or 1.0
1598
- elif not isscalar(x): # assert ...
1599
- raise self._TypeError(op, other, txt=_not_scalar_)
1600
- elif r and self._raiser(r, s): # non-zero residual**fractional
1601
- # raise self._ResidualError(op, other, r, fractional_power=x)
1602
- t = _stresidual(_non_zero_, r, fractional_power=x)
1603
- raise self._Error(op, other, ResidualError, txt=t)
1669
+ elif r: # non-zero residual**fractional
1670
+ if s:
1671
+ n, d = self.as_integer_ratio()
1672
+ if abs(n) > abs(d):
1673
+ n, d, x = d, n, (-x)
1674
+ s = n / d
1675
+ else:
1676
+ s = r
1604
1677
  # assert isscalar(s) and isscalar(x)
1605
- return self._pow_2_3(s, x, other, op)
1678
+ return self._pow_2_3(s, x, other, op, **raiser)
1606
1679
 
1607
- def _ps_1(self, *less):
1608
- '''(INTERNAL) Yield partials, 1-primed and subtract any C{less}.
1609
- '''
1610
- yield _1_0
1611
- for p in self._ps:
1612
- yield p
1613
- for p in less:
1614
- yield -p
1615
- yield _N_1_0
1616
-
1617
- def _ps_acc(self, ps, xs, up=True):
1680
+ def _ps_acc(self, ps, xs, up=True, **unused):
1618
1681
  '''(INTERNAL) Accumulate all scalar C{xs} into C{ps}.
1619
1682
  '''
1620
1683
  n = 0
@@ -1639,7 +1702,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1639
1702
 
1640
1703
  def _ps_mul(self, op, *factors):
1641
1704
  '''(INTERNAL) Multiply this instance' C{partials} with
1642
- each of the B{C{factors}}, all known to be scalar.
1705
+ each of the scalar B{C{factors}} and accumulate.
1643
1706
  '''
1644
1707
  def _pfs(ps, fs):
1645
1708
  if len(ps) < len(fs):
@@ -1659,11 +1722,32 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1659
1722
  for p in self._ps:
1660
1723
  yield -p
1661
1724
 
1662
- def _raiser(self, r, s):
1663
- '''(INTERNAL) Does ratio C{r / s} exceed threshold?
1725
+ def _ps_1primed(self, *less):
1726
+ '''(INTERNAL) Yield partials, 1-primed and subtract any C{less} scalars.
1727
+ '''
1728
+ yield _1_0
1729
+ for p in self._ps:
1730
+ yield p
1731
+ for p in less:
1732
+ yield -p
1733
+ yield _N_1_0
1734
+
1735
+ def _raiser(self, r, s, raiser=True, **RESIDUAL):
1736
+ '''(INTERNAL) Does ratio C{r / s} exceed the RESIDUAL threshold?
1664
1737
  '''
1665
- self._ratio = t = fabs((r / s) if s else r)
1666
- return t > self._RESIDUAL
1738
+ self._ratio = r = (r / s) if s else s # == 0.
1739
+ if r and raiser:
1740
+ R = self._RESIDUAL
1741
+ 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)
1667
1751
 
1668
1752
  @property_RO
1669
1753
  def real(self):
@@ -1697,7 +1781,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1697
1781
  C{PYGEODESY_FSUM_RESIDUAL} or if omitted, keep the
1698
1782
  current setting.
1699
1783
 
1700
- @return: The previous C{RESIDUAL} setting (C{float}), default C{0}.
1784
+ @return: The previous C{RESIDUAL} setting (C{float}), default C{0.0}.
1701
1785
 
1702
1786
  @raise ValueError: Negative B{C{threshold}}.
1703
1787
 
@@ -1724,12 +1808,27 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1724
1808
  t = t.replace(_COMMASPACE_R_, _exceeds_R_)
1725
1809
  return self._Error(op, other, ResidualError, txt=t)
1726
1810
 
1727
- @property_RO
1728
- def _2scalar(self):
1729
- '''(INTERNAL) Get this instance as C{scalar} or C{as-is}.
1811
+ def root(self, root, **raiser):
1812
+ '''Return C{B{self}**(1 / B{root})} as L{Fsum}.
1813
+
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}.
1817
+
1818
+ @return: The C{self ** (1 / B{root})} result (L{Fsum}).
1819
+
1820
+ @see: Method L{Fsum.pow}.
1730
1821
  '''
1731
- s, r = self._fprs2
1732
- return self if r else s
1822
+ x = _1_over(root, **raiser)
1823
+ f = self._copy_2(self.root)
1824
+ return f._fpow(x, f.name, **raiser) # == pow(f, x)
1825
+
1826
+ def _scalar(self, other, op, **txt):
1827
+ '''(INTERNAL) Return scalar C{other}.
1828
+ '''
1829
+ if isscalar(other):
1830
+ return other
1831
+ raise self._TypeError(op, other, **txt) # _invalid_
1733
1832
 
1734
1833
  def signOf(self, res=True):
1735
1834
  '''Determine the sign of this instance.
@@ -1914,8 +2013,8 @@ def fsum_(*xs, **floats):
1914
2013
 
1915
2014
  @arg xs: Values to be added (C{scalar} or L{Fsum} instances), all
1916
2015
  positional.
1917
- @kwarg floats: Use C{B{floats}=True} iff I{all} B{C{xs}} are known
1918
- to be C{float} scalars (C{bool}).
2016
+ @kwarg floats: Use C{B{floats}=True} iff I{all} B{C{xs}} are I{known
2017
+ to be scalar} (C{bool}).
1919
2018
 
1920
2019
  @return: Precision C{fsum} (C{float}).
1921
2020
 
@@ -1926,7 +2025,8 @@ def fsum_(*xs, **floats):
1926
2025
 
1927
2026
 
1928
2027
  def fsumf_(*xs):
1929
- '''Precision floating point summation L{fsum_}C{(*xs, floats=True)}.
2028
+ '''Precision floating point summation, L{fsum_}C{(*B{xs}, floats=True)},
2029
+ but only for C{B{xs}} I{known to be scalar}.
1930
2030
  '''
1931
2031
  return _fsum(xs) if xs else _0_0
1932
2032
 
@@ -1951,8 +2051,8 @@ def fsum1_(*xs, **floats):
1951
2051
 
1952
2052
  @arg xs: Values to be added (C{scalar} or L{Fsum} instances), all
1953
2053
  positional.
1954
- @kwarg floats: Use C{B{floats}=True} iff I{all} B{C{xs}} are known
1955
- to be C{float} scalars (C{bool}).
2054
+ @kwarg floats: Use C{B{floats}=True} iff I{all} B{C{xs}} are I{known
2055
+ to be scalar} (C{bool}).
1956
2056
 
1957
2057
  @return: Precision C{fsum} (C{float}).
1958
2058
 
@@ -1963,7 +2063,8 @@ def fsum1_(*xs, **floats):
1963
2063
 
1964
2064
 
1965
2065
  def fsum1f_(*xs):
1966
- '''Precision floating point summation, L{fsum1_}C{(*xs, floats=True)}.
2066
+ '''Precision floating point summation, L{fsum1_}C{(*B{xs}, floats=True)},
2067
+ but only for C{B{xs}} I{known to be scalar}.
1967
2068
  '''
1968
2069
  return _fsum(_1primed(xs)) if xs else _0_0
1969
2070
 
@@ -1974,23 +2075,14 @@ if __name__ == '__main__':
1974
2075
 
1975
2076
  def _test(n):
1976
2077
  # copied from Hettinger, see L{Fsum} reference
1977
- from pygeodesy import printf
1978
- from random import gauss, random, shuffle
2078
+ from pygeodesy import frandoms, printf
1979
2079
 
1980
2080
  printf(_fsum.__name__, end=_COMMASPACE_)
1981
2081
  printf(_psum.__name__, end=_COMMASPACE_)
1982
2082
 
1983
2083
  F = Fsum()
1984
2084
  if F.is_math_fsum():
1985
- c = (7, 1e100, -7, -1e100, -9e-20, 8e-20) * 10
1986
- for _ in range(n):
1987
- t = list(c)
1988
- s = 0
1989
- for _ in range(n * 8):
1990
- v = gauss(0, random())**7 - s
1991
- t.append(v)
1992
- s += v
1993
- shuffle(t)
2085
+ for t in frandoms(n, seeded=True):
1994
2086
  assert float(F.fset_(*t)) == _fsum(t)
1995
2087
  printf(_DOT_, end=NN)
1996
2088
  printf(NN)