1 from .cldivrem
import cldivrem
2 from .clmul
import clmul
3 from .pack_poly
import pack_poly
, unpack_poly
8 """Polynomial with GF(2) coefficients.
10 `self.coefficients`: a list where `coefficients[-1] != 0`.
11 `coefficients[i]` is the coefficient for `x ** i`.
14 def __init__(self
, coefficients
=None):
15 self
.coefficients
= []
16 if coefficients
is not None:
17 if not isinstance(coefficients
, (tuple, list)):
18 coefficients
= list(coefficients
)
19 # reversed to resize self.coefficients once
20 for i
in reversed(range(len(coefficients
))):
21 self
[i
] = coefficients
[i
]
24 return len(self
.coefficients
)
32 """leading coefficient."""
33 return 0 if len(self
) == 0 else self
.coefficients
[-1]
35 def __getitem__(self
, key
):
38 return self
.coefficients
[key
]
41 def __setitem__(self
, key
, value
):
43 assert value
== 0 or value
== 1
45 self
.coefficients
[key
] = value
46 while len(self
) and self
.coefficients
[-1] == 0:
47 self
.coefficients
.pop()
49 self
.coefficients
+= [0] * (key
+ 1 - len(self
))
50 self
.coefficients
[key
] = value
53 return f
"GF2Poly({self.coefficients})"
55 def __iadd__(self
, rhs
):
56 for i
in range(max(len(self
), len(rhs
))):
60 def __add__(self
, rhs
):
61 return GF2Poly(self
).__iadd
__(rhs
)
63 def __isub__(self
, rhs
):
64 return self
.__iadd
__(rhs
)
66 def __sub__(self
, rhs
):
67 return self
.__add
__(rhs
)
70 return iter(self
.coefficients
)
72 def __mul__(self
, rhs
):
74 # reversed to resize retval.coefficients once
75 for i
in reversed(range(len(self
))):
77 for j
in reversed(range(len(rhs
))):
78 retval
[i
+ j
] ^
= rhs
[j
]
81 def __ilshift__(self
, amount
):
82 """multiplies `self` by the polynomial `x**amount`"""
84 self
.coefficients
[:0] = [0] * amount
87 def __lshift__(self
, amount
):
88 """returns the polynomial `self * x**amount`"""
89 return GF2Poly(self
).__ilshift
__(amount
)
91 def __irshift__(self
, amount
):
92 """divides `self` by the polynomial `x**amount`, discarding the
95 if amount
< len(self
):
96 del self
.coefficients
[:amount
]
98 del self
.coefficients
[:]
101 def __rshift__(self
, amount
):
102 """divides `self` by the polynomial `x**amount`, discarding the
105 return GF2Poly(self
).__irshift
__(amount
)
107 def __divmod__(self
, divisor
):
108 # based on https://en.wikipedia.org/wiki/Polynomial_greatest_common_divisor#Euclidean_division
109 assert isinstance(divisor
, GF2Poly
)
110 if len(divisor
) == 0:
111 raise ZeroDivisionError
114 while r
.degree
>= divisor
.degree
:
115 shift
= r
.degree
- divisor
.degree
117 r
-= divisor
<< shift
120 def __floordiv__(self
, divisor
):
121 q
, r
= divmod(self
, divisor
)
124 def __mod__(self
, divisor
):
125 q
, r
= divmod(self
, divisor
)
128 def __eq__(self
, rhs
):
129 if isinstance(rhs
, GF2Poly
):
130 return self
.coefficients
== rhs
.coefficients
131 return NotImplemented
134 class TestGF2Poly(unittest
.TestCase
):
136 a
= GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1])
137 b
= GF2Poly([0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0])
139 self
.assertEqual(a
, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
140 self
.assertEqual(b
, GF2Poly([0, 0, 1, 0, 1, 1, 1, 1, 1]))
141 self
.assertEqual(c
, GF2Poly([1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1]))
143 self
.assertEqual(a
, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
144 self
.assertEqual(b
, GF2Poly([0, 0, 1, 0, 1, 1, 1, 1, 1]))
145 self
.assertEqual(c
, GF2Poly([1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1]))
146 a
= GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1])
147 b
= GF2Poly([0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1])
149 self
.assertEqual(a
, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
150 self
.assertEqual(b
, GF2Poly([0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1]))
151 self
.assertEqual(c
, GF2Poly([1, 0, 1, 1, 1, 0, 0, 1]))
153 self
.assertEqual(c
, GF2Poly([1, 0, 1, 1, 1, 0, 0, 1]))
155 self
.assertEqual(c
, GF2Poly([1, 0, 1, 1, 1, 0, 0, 1]))
157 def test_shift(self
):
158 a
= GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1])
160 self
.assertEqual(a
, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
161 self
.assertEqual(c
, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
163 self
.assertEqual(a
, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
164 self
.assertEqual(c
, GF2Poly(
165 [0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
167 self
.assertEqual(a
, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
168 self
.assertEqual(c
, GF2Poly(
169 [0] * 10 + [1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
171 self
.assertEqual(a
, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
172 self
.assertEqual(c
, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
174 self
.assertEqual(a
, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
175 self
.assertEqual(c
, GF2Poly([1, 1, 0, 1, 0, 1]))
177 self
.assertEqual(a
, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
178 self
.assertEqual(c
, GF2Poly([1]))
180 self
.assertEqual(a
, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
181 self
.assertEqual(c
, GF2Poly([]))
183 self
.assertEqual(a
, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
184 self
.assertEqual(c
, GF2Poly([]))
187 a
= GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1])
188 b
= GF2Poly([0, 0, 1, 0, 1, 1, 1, 1, 1])
190 expected
= GF2Poly([0, 0, 1, 0, 1, 0, 1, 1, 1, 0,
191 0, 1, 0, 1, 1, 0, 0, 1, 1])
192 self
.assertEqual(a
, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
193 self
.assertEqual(b
, GF2Poly([0, 0, 1, 0, 1, 1, 1, 1, 1]))
194 self
.assertEqual(c
, expected
)
196 def test_divmod(self
):
197 a
= GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1])
198 b
= GF2Poly([0, 0, 1, 0, 1, 1, 1, 1, 1])
200 self
.assertEqual(a
, GF2Poly([1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1]))
201 self
.assertEqual(b
, GF2Poly([0, 0, 1, 0, 1, 1, 1, 1, 1]))
202 self
.assertEqual(q
, GF2Poly([1, 1, 1]))
203 self
.assertEqual(r
, GF2Poly([1, 0, 1, 0, 0, 1, 0, 1]))
205 self
.assertEqual(q
, GF2Poly([1, 1, 1]))
207 self
.assertEqual(r
, GF2Poly([1, 0, 1, 0, 0, 1, 0, 1]))
210 class TestCL(unittest
.TestCase
):
211 def test_cldivrem(self
):
214 width
= max(n_width
, d_width
)
215 for nv
in range(2 ** n_width
):
216 n
= GF2Poly(unpack_poly(nv
))
217 for dv
in range(1, 2 ** d_width
):
218 d
= GF2Poly(unpack_poly(dv
))
219 with self
.subTest(n
=str(n
), nv
=nv
, d
=str(d
), dv
=dv
):
220 q_expected
, r_expected
= divmod(n
, d
)
221 self
.assertEqual(q_expected
* d
+ r_expected
, n
)
222 q
, r
= cldivrem(nv
, dv
, width
)
223 q_expected
= pack_poly(q_expected
.coefficients
)
224 r_expected
= pack_poly(r_expected
.coefficients
)
225 self
.assertEqual((q
, r
), (q_expected
, r_expected
))
227 def test_clmul(self
):
230 for av
in range(2 ** a_width
):
231 a
= GF2Poly(unpack_poly(av
))
232 for bv
in range(2 ** b_width
):
233 b
= GF2Poly(unpack_poly(bv
))
234 with self
.subTest(a
=str(a
), av
=av
, b
=str(b
), bv
=bv
):
236 product
= clmul(av
, bv
)
237 expected
= pack_poly(expected
.coefficients
)
238 self
.assertEqual(product
, expected
)
241 if __name__
== "__main__":