Skip to content
Snippets Groups Projects
Commit 393cc838 authored by florian.burgener's avatar florian.burgener
Browse files

Add comments

parent be40b6a3
No related branches found
No related tags found
No related merge requests found
import math
from numbers import Number
import itertools
import json
from typing import Tuple
def unicode_superscripts(value):
def unicode_superscripts(number):
"""Convert a number into a string of unicode characters superscripts.
Args:
number (int): The number to convert.
Returns:
str: The unicode superscripts string.
"""
exponent_dict = {"0": "", "1": "¹", "2": "²", "3": "³", "4": "", "5": "", "6": "", "7": "", "8": "", "9": ""}
return ("" if value < 0 else "") + "".join(exponent_dict[x] for x in str(abs(value)))
return ("" if number < 0 else "") + "".join(exponent_dict[x] for x in str(abs(number)))
class Polynomial:
"""Class for manipulating polynomials."""
def __init__(self, value=()):
"""Creates an instance of Polynomial with a polynomial defined in a tuple.
Args:
value (tuple, optional): Polynomial defined in a tuple. Defaults to ().
Raises:
TypeError: The type of the parameter "value" is not a tuple.
"""
if not isinstance(value, tuple):
raise TypeError('The "value" parameter is not of type tuple.')
self.value = value
def pass_x_throughout(self, x):
"""Evaluate the polynomial by passing x
Args:
x (int): The x to evaluate.
Returns:
int: The result of the evaluation.
"""
a = list(self.value)
sum = (a[len(a) - 1] * x) + a[len(a) - 2]
for i in reversed(range(len(a) - 2)):
sum = sum * x + a[i]
return sum
def __add__(self, other):
"""Add two polynomials.
Args:
other (Polynomial): The second polynomial to be added.
Returns:
Polynomial: The result of the addition.
"""
a = list(self.value)
b = list(other.value)
......@@ -45,35 +78,51 @@ class Polynomial:
return Polynomial(tuple(c))
def __mul__(self, other):
"""Multiply two polynomials.
Args:
other (Polynomial): The second polynomial to be multiplied.
Returns:
Polynomial: The result of the multiplication.
"""
a = list(self.value)
b = list(other.value)
a_count = len(a)
b_count = len(b)
size = (a_count - 1) + (b_count - 1) + 1
size = (len(a) - 1) + (len(b) - 1) + 1
c = [0] * size
for i in range(a_count):
for j in range(b_count):
for i in range(len(a)):
for j in range(len(b)):
c[i + j] += a[i] * b[j]
return Polynomial(tuple(c))
def __mod__(self, other):
"""Apply a modulo on the polynomial.
Args:
other (int): The modulo to apply.
Returns:
Polynomial: The result of the modolu operation.
"""
a = list(self.value)
result = [0] * len(a)
for i in range(len(a)):
result[i] = a[i] % other
for i in reversed(range(len(result))):
if result[i] == 0:
del result[i]
else:
break
return Polynomial(tuple(result))
def __str__(self):
"""Convert a polynomial into a string.
Returns:
str: The polynomial converts to a string.
"""
str_value = ""
for i, x in enumerate(reversed(self.value)):
......@@ -92,67 +141,96 @@ class Polynomial:
return str_value
def compute_bachet_bezout(a, b):
def get_bezout_coefficients(a, b):
"""Find the Bézout coefficients of a and b.
Args:
a (int): The number a
b (int): The number b.
Returns:
tuple: Bézout coefficients.
"""
r = [a, b]
x = [1, 0]
y = [0, 1]
q = [0, 0]
# Computing
i = 1
while r[i] > 0:
i += 1
r.append(r[i - 2] % r[i - 1])
q.append(int(r[i - 2] / r[i - 1]))
if r[i] > 0:
x.append(x[i - 2] - q[i] * x[i - 1])
y.append(y[i - 2] - q[i] * y[i - 1])
return x[-1], y[-1]
def modular_inverse(a, n):
coefficients = compute_bachet_bezout(a, n)
"""Compute the modular inverse of a number a modolu n.
Args:
a (int): The number to reverse.
n (int): The modolu.
Returns:
int: The reversed number.
"""
coefficients = get_bezout_coefficients(a, n)
if a * coefficients[0] % n == 1:
return coefficients[0] % n
return None
def compute_lagrange_polynomial(points, prime_number):
nb_points = len(points)
lagrange = Polynomial((0,))
"""Compute the Lagrange polynomial passing through all points.
# Create a polynomial for each points
for i in range(nb_points):
poly_li = Polynomial((1,))
divider = 1
Args:
points (list): List of points.
prime_number (int): The prime number.
# Compute the lagrange polynomial
for k in range(nb_points):
if k != i:
dividend = Polynomial((-points[k][0], 1)) # x - value
Returns:
Polynomial: The Lagrange polynomial passing through all points.
"""
# Polynomial L(x)
L_polynomial = Polynomial((0,))
poly_li *= dividend
divider *= points[i][0] - points[k][0]
# Create the Li(x) polynomial.
for i, (xi, yi) in enumerate(points):
Li_polynomial = Polynomial((1,))
divider = modular_inverse(divider, prime_number)
point_yi = points[i][1]
poly_li = poly_li * Polynomial((divider,)) * Polynomial((point_yi,))
# For each point except the point at index i.
for j, (xj, _) in enumerate(points):
if j != i:
Li_polynomial *= Polynomial((-xj, 1))
Li_polynomial *= Polynomial((modular_inverse(xi - xj, prime_number),))
Li_polynomial *= Polynomial((yi,))
L_polynomial += Li_polynomial
return L_polynomial % prime_number
lagrange += poly_li
lagrange %= prime_number
return lagrange
def reed_solomon(points, data_length, last_error_index, prime_number):
"""Applies the Reed-Solomon method to correct message errors.
Args:
points (list): List of points.
data_length (int): Data length.
last_error_index (int): The index of the last error.
prime_number (int): The prime number.
def reed_solomon(points, data_length, last_error_index, prime_number):
Returns:
str: The corrected message.
"""
combination_length = data_length - len(points[:last_error_index])
# Parse each combination of points possible (exclude the correct points)
for x in itertools.combinations(points[: last_error_index + 1], combination_length):
nb_valid_points = 0
# Create a sublist of points with all corrects points and the current combination of points
sub_points = list(x) + points[last_error_index + 1:]
sub_points = list(x) + points[last_error_index + 1 :]
# Create the lagrange polynomial with the sublist of points
lagrange = compute_lagrange_polynomial(sub_points, prime_number)
......@@ -169,7 +247,7 @@ def reed_solomon(points, data_length, last_error_index, prime_number):
# Verify if we have enough valid points, so it must be equal or higher than m + n points
if nb_valid_points >= data_length + (len(points) - data_length) // 2: # // = euclid division
# Decode the message
output = ''
output = ""
for i in range(data_length):
output += chr(lagrange.pass_x_throughout(i) % prime_number)
......@@ -181,7 +259,6 @@ def reed_solomon(points, data_length, last_error_index, prime_number):
def main():
message = {"data_length": 25, "last_error_index": 23, "prime_number": 401, "points": [67, 101, 38, 109, 101, 115, 133, 118, 103, 128, 62, 118, 97, 156, 116, 77, 49, 56, 86, 112, 171, 105, 176, 116, 115, 183, 30, 315, 368, 29, 352, 54, 333, 198, 139, 234, 321, 92, 5, 272, 396, 265, 397, 386, 229, 153, 276]}
points = [(x, y) for x, y in enumerate(message["points"])]
print(points)
corrected_data = reed_solomon(points, message["data_length"], message["last_error_index"], message["prime_number"])
print(corrected_data)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment