const fn build_tables() -> ([u8; 512], [u8; 256]) {
let mut exp = [0u8; 512];
let mut log = [0u8; 256];
let mut x: u16 = 1;
let mut i = 0usize;
while i < 255 {
exp[i] = x as u8;
exp[i + 255] = x as u8; log[x as usize] = i as u8;
x <<= 1;
if x & 0x100 != 0 {
x ^= 0x11D;
}
i += 1;
}
(exp, log)
}
const TABLES: ([u8; 512], [u8; 256]) = build_tables();
pub(crate) const GF_EXP: &[u8; 512] = &TABLES.0;
pub(crate) const GF_LOG: &[u8; 256] = &TABLES.1;
#[inline(always)]
pub fn gf_add(a: u8, b: u8) -> u8 {
a ^ b
}
#[inline]
pub fn gf_mul(a: u8, b: u8) -> u8 {
if a == 0 || b == 0 {
return 0;
}
GF_EXP[GF_LOG[a as usize] as usize + GF_LOG[b as usize] as usize]
}
#[inline]
pub fn gf_inv(a: u8) -> u8 {
debug_assert!(a != 0, "gf_inv(0) is undefined");
GF_EXP[255 - GF_LOG[a as usize] as usize]
}
#[inline]
pub fn gf_div(a: u8, b: u8) -> u8 {
if a == 0 {
return 0;
}
gf_mul(a, gf_inv(b))
}
pub fn gf_poly_eval(p: &[u8], x: u8) -> u8 {
let mut acc = 0u8;
for &coeff in p.iter().rev() {
acc = gf_add(gf_mul(acc, x), coeff);
}
acc
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn exp_log_are_inverses() {
for x in 1u8..=255 {
assert_eq!(GF_EXP[GF_LOG[x as usize] as usize], x, "x={x}");
}
}
#[test]
fn generator_order_is_255() {
assert_eq!(GF_EXP[255], 1);
for i in 1..255usize {
assert_ne!(GF_EXP[i], 1, "α^{i} == 1 but order should be 255");
}
}
#[test]
fn mul_commutativity() {
for a in 0u8..=255 {
for b in 0u8..=255 {
assert_eq!(gf_mul(a, b), gf_mul(b, a), "a={a} b={b}");
}
}
}
#[test]
fn mul_identity_and_zero() {
for x in 0u8..=255 {
assert_eq!(gf_mul(1, x), x);
assert_eq!(gf_mul(0, x), 0);
assert_eq!(gf_mul(x, 0), 0);
}
}
#[test]
fn mul_inv_roundtrip() {
for x in 1u8..=255 {
assert_eq!(gf_mul(x, gf_inv(x)), 1, "x={x}");
}
}
#[test]
fn known_values() {
assert_eq!(GF_EXP[1], 0x02);
assert_eq!(GF_EXP[8], 0x1D);
assert_eq!(GF_EXP[255], 0x01);
}
#[test]
fn distributivity() {
let triples = [(0x53u8, 0xCAu8, 0x7Fu8), (0x01, 0xFF, 0xAB), (0x80, 0x80, 0x01)];
for (a, b, c) in triples {
let lhs = gf_mul(a, gf_add(b, c));
let rhs = gf_add(gf_mul(a, b), gf_mul(a, c));
assert_eq!(lhs, rhs, "a={a:#04x} b={b:#04x} c={c:#04x}");
}
}
}