#![allow(dead_code)]
pub const P: u32 = 113;
pub const ORDER: usize = (P - 1) as usize;
pub fn build_tables() -> ([u8; ORDER], [u8; P as usize]) {
let mut antilog = [0u8; ORDER];
let mut log = [0u8; P as usize];
let mut acc: u32 = 1;
for (i, slot) in antilog.iter_mut().enumerate() {
*slot = acc as u8;
log[acc as usize] = i as u8;
acc = (acc * 3) % P;
}
(antilog, log)
}
pub fn generator_poly(nck: usize) -> Vec<u32> {
let (antilog, _log) = build_tables();
let mut g: Vec<u32> = vec![1];
for i in 1..=nck {
let root = antilog[i % ORDER] as u32;
let neg_root = (P - root) % P;
let mut new_g = vec![0u32; g.len() + 1];
for (j, &coef) in g.iter().enumerate() {
new_g[j + 1] = (new_g[j + 1] + coef) % P;
new_g[j] = (new_g[j] + coef * neg_root) % P;
}
g = new_g;
}
g.reverse();
g
}
pub fn encode(data: &[u32], nck: usize) -> Vec<u32> {
assert!(data.iter().all(|&d| d < P), "data codeword >= {P}");
let gen = generator_poly(nck);
let mut register = vec![0u32; nck];
for &d in data {
let leading = (d + register[0]) % P;
for i in 0..nck - 1 {
register[i] = register[i + 1];
}
register[nck - 1] = 0;
if leading != 0 {
for i in 0..nck {
let term = (leading * gen[i + 1]) % P;
register[i] = (register[i] + (P - term) % P) % P;
}
}
}
let mut out = data.to_vec();
out.extend_from_slice(®ister);
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn antilog_first_entries_match_bwipp_seed() {
let (antilog, _) = build_tables();
assert_eq!(antilog[0], 1);
assert_eq!(antilog[1], 3);
assert_eq!(antilog[2], 9);
assert_eq!(antilog[3], 27);
assert_eq!(antilog[4], 81);
assert_eq!(antilog[5], (81 * 3) % 113);
let mut seen = std::collections::HashSet::new();
for &v in &antilog {
assert!(seen.insert(v), "duplicate in antilog: {v}");
}
assert_eq!(seen.len(), ORDER);
}
#[test]
fn log_is_inverse_of_antilog() {
let (antilog, log) = build_tables();
for (i, &a) in antilog.iter().enumerate() {
assert_eq!(log[a as usize] as usize, i);
}
}
#[test]
fn generator_poly_degree_matches_nck() {
for nck in 1..8 {
let g = generator_poly(nck);
assert_eq!(g.len(), nck + 1);
assert_eq!(g[0], 1);
}
}
#[test]
fn encode_appends_nck_codewords() {
let data = [10u32, 20, 30, 40, 50];
let nck = 4;
let out = encode(&data, nck);
assert_eq!(out.len(), data.len() + nck);
assert_eq!(&out[..data.len()], &data);
}
#[test]
fn encode_is_deterministic() {
let data = [1u32, 2, 3, 4, 5];
let nck = 5;
let a = encode(&data, nck);
let b = encode(&data, nck);
assert_eq!(a, b);
}
#[test]
#[should_panic(expected = "data codeword >= 113")]
fn encode_panics_on_out_of_range_codeword() {
let data = [0u32, 113];
let _ = encode(&data, 2);
}
#[test]
fn build_tables_antilog_log_are_inverses() {
let (antilog, log) = build_tables();
assert_eq!(antilog[0], 1);
assert_eq!(log[1], 0);
assert_eq!(antilog[1], 3);
assert_eq!(log[3], 1);
assert_eq!(antilog[2], 9);
assert_eq!(log[9], 2);
for v in 1..=(P as u8 - 1) {
let l = log[v as usize];
let back = antilog[l as usize];
assert_eq!(back, v, "antilog[log[{v}]] = {back}, want {v}");
}
}
#[test]
fn generator_poly_shape() {
let g0 = generator_poly(0);
assert_eq!(g0, vec![1]);
let g1 = generator_poly(1);
assert_eq!(g1.len(), 2);
assert_eq!(g1[0], 1, "leading coeff must be 1");
let g4 = generator_poly(4);
assert_eq!(g4.len(), 5);
assert_eq!(g4[0], 1);
for &c in &g4 {
assert!(c < P, "coeff {c} >= P={}", P);
}
}
}