Better implementation of _polymorphic_operator
[linpy.git] / pypol / isl.py
1
2 import ctypes, ctypes.util
3 import math
4 import numbers
5 import operator
6 import re
7
8 from decimal import Decimal
9 from fractions import Fraction
10 from functools import wraps
11
12
13 libisl = ctypes.CDLL(ctypes.util.find_library('isl'))
14
15 libisl.isl_printer_get_str.restype = ctypes.c_char_p
16
17
18 class Context:
19
20 __slots__ = ('_ic')
21
22 def __init__(self):
23 self._ic = libisl.isl_ctx_alloc()
24
25 @property
26 def _as_parameter_(self):
27 return self._ic
28
29 def __del__(self):
30 libisl.isl_ctx_free(self)
31
32 def __eq__(self, other):
33 if not isinstance(other, Context):
34 return False
35 return self._ic == other._ic
36
37
38 class Value:
39
40 class _ptr(int):
41 def __new__(cls, iv):
42 return super().__new__(cls, iv)
43 def __repr__(self):
44 return '{}({})'.format(self.__class__.__name__, self)
45
46 _RE_NONFINITE = re.compile(
47 r'^\s*(?P<sign>[-+])?((?P<inf>Inf(inity)?)|(?P<nan>NaN))\s*$',
48 re.IGNORECASE)
49
50 _RE_FRACTION = re.compile(r'^(?P<num>[-+]?\d+)(/(?P<den>\d+))?$')
51
52 __slots__ = ('context', '_iv', '_numerator', '_denominator')
53
54 def __new__(cls, context, numerator=0, denominator=None):
55 self = super().__new__(cls)
56 if not isinstance(context, Context):
57 raise TypeError('first argument should be a context')
58 self.context = context
59 if isinstance(numerator, cls._ptr):
60 assert denominator is None
61 self._iv = numerator
62 if libisl.isl_val_is_rat(self):
63 # retrieve numerator and denominator as strings to avoid integer
64 # overflows
65 ip = libisl.isl_printer_to_str(self.context)
66 ip = libisl.isl_printer_print_val(ip, self)
67 string = libisl.isl_printer_get_str(ip).decode()
68 libisl.isl_printer_free(ip)
69 m = self._RE_FRACTION.match(string)
70 assert m is not None
71 self._numerator = int(m.group('num'))
72 self._denominator = int(m.group('den')) if m.group('den') else 1
73 else:
74 self._numerator = None
75 self._denominator = None
76 return self
77 if isinstance(numerator, str) and denominator is None:
78 m = self._RE_NONFINITE.match(numerator)
79 if m is not None:
80 self._numerator = None
81 self._denominator = None
82 if m.group('inf'):
83 if m.group('sign') == '-':
84 self._iv = libisl.isl_val_neginfty(context)
85 else:
86 self._iv = libisl.isl_val_infty(context)
87 else:
88 assert m.group('nan')
89 self._iv = libisl.isl_val_nan(context)
90 return self
91 try:
92 frac = Fraction(numerator, denominator)
93 except ValueError:
94 raise ValueError('invalid literal for {}: {!r}'.format(
95 cls.__name__, numerator))
96 self._numerator = frac.numerator
97 self._denominator = frac.denominator
98 # values passed as strings to avoid integer overflows
99 if frac.denominator == 1:
100 numerator = str(frac.numerator).encode()
101 self._iv = libisl.isl_val_read_from_str(context, numerator)
102 else:
103 numerator = str(frac.numerator).encode()
104 numerator = libisl.isl_val_read_from_str(context, numerator)
105 denominator = str(frac.denominator).encode()
106 denominator = libisl.isl_val_read_from_str(context, denominator)
107 self._iv = libisl.isl_val_div(numerator, denominator)
108 return self
109
110 @property
111 def _as_parameter_(self):
112 return self._iv
113
114 def __del__(self):
115 libisl.isl_val_free(self)
116 self.context # prevents context from being GC'ed before the value
117
118 @property
119 def numerator(self):
120 if self._numerator is None:
121 raise ValueError('not a rational number')
122 return self._numerator
123
124 @property
125 def denominator(self):
126 if self._denominator is None:
127 raise ValueError('not a rational number')
128 return self._denominator
129
130 def __bool__(self):
131 return not bool(libisl.isl_val_is_zero(self))
132
133 def _polymorphic(func):
134 @wraps(func)
135 def wrapper(self, other):
136 if isinstance(other, Value):
137 return func(self, other)
138 if isinstance(other, numbers.Rational):
139 other = Value(self.context, other)
140 return func(self, other)
141 raise TypeError('operand should be a Value or a Rational')
142 return wrapper
143
144 @_polymorphic
145 def __lt__(self, other):
146 return bool(libisl.isl_val_lt(self, other))
147
148 @_polymorphic
149 def __le__(self, other):
150 return bool(libisl.isl_val_le(self, other))
151
152 @_polymorphic
153 def __gt__(self, other):
154 return bool(libisl.isl_val_gt(self, other))
155
156 @_polymorphic
157 def __ge__(self, other):
158 return bool(libisl.isl_val_ge(self, other))
159
160 @_polymorphic
161 def __eq__(self, other):
162 return bool(libisl.isl_val_eq(self, other))
163
164 # __ne__ is not implemented, ISL semantics does not match Python's on
165 # nan != nan
166
167 def __abs__(self):
168 val = libisl.isl_val_copy(self)
169 val = libisl.isl_val_abs(val)
170 return self.__class__(self.context, self._ptr(val))
171
172 def __pos__(self):
173 return self
174
175 def __neg__(self):
176 val = libisl.isl_val_copy(self)
177 val = libisl.isl_val_neg(val)
178 return self.__class__(self.context, self._ptr(val))
179
180 def __floor__(self):
181 val = libisl.isl_val_copy(self)
182 val = libisl.isl_val_floor(val)
183 return self.__class__(self.context, self._ptr(val))
184
185 def __ceil__(self):
186 val = libisl.isl_val_copy(self)
187 val = libisl.isl_val_ceil(val)
188 return self.__class__(self.context, self._ptr(val))
189
190 def __trunc__(self):
191 val = libisl.isl_val_copy(self)
192 val = libisl.isl_val_trunc(val)
193 return self.__class__(self.context, self._ptr(val))
194
195 @_polymorphic
196 def __add__(self, other):
197 val1 = libisl.isl_val_copy(self)
198 val2 = libisl.isl_val_copy(other)
199 val = libisl.isl_val_add(val1, val2)
200 return self.__class__(self.context, self._ptr(val))
201
202 __radd__ = __add__
203
204 @_polymorphic
205 def __sub__(self, other):
206 val1 = libisl.isl_val_copy(self)
207 val2 = libisl.isl_val_copy(other)
208 val = libisl.isl_val_sub(val1, val2)
209 return self.__class__(self.context, self._ptr(val))
210
211 __rsub__ = __sub__
212
213 @_polymorphic
214 def __mul__(self, other):
215 val1 = libisl.isl_val_copy(self)
216 val2 = libisl.isl_val_copy(other)
217 val = libisl.isl_val_mul(val1, val2)
218 return self.__class__(self.context, self._ptr(val))
219
220 __rmul__ = __mul__
221
222 @_polymorphic
223 def __truediv__(self, other):
224 val1 = libisl.isl_val_copy(self)
225 val2 = libisl.isl_val_copy(other)
226 val = libisl.isl_val_div(val1, val2)
227 return self.__class__(self.context, self._ptr(val))
228
229 __rtruediv__ = __truediv__
230
231 def __float__(self):
232 if libisl.isl_val_is_rat(self):
233 return self.numerator / self.denominator
234 elif libisl.isl_val_is_infty(self):
235 return float('inf')
236 elif libisl.isl_val_is_neginfty(self):
237 return float('-inf')
238 else:
239 assert libisl.isl_val_is_nan(self)
240 return float('nan')
241
242 def is_finite(self):
243 return bool(libisl.isl_val_is_rat(self))
244
245 def is_infinite(self):
246 return bool(libisl.isl_val_is_infty(self) or
247 libisl.isl_val_is_neginfty(self))
248
249 def is_nan(self):
250 return bool(libisl.isl_val_is_nan(self))
251
252 def __str__(self):
253 if libisl.isl_val_is_rat(self):
254 if self.denominator == 1:
255 return '{}'.format(self.numerator)
256 else:
257 return '{}/{}'.format(self.numerator, self.denominator)
258 elif libisl.isl_val_is_infty(self):
259 return 'Infinity'
260 elif libisl.isl_val_is_neginfty(self):
261 return '-Infinity'
262 else:
263 assert libisl.isl_val_is_nan(self)
264 return 'NaN'
265
266 def __repr__(self):
267 return '{}({!r})'.format(self.__class__.__name__, str(self))