Skip to content

Commit 051d0ee

Browse files
committed
fix: some bugs in numpy implementation of RS lib (still not enough, doesn't pass rstest.py).
Signed-off-by: Stephen L. <lrq3000@gmail.com>
1 parent 6e1722d commit 051d0ee

File tree

4 files changed

+235
-26
lines changed

4 files changed

+235
-26
lines changed

lib/brownanrs/nff.py

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,27 +42,46 @@
4242
137, 180, 124, 184, 38, 119, 153, 227, 165, 103, 74, 237, 222, 197,
4343
49, 254, 24, 13, 99, 140, 128, 192, 247, 112, 7]
4444

45-
class nGF256int(int):
45+
class GF256int(int):
4646
"""Instances of this object are elements of the field GF(2^8)
4747
Instances are integers in the range 0 to 255
4848
This field is defined using the irreducable polynomial
4949
x^8 + x^4 + x^3 + x + 1
5050
and using 3 as the generator for the exponent table and log table.
5151
"""
52+
53+
def __add__(a, b):
54+
"Addition in GF(2^8) is the xor of the two"
55+
return GF256int(a ^ b)
56+
__sub__ = __add__
57+
__radd__ = __add__
58+
__rsub__ = __add__
59+
def __neg__(self):
60+
return self
61+
62+
def __mul__(a, b):
63+
"Multiplication in GF(2^8)"
64+
if a == 0 or b == 0:
65+
return GF256int(0)
66+
x = nGF256int_logtable[a]
67+
y = nGF256int_logtable[b]
68+
z = (x + y) % 255
69+
return GF256int(nGF256int_exptable[z])
70+
__rmul__ = __mul__
5271

5372
def __pow__(self, power, modulo=None):
54-
if isinstance(power, nGF256int):
55-
raise TypeError("Raising a Field element to another Field element is not defined. power must be a regular integer")
73+
#if isinstance(power, GF256int):
74+
#raise TypeError("Raising a Field element to another Field element is not defined. power must be a regular integer")
5675
x = nGF256int_logtable[self]
5776
z = (x * power) % 255
58-
return nGF256int(nGF256int_exptable[z])
77+
return GF256int(nGF256int_exptable[z])
5978

6079
def inverse(self):
6180
e = nGF256int_logtable[self]
62-
return nGF256int(nGF256int_exptable[255 - e])
81+
return GF256int(nGF256int_exptable[255 - e])
6382

6483
def __div__(self, other):
65-
return self * nGF256int(other).inverse()
84+
return self * GF256int(other).inverse()
6685
def __rdiv__(self, other):
6786
return self.inverse() * other
6887

lib/brownanrs/npolynomial.py

Lines changed: 52 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,42 @@
4747
137, 180, 124, 184, 38, 119, 153, 227, 165, 103, 74, 237, 222, 197,
4848
49, 254, 24, 13, 99, 140, 128, 192, 247, 112, 7], dtype=int )
4949

50-
class nPolynomial(object):
50+
def GF256int_add(a, b):
51+
"Addition in GF(2^8) is the xor of the two"
52+
return a ^ b
53+
54+
def GF256int_neg(a):
55+
return a
56+
57+
def GF256int_mul(a, b):
58+
"Multiplication in GF(2^8)"
59+
if a == 0 or b == 0:
60+
return 0
61+
x = GF256int_logtable[a]
62+
y = GF256int_logtable[b]
63+
z = (x + y) % 255
64+
return GF256int_exptable[z]
65+
66+
def GF256int_pow(a, power):
67+
x = GF256int_logtable[a]
68+
z = (x * power) % 255
69+
return GF256int_exptable[z]
70+
71+
def GF256int_inverse(a):
72+
e = GF256int_logtable[a]
73+
return GF256int_exptable[255 - e]
74+
75+
def GF256int_div(a, b):
76+
return GF256int_mul(a, GF256int_inverse(b))
77+
78+
class Polynomial(object):
5179
"""Polynomial for GF256int class with numpy implementation.
5280
This class implements vectorized operations on GF256int, thus the type is "defined" implitly in the operations involving GF256int_logtable and GF256int_exptable.
5381
5482
Polynomial objects are immutable.
5583
5684
Implementation note: this class is NOT type agnostic, contrariwise to polynomial.py and cpolynomial.py."""
57-
def __init__(self, coefficients=[], **sparse):
85+
def __init__(self, coefficients=None, **sparse):
5886
"""
5987
There are three ways to initialize a Polynomial object.
6088
1) With a list, tuple, or other iterable, creates a polynomial using
@@ -80,16 +108,15 @@ def __init__(self, coefficients=[], **sparse):
80108
" both")
81109
if coefficients is not None:
82110
# Polynomial( [1, 2, 3, ...] )
83-
#c = coefficients
84-
#if isinstance(coefficients, tuple): c = list(coefficients)
111+
#if isinstance(coefficients, tuple): coefficients = list(coefficients)
85112
# Expunge any leading 0 coefficients
86113
while len(coefficients) > 0 and coefficients[0] == 0:
87114
if isinstance(coefficients, np.ndarray):
88115
coefficients = np.delete(coefficients, 0)
89116
else:
90117
coefficients.pop(0)
91118
if len(coefficients) == 0:
92-
coefficients.append(0)
119+
coefficients = [0]
93120

94121
self.coefficients = np.array(coefficients, dtype=int)
95122
elif sparse:
@@ -109,16 +136,19 @@ def __init__(self, coefficients=[], **sparse):
109136
else:
110137
# Polynomial()
111138
self.coefficients = np.zeros(1, dtype=int)
112-
self.degree = len(self.coefficients)
139+
# In any case, compute the degree of the polynomial (=the maximum degree)
140+
self.degree = len(self.coefficients)-1
113141

114142
def __len__(self):
115143
"""Returns the number of terms in the polynomial"""
116-
return self.degree
144+
return self.degree+1
145+
# return len(self.coefficients)
117146

118147
def degree(self, poly=None):
119148
"""Returns the degree of the polynomial"""
120149
if not poly:
121-
return self.degree - 1
150+
return self.degree
151+
#return len(self.coefficients) - 1
122152
elif poly and hasattr("coefficients", poly):
123153
return len(poly.coefficients) - 1
124154
else:
@@ -127,7 +157,7 @@ def degree(self, poly=None):
127157
return len(poly)-1
128158

129159
def __add__(self, other):
130-
diff = self.degree - other.degree
160+
diff = len(self) - len(other)
131161

132162
# Warning: value must be absolute, there's no negative value accepted here! (in fact they will be accepted but the result will be meaningless! and divmod will be in infinite loop)
133163
t1 = np.pad(np.absolute(self.coefficients), (np.max([0, -diff]),0), mode='constant')
@@ -218,21 +248,26 @@ def __divmod__(dividend, divisor):
218248
remainder = dividend
219249
remainder_power = dividend_power
220250
remainder_coefficient = dividend_coefficient
221-
while remainder_power >= divisor_power: # Until there's no remainder left (or the remainder cannot be divided anymore by the divisor)
222-
quotient_power = remainder_power - divisor_power
223-
quotient_coefficient = remainder_coefficient / divisor_coefficient
251+
quotient_power = remainder_power - divisor_power # need to set at least 1 just to start the loop. Warning if set to remainder_power - divisor_power: because it may skip the loop altogether (and we want to at least do one iteration to set the quotient)
252+
253+
# Compute how many times the highest order term in the divisor goes into the dividend
254+
while quotient_power >= 0 and np.count_nonzero(remainder.coefficients) > 0: # Until there's no remainder left (or the remainder cannot be divided anymore by the divisor)
255+
quotient_coefficient = GF256int_div(remainder_coefficient, divisor_coefficient)
224256
q = class_( np.pad([quotient_coefficient], (0,quotient_power), mode='constant') ) # construct an array with only the quotient major coefficient (we divide the remainder only with the major coeff)
225257
quotient = quotient + q # add the coeff to the full quotient
226258
remainder = remainder - q * divisor # divide the remainder with the major coeff quotient multiplied by the divisor, this gives us the new remainder
227259
remainder_power = remainder.degree # compute the new remainder degree
228260
remainder_coefficient = remainder[0] # Compute the new remainder coefficient
229-
#print "quotient: %s remainder: %s" % (quotient, remainder)
261+
quotient_power = remainder_power - divisor_power
230262
return quotient, remainder
231263

232264
def __eq__(self, other):
233-
return self.coefficients == other.coefficients
265+
if isinstance(self.coefficients, np.ndarray):
266+
return self.coefficients.tolist() == other.coefficients.tolist()
267+
else:
268+
return self.coefficients == other.coefficients
234269
def __ne__(self, other):
235-
return self.coefficients != other.coefficients
270+
return not self == other
236271
def __hash__(self):
237272
return hash(self.coefficients)
238273

@@ -258,7 +293,7 @@ def __str__(self):
258293
return buf.getvalue()[:-3]
259294

260295
def evaluate(self, x):
261-
"Evaluate this polynomial at value x, returning the result."
296+
'''Evaluate this polynomial at value x, returning the result.'''
262297
# Holds the sum over each term in the polynomial
263298
c = 0
264299

@@ -273,7 +308,7 @@ def evaluate(self, x):
273308
return c
274309

275310
def get_coefficient(self, degree):
276-
"""Returns the coefficient of the specified term"""
311+
'''Returns the coefficient of the specified term'''
277312
if degree > self.degree:
278313
return 0
279314
else:

lib/brownanrs/npolynomialtest.py

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
import unittest
2+
3+
from npolynomial import Polynomial
4+
from nff import GF256int
5+
6+
class TestGFPoly(unittest.TestCase):
7+
"""Tests that the Polynomial class works when given GF256int objects
8+
instead of regular integers
9+
"""
10+
def test_add(self):
11+
one = Polynomial(map(GF256int, [8,3,5,1]))
12+
two = Polynomial(map(GF256int, [5,3,1,1,6,8]))
13+
14+
r = one + two
15+
16+
self.assertEqual(list(r.coefficients), [5,3,9,2,3,9])
17+
18+
def test_sub(self):
19+
one = Polynomial(map(GF256int, [8,3,5,1]))
20+
two = Polynomial(map(GF256int, [5,3,1,1,6,8]))
21+
r = one - two
22+
self.assertEqual(list(r.coefficients), [5,3,9,2,3,9])
23+
24+
def test_mul(self):
25+
one = Polynomial(map(GF256int, [8,3,5,1]))
26+
two = Polynomial(map(GF256int, [5,3,1,1,6,8]))
27+
r = one * two
28+
self.assertEqual(list(r.coefficients), [40,23,28,1,53,78,7,46,8])
29+
30+
def test_div(self):
31+
one = Polynomial(map(GF256int, [8,3,5,1]))
32+
two = Polynomial(map(GF256int, [5,3,1,1,6,8]))
33+
q, r = divmod(two,one)
34+
self.assertEqual(list(q.coefficients), [101, 152, 11])
35+
self.assertEqual(list(r.coefficients), [183, 185, 3])
36+
37+
# Make sure they multiply back out okay
38+
self.assertEqual(q*one + r, two)
39+
40+
def test_div_scalar(self):
41+
"""Tests division by a scalar"""
42+
numbers = map(GF256int, [5,20,50,100,134,158,0,148,233,254,4,5,2])
43+
scalar = GF256int(17)
44+
45+
poly = Polynomial(numbers)
46+
scalarpoly = Polynomial(x0=scalar)
47+
48+
self.assertEqual(
49+
list((poly // scalarpoly).coefficients),
50+
map(lambda x: x / scalar, numbers)
51+
)
52+
53+
def test_div_scalar2(self):
54+
"""Test that dividing by a scalar is the same as multiplying by the
55+
scalar's inverse"""
56+
a = Polynomial(map(GF256int, [5,3,1,1,6,8]))
57+
58+
scalar = GF256int(50)
59+
60+
self.assertEqual(
61+
a * Polynomial(x0=scalar),
62+
a // Polynomial(x0=scalar.inverse())
63+
)
64+
65+
66+
67+
# class TestPolynomial(unittest.TestCase):
68+
# def test_add_1(self):
69+
# one = Polynomial([2,4,7,3])
70+
# two = Polynomial([5,2,4,2])
71+
72+
# r = one + two
73+
74+
# self.assertEqual(list(r.coefficients), [7, 6, 11, 5])
75+
76+
# def test_add_2(self):
77+
# one = Polynomial([2,4,7,3,5,2])
78+
# two = Polynomial([5,2,4,2])
79+
80+
# r = one + two
81+
82+
# self.assertEqual(list(r.coefficients), [2,4,12,5,9,4])
83+
84+
# def test_add_3(self):
85+
# one = Polynomial([7,3,5,2])
86+
# two = Polynomial([6,8,5,2,4,2])
87+
88+
# r = one + two
89+
90+
# self.assertEqual(list(r.coefficients), [6,8,12,5,9,4])
91+
92+
# def test_mul_1(self):
93+
# one = Polynomial([2,4,7,3])
94+
# two = Polynomial([5,2,4,2])
95+
96+
# r = one._mul_standard(two)
97+
98+
# self.assertEqual(list(r.coefficients),
99+
# [10,24,51,49,42,26,6])
100+
101+
# def test_div_1(self):
102+
# one = Polynomial([1,4,0,3])
103+
# two = Polynomial([1,0,1])
104+
105+
# q, r = divmod(one, two)
106+
# self.assertEqual(q, one // two)
107+
# self.assertEqual(r, one % two)
108+
109+
# self.assertEqual(list(q.coefficients), [1,4])
110+
# self.assertEqual(list(r.coefficients), [-1,-1])
111+
112+
# def test_div_2(self):
113+
# one = Polynomial([1,0,0,2,2,0,1,2,1])
114+
# two = Polynomial([1,0,-1])
115+
116+
# q, r = divmod(one, two)
117+
# self.assertEqual(q, one // two)
118+
# self.assertEqual(r, one % two)
119+
120+
# self.assertEqual(list(q.coefficients), [1,0,1,2,3,2,4])
121+
# self.assertEqual(list(r.coefficients), [4,5])
122+
123+
# def test_div_3(self):
124+
# # 0 quotient
125+
# one = Polynomial([1,0,-1])
126+
# two = Polynomial([1,1,0,0,-1])
127+
128+
# q, r = divmod(one, two)
129+
# self.assertEqual(q, one // two)
130+
# self.assertEqual(r, one % two)
131+
132+
# self.assertEqual(list(q.coefficients), [0])
133+
# self.assertEqual(list(r.coefficients), [1,0,-1])
134+
135+
# def test_div_4(self):
136+
# # no remander
137+
# one = Polynomial([1,0,0,2,2,0,1,-2,-4])
138+
# two = Polynomial([1,0,-1])
139+
140+
# q, r = divmod(one, two)
141+
# self.assertEqual(q, one // two)
142+
# self.assertEqual(r, one % two)
143+
144+
# self.assertEqual(list(q.coefficients), [1,0,1,2,3,2,4])
145+
# self.assertEqual(list(r.coefficients), [0])
146+
147+
# def test_getcoeff(self):
148+
# p = Polynomial([9,3,3,2,2,3,1,-2,-4])
149+
# self.assertEqual(p.get_coefficient(0), -4)
150+
# self.assertEqual(p.get_coefficient(2), 1)
151+
# self.assertEqual(p.get_coefficient(8), 9)
152+
# self.assertEqual(p.get_coefficient(9), 0)
153+
154+
if __name__ == "__main__":
155+
unittest.main()

lib/brownanrs/rs.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
# See LICENSE.txt for license terms
44

55
# try: # Numpy implementation import. NOTE: it's working correctly but it's actually slower than the pure-python implementation! (although the code is vectorized and __mul__ is faster, but it seems it's not enough to speed things up and the numpy call overhead is too much for any gain here).
6-
# from npolynomial import nPolynomial as Polynomial
7-
# from nff import nGF256int as GF256int
8-
#except ImportError:
6+
# from npolynomial import Polynomial
7+
# from nff import GF256int
8+
# except ImportError:
99
try: # Cython implementation import. This should be a bit faster than using PyPy with the pure-python implementation.
1010
from cff import GF256int
1111
from cpolynomial import Polynomial

0 commit comments

Comments
 (0)