pygeodesy 24.3.24__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. PyGeodesy-24.3.24.dist-info/METADATA +272 -0
  2. PyGeodesy-24.3.24.dist-info/RECORD +115 -0
  3. PyGeodesy-24.3.24.dist-info/WHEEL +6 -0
  4. PyGeodesy-24.3.24.dist-info/top_level.txt +1 -0
  5. pygeodesy/LICENSE +21 -0
  6. pygeodesy/__init__.py +615 -0
  7. pygeodesy/__main__.py +103 -0
  8. pygeodesy/albers.py +867 -0
  9. pygeodesy/auxilats/_CX_4.py +218 -0
  10. pygeodesy/auxilats/_CX_6.py +314 -0
  11. pygeodesy/auxilats/_CX_8.py +475 -0
  12. pygeodesy/auxilats/__init__.py +54 -0
  13. pygeodesy/auxilats/__main__.py +86 -0
  14. pygeodesy/auxilats/auxAngle.py +548 -0
  15. pygeodesy/auxilats/auxDLat.py +302 -0
  16. pygeodesy/auxilats/auxDST.py +296 -0
  17. pygeodesy/auxilats/auxLat.py +848 -0
  18. pygeodesy/auxilats/auxily.py +272 -0
  19. pygeodesy/azimuthal.py +1150 -0
  20. pygeodesy/basics.py +892 -0
  21. pygeodesy/booleans.py +2031 -0
  22. pygeodesy/cartesianBase.py +1062 -0
  23. pygeodesy/clipy.py +704 -0
  24. pygeodesy/constants.py +516 -0
  25. pygeodesy/css.py +660 -0
  26. pygeodesy/datums.py +752 -0
  27. pygeodesy/deprecated/__init__.py +61 -0
  28. pygeodesy/deprecated/bases.py +40 -0
  29. pygeodesy/deprecated/classes.py +262 -0
  30. pygeodesy/deprecated/consterns.py +54 -0
  31. pygeodesy/deprecated/datum.py +40 -0
  32. pygeodesy/deprecated/functions.py +375 -0
  33. pygeodesy/deprecated/nvector.py +48 -0
  34. pygeodesy/deprecated/rhumbBase.py +32 -0
  35. pygeodesy/deprecated/rhumbaux.py +33 -0
  36. pygeodesy/deprecated/rhumbsolve.py +33 -0
  37. pygeodesy/deprecated/rhumbx.py +33 -0
  38. pygeodesy/dms.py +986 -0
  39. pygeodesy/ecef.py +1348 -0
  40. pygeodesy/elevations.py +279 -0
  41. pygeodesy/ellipsoidalBase.py +1224 -0
  42. pygeodesy/ellipsoidalBaseDI.py +913 -0
  43. pygeodesy/ellipsoidalExact.py +343 -0
  44. pygeodesy/ellipsoidalGeodSolve.py +343 -0
  45. pygeodesy/ellipsoidalKarney.py +403 -0
  46. pygeodesy/ellipsoidalNvector.py +685 -0
  47. pygeodesy/ellipsoidalVincenty.py +590 -0
  48. pygeodesy/ellipsoids.py +2476 -0
  49. pygeodesy/elliptic.py +1198 -0
  50. pygeodesy/epsg.py +243 -0
  51. pygeodesy/errors.py +804 -0
  52. pygeodesy/etm.py +1190 -0
  53. pygeodesy/fmath.py +1013 -0
  54. pygeodesy/formy.py +1818 -0
  55. pygeodesy/frechet.py +865 -0
  56. pygeodesy/fstats.py +760 -0
  57. pygeodesy/fsums.py +1898 -0
  58. pygeodesy/gars.py +358 -0
  59. pygeodesy/geodesicw.py +581 -0
  60. pygeodesy/geodesicx/_C4_24.py +1699 -0
  61. pygeodesy/geodesicx/_C4_27.py +2395 -0
  62. pygeodesy/geodesicx/_C4_30.py +3301 -0
  63. pygeodesy/geodesicx/__init__.py +48 -0
  64. pygeodesy/geodesicx/__main__.py +91 -0
  65. pygeodesy/geodesicx/gx.py +1382 -0
  66. pygeodesy/geodesicx/gxarea.py +535 -0
  67. pygeodesy/geodesicx/gxbases.py +154 -0
  68. pygeodesy/geodesicx/gxline.py +669 -0
  69. pygeodesy/geodsolve.py +426 -0
  70. pygeodesy/geohash.py +914 -0
  71. pygeodesy/geoids.py +1884 -0
  72. pygeodesy/hausdorff.py +892 -0
  73. pygeodesy/heights.py +1155 -0
  74. pygeodesy/interns.py +687 -0
  75. pygeodesy/iters.py +545 -0
  76. pygeodesy/karney.py +919 -0
  77. pygeodesy/ktm.py +633 -0
  78. pygeodesy/latlonBase.py +1766 -0
  79. pygeodesy/lazily.py +960 -0
  80. pygeodesy/lcc.py +684 -0
  81. pygeodesy/ltp.py +1107 -0
  82. pygeodesy/ltpTuples.py +1563 -0
  83. pygeodesy/mgrs.py +721 -0
  84. pygeodesy/named.py +1324 -0
  85. pygeodesy/namedTuples.py +683 -0
  86. pygeodesy/nvectorBase.py +695 -0
  87. pygeodesy/osgr.py +781 -0
  88. pygeodesy/points.py +1686 -0
  89. pygeodesy/props.py +628 -0
  90. pygeodesy/resections.py +1048 -0
  91. pygeodesy/rhumb/__init__.py +46 -0
  92. pygeodesy/rhumb/aux_.py +397 -0
  93. pygeodesy/rhumb/bases.py +1148 -0
  94. pygeodesy/rhumb/ekx.py +563 -0
  95. pygeodesy/rhumb/solve.py +572 -0
  96. pygeodesy/simplify.py +647 -0
  97. pygeodesy/solveBase.py +472 -0
  98. pygeodesy/sphericalBase.py +724 -0
  99. pygeodesy/sphericalNvector.py +1264 -0
  100. pygeodesy/sphericalTrigonometry.py +1447 -0
  101. pygeodesy/streprs.py +627 -0
  102. pygeodesy/trf.py +2079 -0
  103. pygeodesy/triaxials.py +1484 -0
  104. pygeodesy/units.py +969 -0
  105. pygeodesy/unitsBase.py +349 -0
  106. pygeodesy/ups.py +538 -0
  107. pygeodesy/utily.py +1231 -0
  108. pygeodesy/utm.py +762 -0
  109. pygeodesy/utmups.py +318 -0
  110. pygeodesy/utmupsBase.py +517 -0
  111. pygeodesy/vector2d.py +785 -0
  112. pygeodesy/vector3d.py +968 -0
  113. pygeodesy/vector3dBase.py +1049 -0
  114. pygeodesy/webmercator.py +383 -0
  115. pygeodesy/wgrs.py +439 -0
pygeodesy/fsums.py ADDED
@@ -0,0 +1,1898 @@
1
+
2
+ # -*- coding: utf-8 -*-
3
+
4
+ u'''Class L{Fsum} for precision floating point summation and I{running}
5
+ summation based on, respectively similar to Python's C{math.fsum}.
6
+
7
+ Generally, an L{Fsum} instance is considered a C{float} plus a small or zero
8
+ C{residual} value, see property L{Fsum.residual}. However, there are several
9
+ C{integer} L{Fsum} cases, for example the result of C{ceil}, C{floor},
10
+ C{Fsum.__floordiv__} and methods L{Fsum.fint} and L{Fsum.fint2}.
11
+
12
+ Also, L{Fsum} methods L{Fsum.pow}, L{Fsum.__ipow__}, L{Fsum.__pow__} and
13
+ L{Fsum.__rpow__} return a (very long) C{int} if invoked with optional argument
14
+ C{mod} set to C{None}. The C{residual} of an C{integer} L{Fsum} may be between
15
+ C{-1.0} and C{+1.0}, including C{INT0} if considered to be I{exact}.
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__}.
25
+ '''
26
+ # make sure int/int division yields float quotient, see .basics
27
+ from __future__ import division as _; del _ # PYCHOK semicolon
28
+
29
+ from pygeodesy.basics import iscomplex, isint, isscalar, itemsorted, \
30
+ signOf, _signOf, _xisscalar
31
+ from pygeodesy.constants import INT0, _isfinite, isinf, isnan, _pos_self, \
32
+ _0_0, _1_0, _N_1_0, Float, Int
33
+ from pygeodesy.errors import _OverflowError, _TypeError, _ValueError, _xError2, \
34
+ _xkwds_get, _ZeroDivisionError
35
+ from pygeodesy.interns import NN, _arg_, _COMMASPACE_, _DASH_, _DOT_, _EQUAL_, \
36
+ _exceeds_, _from_, _iadd_op_, _LANGLE_, _negative_, \
37
+ _NOTEQUAL_, _not_finite_, _not_scalar_, _PERCENT_, \
38
+ _PLUS_, _R_, _RANGLE_, _SLASH_, _SPACE_, _STAR_, _UNDER_
39
+ from pygeodesy.lazily import _ALL_LAZY, _getenv, _sys_version_info2
40
+ from pygeodesy.named import _Named, _NamedTuple, _NotImplemented, Fmt, unstr
41
+ from pygeodesy.props import _allPropertiesOf_n, deprecated_property_RO, \
42
+ Property_RO, property_RO
43
+ # from pygeodesy.streprs import Fmt, unstr # from .named
44
+ # from pygeodesy.units import Float, Int # from .constants
45
+
46
+ from math import ceil as _ceil, fabs, floor as _floor # PYCHOK used! .ltp
47
+
48
+ __all__ = _ALL_LAZY.fsums
49
+ __version__ = '24.03.18'
50
+
51
+ _add_op_ = _PLUS_ # in .auxilats.auxAngle
52
+ _eq_op_ = _EQUAL_ * 2 # _DEQUAL_
53
+ _COMMASPACE_R_ = _COMMASPACE_ + _R_
54
+ _exceeds_R_ = _SPACE_ + _exceeds_(_R_)
55
+ _floordiv_op_ = _SLASH_ * 2 # _DSLASH_
56
+ _fset_op_ = _EQUAL_
57
+ _ge_op_ = _RANGLE_ + _EQUAL_
58
+ _gt_op_ = _RANGLE_
59
+ _integer_ = 'integer'
60
+ _le_op_ = _LANGLE_ + _EQUAL_
61
+ _lt_op_ = _LANGLE_
62
+ _mod_op_ = _PERCENT_
63
+ _mul_op_ = _STAR_
64
+ _ne_op_ = _NOTEQUAL_
65
+ _non_zero_ = 'non-zero'
66
+ _pow_op_ = _STAR_ * 2 # _DSTAR_, in .fmath
67
+ _sub_op_ = _DASH_ # in .auxilats.auxAngle, .fsums
68
+ _truediv_op_ = _SLASH_
69
+ _divmod_op_ = _floordiv_op_ + _mod_op_
70
+ _isub_op_ = _sub_op_ + _fset_op_ # in .auxilats.auxAngle, .fsums
71
+
72
+
73
+ def _2float(index=None, **name_value): # in .fmath, .fstats
74
+ '''(INTERNAL) Raise C{TypeError} or C{ValueError} if not scalar or infinite.
75
+ '''
76
+ n, v = name_value.popitem() # _xkwds_item2(name_value)
77
+ try:
78
+ v = float(v)
79
+ if _isfinite(v):
80
+ 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)
85
+
86
+
87
+ def _2floats(xs, origin=0, sub=False):
88
+ '''(INTERNAL) Yield each B{C{xs}} as a C{float}.
89
+ '''
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)
109
+
110
+
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)
135
+
136
+
137
+ def _1primed(xs):
138
+ '''(INTERNAL) 1-Prime the summation of C{xs}
139
+ arguments I{known} to be C{finite float}.
140
+ '''
141
+ yield _1_0
142
+ for x in xs:
143
+ if x:
144
+ yield x
145
+ yield _N_1_0
146
+
147
+
148
+ def _psum(ps): # PYCHOK used!
149
+ '''(INTERNAL) Partials summation updating C{ps}, I{overridden below}.
150
+ '''
151
+ i = len(ps) - 1 # len(ps) > 2
152
+ s = ps[i]
153
+ _2s = _2sum
154
+ while i > 0:
155
+ i -= 1
156
+ s, r = _2s(s, ps[i])
157
+ 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]
169
+ return s
170
+
171
+
172
+ def _2scalar(other, _raiser=None):
173
+ '''(INTERNAL) Return B{C{other}} as C{int}, C{float} or C{as-is}.
174
+ '''
175
+ if isinstance(other, Fsum):
176
+ s, r = other._fint2
177
+ if r:
178
+ s, r = other._fprs2
179
+ if r: # PYCHOK no cover
180
+ if _raiser and _raiser(r, s):
181
+ raise ValueError(_stresidual(_non_zero_, r))
182
+ s = other # L{Fsum} as-is
183
+ else:
184
+ s = other # C{type} as-is
185
+ if isint(s, both=True):
186
+ s = int(s)
187
+ return s
188
+
189
+
190
+ def _strcomplex(s, *args):
191
+ '''(INTERNAL) C{Complex} 2- or 3-arg C{pow} error C{str}.
192
+ '''
193
+ c = iscomplex.__name__[2:]
194
+ n = _DASH_(len(args), _arg_)
195
+ t = _SPACE_(c, s, _from_, n, pow.__name__)
196
+ return unstr(t, *args)
197
+
198
+
199
+ def _stresidual(prefix, residual, **name_values):
200
+ '''(INTERNAL) Residual error C{str}.
201
+ '''
202
+ p = _SPACE_(prefix, Fsum.residual.name)
203
+ t = Fmt.PARENSPACED(p, Fmt(residual))
204
+ for n, v in itemsorted(name_values):
205
+ n = n.replace(_UNDER_, _SPACE_)
206
+ p = Fmt.PARENSPACED(n, Fmt(v))
207
+ t = _COMMASPACE_(t, p)
208
+ return t
209
+
210
+
211
+ def _2sum(a, b): # by .testFmath
212
+ '''(INTERNAL) Return C{a + b} as 2-tuple (sum, residual).
213
+ '''
214
+ s = a + b
215
+ if not _isfinite(s):
216
+ u = unstr(_2sum.__name__, a, b)
217
+ t = Fmt.PARENSPACED(_not_finite_, s)
218
+ raise _OverflowError(u, txt=t)
219
+ if fabs(a) < fabs(b):
220
+ a, b = b, a
221
+ return s, (b - (s - a))
222
+
223
+
224
+ class Fsum(_Named): # sync __methods__ with .vector3dBase.Vector3dBase
225
+ '''Precision floating point I{running} summation.
226
+
227
+ Unlike Python's C{math.fsum}, this class accumulates values and provides intermediate,
228
+ I{running} precision floating point summation. Accumulation may continue after
229
+ intermediate, I{running} summuation.
230
+
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__}.
234
+
235
+ @note: Handling of exceptions and C{inf}, C{INF}, C{nan} and C{NAN} differs from
236
+ Python's C{math.fsum}.
237
+
238
+ @see: U{Hettinger<https://GitHub.com/ActiveState/code/blob/master/recipes/Python/
239
+ 393090_Binary_floating_point_summatiaccurate_full/recipe-393090.py>}, U{Kahan
240
+ <https://WikiPedia.org/wiki/Kahan_summation_algorithm>}, U{Klein
241
+ <https://Link.Springer.com/article/10.1007/s00607-005-0139-x>}, Python 2.6+
242
+ file I{Modules/mathmodule.c} and the issue log U{Full precision summation
243
+ <https://Bugs.Python.org/issue2819>}.
244
+ '''
245
+ _math_fsum = None
246
+ _n = 0
247
+ # _ps = [] # partial sums
248
+ # _px = 0
249
+ _ratio = None
250
+ _RESIDUAL = max(float(_getenv('PYGEODESY_FSUM_RESIDUAL', _0_0)), _0_0)
251
+
252
+ def __init__(self, *xs, **name_RESIDUAL):
253
+ '''New L{Fsum} for precision floating point I{running} summation.
254
+
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.
260
+
261
+ @see: Methods L{Fsum.fadd} and L{Fsum.RESIDUAL}.
262
+ '''
263
+ if name_RESIDUAL:
264
+ n = _xkwds_get(name_RESIDUAL, name=NN)
265
+ if n: # set name ...
266
+ self.name = n
267
+ r = _xkwds_get(name_RESIDUAL, RESIDUAL=None)
268
+ if r is not None:
269
+ self.RESIDUAL(r) # ... for ResidualError
270
+ # self._n = 0
271
+ 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._ps = [_2float(x=xs[0])]
276
+ self._n = 1
277
+
278
+ def __abs__(self):
279
+ '''Return this instance' absolute value as an L{Fsum}.
280
+ '''
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__)
284
+
285
+ def __add__(self, other):
286
+ '''Return the C{Fsum(B{self}, B{other})}.
287
+
288
+ @arg other: An L{Fsum} or C{scalar}.
289
+
290
+ @return: The sum (L{Fsum}).
291
+
292
+ @see: Method L{Fsum.__iadd__}.
293
+ '''
294
+ f = self._copy_2(self.__add__)
295
+ return f._fadd(other, _add_op_)
296
+
297
+ def __bool__(self): # PYCHOK not special in Python 2-
298
+ '''Return C{True} if this instance is I{exactly} non-zero.
299
+ '''
300
+ s, r = self._fprs2
301
+ return bool(s or r) and s != -r # == self != 0
302
+
303
+ def __ceil__(self): # PYCHOK not special in Python 2-
304
+ '''Return this instance' C{math.ceil} as C{int} or C{float}.
305
+
306
+ @return: An C{int} in Python 3+, but C{float} in Python 2-.
307
+
308
+ @see: Methods L{Fsum.__floor__} and property L{Fsum.ceil}.
309
+ '''
310
+ return self.ceil
311
+
312
+ def __cmp__(self, other): # Python 2-
313
+ '''Compare this with an other instance or C{scalar}.
314
+
315
+ @return: -1, 0 or +1 (C{int}).
316
+
317
+ @raise TypeError: Incompatible B{C{other}} C{type}.
318
+ '''
319
+ s = self._cmp_0(other, self.cmp.__name__)
320
+ return _signOf(s, 0)
321
+
322
+ cmp = __cmp__
323
+
324
+ 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}.
328
+
329
+ @arg other: An L{Fsum} or C{scalar} modulus.
330
+
331
+ @see: Method L{Fsum.__itruediv__}.
332
+ '''
333
+ f = self._copy_2(self.__divmod__)
334
+ return f._fdivmod2(other, _divmod_op_)
335
+
336
+ def __eq__(self, other):
337
+ '''Compare this with an other instance or C{scalar}.
338
+ '''
339
+ return self._cmp_0(other, _eq_op_) == 0
340
+
341
+ def __float__(self):
342
+ '''Return this instance' current precision running sum as C{float}.
343
+
344
+ @see: Methods L{Fsum.fsum} and L{Fsum.int_float}.
345
+ '''
346
+ return float(self._fprs)
347
+
348
+ def __floor__(self): # PYCHOK not special in Python 2-
349
+ '''Return this instance' C{math.floor} as C{int} or C{float}.
350
+
351
+ @return: An C{int} in Python 3+, but C{float} in Python 2-.
352
+
353
+ @see: Methods L{Fsum.__ceil__} and property L{Fsum.floor}.
354
+ '''
355
+ return self.floor
356
+
357
+ def __floordiv__(self, other):
358
+ '''Return C{B{self} // B{other}} as an L{Fsum}.
359
+
360
+ @arg other: An L{Fsum} or C{scalar} divisor.
361
+
362
+ @return: The C{floor} quotient (L{Fsum}).
363
+
364
+ @see: Methods L{Fsum.__ifloordiv__}.
365
+ '''
366
+ f = self._copy_2(self.__floordiv__)
367
+ return f._floordiv(other, _floordiv_op_)
368
+
369
+ def __format__(self, *other): # PYCHOK no cover
370
+ '''Not implemented.'''
371
+ return _NotImplemented(self, *other)
372
+
373
+ def __ge__(self, other):
374
+ '''Compare this with an other instance or C{scalar}.
375
+ '''
376
+ return self._cmp_0(other, _ge_op_) >= 0
377
+
378
+ def __gt__(self, other):
379
+ '''Compare this with an other instance or C{scalar}.
380
+ '''
381
+ return self._cmp_0(other, _gt_op_) > 0
382
+
383
+ def __hash__(self): # PYCHOK no cover
384
+ '''Return this instance' C{hash}.
385
+ '''
386
+ return hash(self._ps) # XXX id(self)?
387
+
388
+ def __iadd__(self, other):
389
+ '''Apply C{B{self} += B{other}} to this instance.
390
+
391
+ @arg other: An L{Fsum} or C{scalar} instance.
392
+
393
+ @return: This instance, updated (L{Fsum}).
394
+
395
+ @raise TypeError: Invalid B{C{other}}, not
396
+ C{scalar} nor L{Fsum}.
397
+
398
+ @see: Methods L{Fsum.fadd} and L{Fsum.fadd_}.
399
+ '''
400
+ return self._fadd(other, _iadd_op_)
401
+
402
+ def __ifloordiv__(self, other):
403
+ '''Apply C{B{self} //= B{other}} to this instance.
404
+
405
+ @arg other: An L{Fsum} or C{scalar} divisor.
406
+
407
+ @return: This instance, updated (L{Fsum}).
408
+
409
+ @raise ResidualError: Non-zero residual in B{C{other}}.
410
+
411
+ @raise TypeError: Invalid B{C{other}} type.
412
+
413
+ @raise ValueError: Invalid or non-finite B{C{other}}.
414
+
415
+ @raise ZeroDivisionError: Zero B{C{other}}.
416
+
417
+ @see: Methods L{Fsum.__itruediv__}.
418
+ '''
419
+ return self._floordiv(other, _floordiv_op_ + _fset_op_)
420
+
421
+ def __imatmul__(self, other): # PYCHOK no cover
422
+ '''Not implemented.'''
423
+ return _NotImplemented(self, other)
424
+
425
+ def __imod__(self, other):
426
+ '''Apply C{B{self} %= B{other}} to this instance.
427
+
428
+ @arg other: An L{Fsum} or C{scalar} modulus.
429
+
430
+ @return: This instance, updated (L{Fsum}).
431
+
432
+ @see: Method L{Fsum.__divmod__}.
433
+ '''
434
+ self._fdivmod2(other, _mod_op_ + _fset_op_)
435
+ return self
436
+
437
+ def __imul__(self, other):
438
+ '''Apply C{B{self} *= B{other}} to this instance.
439
+
440
+ @arg other: An L{Fsum} or C{scalar} factor.
441
+
442
+ @return: This instance, updated (L{Fsum}).
443
+
444
+ @raise OverflowError: Partial C{2sum} overflow.
445
+
446
+ @raise TypeError: Invalid B{C{other}} type.
447
+
448
+ @raise ValueError: Invalid or non-finite B{C{other}}.
449
+ '''
450
+ return self._fmul(other, _mul_op_ + _fset_op_)
451
+
452
+ def __int__(self):
453
+ '''Return this instance as an C{int}.
454
+
455
+ @see: Methods L{Fsum.int_float}, L{Fsum.__ceil__}
456
+ and L{Fsum.__floor__} and properties
457
+ L{Fsum.ceil} and L{Fsum.floor}.
458
+ '''
459
+ i, _ = self._fint2
460
+ return i
461
+
462
+ def __invert__(self): # PYCHOK no cover
463
+ '''Not implemented.'''
464
+ # Luciano Ramalho, "Fluent Python", O'Reilly, 2nd Ed, 2022 p. 567
465
+ return _NotImplemented(self)
466
+
467
+ def __ipow__(self, other, *mod): # PYCHOK 2 vs 3 args
468
+ '''Apply C{B{self} **= B{other}} to this instance.
469
+
470
+ @arg other: The exponent (L{Fsum} or C{scalar}).
471
+ @arg mod: Optional modulus (C{int} or C{None}) for the
472
+ 3-argument C{pow(B{self}, B{other}, B{mod})}
473
+ version.
474
+
475
+ @return: This instance, updated (L{Fsum}).
476
+
477
+ @note: If B{C{mod}} is given, the result will be an C{integer}
478
+ L{Fsum} in Python 3+ if this instance C{is_integer} or
479
+ set to C{as_integer} if B{C{mod}} given as C{None}.
480
+
481
+ @raise OverflowError: Partial C{2sum} overflow.
482
+
483
+ @raise ResidualError: Non-zero residual in B{C{other}} and
484
+ env var C{PYGEODESY_FSUM_RESIDUAL}
485
+ set or this instance has a non-zero
486
+ residual and either B{C{mod}} is
487
+ given and non-C{None} or B{C{other}}
488
+ is a negative or fractional C{scalar}.
489
+
490
+ @raise TypeError: Invalid B{C{other}} type or 3-argument
491
+ C{pow} invocation failed.
492
+
493
+ @raise ValueError: If B{C{other}} is a negative C{scalar}
494
+ and this instance is C{0} or B{C{other}}
495
+ is a fractional C{scalar} and this
496
+ instance is negative or has a non-zero
497
+ residual or B{C{mod}} is given and C{0}.
498
+
499
+ @see: CPython function U{float_pow<https://GitHub.com/
500
+ python/cpython/blob/main/Objects/floatobject.c>}.
501
+ '''
502
+ return self._fpow(other, _pow_op_ + _fset_op_, *mod)
503
+
504
+ def __isub__(self, other):
505
+ '''Apply C{B{self} -= B{other}} to this instance.
506
+
507
+ @arg other: An L{Fsum} or C{scalar}.
508
+
509
+ @return: This instance, updated (L{Fsum}).
510
+
511
+ @raise TypeError: Invalid B{C{other}} type.
512
+
513
+ @see: Method L{Fsum.fadd}.
514
+ '''
515
+ return self._fsub(other, _isub_op_)
516
+
517
+ def __iter__(self):
518
+ '''Return an C{iter}ator over a C{partials} duplicate.
519
+ '''
520
+ return iter(self.partials)
521
+
522
+ def __itruediv__(self, other):
523
+ '''Apply C{B{self} /= B{other}} to this instance.
524
+
525
+ @arg other: An L{Fsum} or C{scalar} divisor.
526
+
527
+ @return: This instance, updated (L{Fsum}).
528
+
529
+ @raise OverflowError: Partial C{2sum} overflow.
530
+
531
+ @raise ResidualError: Non-zero residual in B{C{other}} and
532
+ env var C{PYGEODESY_FSUM_RESIDUAL} set.
533
+
534
+ @raise TypeError: Invalid B{C{other}} type.
535
+
536
+ @raise ValueError: Invalid or non-finite B{C{other}}.
537
+
538
+ @raise ZeroDivisionError: Zero B{C{other}}.
539
+
540
+ @see: Method L{Fsum.__ifloordiv__}.
541
+ '''
542
+ return self._ftruediv(other, _truediv_op_ + _fset_op_)
543
+
544
+ def __le__(self, other):
545
+ '''Compare this with an other instance or C{scalar}.
546
+ '''
547
+ return self._cmp_0(other, _le_op_) <= 0
548
+
549
+ def __len__(self):
550
+ '''Return the number of values accumulated (C{int}).
551
+ '''
552
+ return self._n
553
+
554
+ def __lt__(self, other):
555
+ '''Compare this with an other instance or C{scalar}.
556
+ '''
557
+ return self._cmp_0(other, _lt_op_) < 0
558
+
559
+ def __matmul__(self, other): # PYCHOK no cover
560
+ '''Not implemented.'''
561
+ return _NotImplemented(self, other)
562
+
563
+ def __mod__(self, other):
564
+ '''Return C{B{self} % B{other}} as an L{Fsum}.
565
+
566
+ @see: Method L{Fsum.__imod__}.
567
+ '''
568
+ f = self._copy_2(self.__mod__)
569
+ return f._fdivmod2(other, _mod_op_)[1]
570
+
571
+ def __mul__(self, other):
572
+ '''Return C{B{self} * B{other}} as an L{Fsum}.
573
+
574
+ @see: Method L{Fsum.__imul__}.
575
+ '''
576
+ f = self._copy_2(self.__mul__)
577
+ return f._fmul(other, _mul_op_)
578
+
579
+ def __ne__(self, other):
580
+ '''Compare this with an other instance or C{scalar}.
581
+ '''
582
+ return self._cmp_0(other, _ne_op_) != 0
583
+
584
+ def __neg__(self):
585
+ '''Return I{a copy of} this instance, negated.
586
+ '''
587
+ return self._copy_n(self.__neg__)
588
+
589
+ def __pos__(self):
590
+ '''Return this instance I{as-is}, like C{float.__pos__()}.
591
+ '''
592
+ return self if _pos_self else self._copy_2(self.__pos__)
593
+
594
+ def __pow__(self, other, *mod): # PYCHOK 2 vs 3 args
595
+ '''Return C{B{self}**B{other}} as an L{Fsum}.
596
+
597
+ @see: Method L{Fsum.__ipow__}.
598
+ '''
599
+ f = self._copy_2(self.__pow__)
600
+ return f._fpow(other, _pow_op_, *mod)
601
+
602
+ def __radd__(self, other):
603
+ '''Return C{B{other} + B{self}} as an L{Fsum}.
604
+
605
+ @see: Method L{Fsum.__iadd__}.
606
+ '''
607
+ f = self._copy_r2(other, self.__radd__)
608
+ return f._fadd(self, _add_op_)
609
+
610
+ def __rdivmod__(self, other):
611
+ '''Return C{divmod(B{other}, B{self})} as 2-tuple C{(quotient,
612
+ remainder)}.
613
+
614
+ @see: Method L{Fsum.__divmod__}.
615
+ '''
616
+ f = self._copy_r2(other, self.__rdivmod__)
617
+ return f._fdivmod2(self, _divmod_op_)
618
+
619
+ # def __repr__(self):
620
+ # '''Return the default C{repr(this)}.
621
+ # '''
622
+ # return self.toRepr(lenc=True)
623
+
624
+ def __rfloordiv__(self, other):
625
+ '''Return C{B{other} // B{self}} as an L{Fsum}.
626
+
627
+ @see: Method L{Fsum.__ifloordiv__}.
628
+ '''
629
+ f = self._copy_r2(other, self.__rfloordiv__)
630
+ return f._floordiv(self, _floordiv_op_)
631
+
632
+ def __rmatmul__(self, other): # PYCHOK no cover
633
+ '''Not implemented.'''
634
+ return _NotImplemented(self, other)
635
+
636
+ def __rmod__(self, other):
637
+ '''Return C{B{other} % B{self}} as an L{Fsum}.
638
+
639
+ @see: Method L{Fsum.__imod__}.
640
+ '''
641
+ f = self._copy_r2(other, self.__rmod__)
642
+ return f._fdivmod2(self, _mod_op_)[1]
643
+
644
+ def __rmul__(self, other):
645
+ '''Return C{B{other} * B{self}} as an L{Fsum}.
646
+
647
+ @see: Method L{Fsum.__imul__}.
648
+ '''
649
+ f = self._copy_r2(other, self.__rmul__)
650
+ return f._fmul(self, _mul_op_)
651
+
652
+ def __round__(self, *ndigits): # PYCHOK no cover
653
+ '''Return C{round(B{self}, *B{ndigits}} as an L{Fsum}.
654
+
655
+ @arg ndigits: Optional number of digits (C{int}).
656
+ '''
657
+ # <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
662
+
663
+ def __rpow__(self, other, *mod):
664
+ '''Return C{B{other}**B{self}} as an L{Fsum}.
665
+
666
+ @see: Method L{Fsum.__ipow__}.
667
+ '''
668
+ f = self._copy_r2(other, self.__rpow__)
669
+ return f._fpow(self, _pow_op_, *mod)
670
+
671
+ def __rsub__(self, other):
672
+ '''Return C{B{other} - B{self}} as L{Fsum}.
673
+
674
+ @see: Method L{Fsum.__isub__}.
675
+ '''
676
+ f = self._copy_r2(other, self.__rsub__)
677
+ return f._fsub(self, _sub_op_)
678
+
679
+ def __rtruediv__(self, other):
680
+ '''Return C{B{other} / B{self}} as an L{Fsum}.
681
+
682
+ @see: Method L{Fsum.__itruediv__}.
683
+ '''
684
+ f = self._copy_r2(other, self.__rtruediv__)
685
+ return f._ftruediv(self, _truediv_op_)
686
+
687
+ def __str__(self):
688
+ '''Return the default C{str(self)}.
689
+ '''
690
+ return self.toStr(lenc=True)
691
+
692
+ def __sub__(self, other):
693
+ '''Return C{B{self} - B{other}} as an L{Fsum}.
694
+
695
+ @arg other: An L{Fsum} or C{scalar}.
696
+
697
+ @return: The difference (L{Fsum}).
698
+
699
+ @see: Method L{Fsum.__isub__}.
700
+ '''
701
+ f = self._copy_2(self.__sub__)
702
+ return f._fsub(other, _sub_op_)
703
+
704
+ def __truediv__(self, other):
705
+ '''Return C{B{self} / B{other}} as an L{Fsum}.
706
+
707
+ @arg other: An L{Fsum} or C{scalar} divisor.
708
+
709
+ @return: The quotient (L{Fsum}).
710
+
711
+ @see: Method L{Fsum.__itruediv__}.
712
+ '''
713
+ f = self._copy_2(self.__truediv__)
714
+ return f._ftruediv(other, _truediv_op_)
715
+
716
+ __trunc__ = __int__
717
+
718
+ if _sys_version_info2 < (3, 0): # PYCHOK no cover
719
+ # <https://docs.Python.org/2/library/operator.html#mapping-operators-to-functions>
720
+ __div__ = __truediv__
721
+ __idiv__ = __itruediv__
722
+ __long__ = __int__
723
+ __nonzero__ = __bool__
724
+ __rdiv__ = __rtruediv__
725
+
726
+ def as_integer_ratio(self):
727
+ '''Return this instance as the ratio of 2 integers.
728
+
729
+ @return: 2-Tuple C{(numerator, denominator)} both
730
+ C{int} and with positive C{denominator}.
731
+
732
+ @see: Standard C{float.as_integer_ratio} in Python 3+.
733
+ '''
734
+ n, r = self._fint2
735
+ if r:
736
+ i, d = r.as_integer_ratio()
737
+ n *= d
738
+ n += i
739
+ else: # PYCHOK no cover
740
+ d = 1
741
+ return n, d
742
+
743
+ @property_RO
744
+ def ceil(self):
745
+ '''Get this instance' C{ceil} value (C{int} in Python 3+,
746
+ but C{float} in Python 2-).
747
+
748
+ @note: The C{ceil} takes the C{residual} into account.
749
+
750
+ @see: Method L{Fsum.int_float} and properties L{Fsum.floor},
751
+ L{Fsum.imag} and L{Fsum.real}.
752
+ '''
753
+ s, r = self._fprs2
754
+ c = _ceil(s) + int(r) - 1
755
+ while r > (c - s): # (s + r) > c
756
+ c += 1
757
+ return c
758
+
759
+ def _cmp_0(self, other, op):
760
+ '''(INTERNAL) Return C{scalar(self - B{other})} for 0-comparison.
761
+ '''
762
+ if isscalar(other):
763
+ if other:
764
+ s = _fsum(self._ps_1(other))
765
+ else:
766
+ s, r = self._fprs2
767
+ s = _signOf(s, -r)
768
+ elif isinstance(other, Fsum):
769
+ s = _fsum(self._ps_1(*other._ps))
770
+ else:
771
+ raise self._TypeError(op, other) # txt=_invalid_
772
+ return s
773
+
774
+ def copy(self, deep=False, name=NN):
775
+ '''Copy this instance, C{shallow} or B{C{deep}}.
776
+
777
+ @return: The copy (L{Fsum}).
778
+ '''
779
+ f = _Named.copy(self, deep=deep, name=name)
780
+ f._n = self._n if deep else 1
781
+ f._ps = list(self._ps) # separate list
782
+ return f
783
+
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):
795
+ '''(INTERNAL) Copy for I{dyadic} operators.
796
+ '''
797
+ # NOT .classof due to .Fdot(a, *b) args, etc.
798
+ f = _Named.copy(self, deep=False, name=which.__name__)
799
+ # assert f._n == self._n
800
+ f._ps = list(self._ps) # separate list
801
+ return f
802
+
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):
816
+ '''(INTERNAL) Copy for I{reverse-dyadic} operators.
817
+ '''
818
+ return other._copy_2(which) if isinstance(other, Fsum) else \
819
+ Fsum(other, name=which.__name__) # see ._copy_2
820
+
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
835
+
836
+ def divmod(self, other):
837
+ '''Return C{divmod(B{self}, B{other})} as 2-tuple C{(quotient,
838
+ remainder)}.
839
+
840
+ @arg other: An L{Fsum} or C{scalar} divisor.
841
+
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.
845
+
846
+ @see: Method L{Fsum.__itruediv__}.
847
+ '''
848
+ f = self._copy_2(self.divmod)
849
+ return f._fdivmod2(other, _divmod_op_)
850
+
851
+ def _Error(self, op, other, Error, **txt):
852
+ '''(INTERNAL) Format an B{C{Error}} for C{{self} B{op} B{other}}.
853
+ '''
854
+ return Error(_SPACE_(self.toRepr(), op, repr(other)), **txt)
855
+
856
+ def _ErrorX(self, X, xs, **kwds): # in .fmath
857
+ '''(INTERNAL) Format a caught exception.
858
+ '''
859
+ E, t = _xError2(X)
860
+ n = unstr(self.named3, *xs[:3], _ELLIPSIS=len(xs) > 3, **kwds)
861
+ return E(n, txt=t, cause=X)
862
+
863
+ def _facc(self, xs, up=True): # from .elliptic._Defer.Fsum
864
+ '''(INTERNAL) Accumulate more known C{scalar}s.
865
+ '''
866
+ n, ps, _2s = 0, self._ps, _2sum
867
+ for x in xs: # _iter()
868
+ # 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]
876
+ n += 1
877
+ # assert self._ps is ps
878
+ if n:
879
+ self._n += n
880
+ # Fsum._px = max(Fsum._px, len(ps))
881
+ if up:
882
+ self._update()
883
+ return self
884
+
885
+ def _facc_(self, *xs, **up):
886
+ '''(INTERNAL) Accumulate all positional C{scalar}s.
887
+ '''
888
+ return self._facc(xs, **up) if xs else self
889
+
890
+ # def _facc_up(self, up=True):
891
+ # '''(INTERNAL) Update the C{partials}, by removing
892
+ # and re-accumulating the final C{partial}.
893
+ # '''
894
+ # while len(self._ps) > 1:
895
+ # p = self._ps.pop()
896
+ # if p:
897
+ # n = self._n
898
+ # self._facc_(p, up=False)
899
+ # self._n = n
900
+ # break
901
+ # return self._update() if up else self # ._fpsqz()
902
+
903
+ def fadd(self, xs=()):
904
+ '''Add an iterable of C{scalar} or L{Fsum} instances
905
+ to this instance.
906
+
907
+ @arg xs: Iterable, list, tuple, etc. (C{scalar} or
908
+ L{Fsum} instances).
909
+
910
+ @return: This instance (L{Fsum}).
911
+
912
+ @raise OverflowError: Partial C{2sum} overflow.
913
+
914
+ @raise TypeError: An invalid B{C{xs}} type, not C{scalar}
915
+ nor L{Fsum}.
916
+
917
+ @raise ValueError: Invalid or non-finite B{C{xs}} value.
918
+ '''
919
+ if isinstance(xs, Fsum):
920
+ self._facc(xs._ps)
921
+ elif isscalar(xs): # for backward compatibility
922
+ self._facc_(_2float(x=xs)) # PYCHOK no cover
923
+ elif xs:
924
+ self._facc(_2floats(xs)) # PYCHOK yield
925
+ return self
926
+
927
+ def fadd_(self, *xs):
928
+ '''Add all positional C{scalar} or L{Fsum} instances
929
+ to this instance.
930
+
931
+ @arg xs: Values to add (C{scalar} or L{Fsum} instances),
932
+ all positional.
933
+
934
+ @return: This instance (L{Fsum}).
935
+
936
+ @raise OverflowError: Partial C{2sum} overflow.
937
+
938
+ @raise TypeError: An invalid B{C{xs}} type, not C{scalar}
939
+ nor L{Fsum}.
940
+
941
+ @raise ValueError: Invalid or non-finite B{C{xs}} value.
942
+ '''
943
+ return self._facc(_2floats(xs, origin=1)) # PYCHOK yield
944
+
945
+ def _fadd(self, other, op): # in .fmath.Fhorner
946
+ '''(INTERNAL) Apply C{B{self} += B{other}}.
947
+ '''
948
+ if isinstance(other, Fsum):
949
+ if other is self:
950
+ self._facc_(*other._ps) # == ._facc(tuple(other._ps))
951
+ elif other._ps:
952
+ self._facc(other._ps)
953
+ elif not isscalar(other):
954
+ raise self._TypeError(op, other) # txt=_invalid_
955
+ elif other:
956
+ self._facc_(other)
957
+ return self
958
+
959
+ fcopy = copy # for backward compatibility
960
+ fdiv = __itruediv__ # for backward compatibility
961
+ fdivmod = __divmod__ # for backward compatibility
962
+
963
+ 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}).
966
+ '''
967
+ # result mostly follows CPython function U{float_divmod
968
+ # <https://GitHub.com/python/cpython/blob/main/Objects/floatobject.c>},
969
+ # but at least divmod(-3, 2) equals Cpython's result (-2, 1).
970
+ q = self._copy_up(_fprs2=True)._ftruediv(other, op).floor
971
+ if q: # == float // other == floor(float / other)
972
+ self -= other * q
973
+
974
+ s = signOf(other) # make signOf(self) == signOf(other)
975
+ if s and self.signOf() == -s: # PYCHOK no cover
976
+ self += other
977
+ q -= 1
978
+
979
+ # t = self.signOf()
980
+ # if t and t != s:
981
+ # from pygeodesy.errors import _AssertionError
982
+ # 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-
984
+
985
+ def _finite(self, other, op=None):
986
+ '''(INTERNAL) Return B{C{other}} if C{finite}.
987
+ '''
988
+ if _isfinite(other):
989
+ return other
990
+ raise ValueError(_not_finite_) if not op else \
991
+ self._ValueError(op, other, txt=_not_finite_)
992
+
993
+ def fint(self, raiser=True, name=NN):
994
+ '''Return this instance' current running sum as C{integer}.
995
+
996
+ @kwarg raiser: If C{True} throw a L{ResidualError} if the
997
+ I{integer} residual is non-zero.
998
+ @kwarg name: Optional name (C{str}), overriding C{"fint"}.
999
+
1000
+ @return: The C{integer} (L{Fsum}).
1001
+
1002
+ @raise ResidualError: Non-zero I{integer} residual.
1003
+
1004
+ @see: Methods L{Fsum.int_float} and L{Fsum.is_integer}.
1005
+ '''
1006
+ i, r = self._fint2
1007
+ if r and raiser:
1008
+ t = _stresidual(_integer_, r)
1009
+ raise ResidualError(_integer_, i, txt=t)
1010
+ n = name or self.fint.__name__
1011
+ return Fsum(name=n)._fset(i, asis=True)
1012
+
1013
+ def fint2(self, **name):
1014
+ '''Return this instance' current running sum as C{int} and
1015
+ the I{integer} residual.
1016
+
1017
+ @kwarg name: Optional name (C{str}).
1018
+
1019
+ @return: An L{Fsum2Tuple}C{(fsum, residual)} with C{fsum}
1020
+ an C{int} and I{integer} C{residual} a C{float} or
1021
+ C{INT0} if the C{fsum} is considered to be I{exact}.
1022
+ '''
1023
+ return Fsum2Tuple(*self._fint2, **name)
1024
+
1025
+ @Property_RO
1026
+ def _fint2(self): # see ._fset
1027
+ '''(INTERNAL) Get 2-tuple (C{int}, I{integer} residual).
1028
+ '''
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)
1032
+ return i, (r or INT0)
1033
+
1034
+ @deprecated_property_RO
1035
+ def float_int(self): # PYCHOK no cover
1036
+ '''DEPRECATED, use method C{Fsum.int_float}.'''
1037
+ return self.int_float() # raiser=False
1038
+
1039
+ @property_RO
1040
+ def floor(self):
1041
+ '''Get this instance' C{floor} (C{int} in Python 3+, but
1042
+ C{float} in Python 2-).
1043
+
1044
+ @note: The C{floor} takes the C{residual} into account.
1045
+
1046
+ @see: Method L{Fsum.int_float} and properties L{Fsum.ceil},
1047
+ L{Fsum.imag} and L{Fsum.real}.
1048
+ '''
1049
+ s, r = self._fprs2
1050
+ f = _floor(s) + _floor(r) + 1
1051
+ while r < (f - s): # (s + r) < f
1052
+ f -= 1
1053
+ return f
1054
+
1055
+ # floordiv = __floordiv__ # for naming consistency
1056
+
1057
+ def _floordiv(self, other, op): # rather _ffloordiv?
1058
+ '''Apply C{B{self} //= B{other}}.
1059
+ '''
1060
+ q = self._ftruediv(other, op) # == self
1061
+ return self._fset(q.floor, asis=True) # floor(q)
1062
+
1063
+ fmul = __imul__ # for backward compatibility
1064
+
1065
+ def _fmul(self, other, op):
1066
+ '''(INTERNAL) Apply C{B{self} *= B{other}}.
1067
+ '''
1068
+ if isscalar(other):
1069
+ f = self._mul_scalar(other, op)
1070
+ elif not isinstance(other, Fsum):
1071
+ 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)
1080
+
1081
+ def fover(self, over):
1082
+ '''Apply C{B{self} /= B{over}} and summate.
1083
+
1084
+ @arg over: An L{Fsum} or C{scalar} denominator.
1085
+
1086
+ @return: Precision running sum (C{float}).
1087
+
1088
+ @see: Methods L{Fsum.fsum} and L{Fsum.__itruediv__}.
1089
+ '''
1090
+ return float(self.fdiv(over)._fprs)
1091
+
1092
+ fpow = __ipow__ # for backward compatibility
1093
+
1094
+ def _fpow(self, other, op, *mod):
1095
+ '''Apply C{B{self} **= B{other}}, optional B{C{mod}} or C{None}.
1096
+ '''
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)
1104
+ 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))
1121
+
1122
+ @Property_RO
1123
+ def _fprs(self):
1124
+ '''(INTERNAL) Get and cache this instance' precision
1125
+ running sum (C{float} or C{int}), ignoring C{residual}.
1126
+
1127
+ @note: The precision running C{fsum} after a C{//=} or
1128
+ C{//} C{floor} division is C{int} in Python 3+.
1129
+ '''
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
1145
+
1146
+ @Property_RO
1147
+ def _fprs2(self):
1148
+ '''(INTERNAL) Get and cache this instance' precision
1149
+ running sum and residual (L{Fsum2Tuple}).
1150
+ '''
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__
1154
+
1155
+ # def _fpsqz(self):
1156
+ # '''(INTERNAL) Compress, squeeze the C{partials}.
1157
+ # '''
1158
+ # if len(self._ps) > 2:
1159
+ # _ = self._fprs
1160
+ # return self
1161
+
1162
+ def _fset(self, other, asis=False, n=1):
1163
+ '''(INTERNAL) Overwrite this instance with an other or a C{scalar}.
1164
+ '''
1165
+ if other is self:
1166
+ pass # from ._fmul, ._ftruediv and ._pow_scalar
1167
+ elif isinstance(other, Fsum):
1168
+ self._n = other._n
1169
+ self._ps[:] = other._ps
1170
+ self._copy_RESIDUAL(other)
1171
+ # use or zap the C{Property_RO} values
1172
+ Fsum._fint2._update_from(self, other)
1173
+ Fsum._fprs ._update_from(self, other)
1174
+ Fsum._fprs2._update_from(self, other)
1175
+ elif isscalar(other):
1176
+ s = other if asis else float(other)
1177
+ i = int(s) # see ._fint2
1178
+ 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:
1182
+ # Property's _fset zaps the value just set by the @setter
1183
+ self.__dict__.update(_fint2=t, _fprs=s, _fprs2=Fsum2Tuple(s, INT0))
1184
+ else: # PYCHOK no cover
1185
+ raise self._TypeError(_fset_op_, other) # txt=_invalid_
1186
+ return self
1187
+
1188
+ def fsub(self, xs=()):
1189
+ '''Subtract an iterable of C{scalar} or L{Fsum} instances
1190
+ from this instance.
1191
+
1192
+ @arg xs: Iterable, list, tuple. etc. (C{scalar}
1193
+ or L{Fsum} instances).
1194
+
1195
+ @return: This instance, updated (L{Fsum}).
1196
+
1197
+ @see: Method L{Fsum.fadd}.
1198
+ '''
1199
+ return self._facc(_2floats(xs, sub=True)) if xs else self # PYCHOK yield
1200
+
1201
+ def fsub_(self, *xs):
1202
+ '''Subtract all positional C{scalar} or L{Fsum} instances
1203
+ from this instance.
1204
+
1205
+ @arg xs: Values to subtract (C{scalar} or
1206
+ L{Fsum} instances), all positional.
1207
+
1208
+ @return: This instance, updated (L{Fsum}).
1209
+
1210
+ @see: Method L{Fsum.fadd}.
1211
+ '''
1212
+ return self._facc(_2floats(xs, origin=1, sub=True)) if xs else self # PYCHOK yield
1213
+
1214
+ def _fsub(self, other, op):
1215
+ '''(INTERNAL) Apply C{B{self} -= B{other}}.
1216
+ '''
1217
+ if isinstance(other, Fsum):
1218
+ if other is self: # or other._fprs2 == self._fprs2:
1219
+ self._fset(_0_0, asis=True, n=len(self) * 2) # self -= self
1220
+ elif other._ps:
1221
+ self._facc(other._ps_n())
1222
+ elif not isscalar(other):
1223
+ raise self._TypeError(op, other) # txt=_invalid_
1224
+ elif self._finite(other, op):
1225
+ self._facc_(-other)
1226
+ return self
1227
+
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
+ def fsum(self, xs=()):
1239
+ '''Add more C{scalar} or L{Fsum} instances and summate.
1240
+
1241
+ @kwarg xs: Iterable, list, tuple, etc. (C{scalar} or
1242
+ L{Fsum} instances).
1243
+
1244
+ @return: Precision running sum (C{float} or C{int}).
1245
+
1246
+ @see: Method L{Fsum.fadd}.
1247
+
1248
+ @note: Accumulation can continue after summation.
1249
+ '''
1250
+ f = self._facc(_2floats(xs)) if xs else self # PYCHOK yield
1251
+ return f._fprs
1252
+
1253
+ def fsum_(self, *xs):
1254
+ '''Add all positional C{scalar} or L{Fsum} instances and summate.
1255
+
1256
+ @arg xs: Values to add (C{scalar} or L{Fsum} instances),
1257
+ all positional.
1258
+
1259
+ @return: Precision running sum (C{float} or C{int}).
1260
+
1261
+ @see: Methods L{Fsum.fsum} and L{Fsum.fsumf_}.
1262
+ '''
1263
+ f = self._facc(_2floats(xs, origin=1)) if xs else self # PYCHOK yield
1264
+ return f._fprs
1265
+
1266
+ def fsum2(self, xs=(), **name):
1267
+ '''Add more C{scalar} or L{Fsum} instances and return the
1268
+ current precision running sum and the C{residual}.
1269
+
1270
+ @kwarg xs: Iterable, list, tuple, etc. (C{scalar} or
1271
+ L{Fsum} instances).
1272
+ @kwarg name: Optional name (C{str}).
1273
+
1274
+ @return: L{Fsum2Tuple}C{(fsum, residual)} with C{fsum} the
1275
+ current precision running sum and C{residual}, the
1276
+ (precision) sum of the remaining C{partials}. The
1277
+ C{residual is INT0} if the C{fsum} is considered
1278
+ to be I{exact}.
1279
+
1280
+ @see: Methods L{Fsum.fint2}, L{Fsum.fsum} and L{Fsum.fsum2_}
1281
+ '''
1282
+ f = self._facc(_2floats(xs)) if xs else self # PYCHOK yield
1283
+ t = f._fprs2
1284
+ if name:
1285
+ t = t.dup(name=_xkwds_get(name, name=NN))
1286
+ return t
1287
+
1288
+ def fsum2_(self, *xs):
1289
+ '''Add any positional C{scalar} or L{Fsum} instances and return
1290
+ the precision running sum and the C{differential}.
1291
+
1292
+ @arg xs: Values to add (C{scalar} or L{Fsum} instances),
1293
+ all positional.
1294
+
1295
+ @return: 2-Tuple C{(fsum, delta)} with the current precision
1296
+ running C{fsum} and C{delta}, the difference with
1297
+ the previous running C{fsum} (C{float}s).
1298
+
1299
+ @see: Methods L{Fsum.fsum_} and L{Fsum.fsum}.
1300
+ '''
1301
+ p, r = self._fprs2
1302
+ if xs:
1303
+ s, t = self._facc(_2floats(xs, origin=1))._fprs2 # PYCHOK yield
1304
+ return s, _fsum((s, -p, r, -t)) # ((s - p) + (r - t))
1305
+ else: # PYCHOK no cover
1306
+ return p, _0_0
1307
+
1308
+ def fsumf_(self, *xs):
1309
+ '''Like method L{Fsum.fsum_} but only for known C{float B{xs}}.
1310
+ '''
1311
+ f = self._facc(xs) if xs else self # PYCHOK yield
1312
+ return f._fprs
1313
+
1314
+ # ftruediv = __itruediv__ # for naming consistency
1315
+
1316
+ def _ftruediv(self, other, op):
1317
+ '''(INTERNAL) Apply C{B{self} /= B{other}}.
1318
+ '''
1319
+ n = _1_0
1320
+ 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))
1323
+ d, r = other._fprs2
1324
+ 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:
1330
+ d, n = other.as_integer_ratio()
1331
+ elif isscalar(other):
1332
+ d = other
1333
+ else: # PYCHOK no cover
1334
+ raise self._TypeError(op, other) # txt=_invalid_
1335
+ try:
1336
+ s = 0 if isinf(d) else (
1337
+ 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)
1341
+ f = self._mul_scalar(s, _mul_op_) # handles 0, NAN, etc.
1342
+ return self._fset(f)
1343
+
1344
+ @property_RO
1345
+ def imag(self):
1346
+ '''Get the C{imaginary} part of this instance (C{0.0}, always).
1347
+
1348
+ @see: Properties L{Fsum.ceil}, L{Fsum.floor} and L{Fsum.real}.
1349
+ '''
1350
+ return _0_0
1351
+
1352
+ def int_float(self, raiser=False):
1353
+ '''Return this instance' current running sum as C{int} or C{float}.
1354
+
1355
+ @kwarg raiser: If C{True} throw a L{ResidualError} if the
1356
+ residual is non-zero.
1357
+
1358
+ @return: This C{integer} sum if this instance C{is_integer},
1359
+ otherwise return the C{float} sum if the residual
1360
+ is zero or if C{B{raiser}=False}.
1361
+
1362
+ @raise ResidualError: Non-zero residual and C{B{raiser}=True}.
1363
+
1364
+ @see: Methods L{Fsum.fint} and L{Fsum.fint2}.
1365
+ '''
1366
+ s, r = self._fint2
1367
+ if r:
1368
+ s, r = self._fprs2
1369
+ if r and raiser: # PYCHOK no cover
1370
+ t = _stresidual(_non_zero_, r)
1371
+ raise ResidualError(int_float=s, txt=t)
1372
+ s = float(s) # redundant
1373
+ return s
1374
+
1375
+ def is_exact(self):
1376
+ '''Is this instance' current running C{fsum} considered to
1377
+ be exact? (C{bool}).
1378
+ '''
1379
+ return self.residual is INT0
1380
+
1381
+ def is_integer(self):
1382
+ '''Is this instance' current running sum C{integer}? (C{bool}).
1383
+
1384
+ @see: Methods L{Fsum.fint} and L{Fsum.fint2}.
1385
+ '''
1386
+ _, r = self._fint2
1387
+ return False if r else True
1388
+
1389
+ 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.
1393
+
1394
+ @return: C{2} if all functions and partials summation
1395
+ are based on C{math.fsum}, C{True} if only
1396
+ the functions are based on C{math.fsum} (and
1397
+ partials summation is not) or C{False} if
1398
+ none are.
1399
+ '''
1400
+ f = Fsum._math_fsum
1401
+ return 2 if _psum is f else bool(f)
1402
+
1403
+ def _mul_Fsum(self, other, op=_mul_op_):
1404
+ '''(INTERNAL) Return C{B{self} * Fsum B{other}} as L{Fsum}.
1405
+ '''
1406
+ # assert isinstance(other, Fsum)
1407
+ return self._copy_0()._facc(self._ps_x(op, *other._ps), up=False)
1408
+
1409
+ def _mul_scalar(self, factor, op):
1410
+ '''(INTERNAL) Return C{B{self} * scalar B{factor}} as L{Fsum} or C{0}.
1411
+ '''
1412
+ # 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)
1417
+ else:
1418
+ f = self._copy_0(_0_0)
1419
+ return f
1420
+
1421
+ @property_RO
1422
+ def partials(self):
1423
+ '''Get this instance' current partial sums (C{tuple} of C{float}s and/or C{int}s).
1424
+ '''
1425
+ return tuple(self._ps)
1426
+
1427
+ def pow(self, x, *mod):
1428
+ '''Return C{B{self}**B{x}} as L{Fsum}.
1429
+
1430
+ @arg x: The exponent (L{Fsum} or C{scalar}).
1431
+ @arg mod: Optional modulus (C{int} or C{None}) for the 3-argument
1432
+ C{pow(B{self}, B{other}, B{mod})} version.
1433
+
1434
+ @return: The C{pow(self, B{x})} or C{pow(self, B{x}, *B{mod})}
1435
+ result (L{Fsum}).
1436
+
1437
+ @note: If B{C{mod}} is given as C{None}, the result will be an
1438
+ C{integer} L{Fsum} provided this instance C{is_integer}
1439
+ or set C{integer} with L{Fsum.fint}.
1440
+
1441
+ @see: Methods L{Fsum.__ipow__}, L{Fsum.fint} and L{Fsum.is_integer}.
1442
+ '''
1443
+ 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
1449
+
1450
+ def _pow_0_1(self, x, other):
1451
+ '''(INTERNAL) Return B{C{self}**1} or C{B{self}**0 == 1.0}.
1452
+ '''
1453
+ return self if x else (1 if self.is_integer() and isint(other) else _1_0)
1454
+
1455
+ def _pow_2(self, x, other, op):
1456
+ '''(INTERNAL) 2-arg C{pow(B{self}, scalar B{x})} embellishing errors.
1457
+ '''
1458
+ # assert len(self._ps) == 1 and isscalar(x)
1459
+ b = self._ps[0] # assert isscalar(b)
1460
+ try: # type(s) == type(x) if x in (_1_0, 1)
1461
+ s = pow(b, x) # -1**2.3 == -(1**2.3)
1462
+ if not iscomplex(s):
1463
+ return self._finite(s) # 0**INF == 0.0, 1**INF==1.0
1464
+ # 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)
1469
+
1470
+ def _pow_3(self, other, mod, op):
1471
+ '''(INTERNAL) 3-arg C{pow(B{self}, B{other}, int B{mod} or C{None})}.
1472
+ '''
1473
+ b, r = self._fprs2 if mod is None else self._fint2
1474
+ if r and self._raiser(r, b):
1475
+ t = _non_zero_ if mod is None else _integer_
1476
+ E, t = ResidualError, _stresidual(t, r, mod=mod)
1477
+ 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)
1489
+
1490
+ def _pow_int(self, x, other, op):
1491
+ '''(INTERNAL) Return C{B{self} **= B{x}} for C{int B{x} >= 0}.
1492
+ '''
1493
+ # assert isint(x) and x >= 0
1494
+ if len(self._ps) > 1:
1495
+ if x > 2:
1496
+ p = self._copy_up()
1497
+ m = 1 # single-bit mask
1498
+ if x & m:
1499
+ x -= m # x ^= m
1500
+ f = p._copy_up()
1501
+ else:
1502
+ f = self._copy_0(_1_0)
1503
+ while x:
1504
+ p = p._mul_Fsum(p, op) # p **= 2
1505
+ m += m # m <<= 1
1506
+ if x & m:
1507
+ x -= m # x ^= m
1508
+ 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)
1515
+ else: # PYCHOK no cover
1516
+ # 0**pos_int == 0, but 0**0 == 1
1517
+ f = 0 if x else 1 # like ._fprs
1518
+ return self._fset(f, asis=isint(f), n=len(self))
1519
+
1520
+ def _pow_scalar(self, x, other, op):
1521
+ '''(INTERNAL) Return C{self**B{x}} for C{scalar B{x}}.
1522
+ '''
1523
+ s, r = self._fprs2
1524
+ if isint(x, both=True):
1525
+ x = int(x) # Fsum**int
1526
+ y = abs(x)
1527
+ if y > 1:
1528
+ if r:
1529
+ f = self._copy_up()._pow_int(y, other, op)
1530
+ if x > 0: # > 1
1531
+ return f
1532
+ # assert x < 0 # < -1
1533
+ s, r = f._fprs2
1534
+ if r:
1535
+ return self._copy_0(_1_0)._ftruediv(f, op)
1536
+ # use **= -1 for the CPython float_pow
1537
+ # error if s is zero, and not s = 1 / s
1538
+ 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
+ elif x < 0: # self**-1 == 1 / self
1545
+ if r:
1546
+ return self._copy_0(_1_0)._ftruediv(self, op)
1547
+ else: # self**1 or self**0
1548
+ return self._pow_0_1(x, other) # self or 0.0
1549
+ elif not isscalar(x): # assert ...
1550
+ raise self._TypeError(op, other, txt=_not_scalar_)
1551
+ elif r and self._raiser(r, s): # non-zero residual**fractional
1552
+ # raise self._ResidualError(op, other, r, fractional_power=x)
1553
+ t = _stresidual(_non_zero_, r, fractional_power=x)
1554
+ raise self._Error(op, other, ResidualError, txt=t)
1555
+ # assert isscalar(s) and isscalar(x)
1556
+ return self._copy_0(s)._pow_2(x, other, op)
1557
+
1558
+ def _ps_1(self, *less):
1559
+ '''(INTERNAL) Yield partials, 1-primed and subtract any C{less}.
1560
+ '''
1561
+ yield _1_0
1562
+ for p in self._ps:
1563
+ if p:
1564
+ yield p
1565
+ for p in less:
1566
+ if p:
1567
+ yield -p
1568
+ yield _N_1_0
1569
+
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
1578
+ '''(INTERNAL) Yield all C{partials} times each B{C{factor}},
1579
+ in total, up to C{len(partials) * len(factors)} items.
1580
+ '''
1581
+ ps = self._ps
1582
+ if len(ps) < len(factors):
1583
+ ps, factors = factors, ps
1584
+ _f = _isfinite
1585
+ for f in factors:
1586
+ for p in ps:
1587
+ p *= f
1588
+ if _f(p):
1589
+ yield p
1590
+ else: # PYCHOK no cover
1591
+ self._finite(p, op) # throw ValueError
1592
+
1593
+ @property_RO
1594
+ def real(self):
1595
+ '''Get the C{real} part of this instance (C{float}).
1596
+
1597
+ @see: Methods L{Fsum.__float__} and L{Fsum.fsum}
1598
+ and properties L{Fsum.ceil}, L{Fsum.floor},
1599
+ L{Fsum.imag} and L{Fsum.residual}.
1600
+ '''
1601
+ return float(self._fprs)
1602
+
1603
+ @property_RO
1604
+ def residual(self):
1605
+ '''Get this instance' residual (C{float} or C{int}), the
1606
+ C{sum(partials)} less the precision running sum C{fsum}.
1607
+
1608
+ @note: If the C{residual is INT0}, the precision running
1609
+ C{fsum} is considered to be I{exact}.
1610
+
1611
+ @see: Methods L{Fsum.fsum}, L{Fsum.fsum2} and L{Fsum.is_exact}.
1612
+ '''
1613
+ return self._fprs2.residual
1614
+
1615
+ def _raiser(self, r, s):
1616
+ '''(INTERNAL) Does the ratio C{r / s} exceed threshold?
1617
+ '''
1618
+ self._ratio = t = fabs((r / s) if s else r)
1619
+ return t > self._RESIDUAL
1620
+
1621
+ def RESIDUAL(self, *threshold):
1622
+ '''Get and set this instance' I{ratio} for raising L{ResidualError}s,
1623
+ overriding the default from env variable C{PYGEODESY_FSUM_RESIDUAL}.
1624
+
1625
+ @arg threshold: If C{scalar}, the I{ratio} to exceed for raising
1626
+ L{ResidualError}s in division and exponention, if
1627
+ C{None} restore the default set with env variable
1628
+ C{PYGEODESY_FSUM_RESIDUAL} or if omitted, keep the
1629
+ current setting.
1630
+
1631
+ @return: The previous C{RESIDUAL} setting (C{float}).
1632
+
1633
+ @raise ValueError: Negative B{C{threshold}}.
1634
+
1635
+ @note: A L{ResidualError} is thrown if the non-zero I{ratio}
1636
+ C{residual} / C{fsum} exceeds the B{C{threshold}}.
1637
+ '''
1638
+ r = self._RESIDUAL
1639
+ if threshold:
1640
+ t = threshold[0]
1641
+ t = Fsum._RESIDUAL if t is None else (
1642
+ float(t) if isscalar(t) else ( # for backward ...
1643
+ _0_0 if bool(t) else _1_0)) # ... compatibility
1644
+ if t < 0:
1645
+ u = _DOT_(self, unstr(self.RESIDUAL, *threshold))
1646
+ raise _ValueError(u, RESIDUAL=t, txt=_negative_)
1647
+ self._RESIDUAL = t
1648
+ return r
1649
+
1650
+ def _ResidualError(self, op, other, residual):
1651
+ '''(INTERNAL) Non-zero B{C{residual}} etc.
1652
+ '''
1653
+ t = _stresidual(_non_zero_, residual, ratio=self._ratio,
1654
+ RESIDUAL=self._RESIDUAL)
1655
+ t = t.replace(_COMMASPACE_R_, _exceeds_R_)
1656
+ return self._Error(op, other, ResidualError, txt=t)
1657
+
1658
+ def signOf(self, res=True):
1659
+ '''Determine the sign of this instance.
1660
+
1661
+ @kwarg res: If C{True} consider, otherwise
1662
+ ignore the residual (C{bool}).
1663
+
1664
+ @return: The sign (C{int}, -1, 0 or +1).
1665
+ '''
1666
+ s, r = self._fprs2 if res else (self._fprs, 0)
1667
+ return _signOf(s, -r)
1668
+
1669
+ def toRepr(self, **prec_sep_fmt_lenc): # PYCHOK signature
1670
+ '''Return this C{Fsum} instance as representation.
1671
+
1672
+ @kwarg prec_sep_fmt_lenc: Optional keyword arguments for
1673
+ method L{Fsum2Tuple.toRepr} plus C{B{lenc}=True}
1674
+ (C{bool}) to in-/exclude the current C{[len]}
1675
+ of this L{Fsum} enclosed in I{[brackets]}.
1676
+
1677
+ @return: This instance (C{repr}).
1678
+ '''
1679
+ return self._toT(self._fprs2.toRepr, **prec_sep_fmt_lenc)
1680
+
1681
+ def toStr(self, **prec_sep_fmt_lenc): # PYCHOK signature
1682
+ '''Return this C{Fsum} instance as string.
1683
+
1684
+ @kwarg prec_sep_fmt_lenc: Optional keyword arguments for
1685
+ method L{Fsum2Tuple.toStr} plus C{B{lenc}=True}
1686
+ (C{bool}) to in-/exclude the current C{[len]}
1687
+ of this L{Fsum} enclosed in I{[brackets]}.
1688
+
1689
+ @return: This instance (C{str}).
1690
+ '''
1691
+ return self._toT(self._fprs2.toStr, **prec_sep_fmt_lenc)
1692
+
1693
+ def _toT(self, toT, fmt=Fmt.g, lenc=True, **kwds):
1694
+ '''(INTERNAL) Helper for C{toRepr} and C{toStr}.
1695
+ '''
1696
+ n = self.named3
1697
+ if lenc:
1698
+ n = Fmt.SQUARE(n, len(self))
1699
+ return _SPACE_(n, toT(fmt=fmt, **kwds))
1700
+
1701
+ def _TypeError(self, op, other, **txt): # PYCHOK no cover
1702
+ '''(INTERNAL) Return a C{TypeError}.
1703
+ '''
1704
+ return self._Error(op, other, _TypeError, **txt)
1705
+
1706
+ def _update(self): # see ._fset
1707
+ '''(INTERNAL) Zap all cached C{Property_RO} values.
1708
+ '''
1709
+ Fsum._fint2._update(self)
1710
+ Fsum._fprs ._update(self)
1711
+ Fsum._fprs2._update(self)
1712
+ return self
1713
+
1714
+ def _ValueError(self, op, other, **txt): # PYCHOK no cover
1715
+ '''(INTERNAL) Return a C{ValueError}.
1716
+ '''
1717
+ return self._Error(op, other, _ValueError, **txt)
1718
+
1719
+ def _ZeroDivisionError(self, op, other, **txt): # PYCHOK no cover
1720
+ '''(INTERNAL) Return a C{ZeroDivisionError}.
1721
+ '''
1722
+ return self._Error(op, other, _ZeroDivisionError, **txt)
1723
+
1724
+ _allPropertiesOf_n(3, Fsum, Property_RO) # PYCHOK assert, see Fsum._fset, -._update
1725
+
1726
+
1727
+ def _Float_Int(arg, **name_Error):
1728
+ '''(INTERNAL) Unit of L{Fsum2Tuple} items.
1729
+ '''
1730
+ U = Int if isint(arg) else Float
1731
+ return U(arg, **name_Error)
1732
+
1733
+
1734
+ class Fsum2Tuple(_NamedTuple):
1735
+ '''2-Tuple C{(fsum, residual)} with the precision running C{fsum}
1736
+ and the C{residual}, the sum of the remaining partials. Each
1737
+ item is either C{float} or C{int}.
1738
+
1739
+ @note: If the C{residual is INT0}, the C{fsum} is considered
1740
+ to be I{exact}, see method L{Fsum2Tuple.is_exact}.
1741
+ '''
1742
+ _Names_ = ( Fsum.fsum.__name__, Fsum.residual.name)
1743
+ _Units_ = (_Float_Int, _Float_Int)
1744
+
1745
+ @Property_RO
1746
+ def Fsum(self):
1747
+ '''Get this L{Fsum2Tuple} as an L{Fsum}.
1748
+ '''
1749
+ f = Fsum(name=self.name)
1750
+ return f._copy_0(*(s for s in reversed(self) if s))
1751
+
1752
+ def is_exact(self):
1753
+ '''Is this L{Fsum2Tuple} considered to be exact? (C{bool}).
1754
+ '''
1755
+ return self.Fsum.is_exact()
1756
+
1757
+ def is_integer(self):
1758
+ '''Is this L{Fsum2Tuple} C{integer}? (C{bool}).
1759
+ '''
1760
+ return self.Fsum.is_integer()
1761
+
1762
+
1763
+ class ResidualError(_ValueError):
1764
+ '''Error raised for an operation involving a L{pygeodesy.sums.Fsum}
1765
+ instance with a non-zero C{residual}, I{integer} or otherwise.
1766
+
1767
+ @see: Module L{pygeodesy.fsums} and method L{Fsum.RESIDUAL}.
1768
+ '''
1769
+ pass
1770
+
1771
+
1772
+ try:
1773
+ from math import fsum as _fsum # precision IEEE-754 sum, Python 2.6+
1774
+
1775
+ # make sure _fsum works as expected (XXX check
1776
+ # float.__getformat__('float')[:4] == 'IEEE'?)
1777
+ if _fsum((1, 1e101, 1, -1e101)) != 2: # PYCHOK no cover
1778
+ del _fsum # nope, remove _fsum ...
1779
+ raise ImportError # ... use _fsum below
1780
+
1781
+ Fsum._math_fsum = _sum = _fsum # PYCHOK exported
1782
+
1783
+ if _getenv('PYGEODESY_FSUM_PARTIALS', _fsum.__name__) == _fsum.__name__:
1784
+ _psum = _fsum # PYCHOK redef
1785
+
1786
+ except ImportError:
1787
+ _sum = sum # Fsum(NAN) exception fall-back
1788
+
1789
+ def _fsum(xs):
1790
+ '''(INTERNAL) Precision summation, Python 2.5-.
1791
+ '''
1792
+ return Fsum(name=_fsum.__name__)._facc(xs, up=False)._fprs
1793
+
1794
+
1795
+ def fsum(xs, floats=False):
1796
+ '''Precision floating point summation based on or like Python's C{math.fsum}.
1797
+
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}.
1802
+
1803
+ @return: Precision C{fsum} (C{float}).
1804
+
1805
+ @raise OverflowError: Partial C{2sum} overflow.
1806
+
1807
+ @raise TypeError: Non-scalar B{C{xs}} value.
1808
+
1809
+ @raise ValueError: Invalid or non-finite B{C{xs}} value.
1810
+
1811
+ @note: Exceptions and I{non-finite} handling may differ if not
1812
+ based on Python's C{math.fsum}.
1813
+
1814
+ @see: Class L{Fsum} and methods L{Fsum.fsum} and L{Fsum.fadd}.
1815
+ '''
1816
+ return _fsum(xs if floats else _2floats(xs)) if xs else _0_0 # PYCHOK yield
1817
+
1818
+
1819
+ def fsum_(*xs, **floats):
1820
+ '''Precision floating point summation of all positional arguments.
1821
+
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}.
1826
+
1827
+ @return: Precision C{fsum} (C{float}).
1828
+
1829
+ @see: Function C{fsum}.
1830
+ '''
1831
+ return _fsum(xs if _xkwds_get(floats, floats=False) else
1832
+ _2floats(xs, origin=1)) if xs else _0_0 # PYCHOK yield
1833
+
1834
+
1835
+ def fsumf_(*xs):
1836
+ '''Precision floating point summation L{fsum_}C{(*xs, floats=True)}.
1837
+ '''
1838
+ return _fsum(xs) if xs else _0_0
1839
+
1840
+
1841
+ def fsum1(xs, floats=False):
1842
+ '''Precision floating point summation of a few arguments, 1-primed.
1843
+
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}.
1848
+
1849
+ @return: Precision C{fsum} (C{float}).
1850
+
1851
+ @see: Function C{fsum}.
1852
+ '''
1853
+ return _fsum(_1primed(xs if floats else _2floats(xs))) if xs else _0_0 # PYCHOK yield
1854
+
1855
+
1856
+ def fsum1_(*xs, **floats):
1857
+ '''Precision floating point summation of a few arguments, 1-primed.
1858
+
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}.
1863
+
1864
+ @return: Precision C{fsum} (C{float}).
1865
+
1866
+ @see: Function C{fsum}
1867
+ '''
1868
+ return _fsum(_1primed(xs if _xkwds_get(floats, floats=False) else
1869
+ _2floats(xs, origin=1))) if xs else _0_0 # PYCHOK yield
1870
+
1871
+
1872
+ def fsum1f_(*xs):
1873
+ '''Precision floating point summation L{fsum1_}C{(*xs, floats=True)}.
1874
+ '''
1875
+ return _fsum(_1primed(xs)) if xs else _0_0
1876
+
1877
+
1878
+ # **) MIT License
1879
+ #
1880
+ # Copyright (C) 2016-2024 -- mrJean1 at Gmail -- All Rights Reserved.
1881
+ #
1882
+ # Permission is hereby granted, free of charge, to any person obtaining a
1883
+ # copy of this software and associated documentation files (the "Software"),
1884
+ # to deal in the Software without restriction, including without limitation
1885
+ # the rights to use, copy, modify, merge, publish, distribute, sublicense,
1886
+ # and/or sell copies of the Software, and to permit persons to whom the
1887
+ # Software is furnished to do so, subject to the following conditions:
1888
+ #
1889
+ # The above copyright notice and this permission notice shall be included
1890
+ # in all copies or substantial portions of the Software.
1891
+ #
1892
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
1893
+ # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1894
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
1895
+ # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
1896
+ # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
1897
+ # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
1898
+ # OTHER DEALINGS IN THE SOFTWARE.