decoint 0.1.0__tar.gz

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-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 arjavsharma91
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
decoint-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,23 @@
1
+ Metadata-Version: 2.4
2
+ Name: decoint
3
+ Version: 0.1.0
4
+ Summary: An IEEE 1788-2015 compliant interval arithmetic library
5
+ Author-email: Arjav Sharma <arjav.sharma.91@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/arjavsharma91/IEEE-1788.1-2017-Interval-Arithmetic
8
+ Requires-Python: >=3.8
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Requires-Dist: gmpy2>=2.1.0
12
+ Dynamic: license-file
13
+
14
+ # IEEE-1788.1-2017 Simplified Standard For Interval Arithmetic Python Implementation
15
+
16
+ ## Key Features
17
+
18
+ * **IEEE 1788.1-2017 Compliant:** Tested against the official Interval Test Framework (ITF) suite.
19
+ * **Rigorous Enclosure:** Guarantees interval enclosure using directed rounding modes.
20
+ * **Decorated Intervals:** Complete support for interval decorations (`com`, `dac`, `def`, `trv`, `ill`).
21
+ * **High Precision:** Powered by `gmpy2`'s MPFR bindings for fast, arbitrary-precision floating-point arithmetic.
22
+ * **Full Operator Set:** Includes basic arithmetic, set relations/operations, interval queries, functions, and string parsing.
23
+
@@ -0,0 +1,10 @@
1
+ # IEEE-1788.1-2017 Simplified Standard For Interval Arithmetic Python Implementation
2
+
3
+ ## Key Features
4
+
5
+ * **IEEE 1788.1-2017 Compliant:** Tested against the official Interval Test Framework (ITF) suite.
6
+ * **Rigorous Enclosure:** Guarantees interval enclosure using directed rounding modes.
7
+ * **Decorated Intervals:** Complete support for interval decorations (`com`, `dac`, `def`, `trv`, `ill`).
8
+ * **High Precision:** Powered by `gmpy2`'s MPFR bindings for fast, arbitrary-precision floating-point arithmetic.
9
+ * **Full Operator Set:** Includes basic arithmetic, set relations/operations, interval queries, functions, and string parsing.
10
+
@@ -0,0 +1,23 @@
1
+ from .interval import Interval
2
+ from .decorated_interval import DecoratedInterval
3
+ from .decorations import Decoration
4
+
5
+ # 2. Fully Tracked Core Arithmetic (Using your decorated module)
6
+ from .decorated_arithmetic import add, sub, mul, div, reciprocal, fma
7
+
8
+ # 3. Tracked Elementary Functions
9
+ from .decorated_functions import (
10
+ exp, log, sqrt, pow_int, sign,
11
+ sin, cos, tan, asin, acos, atan,
12
+ sinh, cosh, tanh, asinh, acosh, atanh,
13
+ abs, atan2, interval_min, interval_max, nth_root, sqr, pow_interval, exp2, exp10, log2, log10, interval_trunc, interval_ceil, interval_floor
14
+ )
15
+
16
+ __all__ = [
17
+ 'Interval', 'DecoratedInterval', 'Decoration',
18
+ 'add', 'sub', 'mul', 'div', 'reciprocal',
19
+ 'exp', 'log', 'sqrt', 'pow_int', 'sign',
20
+ 'sin', 'cos', 'tan', 'asin', 'acos', 'atan',
21
+ 'sinh', 'cosh', 'tanh', 'asinh', 'acosh', 'atanh',
22
+ 'abs', 'atan2', 'interval_min', 'interval_max', 'nth_root', 'sqr', 'fma', 'pow_interval', 'exp2', 'exp10', 'log2', 'log10', 'interval_trunc', 'interval_ceil', 'interval_floor'
23
+ ]
@@ -0,0 +1,181 @@
1
+ from .interval import Interval
2
+ from .rounding import add_down, add_up, sub_down, sub_up, div_down, div_up, mul_down, mul_up, fma_up, fma_down
3
+ from gmpy2 import mpfr, is_infinite, is_signed, sign, context, get_context
4
+
5
+ ctx = get_context()
6
+ ctx.precision = 53
7
+ ctx.emin = -1073
8
+ ctx.emax = 1024
9
+
10
+ def add(x, y) -> Interval:
11
+ x = Interval._coerce(x)
12
+ y = Interval._coerce(y)
13
+ if x.is_empty or y.is_empty:
14
+ return Interval.empty()
15
+ lo = add_down(x.lo, y.lo)
16
+ hi = add_up(x.hi, y.hi)
17
+ return Interval(lo, hi)
18
+
19
+ def sub(x, y) -> Interval:
20
+ x = Interval._coerce(x)
21
+ y = Interval._coerce(y)
22
+ if x.is_empty or y.is_empty:
23
+ return Interval.empty()
24
+ lo = sub_down(x.lo, y.hi)
25
+ hi = sub_up(x.hi, y.lo)
26
+ return Interval(lo, hi)
27
+
28
+ def mul(x, y) -> Interval:
29
+ x = Interval._coerce(x)
30
+ y = Interval._coerce(y)
31
+ if x.is_empty or y.is_empty:
32
+ return Interval.empty()
33
+
34
+ def safe_mul_down(a, b):
35
+ if (a == 0 and is_infinite(b)) or (b == 0 and is_infinite(a)):
36
+ is_neg = is_signed(a) * is_signed(b)
37
+ return mpfr('0') if is_neg else mpfr('-0.0')
38
+ return mul_down(a, b)
39
+
40
+ def safe_mul_up(a, b):
41
+ if (a == 0 and is_infinite(b)) or (b == 0 and is_infinite(a)):
42
+ is_neg = is_signed(a) * is_signed(b)
43
+ return mpfr('0') if is_neg else mpfr('-0.0')
44
+ return mul_up(a, b)
45
+
46
+ p1 = safe_mul_down(x.lo, y.lo)
47
+ p2 = safe_mul_down(x.lo, y.hi)
48
+ p3 = safe_mul_down(x.hi, y.lo)
49
+ p4 = safe_mul_down(x.hi, y.hi)
50
+
51
+ lo = min(p1, p2, p3, p4)
52
+
53
+ q1 = safe_mul_up(x.lo, y.lo)
54
+ q2 = safe_mul_up(x.lo, y.hi)
55
+ q3 = safe_mul_up(x.hi, y.lo)
56
+ q4 = safe_mul_up(x.hi, y.hi)
57
+
58
+ hi = max(q1, q2, q3, q4)
59
+
60
+ return Interval(lo, hi)
61
+
62
+ def reciprocal(x) -> Interval:
63
+ x = Interval._coerce(x)
64
+ if x.is_empty:
65
+ return Interval.empty()
66
+ if x.contains_zero:
67
+ if x.lo == 0 and x.hi == 0:
68
+ return Interval.empty()
69
+ if x.lo == 0:
70
+ return Interval(div_down(mpfr(1), x.hi), mpfr('inf'))
71
+ if x.hi == 0:
72
+ return Interval(mpfr('-inf'), div_up(mpfr(1), x.lo))
73
+ return Interval.entire()
74
+
75
+ return Interval(div_down(mpfr(1), x.hi), div_up(mpfr(1), x.lo))
76
+
77
+ def div(x, y) -> Interval:
78
+ x = Interval._coerce(x)
79
+ y = Interval._coerce(y)
80
+ if x.is_empty or y.is_empty:
81
+ return Interval.empty()
82
+
83
+ if x.is_entire and not y.contains(0):
84
+ return Interval.entire()
85
+
86
+ if y.contains(0):
87
+ if y.hi == 0 and y.lo == 0:
88
+ return Interval.empty()
89
+ if x.hi == 0 and x.lo == 0:
90
+ return Interval(mpfr('0'), mpfr('0'))
91
+ if x.lo >= 0:
92
+ if y.lo == 0:
93
+ return Interval(div_down(x.lo, y.hi), mpfr('inf'))
94
+ elif y.hi == 0:
95
+ return Interval(mpfr('-inf'), div_up(x.lo, y.lo))
96
+ else:
97
+ return Interval.entire()
98
+ elif x.hi <= 0:
99
+ if y.lo == 0:
100
+ return Interval(mpfr('-inf'), div_up(x.hi, y.hi))
101
+ elif y.hi == 0:
102
+ return Interval(div_down(x.hi, y.lo), mpfr('inf'))
103
+ else:
104
+ return Interval.entire()
105
+ else:
106
+ return Interval.entire()
107
+
108
+ # --- CLEAN DIRECT REPLACEMENT FOR mul(x, reciprocal(y)) ---
109
+ if y.lo > 0: # Denominator is strictly positive
110
+ # Lower bound logic
111
+ if x.lo == mpfr('-inf'):
112
+ lo = mpfr('-inf')
113
+ else:
114
+ lo = div_down(x.lo, y.hi if x.lo >= 0 else y.lo)
115
+
116
+ # Upper bound logic
117
+ if x.hi == mpfr('inf'):
118
+ hi = mpfr('inf')
119
+ else:
120
+ hi = div_up(x.hi, y.lo if x.hi >= 0 else y.hi)
121
+
122
+ else: # Denominator is strictly negative (y.hi < 0)
123
+ # Lower bound logic
124
+ if x.hi == mpfr('inf'):
125
+ lo = mpfr('-inf')
126
+ else:
127
+ lo = div_down(x.hi, y.hi if x.hi >= 0 else y.lo)
128
+
129
+ # Upper bound logic
130
+ if x.lo == mpfr('-inf'):
131
+ hi = mpfr('inf')
132
+ else:
133
+ hi = div_up(x.lo, y.lo if x.lo >= 0 else y.hi)
134
+
135
+ return Interval(lo, hi)
136
+
137
+ def evaluate_fma_corner(x, y, z, round_up=False):
138
+ # 1. Handle 0 * inf indeterminate form -> results in 0, so 0 + z = z
139
+ if (x == 0 and is_infinite(y)) or (is_infinite(x) and y == 0):
140
+ return z
141
+
142
+ # 2. Handle true inf - inf indeterminate form
143
+ if is_infinite(x) or is_infinite(y):
144
+ if is_infinite(z):
145
+ prod_neg = (x < 0) != (y < 0)
146
+ z_neg = z < 0
147
+ # If the infinite product's sign opposes z's infinite sign, it's inf - inf
148
+ if prod_neg != z_neg:
149
+ return None # Signal containment explosion
150
+
151
+ # Safe to compute standard hardware rounded FMA
152
+ return fma_up(x, y, z) if round_up else fma_down(x, y, z)
153
+
154
+
155
+ def fma(x, y, z):
156
+ x = Interval._coerce(x)
157
+ y = Interval._coerce(y)
158
+ z = Interval._coerce(z)
159
+
160
+ if x.is_empty or y.is_empty or z.is_empty:
161
+ return Interval.empty()
162
+
163
+ # Evaluate lower bound corner combinations
164
+ v_down = []
165
+ for cx in (x.lo, x.hi):
166
+ for cy in (y.lo, y.hi):
167
+ val = evaluate_fma_corner(cx, cy, z.lo, round_up=False)
168
+ if val is None:
169
+ return Interval.entire()
170
+ v_down.append(val)
171
+
172
+ # Evaluate upper bound corner combinations
173
+ v_up = []
174
+ for cx in (x.lo, x.hi):
175
+ for cy in (y.lo, y.hi):
176
+ val = evaluate_fma_corner(cx, cy, z.hi, round_up=True)
177
+ if val is None:
178
+ return Interval.entire()
179
+ v_up.append(val)
180
+
181
+ return Interval(min(v_down), max(v_up))
@@ -0,0 +1,6 @@
1
+ from gmpy2 import const_pi, context, get_context
2
+
3
+ with context(get_context(), precision = 128):
4
+ PI = const_pi()
5
+ HALF_PI = PI / 2
6
+ TWO_PI = PI * 2
@@ -0,0 +1,98 @@
1
+ from .interval import Interval
2
+ from .arithmetic import add as bare_add, sub as bare_sub, mul as bare_mul, div as bare_div, reciprocal as bare_reciprocal, fma as bare_fma
3
+ from .decorations import Decoration, combine
4
+ from .decorated_interval import DecoratedInterval
5
+
6
+ def _finalize_decoration(base_dec: Decoration, interval: Interval) -> Decoration:
7
+ if base_dec == Decoration.COM and not interval.is_bounded:
8
+ return Decoration.DAC
9
+ return base_dec
10
+
11
+ def add(x, y):
12
+ x = DecoratedInterval._coerce(x)
13
+ y = DecoratedInterval._coerce(y)
14
+
15
+ if x.is_nai or y.is_nai:
16
+ return DecoratedInterval.new_nai()
17
+
18
+ interval = bare_add(x.interval, y.interval)
19
+ dec = combine(x.decoration, y.decoration)
20
+ dec = _finalize_decoration(dec, interval)
21
+
22
+ return DecoratedInterval(interval, dec)
23
+
24
+ def sub(x, y):
25
+ x = DecoratedInterval._coerce(x)
26
+ y = DecoratedInterval._coerce(y)
27
+
28
+ if x.is_nai or y.is_nai:
29
+ return DecoratedInterval.new_nai()
30
+
31
+ interval = bare_sub(x.interval, y.interval)
32
+ dec = combine(x.decoration, y.decoration)
33
+ dec = _finalize_decoration(dec, interval)
34
+
35
+ return DecoratedInterval(interval, dec)
36
+
37
+ def mul(x, y):
38
+ x = DecoratedInterval._coerce(x)
39
+ y = DecoratedInterval._coerce(y)
40
+
41
+ if x.is_nai or y.is_nai:
42
+ return DecoratedInterval.new_nai()
43
+
44
+ interval = bare_mul(x.interval, y.interval)
45
+ dec = combine(x.decoration, y.decoration)
46
+ dec = _finalize_decoration(dec, interval)
47
+
48
+ return DecoratedInterval(interval, dec)
49
+
50
+ def reciprocal(x):
51
+ x = DecoratedInterval._coerce(x)
52
+ if x.is_nai:
53
+ return DecoratedInterval.new_nai()
54
+ if x.interval.contains(0):
55
+ if x.interval.lo < 0 and x.interval.hi > 0:
56
+ return DecoratedInterval(Interval.entire(), Decoration.TRV)
57
+ interval = bare_reciprocal(x.interval)
58
+ return DecoratedInterval(interval, Decoration.TRV)
59
+ interval = bare_reciprocal(x.interval)
60
+ dec = x.decoration
61
+ dec = _finalize_decoration(dec, interval)
62
+ return DecoratedInterval(interval, dec)
63
+
64
+ def div(x, y):
65
+ x = DecoratedInterval._coerce(x)
66
+ y = DecoratedInterval._coerce(y)
67
+
68
+ if x.is_nai or y.is_nai:
69
+ return DecoratedInterval.new_nai()
70
+
71
+ if x.is_empty or y.is_empty:
72
+ return DecoratedInterval.empty()
73
+
74
+ if y.interval.contains(0):
75
+ interval = bare_div(x.interval, y.interval)
76
+ dec = combine(x.decoration, y.decoration, Decoration.TRV)
77
+ return DecoratedInterval(interval, Decoration.TRV)
78
+
79
+ interval = bare_div(x.interval, y.interval)
80
+ dec = combine(x.decoration, y.decoration)
81
+ dec = _finalize_decoration(dec, interval)
82
+
83
+ return DecoratedInterval(interval, dec)
84
+
85
+ def fma(x, y, z):
86
+ x = DecoratedInterval._coerce(x)
87
+ y = DecoratedInterval._coerce(y)
88
+ z = DecoratedInterval._coerce(z)
89
+
90
+ if x.is_nai or y.is_nai or z.is_nai:
91
+ return DecoratedInterval.new_nai()
92
+
93
+ interval = bare_fma(x.interval, y.interval, z.interval)
94
+ dec = combine(x.decoration, y.decoration, z.decoration)
95
+
96
+ dec = _finalize_decoration(dec, interval)
97
+
98
+ return DecoratedInterval(interval, dec)