use std;
use collect_slice::CollectSlice;
use crate::coding::galois::{GaloisField, P25Codeword, P25Field, Polynomial, PolynomialCoefs};
pub struct ErrorLocator<P: PolynomialCoefs> {
p_saved: Polynomial<P>,
p_cur: Polynomial<P>,
q_saved: Polynomial<P>,
q_cur: Polynomial<P>,
deg_saved: usize,
deg_cur: usize,
}
impl<P: PolynomialCoefs> ErrorLocator<P> {
pub fn new(syn: Polynomial<P>) -> ErrorLocator<P> {
ErrorLocator {
q_saved: Polynomial::new(
std::iter::once(P25Codeword::for_power(0))
.chain(syn.iter().take(P::syndromes()).cloned()),
),
q_cur: syn,
p_saved: Polynomial::unit_power(P::syndromes() + 1),
p_cur: Polynomial::unit_power(P::syndromes()),
deg_saved: 0,
deg_cur: 1,
}
}
pub fn build(mut self) -> Polynomial<P> {
for _ in 0..P::syndromes() {
self.step();
}
self.p_cur
}
fn step(&mut self) {
let (save, q, p, d) = if self.q_cur.constant().zero() {
self.reduce()
} else {
self.transform()
};
if save {
self.q_saved = self.q_cur;
self.p_saved = self.p_cur;
self.deg_saved = self.deg_cur;
}
self.q_cur = q;
self.p_cur = p;
self.deg_cur = d;
}
fn reduce(&mut self) -> (bool, Polynomial<P>, Polynomial<P>, usize) {
(
false,
self.q_cur.shift(),
self.p_cur.shift(),
2 + self.deg_cur,
)
}
fn transform(&mut self) -> (bool, Polynomial<P>, Polynomial<P>, usize) {
let mult = self.q_cur.constant() / self.q_saved.constant();
(
self.deg_cur >= self.deg_saved,
(self.q_cur + self.q_saved * mult).shift(),
(self.p_cur + self.p_saved * mult).shift(),
2 + std::cmp::min(self.deg_cur, self.deg_saved),
)
}
}
pub struct PolynomialRoots<P: PolynomialCoefs> {
loc: Polynomial<P>,
pow: std::ops::Range<usize>,
}
impl<P: PolynomialCoefs> PolynomialRoots<P> {
pub fn new(loc: Polynomial<P>) -> Self {
PolynomialRoots {
loc,
pow: 0..P25Field::size(),
}
}
fn update_terms(&mut self) {
for (pow, term) in self.loc.iter_mut().enumerate() {
*term = *term * P25Codeword::for_power(pow);
}
}
fn eval(&self) -> P25Codeword {
self.loc
.iter()
.fold(P25Codeword::default(), |sum, &x| sum + x)
}
}
impl<P: PolynomialCoefs> Iterator for PolynomialRoots<P> {
type Item = P25Codeword;
fn next(&mut self) -> Option<Self::Item> {
loop {
let pow = match self.pow.next() {
Some(pow) => pow,
None => return None,
};
let eval = self.eval();
self.update_terms();
if eval.zero() {
return Some(P25Codeword::for_power(pow));
}
}
}
}
pub struct ErrorDescriptions<P: PolynomialCoefs> {
deriv: Polynomial<P>,
vals: Polynomial<P>,
}
impl<P: PolynomialCoefs> ErrorDescriptions<P> {
pub fn new(syn: Polynomial<P>, loc: Polynomial<P>) -> Self {
ErrorDescriptions {
deriv: loc.deriv(),
vals: (loc * syn).truncate(P::syndromes() - 1),
}
}
pub fn for_root(&self, root: P25Codeword) -> (usize, P25Codeword) {
(
root.invert().power().unwrap(),
self.vals.eval(root) / self.deriv.eval(root),
)
}
}
pub struct Errors<P: PolynomialCoefs> {
roots: Polynomial<P>,
descs: ErrorDescriptions<P>,
pos: std::ops::Range<usize>,
}
impl<P: PolynomialCoefs> Errors<P> {
pub fn new(syn: Polynomial<P>) -> Option<(usize, Self)> {
let loc = ErrorLocator::new(syn).build();
let errors = loc.degree().expect("invalid error polynomial");
let mut roots = Polynomial::<P>::default();
let nroots = PolynomialRoots::new(loc).collect_slice_exhaust(&mut roots[..]);
if nroots != errors {
return None;
}
Some((
errors,
Errors {
roots,
descs: ErrorDescriptions::new(syn, loc),
pos: 0..errors,
},
))
}
}
impl<P: PolynomialCoefs> Iterator for Errors<P> {
type Item = (usize, P25Codeword);
fn next(&mut self) -> Option<Self::Item> {
self.pos.next().map(|i| self.descs.for_root(self.roots[i]))
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::coding::galois::{P25Codeword, Polynomial, PolynomialCoefs};
use collect_slice::CollectSlice;
use std;
impl_polynomial_coefs!(TestCoefs, 9);
type TestPolynomial = Polynomial<TestCoefs>;
#[test]
fn test_roots() {
let p = TestPolynomial::new(
[P25Codeword::for_power(0), P25Codeword::for_power(42)]
.iter()
.cloned(),
) * TestPolynomial::new(
[P25Codeword::for_power(0), P25Codeword::for_power(13)]
.iter()
.cloned(),
) * TestPolynomial::new(
[P25Codeword::for_power(0), P25Codeword::for_power(57)]
.iter()
.cloned(),
);
let mut r = PolynomialRoots::new(p);
let mut roots = [P25Codeword::default(); 3];
r.collect_slice_checked(&mut roots[..]);
assert!(roots.contains(&P25Codeword::for_power(42).invert()));
assert!(roots.contains(&P25Codeword::for_power(13).invert()));
assert!(roots.contains(&P25Codeword::for_power(57).invert()));
let p = TestPolynomial::unit_power(0);
let mut r = PolynomialRoots::new(p);
assert!(r.next().is_none());
}
}