pygeodesy 24.3.24__py2.py3-none-any.whl → 24.4.4__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pygeodesy/fsums.py CHANGED
@@ -14,8 +14,8 @@ L{Fsum.__rpow__} return a (very long) C{int} if invoked with optional argument
14
14
  C{mod} set to C{None}. The C{residual} of an C{integer} L{Fsum} may be between
15
15
  C{-1.0} and C{+1.0}, including C{INT0} if considered to be I{exact}.
16
16
 
17
- Set env variable C{PYGEODESY_FSUM_PARTIALS} to an empty string (or anything
18
- other than C{"fsum"}) for backward compatible summation of L{Fsum} partials.
17
+ Set env variable C{PYGEODESY_FSUM_PARTIALS} to string C{"fsum"}) for summation
18
+ of L{Fsum} partials by Python function C{math.fsum}.
19
19
 
20
20
  Set env variable C{PYGEODESY_FSUM_RESIDUAL} to a C{float} string greater
21
21
  than C{"0.0"} as the threshold to throw a L{ResidualError} in division or
@@ -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.04'
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_
82
- except Exception as e:
83
- E, t = _xError2(e)
84
- raise E(Fmt.INDEX(n, index), v, txt=t)
83
+ raise ValueError(_not_finite_)
84
+ except Exception as X:
85
+ raise _xError(X, 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,27 +114,38 @@ 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
- '''(INTERNAL) Partials summation updating C{ps}, I{overridden below}.
126
+ '''(INTERNAL) Partials sum, updating C{ps}, I{overridden below}.
150
127
  '''
151
- i = len(ps) - 1 # len(ps) > 2
152
- s = ps[i]
128
+ # assert isinstance(ps, list)
129
+ i = len(ps) - 1
130
+ s = _0_0 if i < 0 else ps[i]
153
131
  _2s = _2sum
154
132
  while i > 0:
155
133
  i -= 1
156
134
  s, r = _2s(s, ps[i])
157
135
  if r: # sum(ps) became inexact
158
- ps[i:] = [s, r] if s else [r]
159
- if i > 0:
160
- p = ps[i-1] # round half-even
161
- if (p > 0 and r > 0) or \
162
- (p < 0 and r < 0): # signs match
163
- r *= 2
164
- t = s + r
165
- if r == (t - s):
166
- s = t
167
- break
168
- ps[i:] = [s]
136
+ if s:
137
+ ps[i:] = r, s
138
+ if i > 0:
139
+ p = ps[i-1] # round half-even
140
+ if (p > 0 and r > 0) or \
141
+ (p < 0 and r < 0): # signs match
142
+ r *= 2
143
+ t = s + r
144
+ if r == (t - s):
145
+ s = t
146
+ break # return s
147
+ s = r # PYCHOK no cover
148
+ ps[i:] = s,
169
149
  return s
170
150
 
171
151
 
@@ -188,24 +168,24 @@ def _2scalar(other, _raiser=None):
188
168
 
189
169
 
190
170
  def _strcomplex(s, *args):
191
- '''(INTERNAL) C{Complex} 2- or 3-arg C{pow} error C{str}.
171
+ '''(INTERNAL) C{Complex} 2- or 3-arg C{pow} error as C{str}.
192
172
  '''
193
- c = iscomplex.__name__[2:]
173
+ c = _strcomplex.__name__[4:]
194
174
  n = _DASH_(len(args), _arg_)
195
- t = _SPACE_(c, s, _from_, n, pow.__name__)
196
- return unstr(t, *args)
175
+ t = unstr(pow, *args)
176
+ return _SPACE_(c, s, _from_, n, t)
197
177
 
198
178
 
199
179
  def _stresidual(prefix, residual, **name_values):
200
- '''(INTERNAL) Residual error C{str}.
180
+ '''(INTERNAL) Residual error as C{str}.
201
181
  '''
202
- p = _SPACE_(prefix, Fsum.residual.name)
182
+ p = _stresidual.__name__[3:]
203
183
  t = Fmt.PARENSPACED(p, Fmt(residual))
204
184
  for n, v in itemsorted(name_values):
205
185
  n = n.replace(_UNDER_, _SPACE_)
206
186
  p = Fmt.PARENSPACED(n, Fmt(v))
207
187
  t = _COMMASPACE_(t, p)
208
- return t
188
+ return _SPACE_(prefix, t)
209
189
 
210
190
 
211
191
  def _2sum(a, b): # by .testFmath
@@ -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 X:
223
+ raise _xError(X, 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):
@@ -759,14 +756,14 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
759
756
  def _cmp_0(self, other, op):
760
757
  '''(INTERNAL) Return C{scalar(self - B{other})} for 0-comparison.
761
758
  '''
762
- if isscalar(other):
759
+ if isinstance(other, Fsum):
760
+ s = _fsum(self._ps_1(*other._ps))
761
+ elif isscalar(other):
763
762
  if other:
764
763
  s = _fsum(self._ps_1(other))
765
764
  else:
766
765
  s, r = self._fprs2
767
766
  s = _signOf(s, -r)
768
- elif isinstance(other, Fsum):
769
- s = _fsum(self._ps_1(*other._ps))
770
767
  else:
771
768
  raise self._TypeError(op, other) # txt=_invalid_
772
769
  return s
@@ -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,22 +807,30 @@ 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
  '''
848
816
  f = self._copy_2(self.divmod)
849
817
  return f._fdivmod2(other, _divmod_op_)
850
818
 
851
- def _Error(self, op, other, Error, **txt):
819
+ def _Error(self, op, other, Error, **txt_cause):
852
820
  '''(INTERNAL) Format an B{C{Error}} for C{{self} B{op} B{other}}.
853
821
  '''
854
- return Error(_SPACE_(self.toRepr(), op, repr(other)), **txt)
822
+ return Error(_SPACE_(self.toRepr(), op, repr(other)), **txt_cause)
855
823
 
856
- def _ErrorX(self, X, xs, **kwds): # in .fmath
857
- '''(INTERNAL) Format a caught exception.
824
+ def _ErrorX(self, X, op, other, *mod):
825
+ '''(INTERNAL) Format the caught exception C{X}.
826
+ '''
827
+ E, t = _xError2(X)
828
+ if mod:
829
+ t = _COMMASPACE_(Fmt.PARENSPACED(mod=mod[0]), t)
830
+ return self._Error(op, other, E, txt=t, cause=X)
831
+
832
+ def _ErrorXs(self, X, xs, **kwds): # in .fmath
833
+ '''(INTERNAL) Format the caught exception C{X}.
858
834
  '''
859
835
  E, t = _xError2(X)
860
836
  n = unstr(self.named3, *xs[:3], _ELLIPSIS=len(xs) > 3, **kwds)
@@ -866,13 +842,14 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
866
842
  n, ps, _2s = 0, self._ps, _2sum
867
843
  for x in xs: # _iter()
868
844
  # assert isscalar(x) and isfinite(x)
869
- i = 0
870
- for p in ps:
871
- x, p = _2s(x, p)
872
- if p:
873
- ps[i] = p
874
- i += 1
875
- ps[i:] = [x]
845
+ if x:
846
+ i = 0
847
+ for p in ps:
848
+ x, p = _2s(x, p)
849
+ if p:
850
+ ps[i] = p
851
+ i += 1
852
+ ps[i:] = (x,) if x else ()
876
853
  n += 1
877
854
  # assert self._ps is ps
878
855
  if n:
@@ -887,6 +864,38 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
887
864
  '''
888
865
  return self._facc(xs, **up) if xs else self
889
866
 
867
+ def _facc_power(self, power, xs, which): # in .fmath
868
+ '''(INTERNAL) Add each C{xs} as C{float(x**power)}.
869
+ '''
870
+ p = power
871
+ if isinstance(p, Fsum):
872
+ if p.is_exact:
873
+ return self._facc_power(p._fprs, xs, which)
874
+ _Pow = Fsum._pow_any
875
+ elif isint(p, both=True) and p >= 0:
876
+ _Pow, p = Fsum._pow_int, int(p)
877
+ else:
878
+ _Pow, p = Fsum._pow_scalar, _2float(power=p)
879
+
880
+ if p:
881
+ from math import pow as _pow
882
+ op = which.__name__
883
+
884
+ def _X(X):
885
+ f = _Pow(X, p, power, op)
886
+ try: # isinstance(f, Fsum)
887
+ return f._ps
888
+ except AttributeError: # scalar
889
+ return f,
890
+
891
+ def _x(x):
892
+ return _pow(float(x), p)
893
+
894
+ self._facc(_2yield(xs, 1, _X, _x)) # PYCHOK yield
895
+ else:
896
+ self._facc_(float(len(xs))) # x**0 == 1
897
+ return self
898
+
890
899
  # def _facc_up(self, up=True):
891
900
  # '''(INTERNAL) Update the C{partials}, by removing
892
901
  # and re-accumulating the final C{partial}.
@@ -942,18 +951,18 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
942
951
  '''
943
952
  return self._facc(_2floats(xs, origin=1)) # PYCHOK yield
944
953
 
945
- def _fadd(self, other, op): # in .fmath.Fhorner
954
+ def _fadd(self, other, op, **up): # in .fmath.Fhorner
946
955
  '''(INTERNAL) Apply C{B{self} += B{other}}.
947
956
  '''
948
957
  if isinstance(other, Fsum):
949
958
  if other is self:
950
- self._facc_(*other._ps) # == ._facc(tuple(other._ps))
959
+ self._facc_(*other._ps, **up) # == ._facc(tuple(other._ps))
951
960
  elif other._ps:
952
- self._facc(other._ps)
961
+ self._facc(other._ps, **up)
953
962
  elif not isscalar(other):
954
963
  raise self._TypeError(op, other) # txt=_invalid_
955
964
  elif other:
956
- self._facc_(other)
965
+ self._facc_(other, **up)
957
966
  return self
958
967
 
959
968
  fcopy = copy # for backward compatibility
@@ -961,13 +970,12 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
961
970
  fdivmod = __divmod__ # for backward compatibility
962
971
 
963
972
  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}).
973
+ '''(INTERNAL) Apply C{B{self} %= B{other}} and return a L{DivMod2Tuple}.
966
974
  '''
967
975
  # result mostly follows CPython function U{float_divmod
968
976
  # <https://GitHub.com/python/cpython/blob/main/Objects/floatobject.c>},
969
977
  # but at least divmod(-3, 2) equals Cpython's result (-2, 1).
970
- q = self._copy_up(_fprs2=True)._ftruediv(other, op).floor
978
+ q = self._copy_2(self._fdivmod2)._ftruediv(other, op).floor
971
979
  if q: # == float // other == floor(float / other)
972
980
  self -= other * q
973
981
 
@@ -975,22 +983,20 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
975
983
  if s and self.signOf() == -s: # PYCHOK no cover
976
984
  self += other
977
985
  q -= 1
978
-
979
- # t = self.signOf()
986
+ # t = self.signOf()
980
987
  # if t and t != s:
981
- # from pygeodesy.errors import _AssertionError
982
988
  # 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-
989
+ return DivMod2Tuple(q, self) # q is C{int} in Python 3+, but C{float} in Python 2-
984
990
 
985
991
  def _finite(self, other, op=None):
986
992
  '''(INTERNAL) Return B{C{other}} if C{finite}.
987
993
  '''
988
994
  if _isfinite(other):
989
995
  return other
990
- raise ValueError(_not_finite_) if not op else \
996
+ raise ValueError(_not_finite_) if op is None else \
991
997
  self._ValueError(op, other, txt=_not_finite_)
992
998
 
993
- def fint(self, raiser=True, name=NN):
999
+ def fint(self, raiser=True, **name):
994
1000
  '''Return this instance' current running sum as C{integer}.
995
1001
 
996
1002
  @kwarg raiser: If C{True} throw a L{ResidualError} if the
@@ -1007,8 +1013,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1007
1013
  if r and raiser:
1008
1014
  t = _stresidual(_integer_, r)
1009
1015
  raise ResidualError(_integer_, i, txt=t)
1010
- n = name or self.fint.__name__
1011
- return Fsum(name=n)._fset(i, asis=True)
1016
+ f = self._copy_2(self.fint, **name)
1017
+ return f._fset(i)
1012
1018
 
1013
1019
  def fint2(self, **name):
1014
1020
  '''Return this instance' current running sum as C{int} and
@@ -1026,9 +1032,9 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1026
1032
  def _fint2(self): # see ._fset
1027
1033
  '''(INTERNAL) Get 2-tuple (C{int}, I{integer} residual).
1028
1034
  '''
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)
1035
+ s, r = self._fprs2
1036
+ i = int(s)
1037
+ r = _fsum(self._ps_1(i)) if r else float(s - i)
1032
1038
  return i, (r or INT0)
1033
1039
 
1034
1040
  @deprecated_property_RO
@@ -1048,7 +1054,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1048
1054
  '''
1049
1055
  s, r = self._fprs2
1050
1056
  f = _floor(s) + _floor(r) + 1
1051
- while r < (f - s): # (s + r) < f
1057
+ while (f - s) > r: # f > (s + r)
1052
1058
  f -= 1
1053
1059
  return f
1054
1060
 
@@ -1058,25 +1064,25 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1058
1064
  '''Apply C{B{self} //= B{other}}.
1059
1065
  '''
1060
1066
  q = self._ftruediv(other, op) # == self
1061
- return self._fset(q.floor, asis=True) # floor(q)
1067
+ return self._fset(q.floor) # floor(q)
1062
1068
 
1063
1069
  fmul = __imul__ # for backward compatibility
1064
1070
 
1065
1071
  def _fmul(self, other, op):
1066
1072
  '''(INTERNAL) Apply C{B{self} *= B{other}}.
1067
1073
  '''
1068
- if isscalar(other):
1074
+ if isinstance(other, Fsum):
1075
+ if len(self._ps) != 1:
1076
+ f = self._mul_Fsum(other, op)
1077
+ elif len(other._ps) != 1: # and len(self._ps) == 1
1078
+ f = other._mul_scalar(self._ps[0], op)
1079
+ else: # len(other._ps) == len(self._ps) == 1
1080
+ f = self._finite(self._ps[0] * other._ps[0])
1081
+ elif isscalar(other):
1069
1082
  f = self._mul_scalar(other, op)
1070
- elif not isinstance(other, Fsum):
1083
+ else:
1071
1084
  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)
1085
+ return self._fset(f) # n=len(self) + 1
1080
1086
 
1081
1087
  def fover(self, over):
1082
1088
  '''Apply C{B{self} /= B{over}} and summate.
@@ -1094,30 +1100,20 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1094
1100
  def _fpow(self, other, op, *mod):
1095
1101
  '''Apply C{B{self} **= B{other}}, optional B{C{mod}} or C{None}.
1096
1102
  '''
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)
1103
+ if mod:
1104
+ if mod[0] is not None: # == 3-arg C{pow}
1105
+ f = self._pow_3(other, mod[0], op)
1106
+ elif self.is_integer():
1107
+ # return an exact C{int} for C{int}**C{int}
1108
+ x = _2scalar(other) # C{int}, C{float} or other
1109
+ i = self._fint2[0] # assert _fint2[1] == 0
1110
+ f = self._pow_2(i, x, other, op) if isscalar(x) else \
1111
+ _Fsum_ps(i)._pow_any(x, other, op)
1112
+ else: # mod[0] is None, power(self, other)
1113
+ f = self._pow_any(other, other, op)
1104
1114
  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))
1115
+ f = self._pow_any(other, other, op)
1116
+ return self._fset(f, asis=isint(f)) # n=max(len(self), 1)
1121
1117
 
1122
1118
  @Property_RO
1123
1119
  def _fprs(self):
@@ -1127,30 +1123,28 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1127
1123
  @note: The precision running C{fsum} after a C{//=} or
1128
1124
  C{//} C{floor} division is C{int} in Python 3+.
1129
1125
  '''
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
1126
+ return self._fprs2.fsum
1145
1127
 
1146
1128
  @Property_RO
1147
1129
  def _fprs2(self):
1148
1130
  '''(INTERNAL) Get and cache this instance' precision
1149
1131
  running sum and residual (L{Fsum2Tuple}).
1150
1132
  '''
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__
1133
+ ps = self._ps
1134
+ n = len(ps) - 2
1135
+ if n > 0: # len(ps) > 2
1136
+ s = _psum(ps)
1137
+ r = _fsum(self._ps_1(s)) or INT0
1138
+ elif n == 0: # len(ps) == 2
1139
+ ps[:] = _2ps(*_2sum(*ps))
1140
+ r, s = (INT0, ps[0]) if len(ps) != 2 else ps
1141
+ elif ps: # len(ps) == 1
1142
+ s, r = ps[0], INT0
1143
+ else: # len(ps) == 0
1144
+ s, r = _0_0, INT0
1145
+ ps[:] = s,
1146
+ # assert self._ps is ps
1147
+ return Fsum2Tuple(s, r)
1154
1148
 
1155
1149
  # def _fpsqz(self):
1156
1150
  # '''(INTERNAL) Compress, squeeze the C{partials}.
@@ -1159,15 +1153,29 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1159
1153
  # _ = self._fprs
1160
1154
  # return self
1161
1155
 
1162
- def _fset(self, other, asis=False, n=1):
1156
+ def fset_(self, *xs):
1157
+ '''Replace this instance' value with C{xs}.
1158
+
1159
+ @arg xs: Optional, new values (C{scalar} or L{Fsum}
1160
+ instances), all positional.
1161
+
1162
+ @return: This instance (C{Fsum}).
1163
+
1164
+ @see: Method L{Fsum.fadd} for further details.
1165
+ '''
1166
+ self._n = 0
1167
+ self._ps[:] = 0,
1168
+ return self.fadd(xs) if xs else self._update()
1169
+
1170
+ def _fset(self, other, asis=True, n=0):
1163
1171
  '''(INTERNAL) Overwrite this instance with an other or a C{scalar}.
1164
1172
  '''
1165
1173
  if other is self:
1166
1174
  pass # from ._fmul, ._ftruediv and ._pow_scalar
1167
1175
  elif isinstance(other, Fsum):
1168
- self._n = other._n
1176
+ self._n = n or other._n
1169
1177
  self._ps[:] = other._ps
1170
- self._copy_RESIDUAL(other)
1178
+ # self._copy_RESIDUAL(other)
1171
1179
  # use or zap the C{Property_RO} values
1172
1180
  Fsum._fint2._update_from(self, other)
1173
1181
  Fsum._fprs ._update_from(self, other)
@@ -1176,65 +1184,65 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1176
1184
  s = other if asis else float(other)
1177
1185
  i = int(s) # see ._fint2
1178
1186
  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:
1187
+ self._n = n or 1
1188
+ self._ps[:] = s,
1189
+ # Property_ROs _fint2, _fprs and _fprs2 can't be a Property:
1182
1190
  # Property's _fset zaps the value just set by the @setter
1183
1191
  self.__dict__.update(_fint2=t, _fprs=s, _fprs2=Fsum2Tuple(s, INT0))
1184
1192
  else: # PYCHOK no cover
1185
1193
  raise self._TypeError(_fset_op_, other) # txt=_invalid_
1186
1194
  return self
1187
1195
 
1196
+ def _fset_ps(self, other, n=0): # in .fmath
1197
+ '''(INTERNAL) Set a known C{Fsum} or C{scalar}.
1198
+ '''
1199
+ if isinstance(other, Fsum):
1200
+ self._n = n or other._n
1201
+ self._ps[:] = other._ps
1202
+ else: # assert isscalar(other)
1203
+ self._n = n or 1
1204
+ self._ps[:] = other,
1205
+
1188
1206
  def fsub(self, xs=()):
1189
- '''Subtract an iterable of C{scalar} or L{Fsum} instances
1190
- from this instance.
1207
+ '''Subtract an iterable of C{scalar} or L{Fsum} instances from
1208
+ this instance.
1191
1209
 
1192
- @arg xs: Iterable, list, tuple. etc. (C{scalar}
1193
- or L{Fsum} instances).
1210
+ @arg xs: Iterable, list, tuple. etc. (C{scalar} or L{Fsum}
1211
+ instances).
1194
1212
 
1195
1213
  @return: This instance, updated (L{Fsum}).
1196
1214
 
1197
1215
  @see: Method L{Fsum.fadd}.
1198
1216
  '''
1199
- return self._facc(_2floats(xs, sub=True)) if xs else self # PYCHOK yield
1217
+ return self._facc(_2floats(xs, neg=True)) if xs else self # PYCHOK yield
1200
1218
 
1201
1219
  def fsub_(self, *xs):
1202
- '''Subtract all positional C{scalar} or L{Fsum} instances
1203
- from this instance.
1220
+ '''Subtract all positional C{scalar} or L{Fsum} instances from
1221
+ this instance.
1204
1222
 
1205
- @arg xs: Values to subtract (C{scalar} or
1206
- L{Fsum} instances), all positional.
1223
+ @arg xs: Values to subtract (C{scalar} or L{Fsum} instances),
1224
+ all positional.
1207
1225
 
1208
1226
  @return: This instance, updated (L{Fsum}).
1209
1227
 
1210
1228
  @see: Method L{Fsum.fadd}.
1211
1229
  '''
1212
- return self._facc(_2floats(xs, origin=1, sub=True)) if xs else self # PYCHOK yield
1230
+ return self._facc(_2floats(xs, origin=1, neg=True)) if xs else self # PYCHOK yield
1213
1231
 
1214
1232
  def _fsub(self, other, op):
1215
1233
  '''(INTERNAL) Apply C{B{self} -= B{other}}.
1216
1234
  '''
1217
1235
  if isinstance(other, Fsum):
1218
1236
  if other is self: # or other._fprs2 == self._fprs2:
1219
- self._fset(_0_0, asis=True, n=len(self) * 2) # self -= self
1237
+ self._fset(_0_0) # n=len(self) * 2, self -= self
1220
1238
  elif other._ps:
1221
- self._facc(other._ps_n())
1239
+ self._facc(other._ps_neg)
1222
1240
  elif not isscalar(other):
1223
1241
  raise self._TypeError(op, other) # txt=_invalid_
1224
1242
  elif self._finite(other, op):
1225
1243
  self._facc_(-other)
1226
1244
  return self
1227
1245
 
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
1246
  def fsum(self, xs=()):
1239
1247
  '''Add more C{scalar} or L{Fsum} instances and summate.
1240
1248
 
@@ -1263,12 +1271,12 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1263
1271
  f = self._facc(_2floats(xs, origin=1)) if xs else self # PYCHOK yield
1264
1272
  return f._fprs
1265
1273
 
1266
- def fsum2(self, xs=(), **name):
1274
+ def fsum2(self, xs=(), name=NN):
1267
1275
  '''Add more C{scalar} or L{Fsum} instances and return the
1268
1276
  current precision running sum and the C{residual}.
1269
1277
 
1270
- @kwarg xs: Iterable, list, tuple, etc. (C{scalar} or
1271
- L{Fsum} instances).
1278
+ @kwarg xs: Iterable, list, tuple, etc. (C{scalar} or L{Fsum}
1279
+ instances).
1272
1280
  @kwarg name: Optional name (C{str}).
1273
1281
 
1274
1282
  @return: L{Fsum2Tuple}C{(fsum, residual)} with C{fsum} the
@@ -1282,7 +1290,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1282
1290
  f = self._facc(_2floats(xs)) if xs else self # PYCHOK yield
1283
1291
  t = f._fprs2
1284
1292
  if name:
1285
- t = t.dup(name=_xkwds_get(name, name=NN))
1293
+ t = t.dup(name=name)
1286
1294
  return t
1287
1295
 
1288
1296
  def fsum2_(self, *xs):
@@ -1318,16 +1326,16 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1318
1326
  '''
1319
1327
  n = _1_0
1320
1328
  if isinstance(other, Fsum):
1321
- if other is self or other._fprs2 == self._fprs2:
1322
- return self._fset(_1_0, asis=True, n=len(self))
1329
+ if other is self or other == self:
1330
+ return self._fset(_1_0) # n=len(self)
1323
1331
  d, r = other._fprs2
1324
1332
  if r:
1325
- if not d: # PYCHOK no cover
1326
- d = r
1327
- elif self._raiser(r, d):
1328
- raise self._ResidualError(op, other, r)
1329
- else:
1333
+ if d:
1334
+ if self._raiser(r, d):
1335
+ raise self._ResidualError(op, other, r)
1330
1336
  d, n = other.as_integer_ratio()
1337
+ else: # PYCHOK no cover
1338
+ d = r
1331
1339
  elif isscalar(other):
1332
1340
  d = other
1333
1341
  else: # PYCHOK no cover
@@ -1335,11 +1343,10 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1335
1343
  try:
1336
1344
  s = 0 if isinf(d) else (
1337
1345
  d if isnan(d) else self._finite(n / d))
1338
- except Exception as x:
1339
- E, t = _xError2(x)
1340
- raise self._Error(op, other, E, txt=t)
1346
+ except Exception as X:
1347
+ raise self._ErrorX(X, op, other)
1341
1348
  f = self._mul_scalar(s, _mul_op_) # handles 0, NAN, etc.
1342
- return self._fset(f)
1349
+ return self._fset(f, asis=False)
1343
1350
 
1344
1351
  @property_RO
1345
1352
  def imag(self):
@@ -1387,9 +1394,9 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1387
1394
  return False if r else True
1388
1395
 
1389
1396
  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.
1397
+ '''Return whether functions L{fsum}, L{fsum_}, L{fsum1} and
1398
+ L{fsum1_} plus partials summation are based on Python's
1399
+ C{math.fsum} or not.
1393
1400
 
1394
1401
  @return: C{2} if all functions and partials summation
1395
1402
  are based on C{math.fsum}, C{True} if only
@@ -1400,24 +1407,34 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1400
1407
  f = Fsum._math_fsum
1401
1408
  return 2 if _psum is f else bool(f)
1402
1409
 
1403
- def _mul_Fsum(self, other, op=_mul_op_):
1404
- '''(INTERNAL) Return C{B{self} * Fsum B{other}} as L{Fsum}.
1410
+ def _mul_Fsum(self, other, op=_mul_op_): # in .fmath.Fhorner
1411
+ '''(INTERNAL) Return C{B{self} * Fsum B{other}} as L{Fsum} or C{0}.
1405
1412
  '''
1406
1413
  # assert isinstance(other, Fsum)
1407
- return self._copy_0()._facc(self._ps_x(op, *other._ps), up=False)
1414
+ if self._ps and other._ps:
1415
+ f = _Fsum_xs(self._ps_mul(op, *other._ps))
1416
+ else:
1417
+ f = _0_0
1418
+ return f
1408
1419
 
1409
- def _mul_scalar(self, factor, op):
1410
- '''(INTERNAL) Return C{B{self} * scalar B{factor}} as L{Fsum} or C{0}.
1420
+ def _mul_scalar(self, factor, op): # in .fmath.Fhorner
1421
+ '''(INTERNAL) Return C{B{self} * scalar B{factor}} as L{Fsum}, C{0} or C{self}.
1411
1422
  '''
1412
1423
  # 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)
1424
+ if self._ps and self._finite(factor, op):
1425
+ f = self if factor == _1_0 else (
1426
+ self._neg if factor == _N_1_0 else
1427
+ _Fsum_xs(self._ps_mul(op, factor))) # PYCHOK indent
1417
1428
  else:
1418
- f = self._copy_0(_0_0)
1429
+ f = _0_0
1419
1430
  return f
1420
1431
 
1432
+ @property_RO
1433
+ def _neg(self):
1434
+ '''(INTERNAL) Return C{-self}.
1435
+ '''
1436
+ return _Fsum_ps(*self._ps_neg) if self._ps else NEG0
1437
+
1421
1438
  @property_RO
1422
1439
  def partials(self):
1423
1440
  '''Get this instance' current partial sums (C{tuple} of C{float}s and/or C{int}s).
@@ -1436,36 +1453,30 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1436
1453
 
1437
1454
  @note: If B{C{mod}} is given as C{None}, the result will be an
1438
1455
  C{integer} L{Fsum} provided this instance C{is_integer}
1439
- or set C{integer} with L{Fsum.fint}.
1456
+ or set to C{integer} with L{Fsum.fint}.
1440
1457
 
1441
1458
  @see: Methods L{Fsum.__ipow__}, L{Fsum.fint} and L{Fsum.is_integer}.
1442
1459
  '''
1443
1460
  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
1461
+ return f._fpow(x, _pow_op_, *mod) # f = pow(f, x, *mod)
1449
1462
 
1450
1463
  def _pow_0_1(self, x, other):
1451
1464
  '''(INTERNAL) Return B{C{self}**1} or C{B{self}**0 == 1.0}.
1452
1465
  '''
1453
- return self if x else (1 if self.is_integer() and isint(other) else _1_0)
1466
+ return self if x else (1 if isint(other) and self.is_integer() else _1_0)
1454
1467
 
1455
- def _pow_2(self, x, other, op):
1456
- '''(INTERNAL) 2-arg C{pow(B{self}, scalar B{x})} embellishing errors.
1468
+ def _pow_2(self, b, x, other, op):
1469
+ '''(INTERNAL) 2-arg C{pow(B{b}, scalar B{x})} embellishing errors.
1457
1470
  '''
1458
- # assert len(self._ps) == 1 and isscalar(x)
1459
- b = self._ps[0] # assert isscalar(b)
1471
+ # assert isscalar(b) and isscalar(x)
1460
1472
  try: # type(s) == type(x) if x in (_1_0, 1)
1461
1473
  s = pow(b, x) # -1**2.3 == -(1**2.3)
1462
1474
  if not iscomplex(s):
1463
1475
  return self._finite(s) # 0**INF == 0.0, 1**INF==1.0
1464
1476
  # neg**frac == complex in Python 3+, but ValueError in 2-
1465
- E, t = _ValueError, _strcomplex(s, b, x) # PYCHOK no cover
1466
- except Exception as x:
1467
- E, t = _xError2(x)
1468
- raise self._Error(op, other, E, txt=t)
1477
+ raise ValueError(_strcomplex(s, b, x)) # PYCHOK no cover
1478
+ except Exception as X:
1479
+ raise self._ErrorX(X, op, other)
1469
1480
 
1470
1481
  def _pow_3(self, other, mod, op):
1471
1482
  '''(INTERNAL) 3-arg C{pow(B{self}, B{other}, int B{mod} or C{None})}.
@@ -1473,49 +1484,71 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1473
1484
  b, r = self._fprs2 if mod is None else self._fint2
1474
1485
  if r and self._raiser(r, b):
1475
1486
  t = _non_zero_ if mod is None else _integer_
1476
- E, t = ResidualError, _stresidual(t, r, mod=mod)
1487
+ t = _stresidual(t, r, mod=mod)
1488
+ raise self._Error(op, other, ResidualError, txt=t)
1489
+
1490
+ try: # b, other, mod all C{int}, unless C{mod} is C{None}
1491
+ x = _2scalar(other, _raiser=self._raiser)
1492
+ s = pow(b, x, mod)
1493
+ if not iscomplex(s):
1494
+ return self._finite(s)
1495
+ # neg**frac == complex in Python 3+, but ValueError in 2-
1496
+ raise ValueError(_strcomplex(s, b, x, mod)) # PYCHOK no cover
1497
+ except Exception as X:
1498
+ raise self._ErrorX(X, op, other, mod)
1499
+
1500
+ def _pow_any(self, other, unused, op):
1501
+ '''Return C{B{self} ** B{other}}.
1502
+ '''
1503
+ if isinstance(other, Fsum):
1504
+ x, r = other._fprs2
1505
+ f = self._pow_scalar(x, other, op)
1506
+ if r:
1507
+ if self._raiser(r, x):
1508
+ raise self._ResidualError(op, other, r)
1509
+ s = self._pow_scalar(r, other, op)
1510
+ # s = _2scalar(s) # _raiser = None
1511
+ f *= s
1512
+ elif isscalar(other):
1513
+ x = self._finite(other, op)
1514
+ f = self._pow_scalar(x, other, op)
1477
1515
  else:
1478
- try: # b, other, mod all C{int}, unless C{mod} is C{None}
1479
- x = _2scalar(other, _raiser=self._raiser)
1480
- s = pow(b, x, mod)
1481
- if not iscomplex(s):
1482
- return self._finite(s)
1483
- # neg**frac == complex in Python 3+, but ValueError in 2-
1484
- E, t = _ValueError, _strcomplex(s, b, x, mod) # PYCHOK no cover
1485
- except Exception as x:
1486
- E, t = _xError2(x)
1487
- t = _COMMASPACE_(Fmt.PARENSPACED(mod=mod), t)
1488
- raise self._Error(op, other, E, txt=t)
1516
+ raise self._TypeError(op, other) # txt=_invalid_
1517
+ return f
1489
1518
 
1490
1519
  def _pow_int(self, x, other, op):
1491
1520
  '''(INTERNAL) Return C{B{self} **= B{x}} for C{int B{x} >= 0}.
1492
1521
  '''
1493
1522
  # assert isint(x) and x >= 0
1494
- if len(self._ps) > 1:
1495
- if x > 2:
1496
- p = self._copy_up()
1523
+ ps = self._ps
1524
+ if len(ps) > 1:
1525
+ f = self
1526
+ if x > 4:
1497
1527
  m = 1 # single-bit mask
1498
- if x & m:
1528
+ if (x & m):
1499
1529
  x -= m # x ^= m
1500
- f = p._copy_up()
1501
1530
  else:
1502
- f = self._copy_0(_1_0)
1531
+ f = _Fsum_ps(_1_0)
1532
+ p = self
1503
1533
  while x:
1504
1534
  p = p._mul_Fsum(p, op) # p **= 2
1505
1535
  m += m # m <<= 1
1506
- if x & m:
1536
+ if (x & m):
1507
1537
  x -= m # x ^= m
1508
1538
  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)
1539
+ elif x > 1: # self**2, 3 or 4
1540
+ f = f._mul_Fsum(f, op)
1541
+ if x > 2: # self**3 or 4
1542
+ p = self if x < 4 else f
1543
+ f = f._mul_Fsum(p, op)
1544
+ else: # self**1 or self**0 == 1 or _1_0
1545
+ f = f._pow_0_1(x, other)
1546
+ elif ps: # self._ps[0]**x
1547
+ f = self._pow_2(ps[0], x, other, op)
1515
1548
  else: # PYCHOK no cover
1516
1549
  # 0**pos_int == 0, but 0**0 == 1
1517
1550
  f = 0 if x else 1 # like ._fprs
1518
- return self._fset(f, asis=isint(f), n=len(self))
1551
+ return f
1519
1552
 
1520
1553
  def _pow_scalar(self, x, other, op):
1521
1554
  '''(INTERNAL) Return C{self**B{x}} for C{scalar B{x}}.
@@ -1526,26 +1559,21 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1526
1559
  y = abs(x)
1527
1560
  if y > 1:
1528
1561
  if r:
1529
- f = self._copy_up()._pow_int(y, other, op)
1562
+ f = self._pow_int(y, other, op)
1530
1563
  if x > 0: # > 1
1531
1564
  return f
1532
1565
  # assert x < 0 # < -1
1533
- s, r = f._fprs2
1566
+ s, r = f._fprs2 if isinstance(f, Fsum) else (f, 0)
1534
1567
  if r:
1535
- return self._copy_0(_1_0)._ftruediv(f, op)
1568
+ return _Fsum_ps(_1_0)._ftruediv(f, op)
1536
1569
  # use **= -1 for the CPython float_pow
1537
1570
  # error if s is zero, and not s = 1 / s
1538
1571
  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
1572
  elif x < 0: # self**-1 == 1 / self
1545
1573
  if r:
1546
- return self._copy_0(_1_0)._ftruediv(self, op)
1574
+ return _Fsum_ps(_1_0)._ftruediv(self, op)
1547
1575
  else: # self**1 or self**0
1548
- return self._pow_0_1(x, other) # self or 0.0
1576
+ return self._pow_0_1(x, other) # self, 1 or 1.0
1549
1577
  elif not isscalar(x): # assert ...
1550
1578
  raise self._TypeError(op, other, txt=_not_scalar_)
1551
1579
  elif r and self._raiser(r, s): # non-zero residual**fractional
@@ -1553,42 +1581,40 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1553
1581
  t = _stresidual(_non_zero_, r, fractional_power=x)
1554
1582
  raise self._Error(op, other, ResidualError, txt=t)
1555
1583
  # assert isscalar(s) and isscalar(x)
1556
- return self._copy_0(s)._pow_2(x, other, op)
1584
+ return self._pow_2(s, x, other, op)
1557
1585
 
1558
1586
  def _ps_1(self, *less):
1559
1587
  '''(INTERNAL) Yield partials, 1-primed and subtract any C{less}.
1560
1588
  '''
1561
- yield _1_0
1589
+ n = len(self._ps) + len(less) - 4
1590
+ if n < 0:
1591
+ yield _1_0
1562
1592
  for p in self._ps:
1563
- if p:
1564
- yield p
1593
+ yield p
1565
1594
  for p in less:
1566
- if p:
1567
- yield -p
1568
- yield _N_1_0
1595
+ yield -p
1596
+ if n < 0:
1597
+ yield _N_1_0
1569
1598
 
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
1599
+ def _ps_mul(self, op, *factors): # see .fmath.Fhorner
1578
1600
  '''(INTERNAL) Yield all C{partials} times each B{C{factor}},
1579
1601
  in total, up to C{len(partials) * len(factors)} items.
1580
1602
  '''
1581
- ps = self._ps
1603
+ ps = self._ps # tuple(self._ps)
1582
1604
  if len(ps) < len(factors):
1583
1605
  ps, factors = factors, ps
1584
1606
  _f = _isfinite
1585
1607
  for f in factors:
1586
1608
  for p in ps:
1587
1609
  p *= f
1588
- if _f(p):
1589
- yield p
1590
- else: # PYCHOK no cover
1591
- self._finite(p, op) # throw ValueError
1610
+ yield p if _f(p) else self._finite(p, op)
1611
+
1612
+ @property_RO
1613
+ def _ps_neg(self):
1614
+ '''(INTERNAL) Yield the partials, I{negated}.
1615
+ '''
1616
+ for p in self._ps:
1617
+ yield -p
1592
1618
 
1593
1619
  @property_RO
1594
1620
  def real(self):
@@ -1613,7 +1639,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1613
1639
  return self._fprs2.residual
1614
1640
 
1615
1641
  def _raiser(self, r, s):
1616
- '''(INTERNAL) Does the ratio C{r / s} exceed threshold?
1642
+ '''(INTERNAL) Does ratio C{r / s} exceed threshold?
1617
1643
  '''
1618
1644
  self._ratio = t = fabs((r / s) if s else r)
1619
1645
  return t > self._RESIDUAL
@@ -1628,12 +1654,12 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1628
1654
  C{PYGEODESY_FSUM_RESIDUAL} or if omitted, keep the
1629
1655
  current setting.
1630
1656
 
1631
- @return: The previous C{RESIDUAL} setting (C{float}).
1657
+ @return: The previous C{RESIDUAL} setting (C{float}), default C{0}.
1632
1658
 
1633
1659
  @raise ValueError: Negative B{C{threshold}}.
1634
1660
 
1635
1661
  @note: A L{ResidualError} is thrown if the non-zero I{ratio}
1636
- C{residual} / C{fsum} exceeds the B{C{threshold}}.
1662
+ C{residual / fsum} exceeds the B{C{threshold}}.
1637
1663
  '''
1638
1664
  r = self._RESIDUAL
1639
1665
  if threshold:
@@ -1651,7 +1677,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1651
1677
  '''(INTERNAL) Non-zero B{C{residual}} etc.
1652
1678
  '''
1653
1679
  t = _stresidual(_non_zero_, residual, ratio=self._ratio,
1654
- RESIDUAL=self._RESIDUAL)
1680
+ RESIDUAL=self._RESIDUAL)
1655
1681
  t = t.replace(_COMMASPACE_R_, _exceeds_R_)
1656
1682
  return self._Error(op, other, ResidualError, txt=t)
1657
1683
 
@@ -1703,13 +1729,17 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1703
1729
  '''
1704
1730
  return self._Error(op, other, _TypeError, **txt)
1705
1731
 
1706
- def _update(self): # see ._fset
1732
+ def _update(self, updated=True): # see ._fset
1707
1733
  '''(INTERNAL) Zap all cached C{Property_RO} values.
1708
1734
  '''
1709
- Fsum._fint2._update(self)
1710
- Fsum._fprs ._update(self)
1711
- Fsum._fprs2._update(self)
1712
- return self
1735
+ if updated:
1736
+ _pop = self.__dict__.pop
1737
+ for p in _ROs:
1738
+ _ = _pop(p, None)
1739
+ # Fsum._fint2._update(self)
1740
+ # Fsum._fprs ._update(self)
1741
+ # Fsum._fprs2._update(self)
1742
+ return self # for .fset_
1713
1743
 
1714
1744
  def _ValueError(self, op, other, **txt): # PYCHOK no cover
1715
1745
  '''(INTERNAL) Return a C{ValueError}.
@@ -1721,7 +1751,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1721
1751
  '''
1722
1752
  return self._Error(op, other, _ZeroDivisionError, **txt)
1723
1753
 
1724
- _allPropertiesOf_n(3, Fsum, Property_RO) # PYCHOK assert, see Fsum._fset, -._update
1754
+ _ROs = _allPropertiesOf_n(3, Fsum, Property_RO) # PYCHOK assert, see Fsum._fset, -._update
1725
1755
 
1726
1756
 
1727
1757
  def _Float_Int(arg, **name_Error):
@@ -1731,6 +1761,17 @@ def _Float_Int(arg, **name_Error):
1731
1761
  return U(arg, **name_Error)
1732
1762
 
1733
1763
 
1764
+ class DivMod2Tuple(_NamedTuple):
1765
+ '''2-Tuple C{(div, mod)} with the quotient C{div} and remainder
1766
+ C{mod} results of a C{divmod} operation.
1767
+
1768
+ @note: Quotient C{div} an C{int} in Python 3+ or a C{float} in
1769
+ Python 2-. Remainder C{mod} an L{Fsum} instance.
1770
+ '''
1771
+ _Names_ = (_div_, _mod_)
1772
+ _Units_ = (_Float_Int, Fsum)
1773
+
1774
+
1734
1775
  class Fsum2Tuple(_NamedTuple):
1735
1776
  '''2-Tuple C{(fsum, residual)} with the precision running C{fsum}
1736
1777
  and the C{residual}, the sum of the remaining partials. Each
@@ -1746,8 +1787,8 @@ class Fsum2Tuple(_NamedTuple):
1746
1787
  def Fsum(self):
1747
1788
  '''Get this L{Fsum2Tuple} as an L{Fsum}.
1748
1789
  '''
1749
- f = Fsum(name=self.name)
1750
- return f._copy_0(*(s for s in reversed(self) if s))
1790
+ s, r = map(float, self)
1791
+ return _Fsum_ps(*_2ps(s, r), name=self.name)
1751
1792
 
1752
1793
  def is_exact(self):
1753
1794
  '''Is this L{Fsum2Tuple} considered to be exact? (C{bool}).
@@ -1769,6 +1810,23 @@ class ResidualError(_ValueError):
1769
1810
  pass
1770
1811
 
1771
1812
 
1813
+ def _Fsum_ps(*ps, **name):
1814
+ '''(INTERNAL) Return an C{Fsum} from I{ordered} partials C{ps}.
1815
+ '''
1816
+ f = Fsum(**name) if name else Fsum()
1817
+ if ps:
1818
+ f._n = len(ps)
1819
+ f._ps[:] = ps
1820
+ return f
1821
+
1822
+
1823
+ def _Fsum_xs(xs, up=False, **name):
1824
+ '''(INTERNAL) Return an C{Fsum} from known floats C{xs}.
1825
+ '''
1826
+ f = Fsum(**name) if name else Fsum()
1827
+ return f._facc(xs, up=up)
1828
+
1829
+
1772
1830
  try:
1773
1831
  from math import fsum as _fsum # precision IEEE-754 sum, Python 2.6+
1774
1832
 
@@ -1780,8 +1838,8 @@ try:
1780
1838
 
1781
1839
  Fsum._math_fsum = _sum = _fsum # PYCHOK exported
1782
1840
 
1783
- if _getenv('PYGEODESY_FSUM_PARTIALS', _fsum.__name__) == _fsum.__name__:
1784
- _psum = _fsum # PYCHOK redef
1841
+ if _getenv('PYGEODESY_FSUM_PARTIALS', NN) == _fsum.__name__:
1842
+ _psum = _fsum # PYCHOK re-def
1785
1843
 
1786
1844
  except ImportError:
1787
1845
  _sum = sum # Fsum(NAN) exception fall-back
@@ -1789,16 +1847,16 @@ except ImportError:
1789
1847
  def _fsum(xs):
1790
1848
  '''(INTERNAL) Precision summation, Python 2.5-.
1791
1849
  '''
1792
- return Fsum(name=_fsum.__name__)._facc(xs, up=False)._fprs
1850
+ return Fsum(name=_fsum.__name__).fsum(xs) if xs else _0_0
1793
1851
 
1794
1852
 
1795
1853
  def fsum(xs, floats=False):
1796
1854
  '''Precision floating point summation based on or like Python's C{math.fsum}.
1797
1855
 
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}.
1856
+ @arg xs: Iterable, list, tuple, etc. of values (C{scalar} or L{Fsum}
1857
+ instances).
1858
+ @kwarg floats: Optionally, use C{B{floats}=True} iff I{all} B{C{xs}}
1859
+ are known to be C{float}.
1802
1860
 
1803
1861
  @return: Precision C{fsum} (C{float}).
1804
1862
 
@@ -1819,10 +1877,10 @@ def fsum(xs, floats=False):
1819
1877
  def fsum_(*xs, **floats):
1820
1878
  '''Precision floating point summation of all positional arguments.
1821
1879
 
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}.
1880
+ @arg xs: Values to be added (C{scalar} or L{Fsum} instances), all
1881
+ positional.
1882
+ @kwarg floats: Optionally, use C{B{floats}=True} iff I{all} B{C{xs}}
1883
+ are known to be C{float}.
1826
1884
 
1827
1885
  @return: Precision C{fsum} (C{float}).
1828
1886
 
@@ -1841,10 +1899,10 @@ def fsumf_(*xs):
1841
1899
  def fsum1(xs, floats=False):
1842
1900
  '''Precision floating point summation of a few arguments, 1-primed.
1843
1901
 
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}.
1902
+ @arg xs: Iterable, list, tuple, etc. of values (C{scalar} or L{Fsum}
1903
+ instances).
1904
+ @kwarg floats: Optionally, use C{B{floats}=True} iff I{all} B{C{xs}}
1905
+ are known to be C{float}.
1848
1906
 
1849
1907
  @return: Precision C{fsum} (C{float}).
1850
1908
 
@@ -1856,10 +1914,10 @@ def fsum1(xs, floats=False):
1856
1914
  def fsum1_(*xs, **floats):
1857
1915
  '''Precision floating point summation of a few arguments, 1-primed.
1858
1916
 
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}.
1917
+ @arg xs: Values to be added (C{scalar} or L{Fsum} instances), all
1918
+ positional.
1919
+ @kwarg floats: Optionally, use C{B{floats}=True} iff I{all} B{C{xs}}
1920
+ are known to be C{float}.
1863
1921
 
1864
1922
  @return: Precision C{fsum} (C{float}).
1865
1923
 
@@ -1875,6 +1933,35 @@ def fsum1f_(*xs):
1875
1933
  return _fsum(_1primed(xs)) if xs else _0_0
1876
1934
 
1877
1935
 
1936
+ if __name__ == '__main__':
1937
+
1938
+ # usage: [env PYGEODESY_FSUM_PARTIALS=fsum] python3 -m pygeodesy.fsums
1939
+
1940
+ def _test(n):
1941
+ # copied from Hettinger, see L{Fsum} reference
1942
+ from pygeodesy import printf
1943
+ from random import gauss, random, shuffle
1944
+
1945
+ printf(_fsum.__name__, end=_COMMASPACE_)
1946
+ printf(_psum.__name__, end=_COMMASPACE_)
1947
+
1948
+ F = Fsum()
1949
+ if F.is_math_fsum:
1950
+ c = (7, 1e100, -7, -1e100, -9e-20, 8e-20) * 10
1951
+ for _ in range(n):
1952
+ t = list(c)
1953
+ s = 0
1954
+ for _ in range(n * 8):
1955
+ v = gauss(0, random())**7 - s
1956
+ t.append(v)
1957
+ s += v
1958
+ shuffle(t)
1959
+ assert float(F.fset_(*t)) == _fsum(t)
1960
+ printf(_DOT_, end=NN)
1961
+ printf(NN)
1962
+
1963
+ _test(128)
1964
+
1878
1965
  # **) MIT License
1879
1966
  #
1880
1967
  # Copyright (C) 2016-2024 -- mrJean1 at Gmail -- All Rights Reserved.