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.
- PyGeodesy-24.3.24.dist-info/METADATA +272 -0
- PyGeodesy-24.3.24.dist-info/RECORD +115 -0
- PyGeodesy-24.3.24.dist-info/WHEEL +6 -0
- PyGeodesy-24.3.24.dist-info/top_level.txt +1 -0
- pygeodesy/LICENSE +21 -0
- pygeodesy/__init__.py +615 -0
- pygeodesy/__main__.py +103 -0
- pygeodesy/albers.py +867 -0
- pygeodesy/auxilats/_CX_4.py +218 -0
- pygeodesy/auxilats/_CX_6.py +314 -0
- pygeodesy/auxilats/_CX_8.py +475 -0
- pygeodesy/auxilats/__init__.py +54 -0
- pygeodesy/auxilats/__main__.py +86 -0
- pygeodesy/auxilats/auxAngle.py +548 -0
- pygeodesy/auxilats/auxDLat.py +302 -0
- pygeodesy/auxilats/auxDST.py +296 -0
- pygeodesy/auxilats/auxLat.py +848 -0
- pygeodesy/auxilats/auxily.py +272 -0
- pygeodesy/azimuthal.py +1150 -0
- pygeodesy/basics.py +892 -0
- pygeodesy/booleans.py +2031 -0
- pygeodesy/cartesianBase.py +1062 -0
- pygeodesy/clipy.py +704 -0
- pygeodesy/constants.py +516 -0
- pygeodesy/css.py +660 -0
- pygeodesy/datums.py +752 -0
- pygeodesy/deprecated/__init__.py +61 -0
- pygeodesy/deprecated/bases.py +40 -0
- pygeodesy/deprecated/classes.py +262 -0
- pygeodesy/deprecated/consterns.py +54 -0
- pygeodesy/deprecated/datum.py +40 -0
- pygeodesy/deprecated/functions.py +375 -0
- pygeodesy/deprecated/nvector.py +48 -0
- pygeodesy/deprecated/rhumbBase.py +32 -0
- pygeodesy/deprecated/rhumbaux.py +33 -0
- pygeodesy/deprecated/rhumbsolve.py +33 -0
- pygeodesy/deprecated/rhumbx.py +33 -0
- pygeodesy/dms.py +986 -0
- pygeodesy/ecef.py +1348 -0
- pygeodesy/elevations.py +279 -0
- pygeodesy/ellipsoidalBase.py +1224 -0
- pygeodesy/ellipsoidalBaseDI.py +913 -0
- pygeodesy/ellipsoidalExact.py +343 -0
- pygeodesy/ellipsoidalGeodSolve.py +343 -0
- pygeodesy/ellipsoidalKarney.py +403 -0
- pygeodesy/ellipsoidalNvector.py +685 -0
- pygeodesy/ellipsoidalVincenty.py +590 -0
- pygeodesy/ellipsoids.py +2476 -0
- pygeodesy/elliptic.py +1198 -0
- pygeodesy/epsg.py +243 -0
- pygeodesy/errors.py +804 -0
- pygeodesy/etm.py +1190 -0
- pygeodesy/fmath.py +1013 -0
- pygeodesy/formy.py +1818 -0
- pygeodesy/frechet.py +865 -0
- pygeodesy/fstats.py +760 -0
- pygeodesy/fsums.py +1898 -0
- pygeodesy/gars.py +358 -0
- pygeodesy/geodesicw.py +581 -0
- pygeodesy/geodesicx/_C4_24.py +1699 -0
- pygeodesy/geodesicx/_C4_27.py +2395 -0
- pygeodesy/geodesicx/_C4_30.py +3301 -0
- pygeodesy/geodesicx/__init__.py +48 -0
- pygeodesy/geodesicx/__main__.py +91 -0
- pygeodesy/geodesicx/gx.py +1382 -0
- pygeodesy/geodesicx/gxarea.py +535 -0
- pygeodesy/geodesicx/gxbases.py +154 -0
- pygeodesy/geodesicx/gxline.py +669 -0
- pygeodesy/geodsolve.py +426 -0
- pygeodesy/geohash.py +914 -0
- pygeodesy/geoids.py +1884 -0
- pygeodesy/hausdorff.py +892 -0
- pygeodesy/heights.py +1155 -0
- pygeodesy/interns.py +687 -0
- pygeodesy/iters.py +545 -0
- pygeodesy/karney.py +919 -0
- pygeodesy/ktm.py +633 -0
- pygeodesy/latlonBase.py +1766 -0
- pygeodesy/lazily.py +960 -0
- pygeodesy/lcc.py +684 -0
- pygeodesy/ltp.py +1107 -0
- pygeodesy/ltpTuples.py +1563 -0
- pygeodesy/mgrs.py +721 -0
- pygeodesy/named.py +1324 -0
- pygeodesy/namedTuples.py +683 -0
- pygeodesy/nvectorBase.py +695 -0
- pygeodesy/osgr.py +781 -0
- pygeodesy/points.py +1686 -0
- pygeodesy/props.py +628 -0
- pygeodesy/resections.py +1048 -0
- pygeodesy/rhumb/__init__.py +46 -0
- pygeodesy/rhumb/aux_.py +397 -0
- pygeodesy/rhumb/bases.py +1148 -0
- pygeodesy/rhumb/ekx.py +563 -0
- pygeodesy/rhumb/solve.py +572 -0
- pygeodesy/simplify.py +647 -0
- pygeodesy/solveBase.py +472 -0
- pygeodesy/sphericalBase.py +724 -0
- pygeodesy/sphericalNvector.py +1264 -0
- pygeodesy/sphericalTrigonometry.py +1447 -0
- pygeodesy/streprs.py +627 -0
- pygeodesy/trf.py +2079 -0
- pygeodesy/triaxials.py +1484 -0
- pygeodesy/units.py +969 -0
- pygeodesy/unitsBase.py +349 -0
- pygeodesy/ups.py +538 -0
- pygeodesy/utily.py +1231 -0
- pygeodesy/utm.py +762 -0
- pygeodesy/utmups.py +318 -0
- pygeodesy/utmupsBase.py +517 -0
- pygeodesy/vector2d.py +785 -0
- pygeodesy/vector3d.py +968 -0
- pygeodesy/vector3dBase.py +1049 -0
- pygeodesy/webmercator.py +383 -0
- 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.
|