use crate::Exceptions;
use crate::common::Result;
use super::{GenericGF, GenericGFPoly, GenericGFRef};
pub struct ReedSolomonDecoder {
field: GenericGFRef,
}
impl ReedSolomonDecoder {
pub const fn new(field: GenericGFRef) -> Self {
Self { field }
}
pub fn decode(&self, received: &mut [i32], twoS: i32) -> Result<usize> {
let poly = GenericGFPoly::new(self.field, received)?;
let mut syndromeCoefficients = vec![0; twoS as usize];
let mut noError = true;
for i in 0..twoS {
let eval = poly.evaluateAt(self.field.exp(i + self.field.getGeneratorBase()) as usize);
let len = syndromeCoefficients.len();
syndromeCoefficients[len - 1 - i as usize] = eval;
if eval != 0 {
noError = false;
}
}
if noError {
return Ok(0);
}
let Ok(syndrome) = GenericGFPoly::new(self.field, &syndromeCoefficients) else {
return Err(Exceptions::REED_SOLOMON);
};
let sigmaOmega = self.runEuclideanAlgorithm(
&GenericGF::buildMonomial(self.field, twoS as usize, 1),
&syndrome,
twoS as usize,
)?;
let sigma = &sigmaOmega[0];
let omega = &sigmaOmega[1];
let errorLocations = self.findErrorLocations(sigma)?;
let errorMagnitudes = self.findErrorMagnitudes(omega, &errorLocations)?;
for (error_location, error_magnitude) in errorLocations.iter().zip(errorMagnitudes) {
let log_value = self.field.log(*error_location as i32)?;
if log_value > received.len() as i32 - 1 {
return Err(Exceptions::reed_solomon_with("Bad error location"));
}
let position: isize = received.len() as isize - 1 - log_value as isize;
if position < 0 {
return Err(Exceptions::reed_solomon_with("Bad error location"));
}
received[position as usize] =
GenericGF::addOrSubtract(received[position as usize], error_magnitude);
}
Ok(errorLocations.len())
}
fn runEuclideanAlgorithm(
&self,
a: &GenericGFPoly,
b: &GenericGFPoly,
R: usize,
) -> Result<Vec<GenericGFPoly>> {
let mut a = a.clone();
let mut b = b.clone();
if a.getDegree() < b.getDegree() {
std::mem::swap(&mut a, &mut b);
}
let mut rLast = a;
let mut r = b;
let mut tLast = rLast.getZero();
let mut t = rLast.getOne();
while 2 * r.getDegree() >= R {
let rLastLast = rLast;
let tLastLast = tLast;
rLast = r;
tLast = t;
if rLast.isZero() {
return Err(Exceptions::reed_solomon_with("r_{i-1} was zero"));
}
r = rLastLast;
let mut q = r.getZero();
let denominatorLeadingTerm = rLast.getCoefficient(rLast.getDegree());
let dltInverse = self.field.inverse(denominatorLeadingTerm)?;
while r.getDegree() >= rLast.getDegree() && !r.isZero() {
let degreeDiff = r.getDegree() - rLast.getDegree();
let scale = self
.field
.multiply(r.getCoefficient(r.getDegree()), dltInverse);
q = q.addOrSubtract(&GenericGF::buildMonomial(self.field, degreeDiff, scale))?;
r = r.addOrSubtract(&rLast.multiply_by_monomial(degreeDiff, scale)?)?;
}
t = (q.multiply(&tLast)?).addOrSubtract(&tLastLast)?;
if r.getDegree() >= rLast.getDegree() {
return Err(Exceptions::reed_solomon_with(format!(
"Division algorithm failed to reduce polynomial? r: {r}, rLast: {rLast}"
)));
}
}
let sigmaTildeAtZero = t.getCoefficient(0);
if sigmaTildeAtZero == 0 {
return Err(Exceptions::reed_solomon_with("sigmaTilde(0) was zero"));
}
let Ok(inverse) = self.field.inverse(sigmaTildeAtZero) else {
return Err(Exceptions::reed_solomon_with("ArithmetricException"));
};
let sigma = t.multiply_with_scalar(inverse);
let omega = r.multiply_with_scalar(inverse);
Ok(vec![sigma, omega])
}
fn findErrorLocations(&self, errorLocator: &GenericGFPoly) -> Result<Vec<usize>> {
let numErrors = errorLocator.getDegree();
if numErrors == 1 {
return Ok(vec![errorLocator.getCoefficient(1) as usize]);
}
let mut result: Vec<usize> = vec![0; numErrors];
let mut e = 0;
for i in 1..self.field.getSize() {
if e >= numErrors {
break;
}
if errorLocator.evaluateAt(i) == 0 {
result[e] = self.field.inverse(i as i32)? as usize;
e += 1;
}
}
if e != numErrors {
return Err(Exceptions::reed_solomon_with(
"Error locator degree does not match number of roots",
));
}
Ok(result)
}
fn findErrorMagnitudes(
&self,
errorEvaluator: &GenericGFPoly,
errorLocations: &[usize],
) -> Result<Vec<i32>> {
let s = errorLocations.len();
let mut result = vec![0; s];
for i in 0..s {
let xiInverse = self.field.inverse(errorLocations[i] as i32)?;
let mut denominator = 1;
for (j, loc) in errorLocations.iter().enumerate().take(s) {
if i != j {
let term = self.field.multiply(*loc as i32, xiInverse);
let termPlus1 = if (term & 0x1) == 0 {
term | 1
} else {
term & !1
};
denominator = self.field.multiply(denominator, termPlus1);
}
}
result[i] = self.field.multiply(
errorEvaluator.evaluateAt(xiInverse as usize),
self.field.inverse(denominator)?,
);
if self.field.getGeneratorBase() != 0 {
result[i] = self.field.multiply(result[i], xiInverse);
}
}
Ok(result)
}
}