pygeodesy 24.4.2__py2.py3-none-any.whl → 24.4.12__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,14 +14,13 @@ 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.
19
-
20
- Set env variable C{PYGEODESY_FSUM_RESIDUAL} to a C{float} string greater
21
- than C{"0.0"} as the threshold to throw a L{ResidualError} in division or
22
- exponention of an L{Fsum} instance with a I{relative} C{residual} exceeding
23
- the threshold, see methods L{Fsum.RESIDUAL}, L{Fsum.pow}, L{Fsum.__ipow__}
24
- and L{Fsum.__itruediv__}.
17
+ Set env variable C{PYGEODESY_FSUM_PARTIALS} to string C{"fsum"}) for summation
18
+ of L{Fsum} partials by Python function C{math.fsum}.
19
+
20
+ Set env variable C{PYGEODESY_FSUM_RESIDUAL} to a C{float} string greater than
21
+ C{"0.0"} as the threshold to throw a L{ResidualError} in division or exponention
22
+ of an L{Fsum} instance with a I{relative} C{residual} exceeding the threshold,
23
+ see methods L{Fsum.RESIDUAL}, L{Fsum.pow}, L{Fsum.__ipow__} and L{Fsum.__itruediv__}.
25
24
  '''
26
25
  # make sure int/int division yields float quotient, see .basics
27
26
  from __future__ import division as _; del _ # PYCHOK semicolon
@@ -46,7 +45,7 @@ from pygeodesy.props import _allPropertiesOf_n, deprecated_property_RO, \
46
45
  from math import ceil as _ceil, fabs, floor as _floor # PYCHOK used! .ltp
47
46
 
48
47
  __all__ = _ALL_LAZY.fsums
49
- __version__ = '24.04.02'
48
+ __version__ = '24.04.09'
50
49
 
51
50
  _add_op_ = _PLUS_ # in .auxilats.auxAngle
52
51
  _eq_op_ = _EQUAL_ * 2 # _DEQUAL_
@@ -72,84 +71,125 @@ _divmod_op_ = _floordiv_op_ + _mod_op_
72
71
  _isub_op_ = _sub_op_ + _fset_op_ # in .auxilats.auxAngle, .fsums
73
72
 
74
73
 
74
+ def _2delta(*ab):
75
+ '''(INTERNAL) Helper for C{Fsum.fsum2f_}.
76
+ '''
77
+ try:
78
+ a, b = _2sum(*ab)
79
+ except _OverflowError:
80
+ a, b = ab
81
+ return float(a if fabs(a) > fabs(b) else b)
82
+
83
+
84
+ def _2error(unused):
85
+ '''(INTERNAL) Throw a C{not finite} exception.
86
+ '''
87
+ raise ValueError(_not_finite_)
88
+
89
+
75
90
  def _2float(index=None, **name_value): # in .fmath, .fstats
76
91
  '''(INTERNAL) Raise C{TypeError} or C{ValueError} if not scalar or infinite.
77
92
  '''
78
93
  n, v = name_value.popitem() # _xkwds_item2(name_value)
79
94
  try:
80
95
  v = float(v)
81
- if _isfinite(v):
82
- return v
83
- raise ValueError(_not_finite_)
84
- except Exception as e:
85
- raise _xError(e, Fmt.INDEX(n, index), v)
96
+ return v if _isfinite(v) else _2error(v)
97
+ except Exception as X:
98
+ raise _xError(X, Fmt.INDEX(n, index), v)
99
+
86
100
 
101
+ def _X_ps(X): # for _2floats only
102
+ return X._ps
87
103
 
88
- def _2floats(xs, origin=0, neg=False):
104
+
105
+ def _2floats(xs, origin=0, _X=_X_ps, _x=float):
89
106
  '''(INTERNAL) Yield each B{C{xs}} as a C{float}.
90
107
  '''
91
- if neg:
92
- def _X(X):
93
- return X._ps_neg
94
-
95
- def _x(x):
96
- return -float(x)
97
- else:
98
- def _X(X): # PYCHOK re-def
99
- return X._ps
108
+ try:
109
+ i, x = origin, None
110
+ _fin = _isfinite
111
+ _Fs = Fsum
112
+ for x in xs:
113
+ if isinstance(x, _Fs):
114
+ for p in _X(x):
115
+ yield p
116
+ else:
117
+ f = _x(x)
118
+ yield f if _fin(f) else _2error(f)
119
+ i += 1
120
+ except Exception as X:
121
+ raise _xError(X, Fmt.INDEX(xs=i), x)
100
122
 
101
- _x = float
102
123
 
103
- return _2yield(xs, origin, _X, _x)
124
+ def _2halfeven(s, r, p):
125
+ '''(INTERNAL) Round half-even.
126
+ '''
127
+ if (p > 0 and r > 0) or \
128
+ (p < 0 and r < 0): # signs match
129
+ r *= 2
130
+ t = s + r
131
+ if r == (t - s):
132
+ s = t
133
+ return s
104
134
 
105
135
 
106
136
  def _1primed(xs): # in .fmath
107
- '''(INTERNAL) 1-Prime the summation of C{xs}
108
- arguments I{known} to be C{finite float}.
137
+ '''(INTERNAL) 1-Primed summation of iterable C{xs}
138
+ items, all I{known} to be C{finite float}.
109
139
  '''
110
140
  yield _1_0
111
141
  for x in xs:
112
- if x:
113
- yield x
142
+ yield x
114
143
  yield _N_1_0
115
144
 
116
145
 
117
146
  def _2ps(s, r):
118
147
  '''(INTERNAL) Return a C{s} and C{r} pair, I{ps-ordered}.
119
148
  '''
120
- if fabs(s) < fabs(r):
121
- s, r = r, s
122
- return ((r, s) if s else (r,)) if r else (s,)
149
+ return (s, r) if fabs(s) < fabs(r) else (r, s)
123
150
 
124
151
 
125
152
  def _psum(ps): # PYCHOK used!
126
- '''(INTERNAL) Partials summation updating C{ps}, I{overridden below}.
153
+ '''(INTERNAL) Partials sum, updating C{ps}, I{overridden below}.
127
154
  '''
128
155
  # assert isinstance(ps, list)
129
- i = len(ps) - 1 # len(ps) > 2
130
- if i < 0:
131
- return _0_0
132
- s = ps[i]
156
+ i = len(ps) - 1
157
+ s = _0_0 if i < 0 else ps[i]
133
158
  _2s = _2sum
134
159
  while i > 0:
135
160
  i -= 1
136
161
  s, r = _2s(s, ps[i])
137
162
  if r: # sum(ps) became inexact
138
- ps[i:] = (s, r) if s else (r,)
139
- if i > 0:
140
- p = ps[i-1] # round half-even
141
- if (p > 0 and r > 0) or \
142
- (p < 0 and r < 0): # signs match
143
- r *= 2
144
- t = s + r
145
- if r == (t - s):
146
- s = t
147
- break
163
+ if s:
164
+ ps[i:] = r, s
165
+ if i > 0:
166
+ s = _2halfeven(s, r, ps[i-1])
167
+ break # return s
168
+ s = r # PYCHOK no cover
148
169
  ps[i:] = s,
149
170
  return s
150
171
 
151
172
 
152
- def _2scalar(other, _raiser=None):
173
+ def _Psum(ps, **name):
174
+ '''(INTERNAL) Return an C{Fsum} from I{ordered} partials C{ps}.
175
+ '''
176
+ f = Fsum(**name) if name else Fsum()
177
+ if ps:
178
+ f._ps[:] = ps
179
+ f._n = len(f._ps)
180
+ return f
181
+
182
+
183
+ def _Psum_1(p=_1_0, **name):
184
+ '''(INTERNAL) Return an C{Fsum} from a single partial C{p}.
185
+ '''
186
+ f = Fsum(**name) if name else Fsum()
187
+ f._ps[:] = p,
188
+ f._n = 1 # len(f._ps)
189
+ return f
190
+
191
+
192
+ def _2scalar(other, _raiser=None, **mod):
153
193
  '''(INTERNAL) Return B{C{other}} as C{int}, C{float} or C{as-is}.
154
194
  '''
155
195
  if isinstance(other, Fsum):
@@ -158,7 +198,8 @@ def _2scalar(other, _raiser=None):
158
198
  s, r = other._fprs2
159
199
  if r: # PYCHOK no cover
160
200
  if _raiser and _raiser(r, s):
161
- raise ValueError(_stresidual(_non_zero_, r))
201
+ t = _stresidual(_non_zero_, r, **mod)
202
+ raise ResidualError(t, txt=None)
162
203
  s = other # L{Fsum} as-is
163
204
  else:
164
205
  s = other # C{type} as-is
@@ -168,24 +209,24 @@ def _2scalar(other, _raiser=None):
168
209
 
169
210
 
170
211
  def _strcomplex(s, *args):
171
- '''(INTERNAL) C{Complex} 2- or 3-arg C{pow} error C{str}.
212
+ '''(INTERNAL) C{Complex} 2- or 3-arg C{pow} error as C{str}.
172
213
  '''
173
- c = iscomplex.__name__[2:]
214
+ c = _strcomplex.__name__[4:]
174
215
  n = _DASH_(len(args), _arg_)
175
- t = _SPACE_(c, s, _from_, n, pow.__name__)
176
- return unstr(t, *args)
216
+ t = unstr(pow, *args)
217
+ return _SPACE_(c, s, _from_, n, t)
177
218
 
178
219
 
179
220
  def _stresidual(prefix, residual, **name_values):
180
- '''(INTERNAL) Residual error C{str}.
221
+ '''(INTERNAL) Residual error as C{str}.
181
222
  '''
182
- p = _SPACE_(prefix, Fsum.residual.name)
223
+ p = _stresidual.__name__[3:]
183
224
  t = Fmt.PARENSPACED(p, Fmt(residual))
184
225
  for n, v in itemsorted(name_values):
185
226
  n = n.replace(_UNDER_, _SPACE_)
186
227
  p = Fmt.PARENSPACED(n, Fmt(v))
187
228
  t = _COMMASPACE_(t, p)
188
- return t
229
+ return _SPACE_(prefix, t)
189
230
 
190
231
 
191
232
  def _2sum(a, b): # by .testFmath
@@ -193,7 +234,7 @@ def _2sum(a, b): # by .testFmath
193
234
  '''
194
235
  s = a + b
195
236
  if not _isfinite(s):
196
- u = unstr(_2sum.__name__, a, b)
237
+ u = unstr(_2sum, a, b)
197
238
  t = Fmt.PARENSPACED(_not_finite_, s)
198
239
  raise _OverflowError(u, txt=t)
199
240
  if fabs(a) < fabs(b):
@@ -201,28 +242,6 @@ def _2sum(a, b): # by .testFmath
201
242
  return s, (b - (s - a))
202
243
 
203
244
 
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
-
226
245
  class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
227
246
  '''Precision floating point summation and I{running} summation.
228
247
 
@@ -246,8 +265,9 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
246
265
  _math_fsum = None
247
266
  _n = 0
248
267
  # _ps = [] # partial sums
249
- # _px = 0 # max(Fsum._px, len(Fsum._ps))
268
+ # _ps_max = 0 # max(Fsum._ps_max, len(Fsum._ps))
250
269
  _ratio = None
270
+ _recursive = bool(_getenv('PYGEODESY_FSUM_RECURSIVE', NN))
251
271
  _RESIDUAL = max(float(_getenv('PYGEODESY_FSUM_RESIDUAL', _0_0)), _0_0)
252
272
 
253
273
  def __init__(self, *xs, **name_RESIDUAL):
@@ -267,13 +287,9 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
267
287
  r = _xkwds_get(name_RESIDUAL, RESIDUAL=None)
268
288
  if r is not None:
269
289
  self.RESIDUAL(r) # ... ResidualError
270
- # self._n = 0
271
290
  self._ps = [] # [_0_0], see L{Fsum._fprs}
272
- if len(xs) > 1:
273
- self._facc(_2floats(xs, origin=1), up=False) # PYCHOK yield
274
- elif xs: # len(xs) == 1
275
- self._n = 1
276
- self._ps[:] = _2float(x=xs[0]),
291
+ if xs:
292
+ self._facc_any(xs, origin=1, up=False)
277
293
 
278
294
  def __abs__(self):
279
295
  '''Return this instance' absolute value as an L{Fsum}.
@@ -282,7 +298,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
282
298
  return (-self) if s < 0 else self._copy_2(self.__abs__)
283
299
 
284
300
  def __add__(self, other):
285
- '''Return the C{Fsum(B{self}, B{other})}.
301
+ '''Return C{B{self} + B{other}} as an L{Fsum}.
286
302
 
287
303
  @arg other: An L{Fsum} or C{scalar}.
288
304
 
@@ -338,7 +354,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
338
354
  return self._cmp_0(other, _eq_op_) == 0
339
355
 
340
356
  def __float__(self):
341
- '''Return this instance' current precision running sum as C{float}.
357
+ '''Return this instance' current, precision running sum as C{float}.
342
358
 
343
359
  @see: Methods L{Fsum.fsum} and L{Fsum.int_float}.
344
360
  '''
@@ -654,8 +670,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
654
670
  @arg ndigits: Optional number of digits (C{int}).
655
671
  '''
656
672
  # <https://docs.Python.org/3.12/reference/datamodel.html?#object.__round__>
657
- return _Fsum_ps(round(float(self), *ndigits), # can be C{int}
658
- name=self.__round__.__name__)
673
+ return _Psum_1(round(float(self), *ndigits), # can be C{int}
674
+ name=self.__round__.__name__)
659
675
 
660
676
  def __rpow__(self, other, *mod):
661
677
  '''Return C{B{other}**B{self}} as an L{Fsum}.
@@ -756,14 +772,14 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
756
772
  def _cmp_0(self, other, op):
757
773
  '''(INTERNAL) Return C{scalar(self - B{other})} for 0-comparison.
758
774
  '''
759
- if isscalar(other):
775
+ if isinstance(other, Fsum):
776
+ s = _fsum(self._ps_1(*other._ps))
777
+ elif isscalar(other):
760
778
  if other:
761
779
  s = _fsum(self._ps_1(other))
762
780
  else:
763
781
  s, r = self._fprs2
764
782
  s = _signOf(s, -r)
765
- elif isinstance(other, Fsum):
766
- s = _fsum(self._ps_1(*other._ps))
767
783
  else:
768
784
  raise self._TypeError(op, other) # txt=_invalid_
769
785
  return s
@@ -774,8 +790,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
774
790
  @return: The copy (L{Fsum}).
775
791
  '''
776
792
  f = _Named.copy(self, deep=deep, name=name)
777
- f._n = self._n if deep else 1
778
793
  f._ps = list(self._ps) # separate list
794
+ f._n = self._n if deep else 1
779
795
  return f
780
796
 
781
797
  def _copy_2(self, which, name=NN):
@@ -816,77 +832,85 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
816
832
  f = self._copy_2(self.divmod)
817
833
  return f._fdivmod2(other, _divmod_op_)
818
834
 
819
- def _Error(self, op, other, Error, **txt):
835
+ def _Error(self, op, other, Error, **txt_cause):
820
836
  '''(INTERNAL) Format an B{C{Error}} for C{{self} B{op} B{other}}.
821
837
  '''
822
- return Error(_SPACE_(self.toRepr(), op, repr(other)), **txt)
838
+ return Error(_SPACE_(self.toStr(), op, other), **txt_cause)
839
+
840
+ def _ErrorX(self, X, op, other, *mod):
841
+ '''(INTERNAL) Format the caught exception C{X}.
842
+ '''
843
+ E, t = _xError2(X)
844
+ if mod:
845
+ t = _COMMASPACE_(Fmt.PARENSPACED(mod=mod[0]), t)
846
+ return self._Error(op, other, E, txt=t, cause=X)
823
847
 
824
- def _ErrorX(self, X, xs, **kwds): # in .fmath
825
- '''(INTERNAL) Format a caught exception.
848
+ def _ErrorXs(self, X, xs, **kwds): # in .fmath
849
+ '''(INTERNAL) Format the caught exception C{X}.
826
850
  '''
827
851
  E, t = _xError2(X)
828
852
  n = unstr(self.named3, *xs[:3], _ELLIPSIS=len(xs) > 3, **kwds)
829
853
  return E(n, txt=t, cause=X)
830
854
 
831
- def _facc(self, xs, up=True): # from .elliptic._Defer.Fsum
832
- '''(INTERNAL) Accumulate more known C{scalar}s.
833
- '''
834
- n, ps, _2s = 0, self._ps, _2sum
835
- for x in xs: # _iter()
836
- # assert isscalar(x) and isfinite(x)
837
- i = 0
838
- for p in ps:
839
- x, p = _2s(x, p)
840
- if p:
841
- ps[i] = p
842
- i += 1
843
- ps[i:] = x,
844
- n += 1
845
- # assert self._ps is ps
846
- if n:
847
- self._n += n
848
- # Fsum._px = max(Fsum._px, len(ps))
849
- if up:
850
- self._update()
855
+ def _facc(self, xs, **up):
856
+ '''(INTERNAL) Accumulate all C{xs}, known to be scalar.
857
+ '''
858
+ self._ps_acc(self._ps, xs, **up)
851
859
  return self
852
860
 
853
861
  def _facc_(self, *xs, **up):
854
- '''(INTERNAL) Accumulate all positional C{scalar}s.
862
+ '''(INTERNAL) Accumulate all positional C{xs}, known to be scalar.
863
+ '''
864
+ if xs:
865
+ self._ps_acc(self._ps, xs, **up)
866
+ return self
867
+
868
+ def _facc_any(self, xs, up=True, **origin_X_x):
869
+ '''(INTERNAL) Accumulate more C{scalars} or L{Fsum}s.
855
870
  '''
856
- return self._facc(xs, **up) if xs else self
871
+ self._ps[:] = self._ps_acc(list(self._ps),
872
+ _2floats(xs, **origin_X_x), up=up) # PYCHOK yield
873
+ return self
874
+
875
+ def _facc_any_neg(self, xs, up=True, **origin):
876
+ '''(INTERNAL) Accumulate more C{scalars} or L{Fsum}s, negated.
877
+ '''
878
+ def _neg(x):
879
+ return -x
880
+
881
+ self._ps[:] = self._ps_acc(list(self._ps), map(_neg,
882
+ _2floats(xs, **origin)), up=up) # PYCHOK yield
883
+ return self
857
884
 
858
885
  def _facc_power(self, power, xs, which): # in .fmath
859
886
  '''(INTERNAL) Add each C{xs} as C{float(x**power)}.
860
887
  '''
861
- if isinstance(power, Fsum):
862
- if power.is_exact:
863
- return self._facc_power(power._fprs, xs, which)
888
+ p = power
889
+ if isinstance(p, Fsum):
890
+ if p.is_exact:
891
+ return self._facc_power(p._fprs, xs, which)
864
892
  _Pow = Fsum._pow_any
865
- elif isint(power, both=True) and power >= 0:
866
- _Pow = Fsum._pow_int
867
- power = int(power)
893
+ elif isint(p, both=True) and p >= 0:
894
+ _Pow, p = Fsum._pow_int, int(p)
868
895
  else:
869
- _Pow = Fsum._pow_scalar
870
- power = _2float(power=power)
896
+ _Pow, p = Fsum._pow_scalar, _2float(power=p)
871
897
 
872
- if power:
898
+ if p:
873
899
  from math import pow as _pow
874
- op = which.__name__
900
+ op = which.__name__
901
+ _Fs = Fsum
875
902
 
876
903
  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,
904
+ f = _Pow(X, p, power, op)
905
+ return f._ps if isinstance(f, _Fs) else (f,)
882
906
 
883
907
  def _x(x):
884
- return _pow(float(x), power)
908
+ return _pow(float(x), p)
885
909
 
886
- self._facc(_2yield(xs, 1, _X, _x)) # PYCHOK yield
910
+ f = self._facc_any(xs, origin=1, _X=_X, _x=_x)
887
911
  else:
888
- self._facc_(float(len(xs))) # x**0 == 1
889
- return self
912
+ f = self._facc_(float(len(xs))) # x**0 == 1
913
+ return f
890
914
 
891
915
  # def _facc_up(self, up=True):
892
916
  # '''(INTERNAL) Update the C{partials}, by removing
@@ -918,11 +942,11 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
918
942
  @raise ValueError: Invalid or non-finite B{C{xs}} value.
919
943
  '''
920
944
  if isinstance(xs, Fsum):
921
- self._facc(xs._ps)
945
+ self._facc(xs._ps) # tuple
922
946
  elif isscalar(xs): # for backward compatibility
923
947
  self._facc_(_2float(x=xs)) # PYCHOK no cover
924
948
  elif xs:
925
- self._facc(_2floats(xs)) # PYCHOK yield
949
+ self._facc_any(xs)
926
950
  return self
927
951
 
928
952
  def fadd_(self, *xs):
@@ -941,16 +965,13 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
941
965
 
942
966
  @raise ValueError: Invalid or non-finite B{C{xs}} value.
943
967
  '''
944
- return self._facc(_2floats(xs, origin=1)) # PYCHOK yield
968
+ return self._facc_any(xs, origin=1)
945
969
 
946
970
  def _fadd(self, other, op, **up): # in .fmath.Fhorner
947
971
  '''(INTERNAL) Apply C{B{self} += B{other}}.
948
972
  '''
949
973
  if isinstance(other, Fsum):
950
- if other is self:
951
- self._facc_(*other._ps, **up) # == ._facc(tuple(other._ps))
952
- elif other._ps:
953
- self._facc(other._ps, **up)
974
+ self._facc(other._ps, **up) # tuple
954
975
  elif not isscalar(other):
955
976
  raise self._TypeError(op, other) # txt=_invalid_
956
977
  elif other:
@@ -985,14 +1006,14 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
985
1006
  '''
986
1007
  if _isfinite(other):
987
1008
  return other
988
- raise ValueError(_not_finite_) if not op else \
1009
+ raise ValueError(_not_finite_) if op is None else \
989
1010
  self._ValueError(op, other, txt=_not_finite_)
990
1011
 
991
1012
  def fint(self, raiser=True, **name):
992
1013
  '''Return this instance' current running sum as C{integer}.
993
1014
 
994
1015
  @kwarg raiser: If C{True} throw a L{ResidualError} if the
995
- I{integer} residual is non-zero.
1016
+ I{integer} residual is non-zero (C{bool}).
996
1017
  @kwarg name: Optional name (C{str}), overriding C{"fint"}.
997
1018
 
998
1019
  @return: The C{integer} (L{Fsum}).
@@ -1027,7 +1048,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1027
1048
  s, r = self._fprs2
1028
1049
  i = int(s)
1029
1050
  r = _fsum(self._ps_1(i)) if r else float(s - i)
1030
- return i, (r or INT0)
1051
+ return i, (r or INT0) # Fsum2Tuple?
1031
1052
 
1032
1053
  @deprecated_property_RO
1033
1054
  def float_int(self): # PYCHOK no cover
@@ -1071,7 +1092,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1071
1092
  else: # len(other._ps) == len(self._ps) == 1
1072
1093
  f = self._finite(self._ps[0] * other._ps[0])
1073
1094
  elif isscalar(other):
1074
- f = self._mul_scalar(other, op)
1095
+ f = self._mul_scalar(other, op) if other != _1_0 else self
1075
1096
  else:
1076
1097
  raise self._TypeError(op, other) # txt=_invalid_
1077
1098
  return self._fset(f) # n=len(self) + 1
@@ -1094,13 +1115,13 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1094
1115
  '''
1095
1116
  if mod:
1096
1117
  if mod[0] is not None: # == 3-arg C{pow}
1097
- f = self._pow_3(other, mod[0], op)
1118
+ f = self._pow_2_3(self, other, other, op, *mod)
1098
1119
  elif self.is_integer():
1099
1120
  # return an exact C{int} for C{int}**C{int}
1121
+ i, _ = self._fint2 # assert _ == 0
1100
1122
  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)
1123
+ f = self._pow_2_3(i, x, other, op) if isscalar(x) else \
1124
+ _Psum_1(i)._pow_any(x, other, op)
1104
1125
  else: # mod[0] is None, power(self, other)
1105
1126
  f = self._pow_any(other, other, op)
1106
1127
  else: # pow(self, other) == pow(self, other, None)
@@ -1123,11 +1144,14 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1123
1144
  running sum and residual (L{Fsum2Tuple}).
1124
1145
  '''
1125
1146
  ps = self._ps
1126
- n = len(ps)
1127
- if n > 2: # len(ps) > 2
1147
+ n = len(ps) - 2
1148
+ if n > 0: # len(ps) > 2
1128
1149
  s = _psum(ps)
1129
- r = _fsum(self._ps_1(s)) or INT0
1130
- elif n > 1: # len(ps) == 2
1150
+ n = len(ps) - 2
1151
+ if n > 0:
1152
+ r = _fsum(self._ps_1(s)) or INT0
1153
+ return Fsum2Tuple(s, r)
1154
+ if n == 0: # len(ps) == 2
1131
1155
  ps[:] = _2ps(*_2sum(*ps))
1132
1156
  r, s = (INT0, ps[0]) if len(ps) != 2 else ps
1133
1157
  elif ps: # len(ps) == 1
@@ -1142,17 +1166,31 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1142
1166
  # '''(INTERNAL) Compress, squeeze the C{partials}.
1143
1167
  # '''
1144
1168
  # if len(self._ps) > 2:
1145
- # _ = self._fprs
1169
+ # _ = self._fprs2
1146
1170
  # return self
1147
1171
 
1172
+ def fset_(self, *xs):
1173
+ '''Replace this instance' value with C{xs}.
1174
+
1175
+ @arg xs: Optional, new values (C{scalar} or L{Fsum}
1176
+ instances), all positional.
1177
+
1178
+ @return: This instance (C{Fsum}).
1179
+
1180
+ @see: Method L{Fsum.fadd} for further details.
1181
+ '''
1182
+ self._ps[:] = 0,
1183
+ self._n = 0
1184
+ return self.fadd(xs) if xs else self._update()
1185
+
1148
1186
  def _fset(self, other, asis=True, n=0):
1149
1187
  '''(INTERNAL) Overwrite this instance with an other or a C{scalar}.
1150
1188
  '''
1151
1189
  if other is self:
1152
1190
  pass # from ._fmul, ._ftruediv and ._pow_scalar
1153
1191
  elif isinstance(other, Fsum):
1154
- self._n = n or other._n
1155
1192
  self._ps[:] = other._ps
1193
+ self._n = n or other._n
1156
1194
  # self._copy_RESIDUAL(other)
1157
1195
  # use or zap the C{Property_RO} values
1158
1196
  Fsum._fint2._update_from(self, other)
@@ -1162,8 +1200,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1162
1200
  s = other if asis else float(other)
1163
1201
  i = int(s) # see ._fint2
1164
1202
  t = i, ((s - i) or INT0)
1165
- self._n = n or 1
1166
1203
  self._ps[:] = s,
1204
+ self._n = n or 1
1167
1205
  # Property_ROs _fint2, _fprs and _fprs2 can't be a Property:
1168
1206
  # Property's _fset zaps the value just set by the @setter
1169
1207
  self.__dict__.update(_fint2=t, _fprs=s, _fprs2=Fsum2Tuple(s, INT0))
@@ -1172,14 +1210,15 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1172
1210
  return self
1173
1211
 
1174
1212
  def _fset_ps(self, other, n=0): # in .fmath
1175
- '''(INTERNAL) Set a known C{Fsum} or C{scalar}.
1213
+ '''(INTERNAL) Set partials from a known C{Fsum} or C{scalar}.
1176
1214
  '''
1177
1215
  if isinstance(other, Fsum):
1178
- self._n = n or other._n
1179
1216
  self._ps[:] = other._ps
1217
+ self._n = n or other._n
1180
1218
  else: # assert isscalar(other)
1181
- self._n = n or 1
1182
1219
  self._ps[:] = other,
1220
+ self._n = n or 1
1221
+ return self
1183
1222
 
1184
1223
  def fsub(self, xs=()):
1185
1224
  '''Subtract an iterable of C{scalar} or L{Fsum} instances from
@@ -1192,7 +1231,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1192
1231
 
1193
1232
  @see: Method L{Fsum.fadd}.
1194
1233
  '''
1195
- return self._facc(_2floats(xs, neg=True)) if xs else self # PYCHOK yield
1234
+ return self._facc_any_neg(xs) if xs else self
1196
1235
 
1197
1236
  def fsub_(self, *xs):
1198
1237
  '''Subtract all positional C{scalar} or L{Fsum} instances from
@@ -1205,7 +1244,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1205
1244
 
1206
1245
  @see: Method L{Fsum.fadd}.
1207
1246
  '''
1208
- return self._facc(_2floats(xs, origin=1, neg=True)) if xs else self # PYCHOK yield
1247
+ return self._facc_any_neg(xs, origin=1) if xs else self
1209
1248
 
1210
1249
  def _fsub(self, other, op):
1211
1250
  '''(INTERNAL) Apply C{B{self} -= B{other}}.
@@ -1233,23 +1272,30 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1233
1272
 
1234
1273
  @note: Accumulation can continue after summation.
1235
1274
  '''
1236
- f = self._facc(_2floats(xs)) if xs else self # PYCHOK yield
1275
+ f = self._facc_any(xs) if xs else self
1237
1276
  return f._fprs
1238
1277
 
1239
1278
  def fsum_(self, *xs):
1240
1279
  '''Add all positional C{scalar} or L{Fsum} instances and summate.
1241
1280
 
1242
- @arg xs: Values to add (C{scalar} or L{Fsum} instances),
1243
- all positional.
1281
+ @arg xs: Values to add (C{scalar} or L{Fsum} instances), all
1282
+ positional.
1244
1283
 
1245
1284
  @return: Precision running sum (C{float} or C{int}).
1246
1285
 
1247
- @see: Methods L{Fsum.fsum} and L{Fsum.fsumf_}.
1286
+ @see: Methods L{Fsum.fsum}, L{Fsum.Fsum_} and L{Fsum.fsumf_}.
1248
1287
  '''
1249
- f = self._facc(_2floats(xs, origin=1)) if xs else self # PYCHOK yield
1288
+ f = self._facc_any(xs, origin=1) if xs else self
1250
1289
  return f._fprs
1251
1290
 
1252
- def fsum2(self, xs=(), **name):
1291
+ def Fsum_(self, *xs):
1292
+ '''Like method L{Fsum.fsum_} but returning an L{Fsum}.
1293
+
1294
+ @return: Current, precision running sum (L{Fsum}).
1295
+ '''
1296
+ return self._facc_any(xs, origin=1)._copy_2(self.Fsum_)
1297
+
1298
+ def fsum2(self, xs=(), name=NN):
1253
1299
  '''Add more C{scalar} or L{Fsum} instances and return the
1254
1300
  current precision running sum and the C{residual}.
1255
1301
 
@@ -1265,57 +1311,70 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1265
1311
 
1266
1312
  @see: Methods L{Fsum.fint2}, L{Fsum.fsum} and L{Fsum.fsum2_}
1267
1313
  '''
1268
- f = self._facc(_2floats(xs)) if xs else self # PYCHOK yield
1314
+ f = self._facc_any(xs) if xs else self
1269
1315
  t = f._fprs2
1270
1316
  if name:
1271
- n = _xkwds_get(name, name=NN)
1272
- if n:
1273
- t = t.dup(name=n)
1317
+ t = t.dup(name=name)
1274
1318
  return t
1275
1319
 
1276
1320
  def fsum2_(self, *xs):
1277
1321
  '''Add any positional C{scalar} or L{Fsum} instances and return
1278
1322
  the precision running sum and the C{differential}.
1279
1323
 
1280
- @arg xs: Values to add (C{scalar} or L{Fsum} instances),
1281
- all positional.
1324
+ @arg xs: Values to add (C{scalar} or L{Fsum} instances), all
1325
+ positional.
1282
1326
 
1283
- @return: 2-Tuple C{(fsum, delta)} with the current precision
1284
- running C{fsum} and C{delta}, the difference with
1285
- the previous running C{fsum} (C{float}s).
1327
+ @return: 2Tuple C{(fsum, delta)} with the current, precision
1328
+ running C{fsum} like method L{Fsum.fsum} and C{delta},
1329
+ the difference with previous running C{fsum}, C{float}.
1286
1330
 
1287
1331
  @see: Methods L{Fsum.fsum_} and L{Fsum.fsum}.
1288
1332
  '''
1289
- p, r = self._fprs2
1290
- if xs:
1291
- s, t = self._facc(_2floats(xs, origin=1))._fprs2 # PYCHOK yield
1292
- return s, _fsum((s, -p, r, -t)) # ((s - p) + (r - t))
1293
- else: # PYCHOK no cover
1294
- return p, _0_0
1333
+ return self._fsum2f_any(xs, self._facc_any, origin=1)
1295
1334
 
1296
1335
  def fsumf_(self, *xs):
1297
- '''Like method L{Fsum.fsum_} but only for known C{float B{xs}}.
1336
+ '''Like method L{Fsum.fsum_} but only for I{known} C{float B{xs}}.
1298
1337
  '''
1299
- f = self._facc(xs) if xs else self # PYCHOK yield
1338
+ f = self._facc(xs) if xs else self
1300
1339
  return f._fprs
1301
1340
 
1302
- # ftruediv = __itruediv__ # for naming consistency
1341
+ def Fsumf_(self, *xs):
1342
+ '''Like method L{Fsum.Fsum_} but only for I{known} C{float B{xs}}.
1343
+ '''
1344
+ return self._facc(xs)._copy_2(self.Fsumf_)
1345
+
1346
+ def fsum2f_(self, *xs):
1347
+ '''Like method L{Fsum.fsum2_} but only for I{known} C{float B{xs}}.
1348
+ '''
1349
+ return self._fsum2f_any(xs, self._facc)
1350
+
1351
+ def _fsum2f_any(self, xs, _facc, **origin):
1352
+ '''(INTERNAL) Helper for L{Fsum.fsum2_} and L{Fsum.fsum2f_}.
1353
+ '''
1354
+ p, q = self._fprs2
1355
+ if xs:
1356
+ s, r = _facc(xs, **origin)._fprs2
1357
+ return s, _2delta(s - p, r - q) # _fsum(_1primed((s, -p, r, -q))
1358
+ else:
1359
+ return p, _0_0
1360
+
1361
+ # ftruediv = __itruediv__ # for naming consistency?
1303
1362
 
1304
1363
  def _ftruediv(self, other, op):
1305
1364
  '''(INTERNAL) Apply C{B{self} /= B{other}}.
1306
1365
  '''
1307
1366
  n = _1_0
1308
1367
  if isinstance(other, Fsum):
1309
- if other is self or other._fprs2 == self._fprs2:
1368
+ if other is self or other == self:
1310
1369
  return self._fset(_1_0) # n=len(self)
1311
1370
  d, r = other._fprs2
1312
1371
  if r:
1313
- if not d: # PYCHOK no cover
1314
- d = r
1315
- elif self._raiser(r, d):
1316
- raise self._ResidualError(op, other, r)
1317
- else:
1372
+ if d:
1373
+ if self._raiser(r, d):
1374
+ raise self._ResidualError(op, other, r)
1318
1375
  d, n = other.as_integer_ratio()
1376
+ else: # PYCHOK no cover
1377
+ d = r
1319
1378
  elif isscalar(other):
1320
1379
  d = other
1321
1380
  else: # PYCHOK no cover
@@ -1323,9 +1382,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1323
1382
  try:
1324
1383
  s = 0 if isinf(d) else (
1325
1384
  d if isnan(d) else self._finite(n / d))
1326
- except Exception as x:
1327
- E, t = _xError2(x)
1328
- raise self._Error(op, other, E, txt=t)
1385
+ except Exception as X:
1386
+ raise self._ErrorX(X, op, other)
1329
1387
  f = self._mul_scalar(s, _mul_op_) # handles 0, NAN, etc.
1330
1388
  return self._fset(f, asis=False)
1331
1389
 
@@ -1361,13 +1419,12 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1361
1419
  return s
1362
1420
 
1363
1421
  def is_exact(self):
1364
- '''Is this instance' current running C{fsum} considered to
1365
- be exact? (C{bool}).
1422
+ '''Is this instance' running C{fsum} considered to be exact? (C{bool}).
1366
1423
  '''
1367
1424
  return self.residual is INT0
1368
1425
 
1369
1426
  def is_integer(self):
1370
- '''Is this instance' current running sum C{integer}? (C{bool}).
1427
+ '''Is this instance' running sum C{integer}? (C{bool}).
1371
1428
 
1372
1429
  @see: Methods L{Fsum.fint} and L{Fsum.fint2}.
1373
1430
  '''
@@ -1393,7 +1450,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1393
1450
  '''
1394
1451
  # assert isinstance(other, Fsum)
1395
1452
  if self._ps and other._ps:
1396
- f = _Fsum_xs(self._ps_mul(op, *other._ps))
1453
+ f = self._ps_mul(op, *other._ps) # NO ._2scalar
1397
1454
  else:
1398
1455
  f = _0_0
1399
1456
  return f
@@ -1405,20 +1462,20 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1405
1462
  if self._ps and self._finite(factor, op):
1406
1463
  f = self if factor == _1_0 else (
1407
1464
  self._neg if factor == _N_1_0 else
1408
- _Fsum_xs(self._ps_mul(op, factor))) # PYCHOK indent
1465
+ self._ps_mul(op, factor)._2scalar)
1409
1466
  else:
1410
1467
  f = _0_0
1411
1468
  return f
1412
1469
 
1413
1470
  @property_RO
1414
1471
  def _neg(self):
1415
- '''(INTERNAL) Return C{-self}.
1472
+ '''(INTERNAL) Return C{Fsum(-self)} or scalar C{NEG0}.
1416
1473
  '''
1417
- return _Fsum_ps(*self._ps_neg) if self._ps else NEG0
1474
+ return _Psum(self._ps_neg) if self._ps else NEG0
1418
1475
 
1419
1476
  @property_RO
1420
1477
  def partials(self):
1421
- '''Get this instance' current partial sums (C{tuple} of C{float}s and/or C{int}s).
1478
+ '''Get this instance' current, partial sums (C{tuple} of C{float}s).
1422
1479
  '''
1423
1480
  return tuple(self._ps)
1424
1481
 
@@ -1434,7 +1491,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1434
1491
 
1435
1492
  @note: If B{C{mod}} is given as C{None}, the result will be an
1436
1493
  C{integer} L{Fsum} provided this instance C{is_integer}
1437
- or set to C{integer} with L{Fsum.fint}.
1494
+ or set to C{integer} by an L{Fsum.fint} call.
1438
1495
 
1439
1496
  @see: Methods L{Fsum.__ipow__}, L{Fsum.fint} and L{Fsum.is_integer}.
1440
1497
  '''
@@ -1444,54 +1501,39 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1444
1501
  def _pow_0_1(self, x, other):
1445
1502
  '''(INTERNAL) Return B{C{self}**1} or C{B{self}**0 == 1.0}.
1446
1503
  '''
1447
- return self if x else (1 if self.is_integer() and isint(other) else _1_0)
1448
-
1449
- def _pow_2(self, b, x, other, op):
1450
- '''(INTERNAL) 2-arg C{pow(B{b}, scalar B{x})} embellishing errors.
1451
- '''
1452
- # assert isscalar(b) and isscalar(x)
1453
- try: # type(s) == type(x) if x in (_1_0, 1)
1454
- s = pow(b, x) # -1**2.3 == -(1**2.3)
1455
- if not iscomplex(s):
1456
- return self._finite(s) # 0**INF == 0.0, 1**INF==1.0
1457
- # neg**frac == complex in Python 3+, but ValueError in 2-
1458
- E, t = _ValueError, _strcomplex(s, b, x) # PYCHOK no cover
1459
- except Exception as x:
1460
- E, t = _xError2(x)
1461
- raise self._Error(op, other, E, txt=t)
1462
-
1463
- def _pow_3(self, other, mod, op):
1464
- '''(INTERNAL) 3-arg C{pow(B{self}, B{other}, int B{mod} or C{None})}.
1465
- '''
1466
- b, r = self._fprs2 if mod is None else self._fint2
1467
- if r and self._raiser(r, b):
1468
- t = _non_zero_ if mod is None else _integer_
1469
- E, t = ResidualError, _stresidual(t, r, mod=mod)
1470
- else:
1471
- try: # b, other, mod all C{int}, unless C{mod} is C{None}
1472
- x = _2scalar(other, _raiser=self._raiser)
1473
- s = pow(b, x, mod)
1474
- if not iscomplex(s):
1475
- return self._finite(s)
1504
+ return self if x else (1 if isint(other) and self.is_integer() else _1_0)
1505
+
1506
+ def _pow_2_3(self, b, x, other, op, *mod):
1507
+ '''(INTERNAL) 2-arg C{pow(B{b}, scalar B{x})} and 3-arg C{pow(B{b},
1508
+ B{x}, int B{mod} or C{None})}, embellishing errors.
1509
+ '''
1510
+ try:
1511
+ if mod: # b, x, mod all C{int}, unless C{mod} is C{None}
1512
+ m = mod[0]
1513
+ b, r = b._fprs2 if m is None else b._fint2
1514
+ if r and self._raiser(r, b):
1515
+ t = _non_zero_ if m is None else _integer_
1516
+ raise ResidualError(_stresidual(t, r, mod=m), txt=None)
1517
+ x = _2scalar(x, _raiser=self._raiser, mod=m)
1518
+ # 0**INF == 0.0, 1**INF == 1.0, -1**2.3 == -(1**2.3)
1519
+ s = pow(b, x, *mod)
1520
+ if iscomplex(s):
1476
1521
  # neg**frac == complex in Python 3+, but ValueError in 2-
1477
- E, t = _ValueError, _strcomplex(s, b, x, mod) # PYCHOK no cover
1478
- except Exception as x:
1479
- E, t = _xError2(x)
1480
- t = _COMMASPACE_(Fmt.PARENSPACED(mod=mod), t)
1481
- raise self._Error(op, other, E, txt=t)
1522
+ raise ValueError(_strcomplex(s, b, x, *mod))
1523
+ return self._finite(s)
1524
+ except Exception as X:
1525
+ raise self._ErrorX(X, op, other, *mod)
1482
1526
 
1483
1527
  def _pow_any(self, other, unused, op):
1484
1528
  '''Return C{B{self} ** B{other}}.
1485
1529
  '''
1486
1530
  if isinstance(other, Fsum):
1487
1531
  x, r = other._fprs2
1532
+ if r and self._raiser(r, x):
1533
+ raise self._ResidualError(op, other, r)
1488
1534
  f = self._pow_scalar(x, other, op)
1489
1535
  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
1536
+ f *= self._pow_scalar(r, other, op)
1495
1537
  elif isscalar(other):
1496
1538
  x = self._finite(other, op)
1497
1539
  f = self._pow_scalar(x, other, op)
@@ -1505,32 +1547,28 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1505
1547
  # assert isint(x) and x >= 0
1506
1548
  ps = self._ps
1507
1549
  if len(ps) > 1:
1508
- f = self
1550
+ _mul_Fsum = Fsum._mul_Fsum
1509
1551
  if x > 4:
1510
- m = 1 # single-bit mask
1511
- if (x & m):
1512
- x -= m # x ^= m
1513
- else:
1514
- f = _Fsum_ps(_1_0)
1515
1552
  p = self
1516
- while x:
1517
- p = p._mul_Fsum(p, op) # p **= 2
1518
- m += m # m <<= 1
1519
- if (x & m):
1520
- x -= m # x ^= m
1521
- f = f._mul_Fsum(p, op) # f *= p
1553
+ f = self if (x & 1) else _Psum_1()
1554
+ m = x >> 1 # // 2
1555
+ while m:
1556
+ p = _mul_Fsum(p, p, op) # p **= 2
1557
+ if (m & 1):
1558
+ f = _mul_Fsum(f, p, op) # f *= p
1559
+ m >>= 1 # //= 2
1522
1560
  elif x > 1: # self**2, 3 or 4
1523
- f = f._mul_Fsum(f, op)
1561
+ f = _mul_Fsum(self, self, op)
1524
1562
  if x > 2: # self**3 or 4
1525
- p = self if x < 4 else f
1526
- f = f._mul_Fsum(p, op)
1563
+ p = self if x < 4 else f
1564
+ f = _mul_Fsum(f, p, op)._2scalar
1527
1565
  else: # self**1 or self**0 == 1 or _1_0
1528
- f = f._pow_0_1(x, other)
1566
+ f = self._pow_0_1(x, other)
1529
1567
  elif ps: # self._ps[0]**x
1530
- f = self._pow_2(ps[0], x, other, op)
1568
+ f = self._pow_2_3(ps[0], x, other, op)
1531
1569
  else: # PYCHOK no cover
1532
1570
  # 0**pos_int == 0, but 0**0 == 1
1533
- f = 0 if x else 1 # like ._fprs
1571
+ f = 0 if x else 1
1534
1572
  return f
1535
1573
 
1536
1574
  def _pow_scalar(self, x, other, op):
@@ -1548,13 +1586,13 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1548
1586
  # assert x < 0 # < -1
1549
1587
  s, r = f._fprs2 if isinstance(f, Fsum) else (f, 0)
1550
1588
  if r:
1551
- return _Fsum_ps(_1_0)._ftruediv(f, op)
1589
+ return _Psum_1()._ftruediv(f, op)
1552
1590
  # use **= -1 for the CPython float_pow
1553
1591
  # error if s is zero, and not s = 1 / s
1554
1592
  x = -1
1555
- elif x < 0: # self**-1 == 1 / self
1593
+ elif x < 0: # == -1: self**(-1) == 1 / self
1556
1594
  if r:
1557
- return _Fsum_ps(_1_0)._ftruediv(self, op)
1595
+ return _Psum_1()._ftruediv(self, op)
1558
1596
  else: # self**1 or self**0
1559
1597
  return self._pow_0_1(x, other) # self, 1 or 1.0
1560
1598
  elif not isscalar(x): # assert ...
@@ -1564,32 +1602,55 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1564
1602
  t = _stresidual(_non_zero_, r, fractional_power=x)
1565
1603
  raise self._Error(op, other, ResidualError, txt=t)
1566
1604
  # assert isscalar(s) and isscalar(x)
1567
- return self._pow_2(s, x, other, op)
1605
+ return self._pow_2_3(s, x, other, op)
1568
1606
 
1569
1607
  def _ps_1(self, *less):
1570
1608
  '''(INTERNAL) Yield partials, 1-primed and subtract any C{less}.
1571
1609
  '''
1572
1610
  yield _1_0
1573
1611
  for p in self._ps:
1574
- if p:
1575
- yield p
1612
+ yield p
1576
1613
  for p in less:
1577
- if p:
1578
- yield -p
1614
+ yield -p
1579
1615
  yield _N_1_0
1580
1616
 
1581
- def _ps_mul(self, op, *factors): # see .fmath.Fhorner
1582
- '''(INTERNAL) Yield all C{partials} times each B{C{factor}},
1583
- in total, up to C{len(partials) * len(factors)} items.
1617
+ def _ps_acc(self, ps, xs, up=True):
1618
+ '''(INTERNAL) Accumulate all scalar C{xs} into C{ps}.
1619
+ '''
1620
+ n = 0
1621
+ _2s = _2sum
1622
+ for x in (tuple(xs) if xs is ps else xs):
1623
+ # assert isscalar(x) and _isfinite(x)
1624
+ if x:
1625
+ i = 0
1626
+ for p in ps:
1627
+ x, p = _2s(x, p)
1628
+ if p:
1629
+ ps[i] = p
1630
+ i += 1
1631
+ ps[i:] = (x,) if x else ()
1632
+ n += 1
1633
+ if n:
1634
+ self._n += n
1635
+ # Fsum._ps_max = max(Fsum._ps_max, len(ps))
1636
+ if up:
1637
+ self._update()
1638
+ return ps
1639
+
1640
+ def _ps_mul(self, op, *factors):
1641
+ '''(INTERNAL) Multiply this instance' C{partials} with
1642
+ each of the B{C{factors}}, all known to be scalar.
1584
1643
  '''
1585
- ps = self._ps # tuple(self._ps)
1586
- if len(ps) < len(factors):
1587
- ps, factors = factors, ps
1588
- _f = _isfinite
1589
- for f in factors:
1590
- for p in ps:
1591
- p *= f
1592
- yield p if _f(p) else self._finite(p, op)
1644
+ def _pfs(ps, fs):
1645
+ if len(ps) < len(fs):
1646
+ ps, fs = fs, ps
1647
+ _fin = _isfinite
1648
+ for f in fs:
1649
+ for p in ps:
1650
+ p *= f
1651
+ yield p if _fin(p) else self._finite(p, op)
1652
+
1653
+ return _Psum(self._ps_acc([], _pfs(self._ps, factors)))
1593
1654
 
1594
1655
  @property_RO
1595
1656
  def _ps_neg(self):
@@ -1598,6 +1659,12 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1598
1659
  for p in self._ps:
1599
1660
  yield -p
1600
1661
 
1662
+ def _raiser(self, r, s):
1663
+ '''(INTERNAL) Does ratio C{r / s} exceed threshold?
1664
+ '''
1665
+ self._ratio = t = fabs((r / s) if s else r)
1666
+ return t > self._RESIDUAL
1667
+
1601
1668
  @property_RO
1602
1669
  def real(self):
1603
1670
  '''Get the C{real} part of this instance (C{float}).
@@ -1620,12 +1687,6 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1620
1687
  '''
1621
1688
  return self._fprs2.residual
1622
1689
 
1623
- def _raiser(self, r, s):
1624
- '''(INTERNAL) Does ratio C{r / s} exceed threshold?
1625
- '''
1626
- self._ratio = t = fabs((r / s) if s else r)
1627
- return t > self._RESIDUAL
1628
-
1629
1690
  def RESIDUAL(self, *threshold):
1630
1691
  '''Get and set this instance' I{ratio} for raising L{ResidualError}s,
1631
1692
  overriding the default from env variable C{PYGEODESY_FSUM_RESIDUAL}.
@@ -1636,12 +1697,12 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1636
1697
  C{PYGEODESY_FSUM_RESIDUAL} or if omitted, keep the
1637
1698
  current setting.
1638
1699
 
1639
- @return: The previous C{RESIDUAL} setting (C{float}).
1700
+ @return: The previous C{RESIDUAL} setting (C{float}), default C{0}.
1640
1701
 
1641
1702
  @raise ValueError: Negative B{C{threshold}}.
1642
1703
 
1643
- @note: A L{ResidualError} is thrown if the non-zero I{ratio}
1644
- C{residual} / C{fsum} exceeds the B{C{threshold}}.
1704
+ @note: L{ResidualError}s will be thrown if the non-zero I{ratio}
1705
+ C{residual / fsum} exceeds the B{C{threshold}}.
1645
1706
  '''
1646
1707
  r = self._RESIDUAL
1647
1708
  if threshold:
@@ -1659,10 +1720,17 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1659
1720
  '''(INTERNAL) Non-zero B{C{residual}} etc.
1660
1721
  '''
1661
1722
  t = _stresidual(_non_zero_, residual, ratio=self._ratio,
1662
- RESIDUAL=self._RESIDUAL)
1723
+ RESIDUAL=self._RESIDUAL)
1663
1724
  t = t.replace(_COMMASPACE_R_, _exceeds_R_)
1664
1725
  return self._Error(op, other, ResidualError, txt=t)
1665
1726
 
1727
+ @property_RO
1728
+ def _2scalar(self):
1729
+ '''(INTERNAL) Get this instance as C{scalar} or C{as-is}.
1730
+ '''
1731
+ s, r = self._fprs2
1732
+ return self if r else s
1733
+
1666
1734
  def signOf(self, res=True):
1667
1735
  '''Determine the sign of this instance.
1668
1736
 
@@ -1671,8 +1739,8 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1671
1739
 
1672
1740
  @return: The sign (C{int}, -1, 0 or +1).
1673
1741
  '''
1674
- s, r = self._fprs2 if res else (self._fprs, 0)
1675
- return _signOf(s, -r)
1742
+ s, r = self._fprs2
1743
+ return _signOf(s, (-r) if res else 0)
1676
1744
 
1677
1745
  def toRepr(self, **prec_sep_fmt_lenc): # PYCHOK signature
1678
1746
  '''Return this C{Fsum} instance as representation.
@@ -1721,7 +1789,7 @@ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
1721
1789
  # Fsum._fint2._update(self)
1722
1790
  # Fsum._fprs ._update(self)
1723
1791
  # Fsum._fprs2._update(self)
1724
- return self
1792
+ return self # for .fset_
1725
1793
 
1726
1794
  def _ValueError(self, op, other, **txt): # PYCHOK no cover
1727
1795
  '''(INTERNAL) Return a C{ValueError}.
@@ -1766,21 +1834,21 @@ class Fsum2Tuple(_NamedTuple):
1766
1834
  _Units_ = (_Float_Int, _Float_Int)
1767
1835
 
1768
1836
  @Property_RO
1769
- def Fsum(self):
1770
- '''Get this L{Fsum2Tuple} as an L{Fsum}.
1837
+ def _Fsum(self):
1838
+ '''(INTERNAL) Get this L{Fsum2Tuple} as an L{Fsum}.
1771
1839
  '''
1772
1840
  s, r = map(float, self)
1773
- return _Fsum_ps(*_2ps(s, r), name=self.name)
1841
+ return _Psum(_2ps(s, r), name=self.name)
1774
1842
 
1775
1843
  def is_exact(self):
1776
1844
  '''Is this L{Fsum2Tuple} considered to be exact? (C{bool}).
1777
1845
  '''
1778
- return self.Fsum.is_exact()
1846
+ return self._Fsum.is_exact()
1779
1847
 
1780
1848
  def is_integer(self):
1781
1849
  '''Is this L{Fsum2Tuple} C{integer}? (C{bool}).
1782
1850
  '''
1783
- return self.Fsum.is_integer()
1851
+ return self._Fsum.is_integer()
1784
1852
 
1785
1853
 
1786
1854
  class ResidualError(_ValueError):
@@ -1792,23 +1860,6 @@ class ResidualError(_ValueError):
1792
1860
  pass
1793
1861
 
1794
1862
 
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
-
1812
1863
  try:
1813
1864
  from math import fsum as _fsum # precision IEEE-754 sum, Python 2.6+
1814
1865
 
@@ -1820,16 +1871,18 @@ try:
1820
1871
 
1821
1872
  Fsum._math_fsum = _sum = _fsum # PYCHOK exported
1822
1873
 
1823
- if _getenv('PYGEODESY_FSUM_PARTIALS', _fsum.__name__) == _fsum.__name__:
1874
+ if _getenv('PYGEODESY_FSUM_PARTIALS', NN) == _fsum.__name__:
1824
1875
  _psum = _fsum # PYCHOK re-def
1825
1876
 
1826
1877
  except ImportError:
1827
- _sum = sum # Fsum(NAN) exception fall-back
1878
+ _sum = sum # Fsum(NAN) exception fall-back, in .elliptic
1828
1879
 
1829
1880
  def _fsum(xs):
1830
1881
  '''(INTERNAL) Precision summation, Python 2.5-.
1831
1882
  '''
1832
- return Fsum(name=_fsum.__name__).fsum(xs) if xs else _0_0
1883
+ f = Fsum()
1884
+ f.name = _fsum.__name__
1885
+ return f.fsum(xs)
1833
1886
 
1834
1887
 
1835
1888
  def fsum(xs, floats=False):
@@ -1837,8 +1890,8 @@ def fsum(xs, floats=False):
1837
1890
 
1838
1891
  @arg xs: Iterable, list, tuple, etc. of values (C{scalar} or L{Fsum}
1839
1892
  instances).
1840
- @kwarg floats: Optionally, use C{B{floats}=True} iff I{all} B{C{xs}}
1841
- are known to be C{float}.
1893
+ @kwarg floats: Use C{B{floats}=True} iff I{all} B{C{xs}} are known
1894
+ to be C{float} scalars (C{bool}).
1842
1895
 
1843
1896
  @return: Precision C{fsum} (C{float}).
1844
1897
 
@@ -1848,8 +1901,8 @@ def fsum(xs, floats=False):
1848
1901
 
1849
1902
  @raise ValueError: Invalid or non-finite B{C{xs}} value.
1850
1903
 
1851
- @note: Exceptions and I{non-finite} handling may differ if not
1852
- based on Python's C{math.fsum}.
1904
+ @note: Exception and I{non-finite} handling may differ if not based
1905
+ on Python's C{math.fsum}.
1853
1906
 
1854
1907
  @see: Class L{Fsum} and methods L{Fsum.fsum} and L{Fsum.fadd}.
1855
1908
  '''
@@ -1861,8 +1914,8 @@ def fsum_(*xs, **floats):
1861
1914
 
1862
1915
  @arg xs: Values to be added (C{scalar} or L{Fsum} instances), all
1863
1916
  positional.
1864
- @kwarg floats: Optionally, use C{B{floats}=True} iff I{all} B{C{xs}}
1865
- are known to be C{float}.
1917
+ @kwarg floats: Use C{B{floats}=True} iff I{all} B{C{xs}} are known
1918
+ to be C{float} scalars (C{bool}).
1866
1919
 
1867
1920
  @return: Precision C{fsum} (C{float}).
1868
1921
 
@@ -1879,12 +1932,12 @@ def fsumf_(*xs):
1879
1932
 
1880
1933
 
1881
1934
  def fsum1(xs, floats=False):
1882
- '''Precision floating point summation of a few arguments, 1-primed.
1935
+ '''Precision floating point summation, 1-primed.
1883
1936
 
1884
1937
  @arg xs: Iterable, list, tuple, etc. of values (C{scalar} or L{Fsum}
1885
1938
  instances).
1886
- @kwarg floats: Optionally, use C{B{floats}=True} iff I{all} B{C{xs}}
1887
- are known to be C{float}.
1939
+ @kwarg floats: Use C{B{floats}=True} iff I{all} B{C{xs}} are known
1940
+ to be C{float}.
1888
1941
 
1889
1942
  @return: Precision C{fsum} (C{float}).
1890
1943
 
@@ -1894,12 +1947,12 @@ def fsum1(xs, floats=False):
1894
1947
 
1895
1948
 
1896
1949
  def fsum1_(*xs, **floats):
1897
- '''Precision floating point summation of a few arguments, 1-primed.
1950
+ '''Precision floating point summation, 1-primed.
1898
1951
 
1899
1952
  @arg xs: Values to be added (C{scalar} or L{Fsum} instances), all
1900
1953
  positional.
1901
- @kwarg floats: Optionally, use C{B{floats}=True} iff I{all} B{C{xs}}
1902
- are known to be C{float}.
1954
+ @kwarg floats: Use C{B{floats}=True} iff I{all} B{C{xs}} are known
1955
+ to be C{float} scalars (C{bool}).
1903
1956
 
1904
1957
  @return: Precision C{fsum} (C{float}).
1905
1958
 
@@ -1910,11 +1963,40 @@ def fsum1_(*xs, **floats):
1910
1963
 
1911
1964
 
1912
1965
  def fsum1f_(*xs):
1913
- '''Precision floating point summation L{fsum1_}C{(*xs, floats=True)}.
1966
+ '''Precision floating point summation, L{fsum1_}C{(*xs, floats=True)}.
1914
1967
  '''
1915
1968
  return _fsum(_1primed(xs)) if xs else _0_0
1916
1969
 
1917
1970
 
1971
+ if __name__ == '__main__':
1972
+
1973
+ # usage: [env PYGEODESY_FSUM_PARTIALS=fsum] python3 -m pygeodesy.fsums
1974
+
1975
+ def _test(n):
1976
+ # copied from Hettinger, see L{Fsum} reference
1977
+ from pygeodesy import printf
1978
+ from random import gauss, random, shuffle
1979
+
1980
+ printf(_fsum.__name__, end=_COMMASPACE_)
1981
+ printf(_psum.__name__, end=_COMMASPACE_)
1982
+
1983
+ F = Fsum()
1984
+ if F.is_math_fsum():
1985
+ c = (7, 1e100, -7, -1e100, -9e-20, 8e-20) * 10
1986
+ for _ in range(n):
1987
+ t = list(c)
1988
+ s = 0
1989
+ for _ in range(n * 8):
1990
+ v = gauss(0, random())**7 - s
1991
+ t.append(v)
1992
+ s += v
1993
+ shuffle(t)
1994
+ assert float(F.fset_(*t)) == _fsum(t)
1995
+ printf(_DOT_, end=NN)
1996
+ printf(NN)
1997
+
1998
+ _test(128)
1999
+
1918
2000
  # **) MIT License
1919
2001
  #
1920
2002
  # Copyright (C) 2016-2024 -- mrJean1 at Gmail -- All Rights Reserved.