pub struct GF256 {
exp: [u8; 256],
log: [u8; 256],
inv: [u8; 256],
}
impl GF256 {
const PRIM_POLY: u16 = 0x11D;
pub fn new() -> Self {
let mut exp = [0u8; 256];
let mut log = [0u8; 256];
let mut inv = [0u8; 256];
let mut value: u16 = 1;
for i in 0..255 {
exp[i] = value as u8;
log[value as usize] = i as u8;
value <<= 1;
if value & 0x100 != 0 {
value ^= Self::PRIM_POLY;
}
}
exp[255] = exp[0];
log[0] = 0xFF;
for i in 1..255 {
inv[exp[i] as usize] = exp[255 - i];
}
inv[0] = 0;
inv[1] = 1;
Self { exp, log, inv }
}
#[inline]
pub fn mul(&self, a: u8, b: u8) -> u8 {
if a == 0 || b == 0 {
return 0;
}
let log_sum = (self.log[a as usize] as usize) + (self.log[b as usize] as usize);
self.exp[log_sum % 255]
}
#[inline]
pub fn div(&self, a: u8, b: u8) -> u8 {
if a == 0 {
return 0;
}
if b == 0 {
panic!("GF(2^8) division by zero");
}
let log_diff = (self.log[a as usize] as isize) - (self.log[b as usize] as isize);
let idx = ((log_diff % 255) + 255) as usize % 255;
self.exp[idx]
}
#[inline]
pub fn inv(&self, a: u8) -> u8 {
self.inv[a as usize]
}
#[inline]
pub fn pow(&self, k: u8) -> u8 {
let k = k % 255;
self.exp[k as usize]
}
}
impl Default for GF256 {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_field_identity() {
let gf = GF256::new();
assert_eq!(gf.pow(0), 1);
assert_eq!(gf.pow(1), 2);
assert_eq!(gf.pow(255), 1);
}
#[test]
fn test_multiplication() {
let gf = GF256::new();
assert_eq!(gf.mul(1, 1), 1);
assert_eq!(gf.mul(2, 2), 4);
assert_eq!(gf.mul(3, 5), 15);
}
#[test]
fn test_inverse() {
let gf = GF256::new();
assert_eq!(gf.inv(1), 1);
assert_eq!(gf.mul(2, gf.inv(2)), 1);
for a in 1..=255 {
assert_eq!(gf.mul(a, gf.inv(a)), 1);
}
}
#[test]
fn test_division() {
let gf = GF256::new();
assert_eq!(gf.div(4, 2), 2);
for a in 1..=255 {
assert_eq!(gf.div(a, a), 1);
}
}
}