pub const MOD: u32 = 929;
const ALPHA: u32 = 3;
#[inline]
fn add(a: u32, b: u32) -> u32 {
(a + b) % MOD
}
#[inline]
fn sub(a: u32, b: u32) -> u32 {
(a + MOD - b % MOD) % MOD
}
#[inline]
fn mul(a: u32, b: u32) -> u32 {
(a * b) % MOD
}
fn pow(base: u32, mut exp: u32) -> u32 {
let mut acc = 1u32;
let mut b = base % MOD;
while exp > 0 {
if exp & 1 == 1 {
acc = mul(acc, b);
}
b = mul(b, b);
exp >>= 1;
}
acc
}
fn inv(a: u32) -> u32 {
debug_assert!(!a.is_multiple_of(MOD), "inverse of zero in GF(929)");
pow(a, MOD - 2)
}
#[inline]
fn exp(n: u32) -> u32 {
pow(ALPHA, n)
}
pub fn generator(k: usize) -> Vec<u32> {
let mut g = vec![1u32];
for i in 1..=k as u32 {
let root = exp(i);
let mut next = vec![0u32; g.len() + 1];
for (j, &c) in g.iter().enumerate() {
next[j] = add(next[j], mul(c, sub(0, root)));
next[j + 1] = add(next[j + 1], c);
}
g = next;
}
g
}
pub fn encode(data: &[u32], k: usize) -> Vec<u32> {
let g = generator(k);
let mut e = vec![0u32; k];
for &d in data {
let t = add(d % MOD, e[k - 1]);
let mut next = vec![0u32; k];
for j in (1..k).rev() {
next[j] = sub(e[j - 1], mul(t, g[j]));
}
next[0] = sub(0, mul(t, g[0]));
e = next;
}
let mut out = Vec::with_capacity(k);
for j in (0..k).rev() {
out.push(if e[j] == 0 { 0 } else { MOD - e[j] });
}
out
}
pub fn decode(received: &[u32], k: usize) -> Option<Vec<u32>> {
let n = received.len();
let mut syndromes = vec![0u32; k];
let mut all_zero = true;
for (idx, syn) in syndromes.iter_mut().enumerate() {
let root = exp((idx + 1) as u32);
let mut s = 0u32;
for &c in received {
s = add(mul(s, root), c % MOD);
}
*syn = s;
if s != 0 {
all_zero = false;
}
}
if all_zero {
return Some(received.to_vec());
}
let mut sigma = vec![1u32];
let mut prev = vec![1u32];
let mut l = 0usize;
let mut m = 1usize;
let mut b = 1u32;
for i in 0..k {
let mut delta = syndromes[i];
for j in 1..=l {
if j < sigma.len() {
delta = add(delta, mul(sigma[j], syndromes[i - j]));
}
}
if delta == 0 {
m += 1;
} else if 2 * l <= i {
let t = sigma.clone();
sub_shift(&mut sigma, &prev, mul(delta, inv(b)), m);
l = i + 1 - l;
prev = t;
b = delta;
m = 1;
} else {
sub_shift(&mut sigma, &prev, mul(delta, inv(b)), m);
m += 1;
}
}
while sigma.len() > 1 && *sigma.last().unwrap() == 0 {
sigma.pop();
}
let num_errors = sigma.len() - 1;
if num_errors == 0 || num_errors > k / 2 {
return None;
}
let mut positions = Vec::new();
for p in 0..n {
let x_inv = exp(((MOD - 1) - (p as u32 % (MOD - 1))) % (MOD - 1));
let mut val = 0u32;
let mut pw = 1u32;
for &coef in &sigma {
val = add(val, mul(coef, pw));
pw = mul(pw, x_inv);
}
if val == 0 {
positions.push(n - 1 - p);
}
}
if positions.len() != num_errors {
return None;
}
let mut omega = vec![0u32; k];
for (i, &s) in syndromes.iter().enumerate() {
for (j, &sg) in sigma.iter().enumerate() {
if i + j < k {
omega[i + j] = add(omega[i + j], mul(s, sg));
}
}
}
let mut magnitudes = Vec::with_capacity(positions.len());
for &pos in &positions {
let p = (n - 1 - pos) as u32;
let x_inv = inv(exp(p));
let mut omega_val = 0u32;
let mut pw = 1u32;
for &c in &omega {
omega_val = add(omega_val, mul(c, pw));
pw = mul(pw, x_inv);
}
let mut deriv = 0u32;
let mut pw = 1u32;
for (j, &coef) in sigma.iter().enumerate().skip(1) {
deriv = add(deriv, mul(mul((j as u32) % MOD, coef), pw));
pw = mul(pw, x_inv);
}
if deriv == 0 {
return None;
}
magnitudes.push(mul(omega_val, inv(deriv)));
}
for &sign_add in &[false, true] {
let mut corrected = received.to_vec();
for (&pos, &mag) in positions.iter().zip(&magnitudes) {
corrected[pos] = if sign_add {
add(corrected[pos], mag)
} else {
sub(corrected[pos], mag)
};
}
if syndromes_vanish(&corrected, k) {
return Some(corrected);
}
}
None
}
fn syndromes_vanish(block: &[u32], k: usize) -> bool {
for idx in 0..k {
let root = exp((idx + 1) as u32);
let mut s = 0u32;
for &c in block {
s = add(mul(s, root), c);
}
if s != 0 {
return false;
}
}
true
}
fn sub_shift(dst: &mut Vec<u32>, src: &[u32], scale: u32, shift: usize) {
let needed = src.len() + shift;
if dst.len() < needed {
dst.resize(needed, 0);
}
for (i, &s) in src.iter().enumerate() {
dst[i + shift] = sub(dst[i + shift], mul(scale, s));
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn generator_matches_iso_tables() {
let k2 = generator(2);
assert_eq!(&k2[..2], &[27, 917]);
let k4 = generator(4);
assert_eq!(&k4[..4], &[522, 568, 723, 809]);
let k8 = generator(8);
assert_eq!(&k8[..8], &[237, 308, 436, 284, 646, 653, 428, 379]);
assert_eq!(*k2.last().unwrap(), 1);
assert_eq!(*k8.last().unwrap(), 1);
}
#[test]
fn rs_roundtrip_clean() {
let data = [7u32, 100, 200, 300, 400, 500, 600];
let ec = encode(&data, 8);
let mut block = data.to_vec();
block.extend_from_slice(&ec);
assert_eq!(decode(&block, 8).as_deref(), Some(block.as_slice()));
}
#[test]
fn rs_corrects_errors() {
let data = [7u32, 100, 200, 300, 400, 500, 600, 111, 222, 333];
let ec = encode(&data, 16); let mut block = data.to_vec();
block.extend_from_slice(&ec);
let clean = block.clone();
block[0] = (block[0] + 5) % MOD;
block[2] = (block[2] + 900) % MOD;
block[5] = 0;
block[7] = 928;
block[9] = (block[9] + 1) % MOD;
block[11] = (block[11] + 400) % MOD;
block[14] = (block[14] + 77) % MOD;
block[17] = (block[17] + 200) % MOD;
assert_eq!(decode(&block, 16).as_deref(), Some(clean.as_slice()));
}
#[test]
fn rs_detects_uncorrectable() {
let data = [1u32, 2, 3, 4];
let ec = encode(&data, 4); let mut block = data.to_vec();
block.extend_from_slice(&ec);
block[0] = (block[0] + 1) % MOD;
block[1] = (block[1] + 1) % MOD;
block[2] = (block[2] + 1) % MOD;
assert_eq!(decode(&block, 4), None);
}
}