#[inline]
fn multiply(a: u16, b: u16) -> u16 {
((a as u32 * b as u32) % 929) as u16
}
pub(crate) fn generator_poly(k: usize) -> Vec<u16> {
let mut coeffs = vec![0u16; k + 1];
coeffs[0] = 1;
let mut alpha_pow: u16 = 1;
for j in 1..=k {
alpha_pow = multiply(alpha_pow, 3);
coeffs[j] = coeffs[j - 1];
for c in (1..j).rev() {
let v = multiply(coeffs[c], alpha_pow);
let diff = (929 + coeffs[c - 1] as u32 - v as u32) % 929;
coeffs[c] = diff as u16;
}
let v = multiply(coeffs[0], alpha_pow);
coeffs[0] = ((929 - v as u32) % 929) as u16;
}
coeffs
}
pub(crate) fn encode(data: &[u16], k: usize) -> Vec<u16> {
let coeffs = generator_poly(k);
let mut lfsr = vec![0u16; k];
for &d in data {
let feedback = ((929 + d as u32 - lfsr[0] as u32) % 929) as u16;
for l in 0..(k - 1) {
let v = multiply(coeffs[k - l - 1], feedback) as u32;
lfsr[l] = ((lfsr[l + 1] as u32 + v) % 929) as u16;
}
lfsr[k - 1] = multiply(coeffs[0], feedback);
}
lfsr
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn multiply_basic() {
assert_eq!(multiply(0, 5), 0);
assert_eq!(multiply(5, 0), 0);
assert_eq!(multiply(1, 928), 928);
assert_eq!(multiply(3, 3), 9);
assert_eq!(multiply(928, 928), 1);
}
#[test]
fn generator_poly_k8_matches_bwip_js() {
let g = generator_poly(8);
assert_eq!(g, [237, 308, 436, 284, 646, 653, 428, 379, 1]);
}
#[test]
fn encode_pdf417_canonical_vector() {
let data = [6u16, 453, 178, 121, 239, 900];
let check = encode(&data, 8);
assert_eq!(check, [466, 823, 655, 326, 814, 259, 528, 611]);
}
#[test]
fn encode_pdf417_url_vector_k16() {
let data = [
24u16, 817, 589, 468, 854, 589, 816, 259, 230, 59, 512, 432, 889, 137, 115, 13, 841,
79, 811, 668, 465, 886, 528, 900,
];
let check = encode(&data, 16);
assert_eq!(
check,
[506, 21, 681, 617, 210, 844, 772, 529, 305, 209, 43, 171, 133, 55, 229, 162]
);
}
#[test]
fn multiply_gf929_boundaries() {
assert_eq!(multiply(1, 5), 5);
assert_eq!(multiply(7, 1), 7);
assert_eq!(multiply(0, 100), 0);
assert_eq!(multiply(100, 0), 0);
assert_eq!(multiply(5, 7), 35);
assert_eq!(multiply(100, 100), 710);
assert_eq!(multiply(928, 928), 1);
assert_eq!(multiply(3, 3), 9);
assert_eq!(multiply(9, 3), 27);
}
#[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[1], 1, "leading coeff must be 1");
assert_eq!(g1[0], 926, "const term = -α mod 929");
let g2 = generator_poly(2);
assert_eq!(g2.len(), 3);
assert_eq!(g2[2], 1);
let g8 = generator_poly(8);
assert_eq!(g8.len(), 9);
assert_eq!(g8[8], 1);
}
}