#![allow(dead_code)]
#[derive(Debug, Clone, Copy)]
pub(crate) struct GfParams {
pub size: u32,
pub poly: u32,
}
impl GfParams {
pub fn k(&self) -> u32 {
self.size.trailing_zeros()
}
}
pub(crate) const GF16: GfParams = GfParams { size: 16, poly: 19 };
pub(crate) const GF64: GfParams = GfParams { size: 64, poly: 67 };
pub(crate) const GF256: GfParams = GfParams {
size: 256,
poly: 301,
};
pub(crate) const GF1024: GfParams = GfParams {
size: 1024,
poly: 1033,
};
pub(crate) const GF4096: GfParams = GfParams {
size: 4096,
poly: 4201,
};
pub(crate) fn gf_for_bps(bps: u8) -> Option<GfParams> {
match bps {
4 => Some(GF16),
6 => Some(GF64),
8 => Some(GF256),
10 => Some(GF1024),
12 => Some(GF4096),
_ => None,
}
}
pub(crate) fn multiply(a: u32, b: u32, gf: GfParams) -> u32 {
let mask = gf.size - 1; let high_bit = gf.size; debug_assert!(a <= mask);
debug_assert!(b <= mask);
let mut prod: u32 = 0;
let mut a = a;
let mut b = b;
while b != 0 {
if b & 1 != 0 {
prod ^= a;
}
a <<= 1;
if a & high_bit != 0 {
a ^= gf.poly;
}
b >>= 1;
}
prod & mask
}
pub(crate) fn generator_poly(k: usize, gf: GfParams) -> Vec<u32> {
let mask = gf.size - 1;
let mut poly = vec![0u32; k + 1];
poly[0] = 1;
let mut alpha: u32 = 1;
for _root in 1..=k {
alpha <<= 1;
if alpha & gf.size != 0 {
alpha ^= gf.poly;
}
alpha &= mask;
for j in (1..=k).rev() {
poly[j] = poly[j - 1] ^ multiply(poly[j], alpha, gf);
}
poly[0] = multiply(poly[0], alpha, gf);
}
poly
}
pub(crate) fn encode_k(data: &[u32], k: usize, gf: GfParams) -> Vec<u32> {
assert!(k > 0);
let gen = generator_poly(k, gf);
let n = data.len();
let mut codes = vec![0u32; n + k];
for (i, &d) in data.iter().enumerate() {
codes[n + k - 1 - i] = d;
}
for pos in (0..=codes.len() - k - 1).rev() {
let pivot = codes[pos + k];
for j in 0..=k {
codes[pos + j] ^= multiply(gen[j], pivot, gf);
}
}
codes[..k].to_vec()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn gf_for_bps_dispatch() {
assert_eq!(gf_for_bps(6).unwrap().size, 64);
assert_eq!(gf_for_bps(6).unwrap().poly, 67);
assert_eq!(gf_for_bps(8).unwrap().size, 256);
assert_eq!(gf_for_bps(10).unwrap().size, 1024);
assert_eq!(gf_for_bps(12).unwrap().size, 4096);
assert!(gf_for_bps(7).is_none());
}
#[test]
fn gf_for_bps_pins_every_arm_including_bps4() {
let g4 = gf_for_bps(4).expect("bps=4 must dispatch to GF16");
assert_eq!(g4.size, 16, "GF16 size");
assert_eq!(g4.poly, 19, "GF16 poly x^4+x+1 = 0b10011 = 19");
assert_eq!(gf_for_bps(8).unwrap().poly, 301, "GF256 BWIPP poly = 301");
assert_eq!(
gf_for_bps(10).unwrap().poly,
1033,
"GF1024 BWIPP poly = 1033"
);
assert_eq!(
gf_for_bps(12).unwrap().poly,
4201,
"GF4096 BWIPP poly = 4201"
);
assert!(gf_for_bps(0).is_none(), "bps=0 → None");
assert!(gf_for_bps(5).is_none(), "bps=5 between valid arms → None");
assert!(gf_for_bps(9).is_none(), "bps=9 between valid arms → None");
assert!(gf_for_bps(11).is_none(), "bps=11 between valid arms → None");
assert!(gf_for_bps(16).is_none(), "bps=16 above valid arms → None");
}
#[test]
fn multiply_gf64_matches_existing_rs_gf64() {
for a in 0u32..64 {
for b in 0u32..64 {
let want = u32::from(crate::util::rs_gf64::multiply(a as u8, b as u8));
let got = multiply(a, b, GF64);
assert_eq!(got, want, "GF64({a} × {b}) mismatch");
}
}
}
#[test]
fn generator_poly_gf64_k4_matches_auspost() {
let g = generator_poly(4, GF64);
assert_eq!(g, vec![48, 17, 29, 30, 1]);
}
#[test]
fn generator_poly_gf256_k4() {
let g = generator_poly(4, GF256);
assert_eq!(g.len(), 5);
assert_eq!(g[4], 1); }
#[test]
fn encode_k_round_trips_against_legacy_rs_gf64() {
let data = [4u8, 17, 9, 5, 26, 9, 43];
let data_u32: Vec<u32> = data.iter().map(|&v| u32::from(v)).collect();
let got = encode_k(&data_u32, 4, GF64);
let want = crate::util::rs_gf64::encode_4(&data);
let want_u32: Vec<u32> = want.iter().map(|&v| u32::from(v)).collect();
assert_eq!(got, want_u32);
}
#[test]
fn gf_for_bps_supported_widths() {
let cases: &[(u8, u32)] = &[(4, 16), (6, 64), (8, 256), (10, 1024), (12, 4096)];
for &(bps, expected_size) in cases {
let gf = gf_for_bps(bps).unwrap_or_else(|| panic!("gf_for_bps({bps}) returned None"));
assert_eq!(gf.size, expected_size, "bps={bps} size");
}
for bps in [0u8, 1, 3, 5, 7, 9, 11, 13, 255] {
assert!(gf_for_bps(bps).is_none(), "bps={bps} should be None");
}
}
#[test]
fn multiply_gf2k_identity_and_zero() {
for gf in [GF16, GF64, GF256] {
assert_eq!(multiply(0, 1, gf), 0);
assert_eq!(multiply(1, 0, gf), 0);
assert_eq!(multiply(0, 0, gf), 0);
assert_eq!(multiply(1, 1, gf), 1);
let max = gf.size - 1;
assert_eq!(multiply(1, max, gf), max);
assert_eq!(multiply(max, 1, gf), max);
assert_eq!(multiply(3, 5, gf), multiply(5, 3, gf));
assert_eq!(multiply(13, 41 & max, gf), multiply(41 & max, 13, gf));
}
}
}