use crate::gf::{gf_add, gf_mul, gf_div, gf_inv, gf_poly_eval, GF_EXP};
pub(crate) fn rs_generator(delta: usize) -> Vec<u8> {
let nk = 2 * delta;
let mut g = vec![0u8; nk + 1];
g[0] = 1; let mut deg = 0usize;
for i in 1..=nk {
let root = GF_EXP[i]; deg += 1;
for j in (1..=deg).rev() {
g[j] = gf_add(g[j - 1], gf_mul(root, g[j]));
}
g[0] = gf_mul(root, g[0]);
}
debug_assert_eq!(g[nk], 1, "generator must be monic");
g
}
pub fn rs_encode(msg: &[u8], codeword: &mut [u8], delta: usize) {
let n1 = codeword.len();
let k = msg.len();
let nk = 2 * delta;
debug_assert_eq!(k + nk, n1, "k + 2δ must equal n1");
let g = rs_generator(delta);
let mut d = vec![0u8; n1];
for t in 0..k {
d[nk + t] = msg[t];
}
for i in (nk..n1).rev() {
let c = d[i];
for j in 0..=nk {
d[i - nk + j] = gf_add(d[i - nk + j], gf_mul(c, g[j]));
}
}
codeword[..nk].copy_from_slice(&d[..nk]); codeword[nk..].copy_from_slice(msg); }
fn syndromes(received: &[u8], delta: usize) -> (Vec<u8>, bool) {
let mut s = vec![0u8; 2 * delta];
let mut has_error = false;
for j in 0..2 * delta {
s[j] = gf_poly_eval(received, GF_EXP[j + 1]);
if s[j] != 0 {
has_error = true;
}
}
(s, has_error)
}
fn berlekamp_massey(s: &[u8]) -> Vec<u8> {
let n = s.len(); let mut sigma = vec![0u8; n + 1];
sigma[0] = 1;
let mut prev = vec![0u8; n + 1]; prev[0] = 1;
let mut l: usize = 0; let mut m: usize = 1; let mut b: u8 = 1;
for i in 0..n {
let mut delta = s[i];
for j in 1..=l {
delta = gf_add(delta, gf_mul(sigma[j], s[i - j]));
}
if delta == 0 {
m += 1;
} else if 2 * l <= i {
let coeff = gf_div(delta, b);
let t = sigma.clone();
for j in m..=i + 1 {
sigma[j] = gf_add(sigma[j], gf_mul(coeff, prev[j - m]));
}
prev = t;
l = i + 1 - l;
b = delta;
m = 1;
} else {
let coeff = gf_div(delta, b);
for j in m..=i + 1 {
sigma[j] = gf_add(sigma[j], gf_mul(coeff, prev[j - m]));
}
m += 1;
}
}
sigma.truncate(l + 1);
sigma
}
fn chien_search(sigma: &[u8], n1: usize) -> Vec<(usize, u8)> {
let mut roots = Vec::with_capacity(sigma.len().saturating_sub(1));
for i in 0..n1 {
let xi = if i == 0 { GF_EXP[0] } else { GF_EXP[255 - i] };
if gf_poly_eval(sigma, xi) == 0 {
roots.push((i, xi));
}
}
roots
}
fn forney(sigma: &[u8], synd: &[u8], roots: &[(usize, u8)]) -> Vec<u8> {
let two_delta = synd.len();
let mut omega = vec![0u8; two_delta];
for i in 0..synd.len() {
for j in 0..sigma.len() {
if i + j < two_delta {
omega[i + j] = gf_add(omega[i + j], gf_mul(synd[i], sigma[j]));
}
}
}
let mut sigma_prime = vec![0u8; sigma.len()];
for j in (1..sigma.len()).step_by(2) {
sigma_prime[j - 1] = sigma[j];
}
roots
.iter()
.map(|&(_pos, xi)| {
let omega_val = gf_poly_eval(&omega, xi);
let sigma_val = gf_poly_eval(&sigma_prime, xi);
gf_div(omega_val, sigma_val)
})
.collect()
}
pub fn rs_decode(received: &[u8], k: usize, delta: usize) -> Option<Vec<u8>> {
let n1 = received.len();
let nk = 2 * delta;
debug_assert_eq!(k + nk, n1);
let (s, has_error) = syndromes(received, delta);
if !has_error {
return Some(received[nk..].to_vec());
}
let sigma = berlekamp_massey(&s);
let num_errors = sigma.len() - 1;
if num_errors == 0 || num_errors > delta {
return None;
}
let roots = chien_search(&sigma, n1);
if roots.len() != num_errors {
return None;
}
let error_vals = forney(&sigma, &s, &roots);
let mut corrected = received.to_vec();
for (&(pos, _xi), &e) in roots.iter().zip(error_vals.iter()) {
corrected[pos] = gf_add(corrected[pos], e);
}
let (_s2, still_err) = syndromes(&corrected, delta);
if still_err {
return None;
}
Some(corrected[nk..].to_vec())
}
#[cfg(test)]
mod tests {
use super::*;
const S1: (usize, usize, usize) = (46, 16, 15);
const S2: (usize, usize, usize) = (56, 24, 16);
const S3: (usize, usize, usize) = (90, 32, 29);
fn encode(msg: &[u8], n1: usize, delta: usize) -> Vec<u8> {
let mut cw = vec![0u8; n1];
rs_encode(msg, &mut cw, delta);
cw
}
fn inject_errors(cw: &mut [u8], errors: &[(usize, u8)]) {
for &(pos, val) in errors {
cw[pos] ^= val;
}
}
#[test]
fn generator_is_monic_and_correct_degree() {
for &(_, _, delta) in &[S1, S2, S3] {
let g = rs_generator(delta);
assert_eq!(g.len(), 2 * delta + 1);
assert_eq!(g[2 * delta], 1, "must be monic");
}
}
#[test]
fn generator_roots_are_alpha_powers() {
for &(_, _, delta) in &[S1, S2, S3] {
let g = rs_generator(delta);
for i in 1..=2 * delta {
assert_eq!(gf_poly_eval(&g, GF_EXP[i]), 0, "α^{i} should be a root");
}
}
}
#[test]
fn encode_message_in_high_positions() {
for &(n1, k, delta) in &[S1, S2, S3] {
let nk = 2 * delta;
let msg: Vec<u8> = (0..k).map(|i| (i * 7 + 1) as u8).collect();
let cw = encode(&msg, n1, delta);
assert_eq!(cw.len(), n1);
assert_eq!(&cw[nk..], msg.as_slice(), "message must occupy [nk, n1)");
}
}
#[test]
fn encode_zero_message_is_all_zero() {
let (n1, k, delta) = S1;
let cw = encode(&vec![0u8; k], n1, delta);
assert!(cw.iter().all(|&b| b == 0));
}
#[test]
fn valid_codeword_has_zero_syndromes() {
for &(n1, k, delta) in &[S1, S2, S3] {
let msg: Vec<u8> = (0..k).map(|i| (i * 17 + 3) as u8).collect();
let cw = encode(&msg, n1, delta);
let (s, has_err) = syndromes(&cw, delta);
assert!(!has_err, "delta={delta} syndromes nonzero: {s:?}");
}
}
#[test]
fn decode_no_errors() {
for &(n1, k, delta) in &[S1, S2, S3] {
let msg: Vec<u8> = (0..k).map(|i| (i ^ 0xAA) as u8).collect();
let cw = encode(&msg, n1, delta);
let recovered = rs_decode(&cw, k, delta).expect("decode failed");
assert_eq!(recovered, msg, "delta={delta}");
}
}
#[test]
fn decode_single_error() {
let (n1, k, delta) = S1;
let msg: Vec<u8> = (0..k as u8).collect();
let mut cw = encode(&msg, n1, delta);
inject_errors(&mut cw, &[(5, 0x3C)]);
let recovered = rs_decode(&cw, k, delta).expect("decode failed");
assert_eq!(recovered, msg);
}
#[test]
fn decode_at_capacity() {
for &(n1, k, delta) in &[S1, S2, S3] {
let msg: Vec<u8> = (0..k as u8).collect();
let mut cw = encode(&msg, n1, delta);
let errors: Vec<(usize, u8)> =
(0..delta).map(|i| (i, (i as u8).wrapping_add(1).wrapping_mul(7) | 1)).collect();
inject_errors(&mut cw, &errors);
let recovered = rs_decode(&cw, k, delta)
.unwrap_or_else(|| panic!("should correct {delta} errors"));
assert_eq!(recovered, msg, "delta={delta}");
}
}
#[test]
fn decode_errors_in_message_region() {
let (n1, k, delta) = S3;
let nk = 2 * delta;
let msg: Vec<u8> = (0..k as u8).map(|i| i.wrapping_mul(3)).collect();
let mut cw = encode(&msg, n1, delta);
let errors: Vec<(usize, u8)> = (0..delta).map(|i| (nk + (i % k), 0x5A | 1)).collect();
let mut seen = std::collections::HashSet::new();
let errors: Vec<(usize, u8)> = errors.into_iter().filter(|&(p, _)| seen.insert(p)).collect();
inject_errors(&mut cw, &errors);
let recovered = rs_decode(&cw, k, delta).expect("decode failed");
assert_eq!(recovered, msg);
}
#[test]
fn decode_beyond_capacity_returns_none() {
for &(n1, k, delta) in &[S1, S3] {
let msg: Vec<u8> = (0..k as u8).collect();
let mut cw = encode(&msg, n1, delta);
let errors: Vec<(usize, u8)> = (0..=delta).map(|i| (i, 0xFF)).collect();
inject_errors(&mut cw, &errors);
assert!(rs_decode(&cw, k, delta).is_none(),
"delta={delta}: δ+1 errors must be uncorrectable");
}
}
#[test]
fn chien_search_finds_correct_positions() {
let (n1, k, delta) = S1;
let msg: Vec<u8> = (0..k as u8).collect();
let cw = encode(&msg, n1, delta);
let mut received = cw.clone();
inject_errors(&mut received, &[(3, 0x11), (10, 0x22)]);
let (s, _) = syndromes(&received, delta);
let sigma = berlekamp_massey(&s);
let roots = chien_search(&sigma, n1);
let positions: Vec<usize> = roots.iter().map(|&(p, _)| p).collect();
assert!(positions.contains(&3), "should find position 3: {positions:?}");
assert!(positions.contains(&10), "should find position 10: {positions:?}");
assert_eq!(positions.len(), 2);
}
#[test]
fn berlekamp_massey_degree_equals_error_count() {
let (n1, k, delta) = S1;
for num_errors in 1..=delta {
let msg: Vec<u8> = (0..k as u8).collect();
let cw = encode(&msg, n1, delta);
let mut received = cw.clone();
let errors: Vec<(usize, u8)> =
(0..num_errors).map(|i| (i, (i as u8).wrapping_add(1) | 1)).collect();
inject_errors(&mut received, &errors);
let (s, _) = syndromes(&received, delta);
let sigma = berlekamp_massey(&s);
assert_eq!(sigma.len() - 1, num_errors,
"deg σ should equal error count {num_errors}");
}
}
}