initial commit
This commit is contained in:
282
python/latex_gen/reed_solomon_gen.py
Normal file
282
python/latex_gen/reed_solomon_gen.py
Normal file
@@ -0,0 +1,282 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Prints steps of Reed-Solomon decoding
|
||||
|
||||
(C) 2022 Louis Heredero louis.heredero@edu.vs.ch
|
||||
"""
|
||||
|
||||
class GF:
|
||||
def __init__(self, val):
|
||||
self.val = val
|
||||
|
||||
def copy(self):
|
||||
return GF(self.val)
|
||||
|
||||
def __add__(self, n):
|
||||
return GF(self.val ^ n.val)
|
||||
|
||||
def __sub__(self, n):
|
||||
return GF(self.val ^ n.val)
|
||||
|
||||
def __mul__(self, n):
|
||||
if self.val == 0 or n.val == 0:
|
||||
return GF(0)
|
||||
|
||||
return GF.EXP[GF.LOG[self.val].val + GF.LOG[n.val].val].copy()
|
||||
|
||||
def __truediv__(self, n):
|
||||
if n.val == 0:
|
||||
raise ZeroDivisionError
|
||||
if self.val == 0:
|
||||
return GF(0)
|
||||
|
||||
return GF.EXP[(GF.LOG[self.val].val + 255 - GF.LOG[n.val].val)%255].copy()
|
||||
|
||||
def __pow__(self, n):
|
||||
return GF.EXP[(GF.LOG[self.val].val * n.val)%255].copy()
|
||||
|
||||
def __repr__(self):
|
||||
return self.val.__repr__()
|
||||
#return f"GF({self.val})"
|
||||
|
||||
def log(self):
|
||||
return GF.LOG[self.val]
|
||||
|
||||
GF.EXP = [GF(0)]*512
|
||||
GF.LOG = [GF(0)]*256
|
||||
value = 1
|
||||
for exponent in range(255):
|
||||
GF.LOG[value] = GF(exponent)
|
||||
GF.EXP[exponent] = GF(value)
|
||||
value = ((value << 1) ^ 285) if value > 127 else value << 1
|
||||
|
||||
for i in range(255, 512):
|
||||
GF.EXP[i] = GF.EXP[i-255].copy()
|
||||
|
||||
|
||||
class Poly:
|
||||
def __init__(self, coefs):
|
||||
self.coefs = coefs.copy()
|
||||
|
||||
@property
|
||||
def deg(self):
|
||||
return len(self.coefs)
|
||||
|
||||
def copy(self):
|
||||
return Poly(self.coefs)
|
||||
|
||||
def __add__(self, p):
|
||||
d1, d2 = self.deg, p.deg
|
||||
deg = max(d1,d2)
|
||||
result = [GF(0) for i in range(deg)]
|
||||
|
||||
for i in range(d1):
|
||||
result[i + deg - d1] = self.coefs[i]
|
||||
|
||||
for i in range(d2):
|
||||
result[i + deg - d2] += p.coefs[i]
|
||||
|
||||
return Poly(result)
|
||||
|
||||
def __mul__(self, p):
|
||||
result = [GF(0) for i in range(self.deg+p.deg-1)]
|
||||
|
||||
for i in range(p.deg):
|
||||
for j in range(self.deg):
|
||||
result[i+j] += self.coefs[j] * p.coefs[i]
|
||||
|
||||
return Poly(result)
|
||||
|
||||
def __truediv__(self, p):
|
||||
dividend = self.coefs.copy()
|
||||
dividend += [GF(0) for i in range(p.deg-1)]
|
||||
quotient = []
|
||||
|
||||
for i in range(self.deg):
|
||||
coef = dividend[i] / p.coefs[0]
|
||||
quotient.append(coef)
|
||||
print("sub:", p*Poly([coef]))
|
||||
|
||||
for j in range(p.deg):
|
||||
dividend[i+j] -= p.coefs[j] * coef
|
||||
|
||||
print("rem:", dividend)
|
||||
print()
|
||||
|
||||
while dividend[0].val == 0:
|
||||
dividend.pop(0)
|
||||
|
||||
return [Poly(quotient), Poly(dividend)]
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Poly {self.coefs}>"
|
||||
|
||||
def eval(self, x):
|
||||
y = GF(0)
|
||||
|
||||
for i in range(self.deg):
|
||||
y += self.coefs[i] * x**GF(self.deg-i-1)
|
||||
|
||||
return y
|
||||
|
||||
def del_lead_zeros(self):
|
||||
while len(self.coefs) > 1 and self.coefs[0].val == 0:
|
||||
self.coefs.pop(0)
|
||||
|
||||
if len(self.coefs) == 0:
|
||||
self.coefs = [GF(0)]
|
||||
|
||||
return self
|
||||
|
||||
def get_generator_poly(n):
|
||||
poly = Poly([GF(1)])
|
||||
|
||||
for i in range(n):
|
||||
poly *= Poly([GF(1), GF(2)**GF(i)])
|
||||
|
||||
return poly
|
||||
|
||||
class ReedSolomonException(Exception):
|
||||
pass
|
||||
|
||||
def correct(data, ec):
|
||||
n = len(ec)
|
||||
|
||||
data = Poly([GF(int(cw, 2)) for cw in data+ec])
|
||||
##print("data", list(map(lambda c:c.val, data.coefs)))
|
||||
print("r(x)", data)
|
||||
|
||||
syndrome = [0]*n
|
||||
corrupted = False
|
||||
for i in range(n):
|
||||
syndrome[i] = data.eval(GF.EXP[i])
|
||||
if syndrome[i].val != 0:
|
||||
corrupted = True
|
||||
|
||||
if not corrupted:
|
||||
print("No errors")
|
||||
return data
|
||||
|
||||
syndrome = Poly(syndrome[::-1])
|
||||
print("syndrome", syndrome)
|
||||
|
||||
#Find locator poly
|
||||
sigma, omega = euclidean_algorithm(Poly([GF(1)]+[GF(0) for i in range(n)]), syndrome, n)
|
||||
print("locator", sigma)
|
||||
print("evaluator", omega)
|
||||
error_loc = find_error_loc(sigma)
|
||||
print("location", error_loc)
|
||||
|
||||
error_mag = find_error_mag(omega, error_loc)
|
||||
print("mag", error_mag)
|
||||
|
||||
for i in range(len(error_loc)):
|
||||
pos = GF(error_loc[i]).log()
|
||||
pos = data.deg - pos.val - 1
|
||||
if pos < 0:
|
||||
raise ReedSolomonException("Bad error location")
|
||||
|
||||
data.coefs[pos] += GF(error_mag[i])
|
||||
|
||||
return data
|
||||
|
||||
def euclidean_algorithm(a, b, R):
|
||||
if a.deg < b.deg:
|
||||
a, b = b, a
|
||||
|
||||
r_last = a
|
||||
r = b
|
||||
t_last = Poly([GF(0)])
|
||||
t = Poly([GF(1)])
|
||||
|
||||
while r.deg-1 >= int(R/2):
|
||||
r_last_last = r_last
|
||||
t_last_last = t_last
|
||||
r_last = r
|
||||
t_last = t
|
||||
if r_last.coefs[0] == 0:
|
||||
raise ReedSolomonException("r_{i-1} was zero")
|
||||
|
||||
r = r_last_last
|
||||
q = Poly([GF(0)])
|
||||
denom_lead_term = r_last.coefs[0]
|
||||
dlt_inv = denom_lead_term ** GF(-1)
|
||||
I = 0
|
||||
while r.deg >= r_last.deg and r.coefs[0] != 0:
|
||||
I += 1
|
||||
deg_diff = r.deg - r_last.deg
|
||||
scale = r.coefs[0] * dlt_inv
|
||||
q += Poly([scale]+[GF(0) for i in range(deg_diff)])
|
||||
r += r_last * Poly([scale]+[GF(0) for i in range(deg_diff)])
|
||||
q.del_lead_zeros()
|
||||
r.del_lead_zeros()
|
||||
|
||||
if I > 100:
|
||||
raise ReedSolomonException("Too long")
|
||||
|
||||
t = (q * t_last).del_lead_zeros() + t_last_last
|
||||
t.del_lead_zeros()
|
||||
if r.deg >= r_last.deg:
|
||||
raise ReedSolomonException("Division algorithm failed to reduce polynomial")
|
||||
|
||||
sigma_tilde_at_zero = t.coefs[-1]
|
||||
if sigma_tilde_at_zero.val == 0:
|
||||
raise ReedSolomonException("sigma_tilde(0) was zero")
|
||||
|
||||
inv = Poly([sigma_tilde_at_zero ** GF(-1)])
|
||||
sigma = t * inv
|
||||
omega = r * inv
|
||||
|
||||
return [sigma, omega]
|
||||
|
||||
def find_error_loc(error_loc):
|
||||
num_errors = error_loc.deg-1
|
||||
if num_errors == 1:
|
||||
return [error_loc.coefs[-2].val]
|
||||
|
||||
result = [0]*num_errors
|
||||
e = 0
|
||||
i = 1
|
||||
while i < 256 and e < num_errors:
|
||||
if error_loc.eval(GF(i)).val == 0:
|
||||
result[e] = (GF(i) ** GF(-1)).val
|
||||
e += 1
|
||||
|
||||
i += 1
|
||||
|
||||
if e != num_errors:
|
||||
raise ReedSolomonException("Error locator degree does not match number of roots")
|
||||
|
||||
return result
|
||||
|
||||
def find_error_mag(error_eval, error_loc):
|
||||
s = len(error_loc)
|
||||
result = [0]*s
|
||||
for i in range(s):
|
||||
xi_inv = GF(error_loc[i]) ** GF(-1)
|
||||
denom = GF(1)
|
||||
for j in range(s):
|
||||
if i != j:
|
||||
denom *= GF(1) + GF(error_loc[j]) * xi_inv
|
||||
|
||||
result[i] = ( error_eval.eval(xi_inv) * (denom ** GF(-1)) ).val
|
||||
|
||||
return result
|
||||
|
||||
if __name__ == "__main__":
|
||||
m = Poly([GF(67),GF(111),GF(100),GF(101),GF(115)])
|
||||
g = get_generator_poly(4)
|
||||
print()
|
||||
print(g)
|
||||
print(m/g)
|
||||
print()
|
||||
|
||||
data = [67,111,110,101,115]
|
||||
#ec = [119,123,82]
|
||||
ec = [50,166,245,58]
|
||||
|
||||
data = [f"{n:08b}" for n in data]
|
||||
ec = [f"{n:08b}" for n in ec]
|
||||
|
||||
print(correct(data, ec))
|
||||
Reference in New Issue
Block a user