numbertype.py 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. # memoize calls to the class constructors for fields
  2. # this helps typechecking by never creating two separate
  3. # instances of a number class.
  4. def memoize(f):
  5. cache = {}
  6. def memoizedFunction(*args, **kwargs):
  7. argTuple = args + tuple(kwargs)
  8. if argTuple not in cache:
  9. cache[argTuple] = f(*args, **kwargs)
  10. return cache[argTuple]
  11. memoizedFunction.cache = cache
  12. return memoizedFunction
  13. # type check a binary operation, and silently typecast 0 or 1
  14. def typecheck(f):
  15. def newF(self, other):
  16. if (hasattr(other.__class__, 'operatorPrecedence') and
  17. other.__class__.operatorPrecedence > self.__class__.operatorPrecedence):
  18. return NotImplemented
  19. if type(self) is not type(other):
  20. try:
  21. other = self.__class__(other)
  22. except TypeError:
  23. message = 'Not able to typecast %s of type %s to type %s in function %s'
  24. raise TypeError(message % (other, type(other).__name__, type(self).__name__, f.__name__))
  25. except Exception as e:
  26. message = 'Type error on arguments %r, %r for functon %s. Reason:%s'
  27. raise TypeError(message % (self, other, f.__name__, type(other).__name__, type(self).__name__, e))
  28. return f(self, other)
  29. return newF
  30. # require a subclass to implement +-* neg and to perform typechecks on all of
  31. # the binary operations finally, the __init__ must operate when given a single
  32. # argument, provided that argument is the int zero or one
  33. class DomainElement(object):
  34. operatorPrecedence = 1
  35. # the 'r'-operators are only used when typecasting ints
  36. def __radd__(self, other): return self + other
  37. def __rsub__(self, other): return -self + other
  38. def __rmul__(self, other): return self * other
  39. # square-and-multiply algorithm for fast exponentiation
  40. def __pow__(self, n):
  41. if type(n) is not int:
  42. raise TypeError
  43. Q = self
  44. R = self if n & 1 else self.__class__(1)
  45. i = 2
  46. while i <= n:
  47. Q = (Q * Q)
  48. if n & i == i:
  49. R = (Q * R)
  50. i = i << 1
  51. return R
  52. # requires the additional % operator (i.e. a Euclidean Domain)
  53. def powmod(self, n, modulus):
  54. if type(n) is not int:
  55. raise TypeError
  56. Q = self
  57. R = self if n & 1 else self.__class__(1)
  58. i = 2
  59. while i <= n:
  60. Q = (Q * Q) % modulus
  61. if n & i == i:
  62. R = (Q * R) % modulus
  63. i = i << 1
  64. return R
  65. # additionally require inverse() on subclasses
  66. class FieldElement(DomainElement):
  67. def __truediv__(self, other): return self * other.inverse()
  68. def __rtruediv__(self, other): return self.inverse() * other
  69. def __div__(self, other): return self.__truediv__(other)
  70. def __rdiv__(self, other): return self.__rtruediv__(other)