74cc5d3692a3bbb792e1dfcc9920d8098c22d12f
[linpy.git] / pypol / isl.py
1
2 import ctypes, ctypes.util
3 import math
4 import numbers
5 import operator
6 import re
7 import functools
8
9 from decimal import Decimal
10 from fractions import Fraction
11 from functools import wraps
12
13
14 libisl = ctypes.CDLL(ctypes.util.find_library('isl'))
15
16 libisl.isl_printer_get_str.restype = ctypes.c_char_p
17
18 def _polymorphic_method(func):
19 @functools.wraps(func)
20 def wrapper(self, other):
21 if isinstance(other, Value):
22 return func(self, other)
23 if isinstance(other, numbers.Rational):
24 other = Value(self.context, other)
25 return func(self, other)
26 raise TypeError('operand should be a Value or a Rational')
27 return wrapper
28
29 class Context:
30
31 __slots__ = ('_ic')
32
33 def __init__(self):
34 self._ic = libisl.isl_ctx_alloc()
35
36 @property
37 def _as_parameter_(self):
38 return self._ic
39
40 def __del__(self):
41 libisl.isl_ctx_free(self)
42
43 def __eq__(self, other):
44 if not isinstance(other, Context):
45 return False
46 return self._ic == other._ic
47
48
49 class Value:
50
51 class _ptr(int):
52 def __new__(cls, iv):
53 return super().__new__(cls, iv)
54 def __repr__(self):
55 return '{}({})'.format(self.__class__.__name__, self)
56
57 _RE_NONFINITE = re.compile(
58 r'^\s*(?P<sign>[-+])?((?P<inf>Inf(inity)?)|(?P<nan>NaN))\s*$',
59 re.IGNORECASE)
60
61 _RE_FRACTION = re.compile(r'^(?P<num>[-+]?\d+)(/(?P<den>\d+))?$')
62
63 __slots__ = ('context', '_iv', '_numerator', '_denominator')
64
65 def __new__(cls, context, numerator=0, denominator=None):
66 self = super().__new__(cls)
67 if not isinstance(context, Context):
68 raise TypeError('first argument should be a context')
69 self.context = context
70 if isinstance(numerator, cls._ptr):
71 assert denominator is None
72 self._iv = numerator
73 if libisl.isl_val_is_rat(self):
74 # retrieve numerator and denominator as strings to avoid integer
75 # overflows
76 ip = libisl.isl_printer_to_str(self.context)
77 ip = libisl.isl_printer_print_val(ip, self)
78 string = libisl.isl_printer_get_str(ip).decode()
79 libisl.isl_printer_free(ip)
80 m = self._RE_FRACTION.match(string)
81 assert m is not None
82 self._numerator = int(m.group('num'))
83 self._denominator = int(m.group('den')) if m.group('den') else 1
84 else:
85 self._numerator = None
86 self._denominator = None
87 return self
88 if isinstance(numerator, str) and denominator is None:
89 m = self._RE_NONFINITE.match(numerator)
90 if m is not None:
91 self._numerator = None
92 self._denominator = None
93 if m.group('inf'):
94 if m.group('sign') == '-':
95 self._iv = libisl.isl_val_neginfty(context)
96 else:
97 self._iv = libisl.isl_val_infty(context)
98 else:
99 assert m.group('nan')
100 self._iv = libisl.isl_val_nan(context)
101 return self
102 try:
103 frac = Fraction(numerator, denominator)
104 except ValueError:
105 raise ValueError('invalid literal for {}: {!r}'.format(
106 cls.__name__, numerator))
107 self._numerator = frac.numerator
108 self._denominator = frac.denominator
109 # values passed as strings to avoid integer overflows
110 if frac.denominator == 1:
111 numerator = str(frac.numerator).encode()
112 self._iv = libisl.isl_val_read_from_str(context, numerator)
113 else:
114 numerator = str(frac.numerator).encode()
115 numerator = libisl.isl_val_read_from_str(context, numerator)
116 denominator = str(frac.denominator).encode()
117 denominator = libisl.isl_val_read_from_str(context, denominator)
118 self._iv = libisl.isl_val_div(numerator, denominator)
119 return self
120
121 @property
122 def _as_parameter_(self):
123 return self._iv
124
125 def __del__(self):
126 libisl.isl_val_free(self)
127 self.context # prevents context from being GC'ed before the value
128
129 @property
130 def numerator(self):
131 if self._numerator is None:
132 raise ValueError('not a rational number')
133 return self._numerator
134
135 @property
136 def denominator(self):
137 if self._denominator is None:
138 raise ValueError('not a rational number')
139 return self._denominator
140
141 def __bool__(self):
142 return not bool(libisl.isl_val_is_zero(self))
143
144 @_polymorphic_method
145 def __lt__(self, other):
146 return bool(libisl.isl_val_lt(self, other))
147
148 @_polymorphic_method
149 def __le__(self, other):
150 return bool(libisl.isl_val_le(self, other))
151
152 @_polymorphic_method
153 def __gt__(self, other):
154 return bool(libisl.isl_val_gt(self, other))
155
156 @_polymorphic_method
157 def __ge__(self, other):
158 return bool(libisl.isl_val_ge(self, other))
159
160 @_polymorphic_method
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_method
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_method
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_method
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_method
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))