backyard/library/modulos.py

247 lines
6.6 KiB
Python
Raw Permalink Normal View History

2022-06-06 21:48:46 -07:00
# originally written on 2019/05/21 ?
# added division on 2020/06/19
def inverse(a, modulo):
def is_even(a):
return a & 1 == 0
def rotate_right(a):
return a >> 1 if is_even(a) else (a + modulo) >> 1
if a == 0:
raise ZeroDivisionError()
b = 1
c = 0
d = modulo
while a != d:
if a < d:
d -= a
c = (c - b) % modulo
while is_even(d):
d >>= 1
c = rotate_right(c)
else:
a -= d
b = (b - c) % modulo
while is_even(a):
a >>= 1
b = rotate_right(b)
return b
class ModInt:
def __init__(self, value, modulo):
assert isinstance(modulo, int), (type(modulo), modulo)
assert modulo >= 0
if isinstance(value, ModInt):
if value.modulo == modulo:
self.value = value.value
else:
self.value = value.value % modulo
else:
assert isinstance(value, int), (type(value), value)
self.value = value % modulo
self.modulo = modulo
def new(self, value):
return ModInt(value, self.modulo)
@staticmethod
def as_ints(a, b, op="?"):
if isinstance(a, ModInt):
if isinstance(b, ModInt):
assert a.modulo == b.modulo, \
f"mismatched modulos: {repr(a)} {op} {repr(b)}"
return a.value, b.value
else:
assert isinstance(b, int), \
f"incompatible types: {repr(a)} {op} {b} ({type(b)})"
return a.value, b
elif isinstance(b, ModInt):
assert isinstance(a, int), \
f"incompatible types: {repr(a)} ({type(a)}) {op} {repr(b)}"
return a, b.value
else:
assert isinstance(a, int) and isinstance(b, int), (
"incompatible types: " +
f"{repr(a)} ({type(a)}) {op} {repr(b)} ({type(b)})")
return a, b
def __add__(self, other):
a, b = self.as_ints(self, other, "+")
return self.new(a + b)
def __radd__(self, other):
a, b = self.as_ints(other, self, "+")
return self.new(a + b)
def __sub__(self, other):
a, b = self.as_ints(self, other, "-")
return self.new(a - b)
def __rsub__(self, other):
a, b = self.as_ints(other, self, "-")
return self.new(a - b)
def __mul__(self, other):
a, b = self.as_ints(self, other, "*")
return self.new(a * b)
def __rmul__(self, other):
a, b = self.as_ints(other, self, "*")
return self.new(a * b)
def __truediv__(self, other):
a, b = self.as_ints(self, other, "/")
return self.new(a * inverse(b, self.modulo))
def __rtruediv__(self, other):
a, b = self.as_ints(other, self, "/")
return self.new(a * inverse(b, self.modulo))
def __floordiv__(self, other):
a, b = self.as_ints(self, other, "//")
return self.new(a // b)
def __rfloordiv__(self, other):
a, b = self.as_ints(other, self, "//")
return self.new(a // b)
def __mod__(self, other):
a, b = self.as_ints(self, other, "%")
return self.new(a % b)
def __rmod__(self, other):
a, b = self.as_ints(other, self, "%")
return self.new(a % b)
def __divmod__(self, other):
a, b = self.as_ints(self, other, "divmod")
return self.new(a // b), self.new(a % b)
def __pow__(self, other, modulo=None):
a, b = self.as_ints(self, other, "**")
if modulo is None:
return self.new(pow(a, b, self.modulo))
else:
return self.new(pow(a, b, modulo))
def __rpow__(self, other):
a, b = self.as_ints(other, self, "**")
return self.new(pow(a, b, self.modulo))
def __eq__(self, other):
a, b = self.as_ints(self, other, "==")
return a == b
def __ne__(self, other):
a, b = self.as_ints(self, other, "!=")
return a != b
def __gt__(self, other):
a, b = self.as_ints(self, other, ">")
return a > b
def __lt__(self, other):
a, b = self.as_ints(self, other, "<")
return a < b
def __ge__(self, other):
a, b = self.as_ints(self, other, ">=")
return a >= b
def __le__(self, other):
a, b = self.as_ints(self, other, "<=")
return a <= b
def __iadd__(self, other):
a, b = self.as_ints(self, other, "+=")
self.value += b
self.value %= self.modulo
return self
def __isub__(self, other):
a, b = self.as_ints(self, other, "-=")
self.value -= b
self.value %= self.modulo
return self
def __imul__(self, other):
a, b = self.as_ints(self, other, "*=")
self.value *= b
self.value %= self.modulo
return self
def __itruediv__(self, other):
a, b = self.as_ints(self, other, "/=")
self.value *= inverse(b)
self.value %= self.modulo
return self
def __ifloordiv__(self, other):
a, b = self.as_ints(self, other, "//=")
self.value //= b
self.value %= self.modulo
return self
def __imod__(self, other):
a, b = self.as_ints(self, other, "%=")
self.value %= b
self.value %= self.modulo
return self
def __ipow__(self, other):
a, b = self.as_ints(self, other, "**=")
self.value = pow(a, b, self.modulo)
return self
def __pos__(self):
return self
def __neg__(self):
return 0 - self
def __abs__(self):
return self
def __invert__(self):
raise NotImplementedError("operator unimplemented for ModInt: ~")
def __int__(self):
return self.value
def __float__(self):
return float(self.value)
def __complex__(self):
return complex(self.value)
def __index__(self):
return self.value
def __round__(self, digits=None):
return self
def __trunc__(self):
return self
def __floor__(self):
return self
def __ceil__(self):
return self
def __repr__(self):
return f"{self.__class__.__name__}({self.value}, {self.modulo})"
def __str__(self):
return str(self.value)
def __hash__(self):
# it's an error to use ModInts alongside others of differing modulo,
# so this should just act like an int here.
# (hmm, maybe it's better to return (self.value, self.modulo) anyway.
# or just leave it unhashable. then the user has to be conscious.)
return self.value