decoint 0.1.0__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.
- decoint/__init__.py +23 -0
- decoint/arithmetic.py +181 -0
- decoint/constants.py +6 -0
- decoint/decorated_arithmetic.py +98 -0
- decoint/decorated_functions.py +566 -0
- decoint/decorated_interval.py +377 -0
- decoint/decorations.py +18 -0
- decoint/functions.py +564 -0
- decoint/interval.py +546 -0
- decoint/rounding.py +312 -0
- decoint-0.1.0.dist-info/METADATA +23 -0
- decoint-0.1.0.dist-info/RECORD +15 -0
- decoint-0.1.0.dist-info/WHEEL +5 -0
- decoint-0.1.0.dist-info/licenses/LICENSE +21 -0
- decoint-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
from .interval import Interval
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from .decorations import Decoration, combine
|
|
4
|
+
from gmpy2 import mpfr as Number
|
|
5
|
+
from gmpy2 import is_nan, context, get_context
|
|
6
|
+
|
|
7
|
+
ctx = get_context()
|
|
8
|
+
ctx.precision = 53
|
|
9
|
+
ctx.emin = -1073
|
|
10
|
+
ctx.emax = 1024
|
|
11
|
+
|
|
12
|
+
@dataclass(frozen=True)
|
|
13
|
+
class DecoratedInterval:
|
|
14
|
+
interval: Interval = Interval.empty()
|
|
15
|
+
decoration: Decoration = Decoration.COM
|
|
16
|
+
nai: bool = False
|
|
17
|
+
|
|
18
|
+
def __post_init__(self):
|
|
19
|
+
|
|
20
|
+
if isinstance(self.interval, str):
|
|
21
|
+
try:
|
|
22
|
+
parsed_decoration = DecoratedInterval.from_string(self.interval)
|
|
23
|
+
object.__setattr__(self, "interval", parsed_decoration.interval)
|
|
24
|
+
object.__setattr__(self, "decoration", parsed_decoration.decoration)
|
|
25
|
+
object.__setattr__(self, "nai", parsed_decoration.nai)
|
|
26
|
+
except Exception:
|
|
27
|
+
object.__setattr__(self, "interval", Interval(Number('nan'), Number('nan')))
|
|
28
|
+
object.__setattr__(self, "decoration", Decoration.ILL)
|
|
29
|
+
object.__setattr__(self, "nai", True)
|
|
30
|
+
return
|
|
31
|
+
|
|
32
|
+
if not isinstance(self.interval, Interval):
|
|
33
|
+
object.__setattr__(self, "nai", True)
|
|
34
|
+
object.__setattr__(self, "interval", Interval(Number('nan'), Number('nan')))
|
|
35
|
+
object.__setattr__(self, "decoration", Decoration.ILL)
|
|
36
|
+
|
|
37
|
+
if not isinstance(self.decoration, Decoration):
|
|
38
|
+
object.__setattr__(self, "nai", True)
|
|
39
|
+
object.__setattr__(self, "interval", Interval(Number('nan'), Number('nan')))
|
|
40
|
+
object.__setattr__(self, "decoration", Decoration.ILL)
|
|
41
|
+
|
|
42
|
+
if self.nai:
|
|
43
|
+
if self.decoration != Decoration.ILL:
|
|
44
|
+
object.__setattr__(self, "decoration", Decoration.ILL)
|
|
45
|
+
object.__setattr__(self, "interval", Interval(Number('nan'), Number('nan')))
|
|
46
|
+
|
|
47
|
+
if self.decoration == Decoration.ILL:
|
|
48
|
+
object.__setattr__(self, "nai", True)
|
|
49
|
+
object.__setattr__(self, "interval", Interval(Number('nan'), Number('nan')))
|
|
50
|
+
|
|
51
|
+
if is_nan(self.interval.lo) or is_nan(self.interval.hi):
|
|
52
|
+
object.__setattr__(self, "nai", True)
|
|
53
|
+
object.__setattr__(self, "interval", Interval(Number('nan'), Number('nan')))
|
|
54
|
+
object.__setattr__(self, "decoration", Decoration.ILL)
|
|
55
|
+
|
|
56
|
+
if self.interval.is_empty and not self.nai:
|
|
57
|
+
object.__setattr__(self, "decoration", Decoration.TRV)
|
|
58
|
+
|
|
59
|
+
if not self.interval.is_bounded and not self.nai and not self.interval.is_empty and self.decoration > Decoration.DAC:
|
|
60
|
+
object.__setattr__(self, "decoration", Decoration.DAC)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@classmethod
|
|
64
|
+
def empty(cls):
|
|
65
|
+
return cls(Interval.empty(), Decoration.TRV)
|
|
66
|
+
|
|
67
|
+
@classmethod
|
|
68
|
+
def entire(cls):
|
|
69
|
+
return cls(Interval.entire(), Decoration.DAC)
|
|
70
|
+
|
|
71
|
+
@classmethod
|
|
72
|
+
def new_nai(cls):
|
|
73
|
+
return cls(Interval(Number('nan'), Number('nan')), Decoration.ILL, nai = True)
|
|
74
|
+
|
|
75
|
+
@classmethod
|
|
76
|
+
def _coerce(cls, value):
|
|
77
|
+
if isinstance(value, cls):
|
|
78
|
+
return value
|
|
79
|
+
|
|
80
|
+
if isinstance(value, Interval):
|
|
81
|
+
if value.is_empty:
|
|
82
|
+
return cls(value, Decoration.TRV)
|
|
83
|
+
if not value.is_bounded:
|
|
84
|
+
return cls(value, Decoration.DAC)
|
|
85
|
+
return cls(value, Decoration.COM)
|
|
86
|
+
|
|
87
|
+
if isinstance(value, str):
|
|
88
|
+
try:
|
|
89
|
+
x = cls.from_string(value)
|
|
90
|
+
return(x)
|
|
91
|
+
except Exception:
|
|
92
|
+
return cls.new_nai()
|
|
93
|
+
|
|
94
|
+
try:
|
|
95
|
+
bare_interval = Interval._coerce(value)
|
|
96
|
+
return cls._coerce(bare_interval)
|
|
97
|
+
except Exception as e:
|
|
98
|
+
return cls.new_nai()
|
|
99
|
+
|
|
100
|
+
@classmethod
|
|
101
|
+
def from_string(cls, s: str):
|
|
102
|
+
s_s = s.strip()
|
|
103
|
+
|
|
104
|
+
if s_s in ("[nan]", "[nai]", "nai", "[nai]_ill", "[nan, nan]_ill"):
|
|
105
|
+
return cls.new_nai()
|
|
106
|
+
|
|
107
|
+
if "_" in s_s:
|
|
108
|
+
interval_part, dec_part = s.rsplit("_", 1)
|
|
109
|
+
try:
|
|
110
|
+
dec = Decoration[dec_part.upper()]
|
|
111
|
+
except KeyError:
|
|
112
|
+
return cls.new_nai()
|
|
113
|
+
try:
|
|
114
|
+
bare_int = Interval.from_string(interval_part)
|
|
115
|
+
except ValueError:
|
|
116
|
+
return cls.new_nai()
|
|
117
|
+
else:
|
|
118
|
+
try:
|
|
119
|
+
bare_int = Interval.from_string(s_s)
|
|
120
|
+
except ValueError:
|
|
121
|
+
return cls.new_nai()
|
|
122
|
+
if bare_int.is_empty:
|
|
123
|
+
dec = Decoration.TRV
|
|
124
|
+
elif bare_int.lo == Number('-inf') or bare_int.hi == Number('inf'):
|
|
125
|
+
dec = Decoration.DAC
|
|
126
|
+
else:
|
|
127
|
+
dec = Decoration.COM
|
|
128
|
+
return cls(bare_int, dec)
|
|
129
|
+
|
|
130
|
+
@property
|
|
131
|
+
def is_nai(self):
|
|
132
|
+
return self.nai
|
|
133
|
+
|
|
134
|
+
@property
|
|
135
|
+
def is_empty(self):
|
|
136
|
+
return self.interval.is_empty
|
|
137
|
+
|
|
138
|
+
@property
|
|
139
|
+
def is_entire(self):
|
|
140
|
+
return self.interval.is_entire
|
|
141
|
+
|
|
142
|
+
@property
|
|
143
|
+
def is_common(self):
|
|
144
|
+
if self.is_nai:
|
|
145
|
+
return False
|
|
146
|
+
return self.interval.is_common
|
|
147
|
+
|
|
148
|
+
@property
|
|
149
|
+
def width(self):
|
|
150
|
+
if self.is_nai:
|
|
151
|
+
return Number('nan')
|
|
152
|
+
return self.interval.width
|
|
153
|
+
|
|
154
|
+
@property
|
|
155
|
+
def is_singleton(self):
|
|
156
|
+
if self.is_nai:
|
|
157
|
+
return False
|
|
158
|
+
return self.interval.lo == self.interval.hi
|
|
159
|
+
|
|
160
|
+
@property
|
|
161
|
+
def radius(self):
|
|
162
|
+
if self.is_nai:
|
|
163
|
+
return Number('nan')
|
|
164
|
+
return self.interval.radius
|
|
165
|
+
|
|
166
|
+
@property
|
|
167
|
+
def midpoint(self):
|
|
168
|
+
if self.is_nai:
|
|
169
|
+
return Number('nan')
|
|
170
|
+
return self.interval.midpoint
|
|
171
|
+
|
|
172
|
+
@property
|
|
173
|
+
def magnitude(self):
|
|
174
|
+
if self.is_nai:
|
|
175
|
+
return Number('nan')
|
|
176
|
+
return self.interval.magnitude
|
|
177
|
+
|
|
178
|
+
@property
|
|
179
|
+
def mignitude(self):
|
|
180
|
+
if self.is_nai:
|
|
181
|
+
return Number('nan')
|
|
182
|
+
return self.interval.mignitude
|
|
183
|
+
|
|
184
|
+
def contains(self, x):
|
|
185
|
+
if self.is_nai:
|
|
186
|
+
return False
|
|
187
|
+
return self.interval.contains(x)
|
|
188
|
+
|
|
189
|
+
def subset(self, other):
|
|
190
|
+
other = self._coerce(other)
|
|
191
|
+
if self.is_nai or other.is_nai:
|
|
192
|
+
return False
|
|
193
|
+
return self.interval.subset(other.interval)
|
|
194
|
+
|
|
195
|
+
def proper_subset(self, other):
|
|
196
|
+
other = self._coerce(other)
|
|
197
|
+
if self.is_nai or other.is_nai:
|
|
198
|
+
return False
|
|
199
|
+
return self.interval.proper_subset(other.interval)
|
|
200
|
+
|
|
201
|
+
def overlaps(self, other):
|
|
202
|
+
other = self._coerce(other)
|
|
203
|
+
if self.is_nai or other.is_nai:
|
|
204
|
+
return False
|
|
205
|
+
return self.interval.overlaps(other.interval)
|
|
206
|
+
|
|
207
|
+
def disjoint(self, other):
|
|
208
|
+
other = self._coerce(other)
|
|
209
|
+
if self.is_nai or other.is_nai:
|
|
210
|
+
return False
|
|
211
|
+
return self.interval.disjoint(other.interval)
|
|
212
|
+
|
|
213
|
+
def precedes(self, other):
|
|
214
|
+
other = self._coerce(other)
|
|
215
|
+
if self.is_nai or other.is_nai:
|
|
216
|
+
return False
|
|
217
|
+
return self.interval.precedes(other.interval)
|
|
218
|
+
|
|
219
|
+
def meets(self, other):
|
|
220
|
+
other = self._coerce(other)
|
|
221
|
+
if self.is_nai or other.is_nai:
|
|
222
|
+
return False
|
|
223
|
+
return self.interval.meets(other.interval)
|
|
224
|
+
|
|
225
|
+
def sup_sub(self, other):
|
|
226
|
+
other = self._coerce(other)
|
|
227
|
+
if self.is_nai or other.is_nai:
|
|
228
|
+
return False
|
|
229
|
+
return self.interval.sup_sub(other.interval)
|
|
230
|
+
|
|
231
|
+
def interior(self, other):
|
|
232
|
+
other = self._coerce(other)
|
|
233
|
+
if self.is_nai or other.is_nai:
|
|
234
|
+
return False
|
|
235
|
+
return self.interval.interior(other.interval)
|
|
236
|
+
|
|
237
|
+
def inf_sub(self, other):
|
|
238
|
+
other = self._coerce(other)
|
|
239
|
+
if self.is_nai or other.is_nai:
|
|
240
|
+
return False
|
|
241
|
+
return self.interval.inf_sub(other.interval)
|
|
242
|
+
|
|
243
|
+
def __repr__(self):
|
|
244
|
+
if self.nai:
|
|
245
|
+
return "DecoratedInterval(nai)"
|
|
246
|
+
|
|
247
|
+
return (
|
|
248
|
+
f"DecoratedInterval("
|
|
249
|
+
f"{self.interval}, "
|
|
250
|
+
f"{str(self.decoration)})")
|
|
251
|
+
|
|
252
|
+
def __str__(self):
|
|
253
|
+
if self.nai:
|
|
254
|
+
return "[nai]_ill"
|
|
255
|
+
int_str = str(self.interval)
|
|
256
|
+
dec_str = self.decoration.name.lower()
|
|
257
|
+
return f"{int_str}_{dec_str}"
|
|
258
|
+
|
|
259
|
+
def __add__(self, other):
|
|
260
|
+
from .decorated_arithmetic import add
|
|
261
|
+
other = self._coerce(other)
|
|
262
|
+
if self.is_nai or other.is_nai:
|
|
263
|
+
return DecoratedInterval.new_nai()
|
|
264
|
+
return add(self, other)
|
|
265
|
+
|
|
266
|
+
def __sub__(self, other):
|
|
267
|
+
from .decorated_arithmetic import sub
|
|
268
|
+
other = self._coerce(other)
|
|
269
|
+
if self.is_nai or other.is_nai:
|
|
270
|
+
return DecoratedInterval.new_nai()
|
|
271
|
+
return sub(self, other)
|
|
272
|
+
|
|
273
|
+
def __mul__(self, other):
|
|
274
|
+
from .decorated_arithmetic import mul
|
|
275
|
+
other = self._coerce(other)
|
|
276
|
+
if self.is_nai or other.is_nai:
|
|
277
|
+
return DecoratedInterval.new_nai()
|
|
278
|
+
return mul(self, other)
|
|
279
|
+
|
|
280
|
+
def __truediv__(self, other):
|
|
281
|
+
from .decorated_arithmetic import div
|
|
282
|
+
other = self._coerce(other)
|
|
283
|
+
if self.is_nai or other.is_nai:
|
|
284
|
+
return DecoratedInterval.new_nai()
|
|
285
|
+
return div(self, other)
|
|
286
|
+
|
|
287
|
+
def __radd__(self, other):
|
|
288
|
+
from .decorated_arithmetic import add
|
|
289
|
+
other = self._coerce(other)
|
|
290
|
+
if self.is_nai or other.is_nai:
|
|
291
|
+
return DecoratedInterval.new_nai()
|
|
292
|
+
return add(other, self)
|
|
293
|
+
|
|
294
|
+
def __rsub__(self, other):
|
|
295
|
+
from .decorated_arithmetic import sub
|
|
296
|
+
other = self._coerce(other)
|
|
297
|
+
if self.is_nai or other.is_nai:
|
|
298
|
+
return DecoratedInterval.new_nai()
|
|
299
|
+
return sub(other, self)
|
|
300
|
+
|
|
301
|
+
def __rmul__(self, other):
|
|
302
|
+
from .decorated_arithmetic import mul
|
|
303
|
+
other = self._coerce(other)
|
|
304
|
+
if self.is_nai or other.is_nai:
|
|
305
|
+
return DecoratedInterval.new_nai()
|
|
306
|
+
return mul(other, self)
|
|
307
|
+
|
|
308
|
+
def __rtruediv__(self, other):
|
|
309
|
+
from .decorated_arithmetic import div
|
|
310
|
+
other = self._coerce(other)
|
|
311
|
+
if self.is_nai or other.is_nai:
|
|
312
|
+
return DecoratedInterval.new_nai()
|
|
313
|
+
return div(other, self)
|
|
314
|
+
|
|
315
|
+
def __lt__(self, other):
|
|
316
|
+
other = self._coerce(other)
|
|
317
|
+
if self.is_nai or other.is_nai:
|
|
318
|
+
return False
|
|
319
|
+
return self.interval < other.interval
|
|
320
|
+
|
|
321
|
+
def __gt__(self, other):
|
|
322
|
+
other = self._coerce(other)
|
|
323
|
+
if self.is_nai or other.is_nai:
|
|
324
|
+
return False
|
|
325
|
+
return self.interval > other.interval
|
|
326
|
+
|
|
327
|
+
def strictly_less_than(self, other):
|
|
328
|
+
other = self._coerce(other)
|
|
329
|
+
if self.is_nai or other.is_nai:
|
|
330
|
+
return False
|
|
331
|
+
return self.interval.strictly_less_than(other.interval)
|
|
332
|
+
|
|
333
|
+
def intersection(self, other):
|
|
334
|
+
other = self._coerce(other)
|
|
335
|
+
if self.is_nai or other.is_nai:
|
|
336
|
+
return DecoratedInterval.new_nai()
|
|
337
|
+
|
|
338
|
+
interval = self.interval.intersection(other.interval)
|
|
339
|
+
dec = combine(self.decoration, other.decoration, Decoration.TRV)
|
|
340
|
+
return DecoratedInterval(interval, dec)
|
|
341
|
+
|
|
342
|
+
def hull(self, other):
|
|
343
|
+
other = self._coerce(other)
|
|
344
|
+
if self.is_nai or other.is_nai:
|
|
345
|
+
return DecoratedInterval.new_nai()
|
|
346
|
+
interval = self.interval.hull(other.interval)
|
|
347
|
+
dec = combine(self.decoration, other.decoration, Decoration.TRV)
|
|
348
|
+
return DecoratedInterval(interval, dec)
|
|
349
|
+
|
|
350
|
+
def __eq__(self, other):
|
|
351
|
+
other = self._coerce(other)
|
|
352
|
+
if self.is_nai or other.is_nai:
|
|
353
|
+
return False
|
|
354
|
+
if (self.interval.lo == other.interval.lo) and (self.interval.hi == other.interval.hi):
|
|
355
|
+
return True
|
|
356
|
+
return False
|
|
357
|
+
|
|
358
|
+
def strictly_precedes(self, other):
|
|
359
|
+
other = self._coerce(other)
|
|
360
|
+
if self.is_nai or other.is_nai:
|
|
361
|
+
return False
|
|
362
|
+
return self.interval.strictly_precedes(other.interval)
|
|
363
|
+
|
|
364
|
+
def __neg__(self):
|
|
365
|
+
if self.is_nai:
|
|
366
|
+
return DecoratedInterval.new_nai()
|
|
367
|
+
return DecoratedInterval(-self.interval, self.decoration)
|
|
368
|
+
|
|
369
|
+
def inf(self):
|
|
370
|
+
if self.is_nai:
|
|
371
|
+
return Number('nan')
|
|
372
|
+
return self.interval.lo
|
|
373
|
+
|
|
374
|
+
def sup(self):
|
|
375
|
+
if self.is_nai:
|
|
376
|
+
return Number('nan')
|
|
377
|
+
return self.interval.hi
|
decoint/decorations.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from enum import IntEnum
|
|
2
|
+
|
|
3
|
+
class Decoration(IntEnum):
|
|
4
|
+
ILL = 0
|
|
5
|
+
TRV = 1
|
|
6
|
+
DEF = 2
|
|
7
|
+
DAC = 3
|
|
8
|
+
COM = 4
|
|
9
|
+
|
|
10
|
+
def __str__(self):
|
|
11
|
+
return self.name.lower()
|
|
12
|
+
|
|
13
|
+
def combine(*decorations: Decoration) -> Decoration:
|
|
14
|
+
# IEEE 1788 requires that the minimum decoration has to be the one that is returned
|
|
15
|
+
if not decorations:
|
|
16
|
+
return Decoration.ILL
|
|
17
|
+
|
|
18
|
+
return min(decorations)
|