diff options
Diffstat (limited to 'lib/modular_arithmetic.py')
-rw-r--r-- | lib/modular_arithmetic.py | 45 |
1 files changed, 30 insertions, 15 deletions
diff --git a/lib/modular_arithmetic.py b/lib/modular_arithmetic.py index baf979a..0a69b79 100644 --- a/lib/modular_arithmetic.py +++ b/lib/modular_arithmetic.py @@ -2,11 +2,20 @@ # Licensed under GFDL 1.2 https://www.gnu.org/licenses/old-licenses/fdl-1.2.html import operator import functools - + @functools.total_ordering class Mod: + """A class for modular arithmetic, useful to simulate behaviour of uint8 and other limited data types. + + Does not support negative values, therefore it cannot be used to emulate signed integers. + + Overloads a==b, a<b, a>b, a+b, a-b, a*b, a**b, and -a + + :param val: stored integer value + Param mod: modulus + """ __slots__ = ['val','mod'] - + def __init__(self, val, mod): if isinstance(val, Mod): val = val.val @@ -16,13 +25,13 @@ class Mod: raise ValueError('Modulo must be positive integer') self.val = val % mod self.mod = mod - + def __repr__(self): return 'Mod({}, {})'.format(self.val, self.mod) - + def __int__(self): return self.val - + def __eq__(self, other): if isinstance(other, Mod): self.val == other.val @@ -30,7 +39,7 @@ class Mod: return self.val == other else: return NotImplemented - + def __lt__(self, other): if isinstance(other, Mod): return self.val < other.val @@ -38,25 +47,25 @@ class Mod: return self.val < other else: return NotImplemented - + def _check_operand(self, other): if not isinstance(other, (int, Mod)): raise TypeError('Only integer and Mod operands are supported') - + def __pow__(self, other): self._check_operand(other) # We use the built-in modular exponentiation function, this way we can avoid working with huge numbers. return __class__(pow(self.val, int(other), self.mod), self.mod) - + def __neg__(self): return Mod(self.mod - self.val, self.mod) - + def __pos__(self): return self # The unary plus operator does nothing. - + def __abs__(self): return self # The value is always kept non-negative, so the abs function should do nothing. - + # Helper functions to build common operands based on a template. # They need to be implemented as functions for the closures to work properly. def _make_op(opname): @@ -65,14 +74,14 @@ def _make_op(opname): self._check_operand(other) return Mod(op_fun(self.val, int(other)) % self.mod, self.mod) return op - + def _make_reflected_op(opname): op_fun = getattr(operator, opname) def op(self, other): self._check_operand(other) return Mod(op_fun(int(other), self.val) % self.mod, self.mod) return op - + # Build the actual operator overload methods based on the template. for opname, reflected_opname in [('__add__', '__radd__'), ('__sub__', '__rsub__'), ('__mul__', '__rmul__')]: setattr(Mod, opname, _make_op(opname)) @@ -115,7 +124,13 @@ class Uint64(Mod): return 'Uint64({})'.format(self.val) -def simulate_int_type(int_type: str): +def simulate_int_type(int_type: str) -> Mod: + """ + Return `Mod` subclass for given `int_type` + + :param int_type: uint8_t / uint16_t / uint32_t / uint64_t + :returns: `Mod` subclass, e.g. Uint8 + """ if int_type == 'uint8_t': return Uint8 if int_type == 'uint16_t': |