summaryrefslogtreecommitdiff
path: root/lib/modular_arithmetic.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/modular_arithmetic.py')
-rw-r--r--lib/modular_arithmetic.py45
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':