pygeodesy 24.3.24__py2.py3-none-any.whl → 24.4.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
@@ -27,11 +27,11 @@ and L{Fsum.__itruediv__}.
27
27
  from __future__ import division as _; del _ # PYCHOK semicolon
28
28
 
29
29
  from pygeodesy.basics import iscomplex, isint, isscalar, itemsorted, \
30
- signOf, _signOf, _xisscalar
31
- from pygeodesy.constants import INT0, _isfinite, isinf, isnan, _pos_self, \
30
+ signOf, _signOf
31
+ from pygeodesy.constants import INT0, _isfinite, isinf, isnan, NEG0, _pos_self, \
32
32
  _0_0, _1_0, _N_1_0, Float, Int
33
- from pygeodesy.errors import _OverflowError, _TypeError, _ValueError, _xError2, \
34
- _xkwds_get, _ZeroDivisionError
33
+ from pygeodesy.errors import _OverflowError, _TypeError, _ValueError, _xError, \
34
+ _xError2, _xkwds_get, _ZeroDivisionError
35
35
  from pygeodesy.interns import NN, _arg_, _COMMASPACE_, _DASH_, _DOT_, _EQUAL_, \
36
36
  _exceeds_, _from_, _iadd_op_, _LANGLE_, _negative_, \
37
37
  _NOTEQUAL_, _not_finite_, _not_scalar_, _PERCENT_, \
@@ -46,11 +46,12 @@ from pygeodesy.props import _allPropertiesOf_n, deprecated_property_RO, \
46
46
  from math import ceil as _ceil, fabs, floor as _floor # PYCHOK used! .ltp
47
47
 
48
48
  __all__ = _ALL_LAZY.fsums
49
- __version__ = '24.03.18'
49
+ __version__ = '24.04.02'
50
50
 
51
51
  _add_op_ = _PLUS_ # in .auxilats.auxAngle
52
52
  _eq_op_ = _EQUAL_ * 2 # _DEQUAL_
53
53
  _COMMASPACE_R_ = _COMMASPACE_ + _R_
54
+ _div_ = 'div'
54
55
  _exceeds_R_ = _SPACE_ + _exceeds_(_R_)
55
56
  _floordiv_op_ = _SLASH_ * 2 # _DSLASH_
56
57
  _fset_op_ = _EQUAL_
@@ -59,6 +60,7 @@ _gt_op_ = _RANGLE_
59
60
  _integer_ = 'integer'
60
61
  _le_op_ = _LANGLE_ + _EQUAL_
61
62
  _lt_op_ = _LANGLE_
63
+ _mod_ = 'mod'
62
64
  _mod_op_ = _PERCENT_
63
65
  _mul_op_ = _STAR_
64
66
  _ne_op_ = _NOTEQUAL_
@@ -78,63 +80,30 @@ def _2float(index=None, **name_value): # in .fmath, .fstats
78
80
  v = float(v)
79
81
  if _isfinite(v):
80
82
  return v
81
- E, t = _ValueError, _not_finite_
83
+ raise ValueError(_not_finite_)
82
84
  except Exception as e:
83
- E, t = _xError2(e)
84
- raise E(Fmt.INDEX(n, index), v, txt=t)
85
+ raise _xError(e, Fmt.INDEX(n, index), v)
85
86
 
86
87
 
87
- def _2floats(xs, origin=0, sub=False):
88
+ def _2floats(xs, origin=0, neg=False):
88
89
  '''(INTERNAL) Yield each B{C{xs}} as a C{float}.
89
90
  '''
90
- try:
91
- i, x = origin, None
92
- _fin = _isfinite
93
- _Fsum = Fsum
94
- for x in xs:
95
- if isinstance(x, _Fsum):
96
- for p in x._ps:
97
- yield (-p) if sub else p
98
- else:
99
- f = float(x)
100
- if not _fin(f):
101
- raise ValueError(_not_finite_)
102
- if f:
103
- yield (-f) if sub else f
104
- i += 1
105
- except Exception as e:
106
- E, t = _xError2(e)
107
- n = Fmt.SQUARE(xs=i)
108
- raise E(n, x, txt=t)
91
+ if neg:
92
+ def _X(X):
93
+ return X._ps_neg
109
94
 
95
+ def _x(x):
96
+ return -float(x)
97
+ else:
98
+ def _X(X): # PYCHOK re-def
99
+ return X._ps
110
100
 
111
- def _Powers(power, xs, origin=1): # in .fmath
112
- '''(INTERNAL) Yield each C{xs} as C{float(x**power)}.
113
- '''
114
- _xisscalar(power=power)
115
- try:
116
- i, x = origin, None
117
- _fin = _isfinite
118
- _Fsum = Fsum
119
- _pow = pow # XXX math.pow
120
- for x in xs:
121
- if isinstance(x, _Fsum):
122
- P = x.pow(power)
123
- for p in P._ps:
124
- yield p
125
- else:
126
- p = _pow(float(x), power)
127
- if not _fin(p):
128
- raise ValueError(_not_finite_)
129
- yield p
130
- i += 1
131
- except Exception as e:
132
- E, t = _xError2(e)
133
- n = Fmt.SQUARE(xs=i)
134
- raise E(n, x, txt=t)
101
+ _x = float
135
102
 
103
+ return _2yield(xs, origin, _X, _x)
136
104
 
137
- def _1primed(xs):
105
+
106
+ def _1primed(xs): # in .fmath
138
107
  '''(INTERNAL) 1-Prime the summation of C{xs}
139
108
  arguments I{known} to be C{finite float}.
140
109
  '''
@@ -145,17 +114,28 @@ def _1primed(xs):
145
114
  yield _N_1_0
146
115
 
147
116
 
117
+ def _2ps(s, r):
118
+ '''(INTERNAL) Return a C{s} and C{r} pair, I{ps-ordered}.
119
+ '''
120
+ if fabs(s) < fabs(r):
121
+ s, r = r, s
122
+ return ((r, s) if s else (r,)) if r else (s,)
123
+
124
+
148
125
  def _psum(ps): # PYCHOK used!
149
126
  '''(INTERNAL) Partials summation updating C{ps}, I{overridden below}.
150
127
  '''
151
- i = len(ps) - 1 # len(ps) > 2
128
+ # assert isinstance(ps, list)
129
+ i = len(ps) - 1 # len(ps) > 2
130
+ if i < 0:
131
+ return _0_0
152
132
  s = ps[i]
153
133
  _2s = _2sum
154
134
  while i > 0:
155
135
  i -= 1
156
136
  s, r = _2s(s, ps[i])
157
137
  if r: # sum(ps) became inexact
158
- ps[i:] = [s, r] if s else [r]
138
+ ps[i:] = (s, r) if s else (r,)
159
139
  if i > 0:
160
140
  p = ps[i-1] # round half-even
161
141
  if (p > 0 and r > 0) or \
@@ -165,7 +145,7 @@ def _psum(ps): # PYCHOK used!
165
145
  if r == (t - s):
166
146
  s = t
167
147
  break
168
- ps[i:] = [s]
148
+ ps[i:] = s,
169
149
  return s
170
150
 
171
151
 
@@ -221,16 +201,37 @@ def _2sum(a, b): # by .testFmath
221
201
  return s, (b - (s - a))
222
202
 
223
203
 
204
+ def _2yield(xs, i, _X_ps, _x):
205
+ '''(INTERNAL) Yield each B{C{xs}} as a C{float}.
206
+ '''
207
+ x = None
208
+ try:
209
+ _fin = _isfinite
210
+ _Fs = Fsum
211
+ for x in xs:
212
+ if isinstance(x, _Fs):
213
+ for p in _X_ps(x):
214
+ yield p
215
+ else:
216
+ f = _x(x)
217
+ if f:
218
+ if not _fin(f):
219
+ raise ValueError(_not_finite_)
220
+ yield f
221
+ i += 1
222
+ except Exception as e:
223
+ raise _xError(e, Fmt.INDEX(xs=i), x)
224
+
225
+
224
226
  class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
225
- '''Precision floating point I{running} summation.
227
+ '''Precision floating point summation and I{running} summation.
226
228
 
227
229
  Unlike Python's C{math.fsum}, this class accumulates values and provides intermediate,
228
- I{running} precision floating point summation. Accumulation may continue after
230
+ I{running}, precision floating point summations. Accumulation may continue after any
229
231
  intermediate, I{running} summuation.
230
232
 
231
- @note: Accumulated values may be L{Fsum} or C{scalar} instances with C{scalar} meaning
232
- type C{float}, C{int} or any C{type} convertible to a single C{float}, having
233
- method C{__float__}.
233
+ @note: Accumulated values may be L{Fsum} or C{scalar} instances, any C{type} having
234
+ method C{__float__} to convert the C{scalar} to a single C{float}.
234
235
 
235
236
  @note: Handling of exceptions and C{inf}, C{INF}, C{nan} and C{NAN} differs from
236
237
  Python's C{math.fsum}.
@@ -245,42 +246,40 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
245
246
  _math_fsum = None
246
247
  _n = 0
247
248
  # _ps = [] # partial sums
248
- # _px = 0
249
+ # _px = 0 # max(Fsum._px, len(Fsum._ps))
249
250
  _ratio = None
250
251
  _RESIDUAL = max(float(_getenv('PYGEODESY_FSUM_RESIDUAL', _0_0)), _0_0)
251
252
 
252
253
  def __init__(self, *xs, **name_RESIDUAL):
253
- '''New L{Fsum} for precision floating point I{running} summation.
254
+ '''New L{Fsum} for I{running} precision floating point summation.
254
255
 
255
- @arg xs: No, one or more initial values (each C{scalar} or an
256
- L{Fsum} instance).
257
- @kwarg name_RESIDUAL: Optional C{B{name}=NN} for this L{Fsum}
258
- (C{str}) and C{B{RESIDUAL}=None} for the
259
- L{ResidualError} threshold.
256
+ @arg xs: No, one or more initial values, all positional (each C{scalar}
257
+ or an L{Fsum} instance).
258
+ @kwarg name_RESIDUAL: Optional C{B{name}=NN} for this L{Fsum} and
259
+ C{B{RESIDUAL}=None} for the L{ResidualError} threshold.
260
260
 
261
261
  @see: Methods L{Fsum.fadd} and L{Fsum.RESIDUAL}.
262
262
  '''
263
263
  if name_RESIDUAL:
264
264
  n = _xkwds_get(name_RESIDUAL, name=NN)
265
- if n: # set name ...
265
+ if n: # set name before ...
266
266
  self.name = n
267
267
  r = _xkwds_get(name_RESIDUAL, RESIDUAL=None)
268
268
  if r is not None:
269
- self.RESIDUAL(r) # ... for ResidualError
269
+ self.RESIDUAL(r) # ... ResidualError
270
270
  # self._n = 0
271
271
  self._ps = [] # [_0_0], see L{Fsum._fprs}
272
272
  if len(xs) > 1:
273
273
  self._facc(_2floats(xs, origin=1), up=False) # PYCHOK yield
274
274
  elif xs: # len(xs) == 1
275
- self._ps = [_2float(x=xs[0])]
276
- self._n = 1
275
+ self._n = 1
276
+ self._ps[:] = _2float(x=xs[0]),
277
277
 
278
278
  def __abs__(self):
279
279
  '''Return this instance' absolute value as an L{Fsum}.
280
280
  '''
281
281
  s = _fsum(self._ps_1()) # == self._cmp_0(0, ...)
282
- return self._copy_n(self.__abs__) if s < 0 else \
283
- self._copy_2(self.__abs__)
282
+ return (-self) if s < 0 else self._copy_2(self.__abs__)
284
283
 
285
284
  def __add__(self, other):
286
285
  '''Return the C{Fsum(B{self}, B{other})}.
@@ -322,9 +321,9 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
322
321
  cmp = __cmp__
323
322
 
324
323
  def __divmod__(self, other):
325
- '''Return C{divmod(B{self}, B{other})} as 2-tuple C{(quotient,
326
- remainder)}, an C{int} in Python 3+ or C{float} in Python 2-
327
- and an L{Fsum}.
324
+ '''Return C{divmod(B{self}, B{other})} as a L{DivMod2Tuple}
325
+ with quotient C{div} an C{int} in Python 3+ or C{float}
326
+ in Python 2- and remainder C{mod} an L{Fsum}.
328
327
 
329
328
  @arg other: An L{Fsum} or C{scalar} modulus.
330
329
 
@@ -431,8 +430,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
431
430
 
432
431
  @see: Method L{Fsum.__divmod__}.
433
432
  '''
434
- self._fdivmod2(other, _mod_op_ + _fset_op_)
435
- return self
433
+ return self._fdivmod2(other, _mod_op_ + _fset_op_).mod
436
434
 
437
435
  def __imul__(self, other):
438
436
  '''Apply C{B{self} *= B{other}} to this instance.
@@ -566,7 +564,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
566
564
  @see: Method L{Fsum.__imod__}.
567
565
  '''
568
566
  f = self._copy_2(self.__mod__)
569
- return f._fdivmod2(other, _mod_op_)[1]
567
+ return f._fdivmod2(other, _mod_op_).mod
570
568
 
571
569
  def __mul__(self, other):
572
570
  '''Return C{B{self} * B{other}} as an L{Fsum}.
@@ -582,9 +580,10 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
582
580
  return self._cmp_0(other, _ne_op_) != 0
583
581
 
584
582
  def __neg__(self):
585
- '''Return I{a copy of} this instance, negated.
583
+ '''Return I{a copy of} this instance, I{negated}.
586
584
  '''
587
- return self._copy_n(self.__neg__)
585
+ f = self._copy_2(self.__neg__)
586
+ return f._fset(self._neg)
588
587
 
589
588
  def __pos__(self):
590
589
  '''Return this instance I{as-is}, like C{float.__pos__()}.
@@ -604,7 +603,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
604
603
 
605
604
  @see: Method L{Fsum.__iadd__}.
606
605
  '''
607
- f = self._copy_r2(other, self.__radd__)
606
+ f = self._copy_2r(other, self.__radd__)
608
607
  return f._fadd(self, _add_op_)
609
608
 
610
609
  def __rdivmod__(self, other):
@@ -613,7 +612,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
613
612
 
614
613
  @see: Method L{Fsum.__divmod__}.
615
614
  '''
616
- f = self._copy_r2(other, self.__rdivmod__)
615
+ f = self._copy_2r(other, self.__rdivmod__)
617
616
  return f._fdivmod2(self, _divmod_op_)
618
617
 
619
618
  # def __repr__(self):
@@ -626,7 +625,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
626
625
 
627
626
  @see: Method L{Fsum.__ifloordiv__}.
628
627
  '''
629
- f = self._copy_r2(other, self.__rfloordiv__)
628
+ f = self._copy_2r(other, self.__rfloordiv__)
630
629
  return f._floordiv(self, _floordiv_op_)
631
630
 
632
631
  def __rmatmul__(self, other): # PYCHOK no cover
@@ -638,15 +637,15 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
638
637
 
639
638
  @see: Method L{Fsum.__imod__}.
640
639
  '''
641
- f = self._copy_r2(other, self.__rmod__)
642
- return f._fdivmod2(self, _mod_op_)[1]
640
+ f = self._copy_2r(other, self.__rmod__)
641
+ return f._fdivmod2(self, _mod_op_).mod
643
642
 
644
643
  def __rmul__(self, other):
645
644
  '''Return C{B{other} * B{self}} as an L{Fsum}.
646
645
 
647
646
  @see: Method L{Fsum.__imul__}.
648
647
  '''
649
- f = self._copy_r2(other, self.__rmul__)
648
+ f = self._copy_2r(other, self.__rmul__)
650
649
  return f._fmul(self, _mul_op_)
651
650
 
652
651
  def __round__(self, *ndigits): # PYCHOK no cover
@@ -655,17 +654,15 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
655
654
  @arg ndigits: Optional number of digits (C{int}).
656
655
  '''
657
656
  # <https://docs.Python.org/3.12/reference/datamodel.html?#object.__round__>
658
- f = Fsum(name=self.__round__.__name__)
659
- f._n = 1
660
- f._ps = [round(float(self), *ndigits)] # can be C{int}
661
- return f
657
+ return _Fsum_ps(round(float(self), *ndigits), # can be C{int}
658
+ name=self.__round__.__name__)
662
659
 
663
660
  def __rpow__(self, other, *mod):
664
661
  '''Return C{B{other}**B{self}} as an L{Fsum}.
665
662
 
666
663
  @see: Method L{Fsum.__ipow__}.
667
664
  '''
668
- f = self._copy_r2(other, self.__rpow__)
665
+ f = self._copy_2r(other, self.__rpow__)
669
666
  return f._fpow(self, _pow_op_, *mod)
670
667
 
671
668
  def __rsub__(self, other):
@@ -673,7 +670,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
673
670
 
674
671
  @see: Method L{Fsum.__isub__}.
675
672
  '''
676
- f = self._copy_r2(other, self.__rsub__)
673
+ f = self._copy_2r(other, self.__rsub__)
677
674
  return f._fsub(self, _sub_op_)
678
675
 
679
676
  def __rtruediv__(self, other):
@@ -681,7 +678,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
681
678
 
682
679
  @see: Method L{Fsum.__itruediv__}.
683
680
  '''
684
- f = self._copy_r2(other, self.__rtruediv__)
681
+ f = self._copy_2r(other, self.__rtruediv__)
685
682
  return f._ftruediv(self, _truediv_op_)
686
683
 
687
684
  def __str__(self):
@@ -781,57 +778,28 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
781
778
  f._ps = list(self._ps) # separate list
782
779
  return f
783
780
 
784
- def _copy_0(self, *xs):
785
- '''(INTERNAL) Copy with/-out overriding C{partials}.
786
- '''
787
- # for x in xs:
788
- # assert isscalar(x)
789
- f = self._Fsum(self._n + len(xs), *xs)
790
- if self.name:
791
- f._name = self.name # .rename calls _update_attrs
792
- return f
793
-
794
- def _copy_2(self, which):
781
+ def _copy_2(self, which, name=NN):
795
782
  '''(INTERNAL) Copy for I{dyadic} operators.
796
783
  '''
784
+ n = name or which.__name__
797
785
  # NOT .classof due to .Fdot(a, *b) args, etc.
798
- f = _Named.copy(self, deep=False, name=which.__name__)
786
+ f = _Named.copy(self, deep=False, name=n)
799
787
  # assert f._n == self._n
800
788
  f._ps = list(self._ps) # separate list
801
789
  return f
802
790
 
803
- def _copy_n(self, which):
804
- '''(INTERNAL) Negated copy for I{monadic} C{__abs__} and C{__neg__}.
805
- '''
806
- if self._ps:
807
- f = self._Fsum(self._n)
808
- f._ps[:] = self._ps_n()
809
- # f._facc_up(up=False)
810
- else:
811
- f = self._Fsum(self._n, _0_0)
812
- f._name = which.__name__ # .rename calls _update_attrs
813
- return f
814
-
815
- def _copy_r2(self, other, which):
791
+ def _copy_2r(self, other, which):
816
792
  '''(INTERNAL) Copy for I{reverse-dyadic} operators.
817
793
  '''
818
794
  return other._copy_2(which) if isinstance(other, Fsum) else \
819
- Fsum(other, name=which.__name__) # see ._copy_2
795
+ Fsum(other, name=which.__name__)
820
796
 
821
- def _copy_RESIDUAL(self, other):
822
- '''(INTERNAL) Copy C{other._RESIDUAL}.
823
- '''
824
- R = other._RESIDUAL
825
- if R is not Fsum._RESIDUAL:
826
- self._RESIDUAL = R
827
-
828
- def _copy_up(self, _fprs2=False):
829
- '''(INTERNAL) Minimal, anonymous copy.
830
- '''
831
- f = self._Fsum(self._n, *self._ps)
832
- if _fprs2: # only the ._fprs2 2-tuple
833
- Fsum._fprs2._update_from(f, self)
834
- return f
797
+ # def _copy_RESIDUAL(self, other):
798
+ # '''(INTERNAL) Copy C{other._RESIDUAL}.
799
+ # '''
800
+ # R = other._RESIDUAL
801
+ # if R is not Fsum._RESIDUAL:
802
+ # self._RESIDUAL = R
835
803
 
836
804
  def divmod(self, other):
837
805
  '''Return C{divmod(B{self}, B{other})} as 2-tuple C{(quotient,
@@ -839,9 +807,9 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
839
807
 
840
808
  @arg other: An L{Fsum} or C{scalar} divisor.
841
809
 
842
- @return: 2-Tuple C{(quotient, remainder)}, with the C{quotient}
843
- an C{int} in Python 3+ or a C{float} in Python 2- and
844
- the C{remainder} an L{Fsum} instance.
810
+ @return: A L{DivMod2Tuple}C{(div, mod)}, with quotient C{div}
811
+ an C{int} in Python 3+ or C{float} in Python 2- and
812
+ remainder C{mod} an L{Fsum} instance.
845
813
 
846
814
  @see: Method L{Fsum.__itruediv__}.
847
815
  '''
@@ -872,7 +840,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
872
840
  if p:
873
841
  ps[i] = p
874
842
  i += 1
875
- ps[i:] = [x]
843
+ ps[i:] = x,
876
844
  n += 1
877
845
  # assert self._ps is ps
878
846
  if n:
@@ -887,6 +855,39 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
887
855
  '''
888
856
  return self._facc(xs, **up) if xs else self
889
857
 
858
+ def _facc_power(self, power, xs, which): # in .fmath
859
+ '''(INTERNAL) Add each C{xs} as C{float(x**power)}.
860
+ '''
861
+ if isinstance(power, Fsum):
862
+ if power.is_exact:
863
+ return self._facc_power(power._fprs, xs, which)
864
+ _Pow = Fsum._pow_any
865
+ elif isint(power, both=True) and power >= 0:
866
+ _Pow = Fsum._pow_int
867
+ power = int(power)
868
+ else:
869
+ _Pow = Fsum._pow_scalar
870
+ power = _2float(power=power)
871
+
872
+ if power:
873
+ from math import pow as _pow
874
+ op = which.__name__
875
+
876
+ def _X(X):
877
+ f = _Pow(X, power, power, op)
878
+ try: # isinstance(f, Fsum)
879
+ return f._ps
880
+ except AttributeError: # scalar
881
+ return f,
882
+
883
+ def _x(x):
884
+ return _pow(float(x), power)
885
+
886
+ self._facc(_2yield(xs, 1, _X, _x)) # PYCHOK yield
887
+ else:
888
+ self._facc_(float(len(xs))) # x**0 == 1
889
+ return self
890
+
890
891
  # def _facc_up(self, up=True):
891
892
  # '''(INTERNAL) Update the C{partials}, by removing
892
893
  # and re-accumulating the final C{partial}.
@@ -942,18 +943,18 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
942
943
  '''
943
944
  return self._facc(_2floats(xs, origin=1)) # PYCHOK yield
944
945
 
945
- def _fadd(self, other, op): # in .fmath.Fhorner
946
+ def _fadd(self, other, op, **up): # in .fmath.Fhorner
946
947
  '''(INTERNAL) Apply C{B{self} += B{other}}.
947
948
  '''
948
949
  if isinstance(other, Fsum):
949
950
  if other is self:
950
- self._facc_(*other._ps) # == ._facc(tuple(other._ps))
951
+ self._facc_(*other._ps, **up) # == ._facc(tuple(other._ps))
951
952
  elif other._ps:
952
- self._facc(other._ps)
953
+ self._facc(other._ps, **up)
953
954
  elif not isscalar(other):
954
955
  raise self._TypeError(op, other) # txt=_invalid_
955
956
  elif other:
956
- self._facc_(other)
957
+ self._facc_(other, **up)
957
958
  return self
958
959
 
959
960
  fcopy = copy # for backward compatibility
@@ -961,13 +962,12 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
961
962
  fdivmod = __divmod__ # for backward compatibility
962
963
 
963
964
  def _fdivmod2(self, other, op):
964
- '''(INTERNAL) C{divmod(B{self}, B{other})} as 2-tuple
965
- (C{int} or C{float}, remainder C{self}).
965
+ '''(INTERNAL) Apply C{B{self} %= B{other}} and return a L{DivMod2Tuple}.
966
966
  '''
967
967
  # result mostly follows CPython function U{float_divmod
968
968
  # <https://GitHub.com/python/cpython/blob/main/Objects/floatobject.c>},
969
969
  # but at least divmod(-3, 2) equals Cpython's result (-2, 1).
970
- q = self._copy_up(_fprs2=True)._ftruediv(other, op).floor
970
+ q = self._copy_2(self._fdivmod2)._ftruediv(other, op).floor
971
971
  if q: # == float // other == floor(float / other)
972
972
  self -= other * q
973
973
 
@@ -975,12 +975,10 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
975
975
  if s and self.signOf() == -s: # PYCHOK no cover
976
976
  self += other
977
977
  q -= 1
978
-
979
- # t = self.signOf()
978
+ # t = self.signOf()
980
979
  # if t and t != s:
981
- # from pygeodesy.errors import _AssertionError
982
980
  # raise self._Error(op, other, _AssertionError, txt=signOf.__name__)
983
- return q, self # q is C{int} in Python 3+, but C{float} in Python 2-
981
+ return DivMod2Tuple(q, self) # q is C{int} in Python 3+, but C{float} in Python 2-
984
982
 
985
983
  def _finite(self, other, op=None):
986
984
  '''(INTERNAL) Return B{C{other}} if C{finite}.
@@ -990,7 +988,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
990
988
  raise ValueError(_not_finite_) if not op else \
991
989
  self._ValueError(op, other, txt=_not_finite_)
992
990
 
993
- def fint(self, raiser=True, name=NN):
991
+ def fint(self, raiser=True, **name):
994
992
  '''Return this instance' current running sum as C{integer}.
995
993
 
996
994
  @kwarg raiser: If C{True} throw a L{ResidualError} if the
@@ -1007,8 +1005,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1007
1005
  if r and raiser:
1008
1006
  t = _stresidual(_integer_, r)
1009
1007
  raise ResidualError(_integer_, i, txt=t)
1010
- n = name or self.fint.__name__
1011
- return Fsum(name=n)._fset(i, asis=True)
1008
+ f = self._copy_2(self.fint, **name)
1009
+ return f._fset(i)
1012
1010
 
1013
1011
  def fint2(self, **name):
1014
1012
  '''Return this instance' current running sum as C{int} and
@@ -1026,9 +1024,9 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1026
1024
  def _fint2(self): # see ._fset
1027
1025
  '''(INTERNAL) Get 2-tuple (C{int}, I{integer} residual).
1028
1026
  '''
1029
- i = int(self._fprs) # int(self)
1030
- r = _fsum(self._ps_1(i)) if len(self._ps) > 1 else (
1031
- (self._ps[0] - i) if self._ps else -i)
1027
+ s, r = self._fprs2
1028
+ i = int(s)
1029
+ r = _fsum(self._ps_1(i)) if r else float(s - i)
1032
1030
  return i, (r or INT0)
1033
1031
 
1034
1032
  @deprecated_property_RO
@@ -1048,7 +1046,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1048
1046
  '''
1049
1047
  s, r = self._fprs2
1050
1048
  f = _floor(s) + _floor(r) + 1
1051
- while r < (f - s): # (s + r) < f
1049
+ while (f - s) > r: # f > (s + r)
1052
1050
  f -= 1
1053
1051
  return f
1054
1052
 
@@ -1058,25 +1056,25 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1058
1056
  '''Apply C{B{self} //= B{other}}.
1059
1057
  '''
1060
1058
  q = self._ftruediv(other, op) # == self
1061
- return self._fset(q.floor, asis=True) # floor(q)
1059
+ return self._fset(q.floor) # floor(q)
1062
1060
 
1063
1061
  fmul = __imul__ # for backward compatibility
1064
1062
 
1065
1063
  def _fmul(self, other, op):
1066
1064
  '''(INTERNAL) Apply C{B{self} *= B{other}}.
1067
1065
  '''
1068
- if isscalar(other):
1066
+ if isinstance(other, Fsum):
1067
+ if len(self._ps) != 1:
1068
+ f = self._mul_Fsum(other, op)
1069
+ elif len(other._ps) != 1: # and len(self._ps) == 1
1070
+ f = other._mul_scalar(self._ps[0], op)
1071
+ else: # len(other._ps) == len(self._ps) == 1
1072
+ f = self._finite(self._ps[0] * other._ps[0])
1073
+ elif isscalar(other):
1069
1074
  f = self._mul_scalar(other, op)
1070
- elif not isinstance(other, Fsum):
1075
+ else:
1071
1076
  raise self._TypeError(op, other) # txt=_invalid_
1072
- elif len(self._ps) != 1:
1073
- f = self._mul_Fsum(other, op)
1074
- elif len(other._ps) != 1: # len(self._ps) == 1
1075
- f = other._copy_up()._mul_scalar(self._ps[0], op)
1076
- else: # len(other._ps) == len(self._ps) == 1
1077
- s = self._finite(self._ps[0] * other._ps[0])
1078
- return self._fset(s, asis=True, n=len(self) + 1)
1079
- return self._fset(f)
1077
+ return self._fset(f) # n=len(self) + 1
1080
1078
 
1081
1079
  def fover(self, over):
1082
1080
  '''Apply C{B{self} /= B{over}} and summate.
@@ -1094,30 +1092,20 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1094
1092
  def _fpow(self, other, op, *mod):
1095
1093
  '''Apply C{B{self} **= B{other}}, optional B{C{mod}} or C{None}.
1096
1094
  '''
1097
- if mod and mod[0] is not None: # == 3-arg C{pow}
1098
- s = self._pow_3(other, mod[0], op)
1099
- elif mod and mod[0] is None and self.is_integer():
1100
- # return an exact C{int} for C{int}**C{int}
1101
- i = self._copy_0(self._fint2[0]) # assert _fint2[1] == 0
1102
- x = _2scalar(other) # C{int}, C{float} or other
1103
- s = i._pow_2(x, other, op) if isscalar(x) else i._fpow(x, op)
1095
+ if mod:
1096
+ if mod[0] is not None: # == 3-arg C{pow}
1097
+ f = self._pow_3(other, mod[0], op)
1098
+ elif self.is_integer():
1099
+ # return an exact C{int} for C{int}**C{int}
1100
+ x = _2scalar(other) # C{int}, C{float} or other
1101
+ i = self._fint2[0] # assert _fint2[1] == 0
1102
+ f = self._pow_2(i, x, other, op) if isscalar(x) else \
1103
+ _Fsum_ps(i)._pow_any(x, other, op)
1104
+ else: # mod[0] is None, power(self, other)
1105
+ f = self._pow_any(other, other, op)
1104
1106
  else: # pow(self, other) == pow(self, other, None)
1105
- p = None
1106
- if isinstance(other, Fsum):
1107
- x, r = other._fprs2
1108
- if r:
1109
- if self._raiser(r, x):
1110
- raise self._ResidualError(op, other, r)
1111
- p = self._pow_scalar(r, other, op)
1112
- # p = _2scalar(p) # _raiser = None
1113
- elif not isscalar(other):
1114
- raise self._TypeError(op, other) # txt=_invalid_
1115
- else:
1116
- x = self._finite(other, op)
1117
- s = self._pow_scalar(x, other, op)
1118
- if p is not None:
1119
- s *= p
1120
- return self._fset(s, asis=isint(s), n=max(len(self), 1))
1107
+ f = self._pow_any(other, other, op)
1108
+ return self._fset(f, asis=isint(f)) # n=max(len(self), 1)
1121
1109
 
1122
1110
  @Property_RO
1123
1111
  def _fprs(self):
@@ -1127,30 +1115,28 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1127
1115
  @note: The precision running C{fsum} after a C{//=} or
1128
1116
  C{//} C{floor} division is C{int} in Python 3+.
1129
1117
  '''
1130
- ps = self._ps
1131
- n = len(ps) - 1
1132
- if n > 1:
1133
- s = _psum(ps)
1134
- elif n > 0: # len(ps) == 2
1135
- s, p = _2sum(*ps) if ps[1] else ps
1136
- ps[:] = ([p, s] if s else [p]) if p else [s]
1137
- elif n < 0: # see L{Fsum.__init__}
1138
- s = _0_0
1139
- ps[:] = [s]
1140
- else: # len(ps) == 1
1141
- s = ps[0]
1142
- # assert self._ps is ps
1143
- # assert Fsum._fprs2.name not in self.__dict__
1144
- return s
1118
+ return self._fprs2.fsum
1145
1119
 
1146
1120
  @Property_RO
1147
1121
  def _fprs2(self):
1148
1122
  '''(INTERNAL) Get and cache this instance' precision
1149
1123
  running sum and residual (L{Fsum2Tuple}).
1150
1124
  '''
1151
- s = self._fprs
1152
- r = _fsum(self._ps_1(s)) if len(self._ps) > 1 else INT0
1153
- return Fsum2Tuple(s, r) # name=Fsum.fsum2.__name__
1125
+ ps = self._ps
1126
+ n = len(ps)
1127
+ if n > 2: # len(ps) > 2
1128
+ s = _psum(ps)
1129
+ r = _fsum(self._ps_1(s)) or INT0
1130
+ elif n > 1: # len(ps) == 2
1131
+ ps[:] = _2ps(*_2sum(*ps))
1132
+ r, s = (INT0, ps[0]) if len(ps) != 2 else ps
1133
+ elif ps: # len(ps) == 1
1134
+ s, r = ps[0], INT0
1135
+ else: # len(ps) == 0
1136
+ s, r = _0_0, INT0
1137
+ ps[:] = s,
1138
+ # assert self._ps is ps
1139
+ return Fsum2Tuple(s, r)
1154
1140
 
1155
1141
  # def _fpsqz(self):
1156
1142
  # '''(INTERNAL) Compress, squeeze the C{partials}.
@@ -1159,15 +1145,15 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1159
1145
  # _ = self._fprs
1160
1146
  # return self
1161
1147
 
1162
- def _fset(self, other, asis=False, n=1):
1148
+ def _fset(self, other, asis=True, n=0):
1163
1149
  '''(INTERNAL) Overwrite this instance with an other or a C{scalar}.
1164
1150
  '''
1165
1151
  if other is self:
1166
1152
  pass # from ._fmul, ._ftruediv and ._pow_scalar
1167
1153
  elif isinstance(other, Fsum):
1168
- self._n = other._n
1154
+ self._n = n or other._n
1169
1155
  self._ps[:] = other._ps
1170
- self._copy_RESIDUAL(other)
1156
+ # self._copy_RESIDUAL(other)
1171
1157
  # use or zap the C{Property_RO} values
1172
1158
  Fsum._fint2._update_from(self, other)
1173
1159
  Fsum._fprs ._update_from(self, other)
@@ -1176,65 +1162,65 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1176
1162
  s = other if asis else float(other)
1177
1163
  i = int(s) # see ._fint2
1178
1164
  t = i, ((s - i) or INT0)
1179
- self._n = n
1180
- self._ps[:] = [s]
1181
- # Property_RO _fint2, _fprs and _fprs2 can't be a Property:
1165
+ self._n = n or 1
1166
+ self._ps[:] = s,
1167
+ # Property_ROs _fint2, _fprs and _fprs2 can't be a Property:
1182
1168
  # Property's _fset zaps the value just set by the @setter
1183
1169
  self.__dict__.update(_fint2=t, _fprs=s, _fprs2=Fsum2Tuple(s, INT0))
1184
1170
  else: # PYCHOK no cover
1185
1171
  raise self._TypeError(_fset_op_, other) # txt=_invalid_
1186
1172
  return self
1187
1173
 
1174
+ def _fset_ps(self, other, n=0): # in .fmath
1175
+ '''(INTERNAL) Set a known C{Fsum} or C{scalar}.
1176
+ '''
1177
+ if isinstance(other, Fsum):
1178
+ self._n = n or other._n
1179
+ self._ps[:] = other._ps
1180
+ else: # assert isscalar(other)
1181
+ self._n = n or 1
1182
+ self._ps[:] = other,
1183
+
1188
1184
  def fsub(self, xs=()):
1189
- '''Subtract an iterable of C{scalar} or L{Fsum} instances
1190
- from this instance.
1185
+ '''Subtract an iterable of C{scalar} or L{Fsum} instances from
1186
+ this instance.
1191
1187
 
1192
- @arg xs: Iterable, list, tuple. etc. (C{scalar}
1193
- or L{Fsum} instances).
1188
+ @arg xs: Iterable, list, tuple. etc. (C{scalar} or L{Fsum}
1189
+ instances).
1194
1190
 
1195
1191
  @return: This instance, updated (L{Fsum}).
1196
1192
 
1197
1193
  @see: Method L{Fsum.fadd}.
1198
1194
  '''
1199
- return self._facc(_2floats(xs, sub=True)) if xs else self # PYCHOK yield
1195
+ return self._facc(_2floats(xs, neg=True)) if xs else self # PYCHOK yield
1200
1196
 
1201
1197
  def fsub_(self, *xs):
1202
- '''Subtract all positional C{scalar} or L{Fsum} instances
1203
- from this instance.
1198
+ '''Subtract all positional C{scalar} or L{Fsum} instances from
1199
+ this instance.
1204
1200
 
1205
- @arg xs: Values to subtract (C{scalar} or
1206
- L{Fsum} instances), all positional.
1201
+ @arg xs: Values to subtract (C{scalar} or L{Fsum} instances),
1202
+ all positional.
1207
1203
 
1208
1204
  @return: This instance, updated (L{Fsum}).
1209
1205
 
1210
1206
  @see: Method L{Fsum.fadd}.
1211
1207
  '''
1212
- return self._facc(_2floats(xs, origin=1, sub=True)) if xs else self # PYCHOK yield
1208
+ return self._facc(_2floats(xs, origin=1, neg=True)) if xs else self # PYCHOK yield
1213
1209
 
1214
1210
  def _fsub(self, other, op):
1215
1211
  '''(INTERNAL) Apply C{B{self} -= B{other}}.
1216
1212
  '''
1217
1213
  if isinstance(other, Fsum):
1218
1214
  if other is self: # or other._fprs2 == self._fprs2:
1219
- self._fset(_0_0, asis=True, n=len(self) * 2) # self -= self
1215
+ self._fset(_0_0) # n=len(self) * 2, self -= self
1220
1216
  elif other._ps:
1221
- self._facc(other._ps_n())
1217
+ self._facc(other._ps_neg)
1222
1218
  elif not isscalar(other):
1223
1219
  raise self._TypeError(op, other) # txt=_invalid_
1224
1220
  elif self._finite(other, op):
1225
1221
  self._facc_(-other)
1226
1222
  return self
1227
1223
 
1228
- def _Fsum(self, n, *ps):
1229
- '''(INTERNAL) New L{Fsum} instance.
1230
- '''
1231
- f = Fsum()
1232
- f._n = n
1233
- if ps:
1234
- f._ps[:] = ps
1235
- f._copy_RESIDUAL(self)
1236
- return f
1237
-
1238
1224
  def fsum(self, xs=()):
1239
1225
  '''Add more C{scalar} or L{Fsum} instances and summate.
1240
1226
 
@@ -1267,8 +1253,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1267
1253
  '''Add more C{scalar} or L{Fsum} instances and return the
1268
1254
  current precision running sum and the C{residual}.
1269
1255
 
1270
- @kwarg xs: Iterable, list, tuple, etc. (C{scalar} or
1271
- L{Fsum} instances).
1256
+ @kwarg xs: Iterable, list, tuple, etc. (C{scalar} or L{Fsum}
1257
+ instances).
1272
1258
  @kwarg name: Optional name (C{str}).
1273
1259
 
1274
1260
  @return: L{Fsum2Tuple}C{(fsum, residual)} with C{fsum} the
@@ -1282,7 +1268,9 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1282
1268
  f = self._facc(_2floats(xs)) if xs else self # PYCHOK yield
1283
1269
  t = f._fprs2
1284
1270
  if name:
1285
- t = t.dup(name=_xkwds_get(name, name=NN))
1271
+ n = _xkwds_get(name, name=NN)
1272
+ if n:
1273
+ t = t.dup(name=n)
1286
1274
  return t
1287
1275
 
1288
1276
  def fsum2_(self, *xs):
@@ -1319,7 +1307,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1319
1307
  n = _1_0
1320
1308
  if isinstance(other, Fsum):
1321
1309
  if other is self or other._fprs2 == self._fprs2:
1322
- return self._fset(_1_0, asis=True, n=len(self))
1310
+ return self._fset(_1_0) # n=len(self)
1323
1311
  d, r = other._fprs2
1324
1312
  if r:
1325
1313
  if not d: # PYCHOK no cover
@@ -1339,7 +1327,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1339
1327
  E, t = _xError2(x)
1340
1328
  raise self._Error(op, other, E, txt=t)
1341
1329
  f = self._mul_scalar(s, _mul_op_) # handles 0, NAN, etc.
1342
- return self._fset(f)
1330
+ return self._fset(f, asis=False)
1343
1331
 
1344
1332
  @property_RO
1345
1333
  def imag(self):
@@ -1387,9 +1375,9 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1387
1375
  return False if r else True
1388
1376
 
1389
1377
  def is_math_fsum(self):
1390
- '''Return whether functions L{fsum}, L{fsum_}, L{fsum1}
1391
- and L{fsum1_} plus partials summation are based on
1392
- Python's C{math.fsum} or not.
1378
+ '''Return whether functions L{fsum}, L{fsum_}, L{fsum1} and
1379
+ L{fsum1_} plus partials summation are based on Python's
1380
+ C{math.fsum} or not.
1393
1381
 
1394
1382
  @return: C{2} if all functions and partials summation
1395
1383
  are based on C{math.fsum}, C{True} if only
@@ -1400,24 +1388,34 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1400
1388
  f = Fsum._math_fsum
1401
1389
  return 2 if _psum is f else bool(f)
1402
1390
 
1403
- def _mul_Fsum(self, other, op=_mul_op_):
1404
- '''(INTERNAL) Return C{B{self} * Fsum B{other}} as L{Fsum}.
1391
+ def _mul_Fsum(self, other, op=_mul_op_): # in .fmath.Fhorner
1392
+ '''(INTERNAL) Return C{B{self} * Fsum B{other}} as L{Fsum} or C{0}.
1405
1393
  '''
1406
1394
  # assert isinstance(other, Fsum)
1407
- return self._copy_0()._facc(self._ps_x(op, *other._ps), up=False)
1395
+ if self._ps and other._ps:
1396
+ f = _Fsum_xs(self._ps_mul(op, *other._ps))
1397
+ else:
1398
+ f = _0_0
1399
+ return f
1408
1400
 
1409
- def _mul_scalar(self, factor, op):
1410
- '''(INTERNAL) Return C{B{self} * scalar B{factor}} as L{Fsum} or C{0}.
1401
+ def _mul_scalar(self, factor, op): # in .fmath.Fhorner
1402
+ '''(INTERNAL) Return C{B{self} * scalar B{factor}} as L{Fsum}, C{0} or C{self}.
1411
1403
  '''
1412
1404
  # assert isscalar(factor)
1413
- if self._finite(factor, op) and self._ps:
1414
- if factor == _1_0:
1415
- return self
1416
- f = self._copy_0()._facc(self._ps_x(op, factor), up=False)
1405
+ if self._ps and self._finite(factor, op):
1406
+ f = self if factor == _1_0 else (
1407
+ self._neg if factor == _N_1_0 else
1408
+ _Fsum_xs(self._ps_mul(op, factor))) # PYCHOK indent
1417
1409
  else:
1418
- f = self._copy_0(_0_0)
1410
+ f = _0_0
1419
1411
  return f
1420
1412
 
1413
+ @property_RO
1414
+ def _neg(self):
1415
+ '''(INTERNAL) Return C{-self}.
1416
+ '''
1417
+ return _Fsum_ps(*self._ps_neg) if self._ps else NEG0
1418
+
1421
1419
  @property_RO
1422
1420
  def partials(self):
1423
1421
  '''Get this instance' current partial sums (C{tuple} of C{float}s and/or C{int}s).
@@ -1436,27 +1434,22 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1436
1434
 
1437
1435
  @note: If B{C{mod}} is given as C{None}, the result will be an
1438
1436
  C{integer} L{Fsum} provided this instance C{is_integer}
1439
- or set C{integer} with L{Fsum.fint}.
1437
+ or set to C{integer} with L{Fsum.fint}.
1440
1438
 
1441
1439
  @see: Methods L{Fsum.__ipow__}, L{Fsum.fint} and L{Fsum.is_integer}.
1442
1440
  '''
1443
1441
  f = self._copy_2(self.pow)
1444
- if f and isint(x) and x >= 0 and not mod:
1445
- f._pow_int(x, x, _pow_op_) # f **= x
1446
- else:
1447
- f._fpow(x, _pow_op_, *mod) # f = pow(f, x, *mod)
1448
- return f
1442
+ return f._fpow(x, _pow_op_, *mod) # f = pow(f, x, *mod)
1449
1443
 
1450
1444
  def _pow_0_1(self, x, other):
1451
1445
  '''(INTERNAL) Return B{C{self}**1} or C{B{self}**0 == 1.0}.
1452
1446
  '''
1453
1447
  return self if x else (1 if self.is_integer() and isint(other) else _1_0)
1454
1448
 
1455
- def _pow_2(self, x, other, op):
1456
- '''(INTERNAL) 2-arg C{pow(B{self}, scalar B{x})} embellishing errors.
1449
+ def _pow_2(self, b, x, other, op):
1450
+ '''(INTERNAL) 2-arg C{pow(B{b}, scalar B{x})} embellishing errors.
1457
1451
  '''
1458
- # assert len(self._ps) == 1 and isscalar(x)
1459
- b = self._ps[0] # assert isscalar(b)
1452
+ # assert isscalar(b) and isscalar(x)
1460
1453
  try: # type(s) == type(x) if x in (_1_0, 1)
1461
1454
  s = pow(b, x) # -1**2.3 == -(1**2.3)
1462
1455
  if not iscomplex(s):
@@ -1487,35 +1480,58 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1487
1480
  t = _COMMASPACE_(Fmt.PARENSPACED(mod=mod), t)
1488
1481
  raise self._Error(op, other, E, txt=t)
1489
1482
 
1483
+ def _pow_any(self, other, unused, op):
1484
+ '''Return C{B{self} ** B{other}}.
1485
+ '''
1486
+ if isinstance(other, Fsum):
1487
+ x, r = other._fprs2
1488
+ f = self._pow_scalar(x, other, op)
1489
+ if r:
1490
+ if self._raiser(r, x):
1491
+ raise self._ResidualError(op, other, r)
1492
+ s = self._pow_scalar(r, other, op)
1493
+ # s = _2scalar(s) # _raiser = None
1494
+ f *= s
1495
+ elif isscalar(other):
1496
+ x = self._finite(other, op)
1497
+ f = self._pow_scalar(x, other, op)
1498
+ else:
1499
+ raise self._TypeError(op, other) # txt=_invalid_
1500
+ return f
1501
+
1490
1502
  def _pow_int(self, x, other, op):
1491
1503
  '''(INTERNAL) Return C{B{self} **= B{x}} for C{int B{x} >= 0}.
1492
1504
  '''
1493
1505
  # assert isint(x) and x >= 0
1494
- if len(self._ps) > 1:
1495
- if x > 2:
1496
- p = self._copy_up()
1506
+ ps = self._ps
1507
+ if len(ps) > 1:
1508
+ f = self
1509
+ if x > 4:
1497
1510
  m = 1 # single-bit mask
1498
- if x & m:
1511
+ if (x & m):
1499
1512
  x -= m # x ^= m
1500
- f = p._copy_up()
1501
1513
  else:
1502
- f = self._copy_0(_1_0)
1514
+ f = _Fsum_ps(_1_0)
1515
+ p = self
1503
1516
  while x:
1504
1517
  p = p._mul_Fsum(p, op) # p **= 2
1505
1518
  m += m # m <<= 1
1506
- if x & m:
1519
+ if (x & m):
1507
1520
  x -= m # x ^= m
1508
1521
  f = f._mul_Fsum(p, op) # f *= p
1509
- elif x > 1: # self**2
1510
- f = self._mul_Fsum(self, op)
1511
- else: # self**1 or self**0
1512
- f = self._pow_0_1(x, other)
1513
- elif self._ps: # self._ps[0]**x
1514
- f = self._pow_2(x, other, op)
1522
+ elif x > 1: # self**2, 3 or 4
1523
+ f = f._mul_Fsum(f, op)
1524
+ if x > 2: # self**3 or 4
1525
+ p = self if x < 4 else f
1526
+ f = f._mul_Fsum(p, op)
1527
+ else: # self**1 or self**0 == 1 or _1_0
1528
+ f = f._pow_0_1(x, other)
1529
+ elif ps: # self._ps[0]**x
1530
+ f = self._pow_2(ps[0], x, other, op)
1515
1531
  else: # PYCHOK no cover
1516
1532
  # 0**pos_int == 0, but 0**0 == 1
1517
1533
  f = 0 if x else 1 # like ._fprs
1518
- return self._fset(f, asis=isint(f), n=len(self))
1534
+ return f
1519
1535
 
1520
1536
  def _pow_scalar(self, x, other, op):
1521
1537
  '''(INTERNAL) Return C{self**B{x}} for C{scalar B{x}}.
@@ -1526,26 +1542,21 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1526
1542
  y = abs(x)
1527
1543
  if y > 1:
1528
1544
  if r:
1529
- f = self._copy_up()._pow_int(y, other, op)
1545
+ f = self._pow_int(y, other, op)
1530
1546
  if x > 0: # > 1
1531
1547
  return f
1532
1548
  # assert x < 0 # < -1
1533
- s, r = f._fprs2
1549
+ s, r = f._fprs2 if isinstance(f, Fsum) else (f, 0)
1534
1550
  if r:
1535
- return self._copy_0(_1_0)._ftruediv(f, op)
1551
+ return _Fsum_ps(_1_0)._ftruediv(f, op)
1536
1552
  # use **= -1 for the CPython float_pow
1537
1553
  # error if s is zero, and not s = 1 / s
1538
1554
  x = -1
1539
- # elif y > 1: # self**2 or self**-2
1540
- # f = self._mul_Fsum(self, op)
1541
- # if x < 0:
1542
- # f = f._copy_0(_1_0)._ftruediv(f, op)
1543
- # return f
1544
1555
  elif x < 0: # self**-1 == 1 / self
1545
1556
  if r:
1546
- return self._copy_0(_1_0)._ftruediv(self, op)
1557
+ return _Fsum_ps(_1_0)._ftruediv(self, op)
1547
1558
  else: # self**1 or self**0
1548
- return self._pow_0_1(x, other) # self or 0.0
1559
+ return self._pow_0_1(x, other) # self, 1 or 1.0
1549
1560
  elif not isscalar(x): # assert ...
1550
1561
  raise self._TypeError(op, other, txt=_not_scalar_)
1551
1562
  elif r and self._raiser(r, s): # non-zero residual**fractional
@@ -1553,7 +1564,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1553
1564
  t = _stresidual(_non_zero_, r, fractional_power=x)
1554
1565
  raise self._Error(op, other, ResidualError, txt=t)
1555
1566
  # assert isscalar(s) and isscalar(x)
1556
- return self._copy_0(s)._pow_2(x, other, op)
1567
+ return self._pow_2(s, x, other, op)
1557
1568
 
1558
1569
  def _ps_1(self, *less):
1559
1570
  '''(INTERNAL) Yield partials, 1-primed and subtract any C{less}.
@@ -1567,28 +1578,25 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1567
1578
  yield -p
1568
1579
  yield _N_1_0
1569
1580
 
1570
- def _ps_n(self):
1571
- '''(INTERNAL) Yield partials, negated.
1572
- '''
1573
- for p in self._ps:
1574
- if p:
1575
- yield -p
1576
-
1577
- def _ps_x(self, op, *factors): # see .fmath.Fhorner
1581
+ def _ps_mul(self, op, *factors): # see .fmath.Fhorner
1578
1582
  '''(INTERNAL) Yield all C{partials} times each B{C{factor}},
1579
1583
  in total, up to C{len(partials) * len(factors)} items.
1580
1584
  '''
1581
- ps = self._ps
1585
+ ps = self._ps # tuple(self._ps)
1582
1586
  if len(ps) < len(factors):
1583
1587
  ps, factors = factors, ps
1584
1588
  _f = _isfinite
1585
1589
  for f in factors:
1586
1590
  for p in ps:
1587
1591
  p *= f
1588
- if _f(p):
1589
- yield p
1590
- else: # PYCHOK no cover
1591
- self._finite(p, op) # throw ValueError
1592
+ yield p if _f(p) else self._finite(p, op)
1593
+
1594
+ @property_RO
1595
+ def _ps_neg(self):
1596
+ '''(INTERNAL) Yield the partials, I{negated}.
1597
+ '''
1598
+ for p in self._ps:
1599
+ yield -p
1592
1600
 
1593
1601
  @property_RO
1594
1602
  def real(self):
@@ -1613,7 +1621,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1613
1621
  return self._fprs2.residual
1614
1622
 
1615
1623
  def _raiser(self, r, s):
1616
- '''(INTERNAL) Does the ratio C{r / s} exceed threshold?
1624
+ '''(INTERNAL) Does ratio C{r / s} exceed threshold?
1617
1625
  '''
1618
1626
  self._ratio = t = fabs((r / s) if s else r)
1619
1627
  return t > self._RESIDUAL
@@ -1703,12 +1711,16 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1703
1711
  '''
1704
1712
  return self._Error(op, other, _TypeError, **txt)
1705
1713
 
1706
- def _update(self): # see ._fset
1714
+ def _update(self, updated=True): # see ._fset
1707
1715
  '''(INTERNAL) Zap all cached C{Property_RO} values.
1708
1716
  '''
1709
- Fsum._fint2._update(self)
1710
- Fsum._fprs ._update(self)
1711
- Fsum._fprs2._update(self)
1717
+ if updated:
1718
+ _pop = self.__dict__.pop
1719
+ for p in _ROs:
1720
+ _ = _pop(p, None)
1721
+ # Fsum._fint2._update(self)
1722
+ # Fsum._fprs ._update(self)
1723
+ # Fsum._fprs2._update(self)
1712
1724
  return self
1713
1725
 
1714
1726
  def _ValueError(self, op, other, **txt): # PYCHOK no cover
@@ -1721,7 +1733,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1721
1733
  '''
1722
1734
  return self._Error(op, other, _ZeroDivisionError, **txt)
1723
1735
 
1724
- _allPropertiesOf_n(3, Fsum, Property_RO) # PYCHOK assert, see Fsum._fset, -._update
1736
+ _ROs = _allPropertiesOf_n(3, Fsum, Property_RO) # PYCHOK assert, see Fsum._fset, -._update
1725
1737
 
1726
1738
 
1727
1739
  def _Float_Int(arg, **name_Error):
@@ -1731,6 +1743,17 @@ def _Float_Int(arg, **name_Error):
1731
1743
  return U(arg, **name_Error)
1732
1744
 
1733
1745
 
1746
+ class DivMod2Tuple(_NamedTuple):
1747
+ '''2-Tuple C{(div, mod)} with the quotient C{div} and remainder
1748
+ C{mod} results of a C{divmod} operation.
1749
+
1750
+ @note: Quotient C{div} an C{int} in Python 3+ or a C{float} in
1751
+ Python 2-. Remainder C{mod} an L{Fsum} instance.
1752
+ '''
1753
+ _Names_ = (_div_, _mod_)
1754
+ _Units_ = (_Float_Int, Fsum)
1755
+
1756
+
1734
1757
  class Fsum2Tuple(_NamedTuple):
1735
1758
  '''2-Tuple C{(fsum, residual)} with the precision running C{fsum}
1736
1759
  and the C{residual}, the sum of the remaining partials. Each
@@ -1746,8 +1769,8 @@ class Fsum2Tuple(_NamedTuple):
1746
1769
  def Fsum(self):
1747
1770
  '''Get this L{Fsum2Tuple} as an L{Fsum}.
1748
1771
  '''
1749
- f = Fsum(name=self.name)
1750
- return f._copy_0(*(s for s in reversed(self) if s))
1772
+ s, r = map(float, self)
1773
+ return _Fsum_ps(*_2ps(s, r), name=self.name)
1751
1774
 
1752
1775
  def is_exact(self):
1753
1776
  '''Is this L{Fsum2Tuple} considered to be exact? (C{bool}).
@@ -1769,6 +1792,23 @@ class ResidualError(_ValueError):
1769
1792
  pass
1770
1793
 
1771
1794
 
1795
+ def _Fsum_ps(*ps, **name):
1796
+ '''(INTERNAL) Return an C{Fsum} from I{ordered} partials C{ps}.
1797
+ '''
1798
+ f = Fsum(**name) if name else Fsum()
1799
+ if ps:
1800
+ f._n = len(ps)
1801
+ f._ps[:] = ps
1802
+ return f
1803
+
1804
+
1805
+ def _Fsum_xs(xs, up=False, **name):
1806
+ '''(INTERNAL) Return an C{Fsum} from known floats C{xs}.
1807
+ '''
1808
+ f = Fsum(**name) if name else Fsum()
1809
+ return f._facc(xs, up=up)
1810
+
1811
+
1772
1812
  try:
1773
1813
  from math import fsum as _fsum # precision IEEE-754 sum, Python 2.6+
1774
1814
 
@@ -1781,7 +1821,7 @@ try:
1781
1821
  Fsum._math_fsum = _sum = _fsum # PYCHOK exported
1782
1822
 
1783
1823
  if _getenv('PYGEODESY_FSUM_PARTIALS', _fsum.__name__) == _fsum.__name__:
1784
- _psum = _fsum # PYCHOK redef
1824
+ _psum = _fsum # PYCHOK re-def
1785
1825
 
1786
1826
  except ImportError:
1787
1827
  _sum = sum # Fsum(NAN) exception fall-back
@@ -1789,16 +1829,16 @@ except ImportError:
1789
1829
  def _fsum(xs):
1790
1830
  '''(INTERNAL) Precision summation, Python 2.5-.
1791
1831
  '''
1792
- return Fsum(name=_fsum.__name__)._facc(xs, up=False)._fprs
1832
+ return Fsum(name=_fsum.__name__).fsum(xs) if xs else _0_0
1793
1833
 
1794
1834
 
1795
1835
  def fsum(xs, floats=False):
1796
1836
  '''Precision floating point summation based on or like Python's C{math.fsum}.
1797
1837
 
1798
- @arg xs: Iterable, list, tuple, etc. of values (C{scalar} or
1799
- L{Fsum} instances).
1800
- @kwarg floats: Optionally, use C{B{floats}=True} iff I{all}
1801
- B{C{xs}} are known to be C{float}.
1838
+ @arg xs: Iterable, list, tuple, etc. of values (C{scalar} or L{Fsum}
1839
+ instances).
1840
+ @kwarg floats: Optionally, use C{B{floats}=True} iff I{all} B{C{xs}}
1841
+ are known to be C{float}.
1802
1842
 
1803
1843
  @return: Precision C{fsum} (C{float}).
1804
1844
 
@@ -1819,10 +1859,10 @@ def fsum(xs, floats=False):
1819
1859
  def fsum_(*xs, **floats):
1820
1860
  '''Precision floating point summation of all positional arguments.
1821
1861
 
1822
- @arg xs: Values to be added (C{scalar} or L{Fsum} instances),
1823
- all positional.
1824
- @kwarg floats: Optionally, use C{B{floats}=True} iff I{all}
1825
- B{C{xs}} are known to be C{float}.
1862
+ @arg xs: Values to be added (C{scalar} or L{Fsum} instances), all
1863
+ positional.
1864
+ @kwarg floats: Optionally, use C{B{floats}=True} iff I{all} B{C{xs}}
1865
+ are known to be C{float}.
1826
1866
 
1827
1867
  @return: Precision C{fsum} (C{float}).
1828
1868
 
@@ -1841,10 +1881,10 @@ def fsumf_(*xs):
1841
1881
  def fsum1(xs, floats=False):
1842
1882
  '''Precision floating point summation of a few arguments, 1-primed.
1843
1883
 
1844
- @arg xs: Iterable, list, tuple, etc. of values (C{scalar} or
1845
- L{Fsum} instances).
1846
- @kwarg floats: Optionally, use C{B{floats}=True} iff I{all}
1847
- B{C{xs}} are known to be C{float}.
1884
+ @arg xs: Iterable, list, tuple, etc. of values (C{scalar} or L{Fsum}
1885
+ instances).
1886
+ @kwarg floats: Optionally, use C{B{floats}=True} iff I{all} B{C{xs}}
1887
+ are known to be C{float}.
1848
1888
 
1849
1889
  @return: Precision C{fsum} (C{float}).
1850
1890
 
@@ -1856,10 +1896,10 @@ def fsum1(xs, floats=False):
1856
1896
  def fsum1_(*xs, **floats):
1857
1897
  '''Precision floating point summation of a few arguments, 1-primed.
1858
1898
 
1859
- @arg xs: Values to be added (C{scalar} or L{Fsum} instances),
1860
- all positional.
1861
- @kwarg floats: Optionally, use C{B{floats}=True} iff I{all}
1862
- B{C{xs}} are known to be C{float}.
1899
+ @arg xs: Values to be added (C{scalar} or L{Fsum} instances), all
1900
+ positional.
1901
+ @kwarg floats: Optionally, use C{B{floats}=True} iff I{all} B{C{xs}}
1902
+ are known to be C{float}.
1863
1903
 
1864
1904
  @return: Precision C{fsum} (C{float}).
1865
1905