-
Notifications
You must be signed in to change notification settings - Fork 17
/
finitefield.py
128 lines (99 loc) · 4.02 KB
/
finitefield.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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import random
from polynomial import polynomialsOver
from modp import *
# isIrreducible: Polynomial, int -> bool
# determine if the given monic polynomial with coefficients in Z/p is
# irreducible over Z/p where p is the given integer
# Algorithm 4.69 in the Handbook of Applied Cryptography
def isIrreducible(polynomial, p):
ZmodP = IntegersModP(p)
if polynomial.field is not ZmodP:
raise TypeError("Given a polynomial that's not over %s, but instead %r" %
(ZmodP.__name__, polynomial.field.__name__))
poly = polynomialsOver(ZmodP).factory
x = poly([0,1])
powerTerm = x
isUnit = lambda p: p.degree() == 0
for _ in range(int(polynomial.degree() / 2)):
powerTerm = powerTerm.powmod(p, polynomial)
gcdOverZmodp = gcd(polynomial, powerTerm - x)
if not isUnit(gcdOverZmodp):
return False
return True
# generateIrreduciblePolynomial: int, int -> Polynomial
# generate a random irreducible polynomial of a given degree over Z/p, where p
# is given by the integer 'modulus'. This algorithm is expected to terminate
# after 'degree' many irreducilibity tests. By Chernoff bounds the probability
# it deviates from this by very much is exponentially small.
def generateIrreduciblePolynomial(modulus, degree):
Zp = IntegersModP(modulus)
Polynomial = polynomialsOver(Zp)
while True:
coefficients = [Zp(random.randint(0, modulus-1)) for _ in range(degree)]
randomMonicPolynomial = Polynomial(coefficients + [Zp(1)])
print(randomMonicPolynomial)
if isIrreducible(randomMonicPolynomial, modulus):
return randomMonicPolynomial
# create a type constructor for the finite field of order p^m for p prime, m >= 1
@memoize
def FiniteField(p, m, polynomialModulus=None):
Zp = IntegersModP(p)
if m == 1:
return Zp
Polynomial = polynomialsOver(Zp)
if polynomialModulus is None:
polynomialModulus = generateIrreduciblePolynomial(modulus=p, degree=m)
class Fq(FieldElement):
fieldSize = int(p ** m)
primeSubfield = Zp
idealGenerator = polynomialModulus
operatorPrecedence = 3
def __init__(self, poly):
if type(poly) is Fq:
self.poly = poly.poly
elif type(poly) is int or type(poly) is Zp:
self.poly = Polynomial([Zp(poly)])
elif isinstance(poly, Polynomial):
self.poly = poly % polynomialModulus
else:
self.poly = Polynomial([Zp(x) for x in poly]) % polynomialModulus
self.field = Fq
@typecheck
def __add__(self, other): return Fq(self.poly + other.poly)
@typecheck
def __sub__(self, other): return Fq(self.poly - other.poly)
@typecheck
def __mul__(self, other): return Fq(self.poly * other.poly)
@typecheck
def __eq__(self, other): return isinstance(other, Fq) and self.poly == other.poly
@typecheck
def __ne__(self, other): return not self == other
def __pow__(self, n):
if n==0: return Fq([1])
if n==1: return self
if n%2==0:
sqrut = self**(n//2)
return sqrut*sqrut
if n%2==1: return (self**(n-1))*self
#def __pow__(self, n): return Fq(pow(self.poly, n))
def __neg__(self): return Fq(-self.poly)
def __abs__(self): return abs(self.poly)
def __repr__(self): return repr(self.poly) + ' \u2208 ' + self.__class__.__name__
@typecheck
def __divmod__(self, divisor):
q,r = divmod(self.poly, divisor.poly)
return (Fq(q), Fq(r))
def inverse(self):
if self == Fq(0):
raise ZeroDivisionError
x,y,d = extendedEuclideanAlgorithm(self.poly, self.idealGenerator)
if d.degree() != 0:
raise Exception('Somehow, this element has no inverse! Maybe intialized with a non-prime?')
return Fq(x) * Fq(d.coefficients[0].inverse())
Fq.__name__ = 'F_{%d^%d}' % (p,m)
return Fq
if __name__ == "__main__":
F23 = FiniteField(2,3)
x = F23([1,1])
F35 = FiniteField(3,5)
y = F35([1,1,2])