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
decoint/interval.py
ADDED
|
@@ -0,0 +1,546 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from gmpy2 import mpfr, RoundUp, RoundDown, context, get_context, is_nan, is_infinite, next_below
|
|
3
|
+
import re
|
|
4
|
+
from typing import Tuple
|
|
5
|
+
|
|
6
|
+
ctx = get_context()
|
|
7
|
+
ctx.precision = 53
|
|
8
|
+
ctx.emin = -1074
|
|
9
|
+
ctx.emax = 1024
|
|
10
|
+
|
|
11
|
+
BOX_REGEX = re.compile(r"^\[\s*([^,;\]]+?)\s*(?:[,;]\s*([^,;\]]*?))?\s*\]$")
|
|
12
|
+
UNC_REGEX = re.compile(r"^([^?]+)\?(.*)$")
|
|
13
|
+
|
|
14
|
+
Number = mpfr
|
|
15
|
+
@dataclass(frozen = True)
|
|
16
|
+
class Interval:
|
|
17
|
+
lo: Number = Number('-inf')
|
|
18
|
+
hi: Number = Number('inf')
|
|
19
|
+
|
|
20
|
+
def __post_init__(self):
|
|
21
|
+
|
|
22
|
+
if isinstance(self.lo, str):
|
|
23
|
+
parsed_interval = Interval.from_string(self.lo)
|
|
24
|
+
if not (is_nan(parsed_interval.lo) or is_nan(parsed_interval.hi)):
|
|
25
|
+
object.__setattr__(self, "lo", parsed_interval.lo)
|
|
26
|
+
object.__setattr__(self, "hi", parsed_interval.hi)
|
|
27
|
+
else:
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
with context(get_context(), round=RoundDown):
|
|
32
|
+
lo = Number(self.lo)
|
|
33
|
+
with context(get_context(), round=RoundUp):
|
|
34
|
+
hi = Number(self.hi)
|
|
35
|
+
|
|
36
|
+
if lo > hi:
|
|
37
|
+
object.__setattr__(self, "lo", Number('inf'))
|
|
38
|
+
object.__setattr__(self, "hi", Number('-inf'))
|
|
39
|
+
|
|
40
|
+
if is_nan(lo) or is_nan(hi):
|
|
41
|
+
object.__setattr__(self, "lo", Number('NaN'))
|
|
42
|
+
object.__setattr__(self, "hi", Number('NaN'))
|
|
43
|
+
|
|
44
|
+
elif lo == Number('-inf') and hi == Number('-inf'):
|
|
45
|
+
object.__setattr__(self, "lo", Number('NaN'))
|
|
46
|
+
object.__setattr__(self, "hi", Number('NaN'))
|
|
47
|
+
|
|
48
|
+
elif lo == Number('inf') and hi == Number('inf'):
|
|
49
|
+
object.__setattr__(self, "lo", Number('NaN'))
|
|
50
|
+
object.__setattr__(self, "hi", Number('NaN'))
|
|
51
|
+
|
|
52
|
+
else:
|
|
53
|
+
object.__setattr__(self, "lo", lo)
|
|
54
|
+
object.__setattr__(self, "hi", hi)
|
|
55
|
+
|
|
56
|
+
@classmethod
|
|
57
|
+
def empty(cls):
|
|
58
|
+
return cls(Number('inf'), Number('-inf'))
|
|
59
|
+
@classmethod
|
|
60
|
+
def entire(cls):
|
|
61
|
+
return cls(Number('-inf'), Number('inf'))
|
|
62
|
+
|
|
63
|
+
@classmethod
|
|
64
|
+
def _coerce(cls, value):
|
|
65
|
+
if isinstance(value, cls):
|
|
66
|
+
return value
|
|
67
|
+
if isinstance(value, str):
|
|
68
|
+
return cls.from_string(value)
|
|
69
|
+
if hasattr(value, "interval"):
|
|
70
|
+
return value.interval
|
|
71
|
+
return cls(value, value)
|
|
72
|
+
|
|
73
|
+
@classmethod
|
|
74
|
+
def from_string(cls, s: str):
|
|
75
|
+
content = s.strip()
|
|
76
|
+
content_compact = "".join(content.split()).lower()
|
|
77
|
+
|
|
78
|
+
if content_compact in ("[]", "[empty]"):
|
|
79
|
+
return cls.empty()
|
|
80
|
+
if content_compact in ("[entire]", "[,]"):
|
|
81
|
+
return cls(Number('-inf'), Number('inf'))
|
|
82
|
+
|
|
83
|
+
box_match = BOX_REGEX.match(content)
|
|
84
|
+
if box_match:
|
|
85
|
+
left, right = box_match.groups()
|
|
86
|
+
if right is None:
|
|
87
|
+
lo = cls._parse_bound(left, round_up = False)
|
|
88
|
+
hi = cls._parse_bound(left, round_up = True)
|
|
89
|
+
else:
|
|
90
|
+
lo = cls._parse_bound(left, round_up = False)
|
|
91
|
+
hi = cls._parse_bound(right, round_up = True)
|
|
92
|
+
return cls(lo, hi)
|
|
93
|
+
|
|
94
|
+
unc_match = UNC_REGEX.match(content)
|
|
95
|
+
if unc_match:
|
|
96
|
+
lo, hi = cls._parse_uncertainty(content)
|
|
97
|
+
return cls(lo, hi)
|
|
98
|
+
|
|
99
|
+
return Interval(Number('nan'), Number('nan'))
|
|
100
|
+
|
|
101
|
+
@classmethod
|
|
102
|
+
def _parse_bound(cls, s: str, round_up: bool):
|
|
103
|
+
s_clean = s.strip() if s else ""
|
|
104
|
+
s_lower = s_clean.lower()
|
|
105
|
+
if not s_clean:
|
|
106
|
+
return Number('inf') if round_up else Number('-inf')
|
|
107
|
+
if s_lower in ('inf', '+inf', 'infinity', '+infinity'):
|
|
108
|
+
return Number('inf')
|
|
109
|
+
if s_lower in ('-inf', '-infinity'):
|
|
110
|
+
return Number('-inf')
|
|
111
|
+
|
|
112
|
+
target_round = RoundUp if round_up else RoundDown
|
|
113
|
+
|
|
114
|
+
with context(get_context(), round = target_round):
|
|
115
|
+
if '/' in s_clean:
|
|
116
|
+
parts = s_clean.split('/')
|
|
117
|
+
if len(parts) == 2:
|
|
118
|
+
return Number(parts[0]) / Number(parts[1])
|
|
119
|
+
|
|
120
|
+
return Number(s_clean)
|
|
121
|
+
|
|
122
|
+
@classmethod
|
|
123
|
+
def _parse_uncertainty(cls, text: str):
|
|
124
|
+
if "??" in text:
|
|
125
|
+
if text.lower().endswith("??u"):
|
|
126
|
+
center_str = text.split("??")[0]
|
|
127
|
+
lo = cls._parse_bound(center_str, round_up = False)
|
|
128
|
+
return lo, Number('inf')
|
|
129
|
+
elif text.lower().endswith("??d"):
|
|
130
|
+
center_str = text.split("??")[0]
|
|
131
|
+
hi = cls._parse_bound(center_str, round_up = True)
|
|
132
|
+
return Number('-inf'), hi
|
|
133
|
+
else:
|
|
134
|
+
return Number('-inf'), Number('inf')
|
|
135
|
+
|
|
136
|
+
match = UNC_REGEX.match(text)
|
|
137
|
+
if not match:
|
|
138
|
+
raise ValueError("Malformed Uncertainty Syntax")
|
|
139
|
+
|
|
140
|
+
center_str, unc_str = match.groups()
|
|
141
|
+
center_str = center_str.strip()
|
|
142
|
+
unc_str = unc_str.strip() if unc_str else ""
|
|
143
|
+
unc_str_lower = unc_str.lower()
|
|
144
|
+
|
|
145
|
+
dec_digits = 0
|
|
146
|
+
check_str = center_str.lower().lstrip('+-')
|
|
147
|
+
if '.' in center_str and not check_str.startswith('0x'):
|
|
148
|
+
dec_digits = len(center_str.split('.')[1])
|
|
149
|
+
|
|
150
|
+
# DIRECTION FIX 1: Detect 'u' and 'd' flags, then strip them so
|
|
151
|
+
# "5ue-5" becomes "5e-5" for clean exponent splitting.
|
|
152
|
+
is_upper_only = 'u' in unc_str_lower
|
|
153
|
+
is_lower_only = 'd' in unc_str_lower
|
|
154
|
+
if is_upper_only:
|
|
155
|
+
unc_str_lower = unc_str_lower.replace('u', '')
|
|
156
|
+
if is_lower_only:
|
|
157
|
+
unc_str_lower = unc_str_lower.replace('d', '')
|
|
158
|
+
|
|
159
|
+
unc_str_lower = unc_str_lower.strip()
|
|
160
|
+
|
|
161
|
+
# --- UPPER BOUND (hi) ---
|
|
162
|
+
if is_lower_only:
|
|
163
|
+
# DIRECTION FIX 2: If 'd' was present, upper bound is locked to the center value
|
|
164
|
+
# scaled by the exponent (e.g., 2.5 * 10^-5). No tolerance is added.
|
|
165
|
+
with context(get_context(), round = RoundUp):
|
|
166
|
+
hi = Number(center_str)
|
|
167
|
+
if 'e' in unc_str_lower:
|
|
168
|
+
_, exp_part = unc_str_lower.split('e')
|
|
169
|
+
scale_factor = Number(10) ** int(exp_part)
|
|
170
|
+
hi = hi * scale_factor
|
|
171
|
+
else:
|
|
172
|
+
# Standard upper bound (adds tolerance + center scaled by exponent)
|
|
173
|
+
with context(get_context(), round = RoundUp):
|
|
174
|
+
mid_hi = Number(center_str)
|
|
175
|
+
if not unc_str_lower:
|
|
176
|
+
tol_hi = Number('0.5') * (Number(10) ** (-dec_digits))
|
|
177
|
+
elif 'e' in unc_str_lower:
|
|
178
|
+
unc_digits, exp_part = unc_str_lower.split('e')
|
|
179
|
+
u_val = Number(unc_digits) if unc_digits else Number('0.5')
|
|
180
|
+
base_tol = u_val * (Number(10) ** (-dec_digits))
|
|
181
|
+
scale_factor = Number(10) ** int(exp_part)
|
|
182
|
+
mid_hi = mid_hi * scale_factor
|
|
183
|
+
tol_hi = base_tol * scale_factor
|
|
184
|
+
else:
|
|
185
|
+
tol_hi = Number(unc_str_lower) * (Number(10) ** (-dec_digits))
|
|
186
|
+
|
|
187
|
+
hi = mid_hi + tol_hi
|
|
188
|
+
|
|
189
|
+
# --- LOWER BOUND (lo) ---
|
|
190
|
+
if is_upper_only:
|
|
191
|
+
# DIRECTION FIX 3: If 'u' was present, lower bound is locked to the center value
|
|
192
|
+
# scaled by the exponent (e.g., 2.5 * 10^-5). No tolerance is subtracted.
|
|
193
|
+
with context(get_context(), round = RoundDown):
|
|
194
|
+
mid_lo = Number(center_str)
|
|
195
|
+
if 'e' in unc_str_lower:
|
|
196
|
+
_, exp_part = unc_str_lower.split('e')
|
|
197
|
+
scale_factor = (Number(10) ** int(exp_part))
|
|
198
|
+
mid_lo = mid_lo * scale_factor
|
|
199
|
+
lo = mid_lo
|
|
200
|
+
else:
|
|
201
|
+
# Standard lower bound (subtracts tolerance)
|
|
202
|
+
with context(get_context(), round = RoundUp):
|
|
203
|
+
if not unc_str_lower:
|
|
204
|
+
tol_lo = Number('0.5') * (Number(10) ** (-dec_digits))
|
|
205
|
+
elif 'e' in unc_str_lower:
|
|
206
|
+
unc_digits, exp_part = unc_str_lower.split('e')
|
|
207
|
+
u_val = Number(unc_digits) if unc_digits else Number('0.5')
|
|
208
|
+
base_tol = u_val * (Number(10) ** (-dec_digits))
|
|
209
|
+
scale_factor = Number(10) ** int(exp_part)
|
|
210
|
+
tol_lo = base_tol * scale_factor
|
|
211
|
+
else:
|
|
212
|
+
tol_lo = Number(unc_str_lower) * (Number(10) ** (-dec_digits))
|
|
213
|
+
|
|
214
|
+
with context(get_context(), round = RoundDown):
|
|
215
|
+
mid_lo = Number(center_str)
|
|
216
|
+
if 'e' in unc_str_lower:
|
|
217
|
+
_, exp_part = unc_str_lower.split('e')
|
|
218
|
+
scale_factor = Number(10) ** int(exp_part)
|
|
219
|
+
mid_lo = mid_lo * scale_factor
|
|
220
|
+
|
|
221
|
+
lo = mid_lo - tol_lo
|
|
222
|
+
|
|
223
|
+
return lo, hi
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
@property
|
|
228
|
+
def is_common(self):
|
|
229
|
+
if not is_nan(self.lo) and self.is_bounded and not self.is_empty:
|
|
230
|
+
return True
|
|
231
|
+
return False
|
|
232
|
+
|
|
233
|
+
@property
|
|
234
|
+
def is_empty(self):
|
|
235
|
+
return self.lo > self.hi
|
|
236
|
+
|
|
237
|
+
@property
|
|
238
|
+
def is_entire(self):
|
|
239
|
+
return not self.is_empty and self.lo == Number('-inf') and self.hi == Number('inf')
|
|
240
|
+
|
|
241
|
+
@property
|
|
242
|
+
def is_bounded(self):
|
|
243
|
+
return not self.is_empty and self.lo != Number('-inf') and self.hi != Number('inf')
|
|
244
|
+
|
|
245
|
+
@property
|
|
246
|
+
def is_singleton(self):
|
|
247
|
+
return not self.is_empty and self.lo == self.hi
|
|
248
|
+
|
|
249
|
+
@property
|
|
250
|
+
def width(self):
|
|
251
|
+
if self.is_empty:
|
|
252
|
+
return Number('nan')
|
|
253
|
+
if not self.is_bounded:
|
|
254
|
+
return Number('inf')
|
|
255
|
+
with context(get_context()) as ctx:
|
|
256
|
+
ctx.round = RoundUp
|
|
257
|
+
return self.hi - self.lo
|
|
258
|
+
|
|
259
|
+
@property
|
|
260
|
+
def radius(self):
|
|
261
|
+
if self.is_empty:
|
|
262
|
+
return Number('nan')
|
|
263
|
+
if not self.is_bounded:
|
|
264
|
+
return Number('inf')
|
|
265
|
+
|
|
266
|
+
m = self.midpoint
|
|
267
|
+
with context(get_context(), round = RoundUp):
|
|
268
|
+
d_lo = m - self.lo
|
|
269
|
+
d_hi = self.hi - m
|
|
270
|
+
return max(d_lo, d_hi)
|
|
271
|
+
|
|
272
|
+
@property
|
|
273
|
+
def midpoint(self):
|
|
274
|
+
if self.is_empty:
|
|
275
|
+
return Number("nan")
|
|
276
|
+
lo, hi = self.lo, self.hi
|
|
277
|
+
if self.is_entire or (is_infinite(lo) and is_infinite(hi)):
|
|
278
|
+
return Number(0)
|
|
279
|
+
if is_infinite(lo):
|
|
280
|
+
return Number(next_below(Number("-inf")))
|
|
281
|
+
if is_infinite(hi):
|
|
282
|
+
return Number(next_below(Number("inf")))
|
|
283
|
+
return (lo / 2) + (hi / 2)
|
|
284
|
+
|
|
285
|
+
@property
|
|
286
|
+
def magnitude(self):
|
|
287
|
+
if self.is_empty:
|
|
288
|
+
return Number("nan")
|
|
289
|
+
return max(abs(self.lo), abs(self.hi))
|
|
290
|
+
@property
|
|
291
|
+
def mignitude(self):
|
|
292
|
+
if self.is_empty:
|
|
293
|
+
return Number("nan")
|
|
294
|
+
if self.contains(0):
|
|
295
|
+
return Number(0)
|
|
296
|
+
|
|
297
|
+
return min(abs(self.lo), abs(self.hi))
|
|
298
|
+
|
|
299
|
+
def contains(self, x):
|
|
300
|
+
try:
|
|
301
|
+
x = Number(x)
|
|
302
|
+
except Exception:
|
|
303
|
+
return False
|
|
304
|
+
if self.is_empty:
|
|
305
|
+
return False
|
|
306
|
+
if is_infinite(x):
|
|
307
|
+
return False
|
|
308
|
+
return self.lo <= x <= self.hi
|
|
309
|
+
|
|
310
|
+
def subset(self, other):
|
|
311
|
+
if not isinstance(other, Interval):
|
|
312
|
+
return False
|
|
313
|
+
if self.is_empty:
|
|
314
|
+
return True
|
|
315
|
+
if other.is_empty:
|
|
316
|
+
return False
|
|
317
|
+
return other.lo <= self.lo and other.hi >= self.hi
|
|
318
|
+
|
|
319
|
+
def proper_subset(self, other):
|
|
320
|
+
if not isinstance(other, Interval):
|
|
321
|
+
return False
|
|
322
|
+
return self.subset(other) and self != other
|
|
323
|
+
|
|
324
|
+
def overlaps(self, other):
|
|
325
|
+
if not isinstance(other, Interval):
|
|
326
|
+
return False
|
|
327
|
+
if self.is_empty or other.is_empty:
|
|
328
|
+
return False
|
|
329
|
+
return max(other.lo, self.lo) <= min(self.hi, other.hi)
|
|
330
|
+
|
|
331
|
+
def intersection(self, other):
|
|
332
|
+
if not isinstance(other, Interval):
|
|
333
|
+
return Interval(Number('nan'), Number('nan'))
|
|
334
|
+
if self.is_empty or other.is_empty:
|
|
335
|
+
return Interval.empty()
|
|
336
|
+
return Interval(max(self.lo, other.lo), min(self.hi, other.hi))
|
|
337
|
+
|
|
338
|
+
def hull(self, other):
|
|
339
|
+
if not isinstance(other, Interval):
|
|
340
|
+
return Interval(Number('nan'), Number('nan'))
|
|
341
|
+
if self.is_empty:
|
|
342
|
+
return other
|
|
343
|
+
if other.is_empty:
|
|
344
|
+
return self
|
|
345
|
+
return Interval(min(self.lo, other.lo), max(self.hi, other.hi))
|
|
346
|
+
|
|
347
|
+
def __repr__(self):
|
|
348
|
+
if self.is_empty:
|
|
349
|
+
return "Interval.empty()"
|
|
350
|
+
if self.is_entire:
|
|
351
|
+
return "Interval.entire()"
|
|
352
|
+
return f"Interval({self.lo}, {self.hi})"
|
|
353
|
+
|
|
354
|
+
def __str__(self):
|
|
355
|
+
if self.is_empty:
|
|
356
|
+
return "[empty]"
|
|
357
|
+
if self.is_entire:
|
|
358
|
+
return "[entire]"
|
|
359
|
+
return f"[{self.lo}, {self.hi}]"
|
|
360
|
+
|
|
361
|
+
def disjoint(self, other):
|
|
362
|
+
if not isinstance(other, Interval):
|
|
363
|
+
return False
|
|
364
|
+
return not self.overlaps(other)
|
|
365
|
+
|
|
366
|
+
def interior_contains(self, x):
|
|
367
|
+
try:
|
|
368
|
+
x = Number(x)
|
|
369
|
+
except:
|
|
370
|
+
return False
|
|
371
|
+
if self.is_empty:
|
|
372
|
+
return False
|
|
373
|
+
return self.lo < x < self.hi
|
|
374
|
+
|
|
375
|
+
def interior(self, other):
|
|
376
|
+
if not isinstance(other, Interval):
|
|
377
|
+
return False
|
|
378
|
+
if self.is_empty:
|
|
379
|
+
return True
|
|
380
|
+
if other.is_empty:
|
|
381
|
+
return False
|
|
382
|
+
if self.is_entire and other.is_entire:
|
|
383
|
+
return True
|
|
384
|
+
|
|
385
|
+
return other.lo < self.lo and self.hi < other.hi
|
|
386
|
+
|
|
387
|
+
def precedes(self, other):
|
|
388
|
+
other = self._coerce(other)
|
|
389
|
+
if self.is_empty or other.is_empty:
|
|
390
|
+
return True
|
|
391
|
+
return self.hi <= other.lo
|
|
392
|
+
|
|
393
|
+
def meets(self, other):
|
|
394
|
+
if not isinstance(other, Interval):
|
|
395
|
+
return False
|
|
396
|
+
if self.is_empty or other.is_empty:
|
|
397
|
+
return False
|
|
398
|
+
return self.hi == other.lo or other.hi == self.lo
|
|
399
|
+
|
|
400
|
+
def __add__(self, other):
|
|
401
|
+
from .arithmetic import add
|
|
402
|
+
other = self._coerce(other)
|
|
403
|
+
return add(self, other)
|
|
404
|
+
|
|
405
|
+
def __sub__(self, other):
|
|
406
|
+
from .arithmetic import sub
|
|
407
|
+
other = self._coerce(other)
|
|
408
|
+
return sub(self, other)
|
|
409
|
+
|
|
410
|
+
def __mul__(self, other):
|
|
411
|
+
from .arithmetic import mul
|
|
412
|
+
other = self._coerce(other)
|
|
413
|
+
return mul(self, other)
|
|
414
|
+
|
|
415
|
+
def __truediv__(self, other):
|
|
416
|
+
from .arithmetic import div
|
|
417
|
+
other = self._coerce(other)
|
|
418
|
+
return div(self, other)
|
|
419
|
+
|
|
420
|
+
def __neg__(self):
|
|
421
|
+
if self.is_empty:
|
|
422
|
+
return Interval.empty()
|
|
423
|
+
return Interval(-self.hi, -self.lo)
|
|
424
|
+
|
|
425
|
+
def __abs__(self):
|
|
426
|
+
if self.is_empty:
|
|
427
|
+
return Interval.empty()
|
|
428
|
+
|
|
429
|
+
if self.lo >= 0:
|
|
430
|
+
return self
|
|
431
|
+
if self.hi <= 0:
|
|
432
|
+
return Interval(-self.hi, -self.lo)
|
|
433
|
+
return Interval(Number(0), Number(max(-self.lo, self.hi)))
|
|
434
|
+
|
|
435
|
+
def __radd__(self, other):
|
|
436
|
+
from .arithmetic import add
|
|
437
|
+
other = self._coerce(other)
|
|
438
|
+
return add(other, self)
|
|
439
|
+
|
|
440
|
+
def __rsub__(self, other):
|
|
441
|
+
from .arithmetic import sub
|
|
442
|
+
other = self._coerce(other)
|
|
443
|
+
return sub(other, self)
|
|
444
|
+
|
|
445
|
+
def __rmul__(self, other):
|
|
446
|
+
from .arithmetic import mul
|
|
447
|
+
other = self._coerce(other)
|
|
448
|
+
return mul(other, self)
|
|
449
|
+
|
|
450
|
+
def __rtruediv__(self, other):
|
|
451
|
+
from .arithmetic import div
|
|
452
|
+
other = self._coerce(other)
|
|
453
|
+
return div(other, self)
|
|
454
|
+
|
|
455
|
+
@property
|
|
456
|
+
def is_strictly_positive(self):
|
|
457
|
+
return not self.is_empty and self.lo > 0
|
|
458
|
+
|
|
459
|
+
@property
|
|
460
|
+
def is_strictly_negative(self):
|
|
461
|
+
return not self.is_empty and self.hi < 0
|
|
462
|
+
|
|
463
|
+
@property
|
|
464
|
+
def is_nonnegative(self):
|
|
465
|
+
return not self.is_empty and self.lo >= 0
|
|
466
|
+
|
|
467
|
+
@property
|
|
468
|
+
def is_nonpositive(self):
|
|
469
|
+
return not self.is_empty and self.hi <= 0
|
|
470
|
+
|
|
471
|
+
@property
|
|
472
|
+
def contains_zero(self):
|
|
473
|
+
return self.contains(0)
|
|
474
|
+
|
|
475
|
+
def __eq__(self, other):
|
|
476
|
+
other = self._coerce(other)
|
|
477
|
+
if self.is_empty and other.is_empty:
|
|
478
|
+
return True
|
|
479
|
+
return self.lo == other.lo and self.hi == other.hi
|
|
480
|
+
|
|
481
|
+
def __lt__(self, other):
|
|
482
|
+
other = self._coerce(other)
|
|
483
|
+
if self.is_empty and other.is_empty:
|
|
484
|
+
return True
|
|
485
|
+
if self.is_empty or other.is_empty:
|
|
486
|
+
return False
|
|
487
|
+
return self.lo <= other.lo and self.hi <= other.hi
|
|
488
|
+
|
|
489
|
+
def __gt__(self, other):
|
|
490
|
+
if not isinstance(other, Interval):
|
|
491
|
+
other = self._coerce(other)
|
|
492
|
+
if self.is_empty or other.is_empty:
|
|
493
|
+
return False
|
|
494
|
+
return self.lo >= other.lo and self.hi >= other.hi
|
|
495
|
+
|
|
496
|
+
def strictly_less_than(self, other):
|
|
497
|
+
other = self._coerce(other)
|
|
498
|
+
if self.is_empty and other.is_empty:
|
|
499
|
+
return True
|
|
500
|
+
if self.is_entire and other.is_entire:
|
|
501
|
+
return True
|
|
502
|
+
if other.is_empty or self.is_empty:
|
|
503
|
+
return False
|
|
504
|
+
return self.lo < other.lo and self.hi < other.hi
|
|
505
|
+
|
|
506
|
+
def bisect(self):
|
|
507
|
+
if self.is_empty:
|
|
508
|
+
return (Interval.empty(), Interval.empty())
|
|
509
|
+
|
|
510
|
+
m = self.midpoint
|
|
511
|
+
return (Interval(self.lo, m), Interval(m, self.hi))
|
|
512
|
+
|
|
513
|
+
def inf_sub(self, other):
|
|
514
|
+
other = self._coerce(other)
|
|
515
|
+
if self.is_empty or other.is_empty:
|
|
516
|
+
return Number('nan')
|
|
517
|
+
with context(get_context()) as ctx:
|
|
518
|
+
ctx.round = RoundDown
|
|
519
|
+
return self.lo - other.lo
|
|
520
|
+
|
|
521
|
+
def sup_sub(self, other):
|
|
522
|
+
other = self._coerce(other)
|
|
523
|
+
if self.is_empty or other.is_empty:
|
|
524
|
+
return Number('nan')
|
|
525
|
+
with context(get_context()) as ctx:
|
|
526
|
+
ctx.round = RoundUp
|
|
527
|
+
return self.hi - other.hi
|
|
528
|
+
|
|
529
|
+
def strictly_precedes(self, other):
|
|
530
|
+
other = self._coerce(other)
|
|
531
|
+
if self.is_empty or other.is_empty:
|
|
532
|
+
return True
|
|
533
|
+
return self.hi < other.lo
|
|
534
|
+
|
|
535
|
+
def __neg__(self):
|
|
536
|
+
if self.is_empty:
|
|
537
|
+
return self
|
|
538
|
+
if self.is_entire:
|
|
539
|
+
return self
|
|
540
|
+
return Interval(-self.hi, -self.lo)
|
|
541
|
+
|
|
542
|
+
def inf(self):
|
|
543
|
+
return self.lo
|
|
544
|
+
|
|
545
|
+
def sup(self):
|
|
546
|
+
return self.hi
|