# memoize calls to the class constructors for fields # this helps typechecking by never creating two separate # instances of a number class. def memoize(f): cache = {} def memoizedFunction(*args, **kwargs): argTuple = args + tuple(kwargs) if argTuple not in cache: cache[argTuple] = f(*args, **kwargs) return cache[argTuple] memoizedFunction.cache = cache return memoizedFunction # type check a binary operation, and silently typecast 0 or 1 def typecheck(f): def newF(self, other): if (hasattr(other.__class__, 'operatorPrecedence') and other.__class__.operatorPrecedence > self.__class__.operatorPrecedence): return NotImplemented if type(self) is not type(other): try: other = self.__class__(other) except TypeError: message = 'Not able to typecast %s of type %s to type %s in function %s' raise TypeError(message % (other, type(other).__name__, type(self).__name__, f.__name__)) except Exception as e: message = 'Type error on arguments %r, %r for functon %s. Reason:%s' raise TypeError(message % (self, other, f.__name__, type(other).__name__, type(self).__name__, e)) return f(self, other) return newF # require a subclass to implement +-* neg and to perform typechecks on all of # the binary operations finally, the __init__ must operate when given a single # argument, provided that argument is the int zero or one class DomainElement(object): operatorPrecedence = 1 # the 'r'-operators are only used when typecasting ints def __radd__(self, other): return self + other def __rsub__(self, other): return -self + other def __rmul__(self, other): return self * other # square-and-multiply algorithm for fast exponentiation def __pow__(self, n): if type(n) is not int: raise TypeError Q = self R = self if n & 1 else self.__class__(1) i = 2 while i <= n: Q = (Q * Q) if n & i == i: R = (Q * R) i = i << 1 return R # requires the additional % operator (i.e. a Euclidean Domain) def powmod(self, n, modulus): if type(n) is not int: raise TypeError Q = self R = self if n & 1 else self.__class__(1) i = 2 while i <= n: Q = (Q * Q) % modulus if n & i == i: R = (Q * R) % modulus i = i << 1 return R # additionally require inverse() on subclasses class FieldElement(DomainElement): def __truediv__(self, other): return self * other.inverse() def __rtruediv__(self, other): return self.inverse() * other def __div__(self, other): return self.__truediv__(other) def __rdiv__(self, other): return self.__rtruediv__(other)