use crate::{
Exceptions,
common::Result,
pdf417::{decoder::ec::ModulusGF, pdf_417_common::NUMBER_OF_CODEWORDS},
};
use super::ModulusPoly;
use once_cell::sync::Lazy;
static FLD_INTERIOR: Lazy<ModulusGF> = Lazy::new(|| ModulusGF::new(NUMBER_OF_CODEWORDS, 3));
pub fn decode(received: &mut [u32], numECCodewords: u32, erasures: &mut [u32]) -> Result<usize> {
let field: &'static ModulusGF = &FLD_INTERIOR;
let poly = ModulusPoly::new(field, received.to_vec())?;
let mut S = vec![0u32; numECCodewords as usize];
let mut error = false;
for i in (1..=numECCodewords).rev() {
let eval = poly.evaluateAt(field.exp(i));
S[(numECCodewords - i) as usize] = eval;
if eval != 0 {
error = true;
}
}
if !error {
return Ok(0);
}
let mut knownErrors: ModulusPoly = ModulusPoly::getOne(field);
let mut b;
let mut term;
let mut kE: ModulusPoly;
if !erasures.is_empty() {
for erasure in erasures {
b = field.exp(received.len() as u32 - 1 - *erasure);
term = ModulusPoly::new(field, vec![field.subtract(0, b), 1])?;
kE = knownErrors.clone();
knownErrors = kE.multiply(term)?;
}
}
let syndrome = ModulusPoly::new(field, S)?;
let sigmaOmega = runEuclideanAlgorithm(
ModulusPoly::buildMonomial(field, numECCodewords as usize, 1),
syndrome,
numECCodewords,
field,
)?;
let sigma = sigmaOmega[0].clone();
let omega = sigmaOmega[1].clone();
let mut errorLocations = findErrorLocations(sigma.clone(), field)?;
let errorMagnitudes = findErrorMagnitudes(omega, sigma, &mut errorLocations, field);
for i in 0..errorLocations.len() {
let position = received.len() as isize - 1 - field.log(errorLocations[i])? as isize;
if position < 0 {
return Err(Exceptions::checksum_with(file!()));
}
received[position as usize] =
field.subtract(received[position as usize], errorMagnitudes[i]);
}
Ok(errorLocations.len())
}
fn runEuclideanAlgorithm(
a: ModulusPoly,
b: ModulusPoly,
R: u32,
field: &'static ModulusGF,
) -> Result<[ModulusPoly; 2]> {
let mut a = a;
let mut b = b;
if a.getDegree() < b.getDegree() {
std::mem::swap(&mut a, &mut b);
}
let mut rLast = a;
let mut r = b;
let mut tLast = ModulusPoly::getZero(field);
let mut t = ModulusPoly::getOne(field);
while r.getDegree() >= R / 2 {
let rLastLast = rLast.clone();
let tLastLast = tLast.clone();
rLast = r;
tLast = t;
if rLast.isZero() {
return Err(Exceptions::checksum_with(file!()));
}
r = rLastLast;
let mut q = ModulusPoly::getZero(field); let denominatorLeadingTerm = rLast.getCoefficient(rLast.getDegree() as usize);
let dltInverse = field.inverse(denominatorLeadingTerm)?;
while r.getDegree() >= rLast.getDegree() && !r.isZero() {
let degreeDiff = r.getDegree() - rLast.getDegree();
let scale = field.multiply(r.getCoefficient(r.getDegree() as usize), dltInverse);
q = q.add(ModulusPoly::buildMonomial(
field,
degreeDiff as usize,
scale,
))?;
r = r.subtract(rLast.multiplyByMonomial(degreeDiff as usize, scale))?;
}
t = q.multiply(tLast.clone())?.subtract(tLastLast)?.negative();
}
let sigmaTildeAtZero = t.getCoefficient(0);
if sigmaTildeAtZero == 0 {
return Err(Exceptions::checksum_with(file!()));
}
let inverse = field.inverse(sigmaTildeAtZero)?;
let sigma = t.multiplyByScaler(inverse);
let omega = r.multiplyByScaler(inverse);
Ok([sigma, omega])
}
fn findErrorLocations(errorLocator: ModulusPoly, field: &ModulusGF) -> Result<Vec<u32>> {
let numErrors = errorLocator.getDegree();
let mut result = vec![0u32; numErrors as usize];
let mut e = 0;
let mut i = 1;
while i < field.getSize() && e < numErrors {
if errorLocator.evaluateAt(i) == 0 {
result[e as usize] = field.inverse(i)?;
e += 1;
}
i += 1;
}
if e != numErrors {
return Err(Exceptions::checksum_with(file!()));
}
Ok(result)
}
fn findErrorMagnitudes(
errorEvaluator: ModulusPoly,
errorLocator: ModulusPoly,
errorLocations: &mut [u32],
field: &'static ModulusGF,
) -> Vec<u32> {
let errorLocatorDegree = errorLocator.getDegree();
if errorLocatorDegree < 1 {
return vec![0; 0];
}
let mut formalDerivativeCoefficients = vec![0u32; errorLocatorDegree as usize];
for i in 1..=errorLocatorDegree {
formalDerivativeCoefficients[errorLocatorDegree as usize - i as usize] =
field.multiply(i, errorLocator.getCoefficient(i as usize));
}
let formalDerivative =
ModulusPoly::new(field, formalDerivativeCoefficients).expect("should generate good poly");
let s = errorLocations.len();
let mut result = vec![0u32; s];
for i in 0..s {
let xiInverse = field.inverse(errorLocations[i]).expect("must invert");
let numerator = field.subtract(0, errorEvaluator.evaluateAt(xiInverse));
let denominator = field
.inverse(formalDerivative.evaluateAt(xiInverse))
.expect("must invert");
result[i] = field.multiply(numerator, denominator);
}
result
}