pub const PRIM: u16 = 0x11D;
#[allow(dead_code)]
pub const GENERATOR: u8 = 2;
pub const EXP: [u8; 512] = build_exp();
pub const LOG: [u8; 256] = build_log(&EXP);
const fn build_exp() -> [u8; 512] {
let mut exp = [0u8; 512];
let mut x: u16 = 1;
let mut i = 0;
while i < 255 {
exp[i] = x as u8;
x <<= 1;
if x & 0x100 != 0 {
x ^= PRIM;
}
i += 1;
}
let mut j = 255;
while j < 512 {
exp[j] = exp[j - 255];
j += 1;
}
exp
}
const fn build_log(exp: &[u8; 512]) -> [u8; 256] {
let mut log = [0u8; 256];
let mut i = 0;
while i < 255 {
log[exp[i] as usize] = i as u8;
i += 1;
}
log
}
#[inline]
#[allow(dead_code)]
pub fn add(a: u8, b: u8) -> u8 {
a ^ b
}
#[inline]
pub fn mul(a: u8, b: u8) -> u8 {
if a == 0 || b == 0 {
return 0;
}
EXP[LOG[a as usize] as usize + LOG[b as usize] as usize]
}
#[inline]
pub fn div(a: u8, b: u8) -> u8 {
debug_assert!(b != 0, "GF(256) division by zero");
if a == 0 {
return 0;
}
let diff = LOG[a as usize] as i16 - LOG[b as usize] as i16 + 255;
EXP[diff as usize]
}
#[inline]
#[allow(dead_code)]
pub fn inv(a: u8) -> u8 {
debug_assert!(a != 0, "GF(256) inverse of zero");
EXP[255 - LOG[a as usize] as usize]
}
#[allow(dead_code)]
pub fn pow(a: u8, n: u32) -> u8 {
if a == 0 {
return if n == 0 { 1 } else { 0 };
}
let exp_idx = ((LOG[a as usize] as u32) * n) % 255;
EXP[exp_idx as usize]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn alpha_powers_basic() {
assert_eq!(EXP[0], 1);
assert_eq!(EXP[1], 2);
assert_eq!(EXP[2], 4);
assert_eq!(EXP[7], 128);
assert_eq!(EXP[8], 29);
}
#[test]
fn log_is_inverse_of_exp() {
for i in 0..255 {
assert_eq!(LOG[EXP[i] as usize] as usize, i);
}
}
#[test]
fn exp_wraps_at_255() {
for i in 0..255 {
assert_eq!(EXP[i + 255], EXP[i]);
}
}
#[test]
fn mul_zero_short_circuits() {
for a in 0u8..=255 {
assert_eq!(mul(0, a), 0);
assert_eq!(mul(a, 0), 0);
}
}
#[test]
fn mul_one_is_identity() {
for a in 0u8..=255 {
assert_eq!(mul(1, a), a);
assert_eq!(mul(a, 1), a);
}
}
#[test]
fn mul_commutative_and_associative() {
for &(a, b, c) in &[(2u8, 3, 5), (17, 200, 33), (0xAB, 0xCD, 0xEF)] {
assert_eq!(mul(a, b), mul(b, a));
assert_eq!(mul(mul(a, b), c), mul(a, mul(b, c)));
}
}
#[test]
fn mul_distributive_over_add() {
for &(a, b, c) in &[(2u8, 3, 5), (17, 200, 33), (0xAB, 0xCD, 0xEF)] {
assert_eq!(mul(a, add(b, c)), add(mul(a, b), mul(a, c)));
}
}
#[test]
fn div_is_inverse_of_mul() {
for a in 1u8..=255 {
for b in 1u8..=255 {
let p = mul(a, b);
assert_eq!(div(p, b), a);
assert_eq!(div(p, a), b);
}
}
}
#[test]
fn inv_times_self_is_one() {
for a in 1u8..=255 {
assert_eq!(mul(a, inv(a)), 1);
}
}
#[test]
fn pow_basic_cases() {
assert_eq!(pow(0, 0), 1);
assert_eq!(pow(0, 5), 0);
assert_eq!(pow(7, 0), 1);
assert_eq!(pow(7, 1), 7);
for k in 0u32..255 {
assert_eq!(pow(2, k), EXP[k as usize]);
}
for a in 1u8..=255 {
assert_eq!(pow(a, 255), 1);
}
}
}