-
Notifications
You must be signed in to change notification settings - Fork 17
/
numbertype.py
98 lines (70 loc) · 2.76 KB
/
numbertype.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# 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)