use std::sync::OnceLock;
const PRIMITIVE_POLY: u16 = 0x11D;
static SHARED: OnceLock<Gf256> = OnceLock::new();
#[derive(Debug, Clone)]
pub struct Gf256 {
exp: [u8; 512],
log: [u8; 256],
}
impl Default for Gf256 {
fn default() -> Self {
Self::new()
}
}
impl Gf256 {
pub fn new() -> Self {
let mut exp = [0u8; 512];
let mut log = [0u8; 256];
#[allow(clippy::needless_range_loop)]
{
let mut x: u16 = 1;
for i in 0..255 {
exp[i] = x as u8;
log[x as usize] = i as u8;
x <<= 1;
if x & 0x100 != 0 {
x ^= PRIMITIVE_POLY;
}
}
}
for i in 255..512 {
exp[i] = exp[i - 255];
}
Self { exp, log }
}
pub fn shared() -> &'static Gf256 {
SHARED.get_or_init(Gf256::new)
}
#[inline(always)]
pub fn add(&self, a: u8, b: u8) -> u8 {
a ^ b
}
#[inline(always)]
pub fn mul(&self, a: u8, b: u8) -> u8 {
if a == 0 || b == 0 {
0
} else {
self.exp[self.log[a as usize] as usize + self.log[b as usize] as usize]
}
}
#[inline]
pub fn div(&self, a: u8, b: u8) -> u8 {
assert!(b != 0, "GF(2^8) division by zero");
if a == 0 {
0
} else {
let idx = self.log[a as usize] as usize + 255 - self.log[b as usize] as usize;
self.exp[idx]
}
}
#[inline]
pub fn inv(&self, a: u8) -> u8 {
assert!(a != 0, "GF(2^8) inverse of zero");
self.exp[255 - self.log[a as usize] as usize]
}
#[inline]
pub fn pow(&self, a: u8, n: usize) -> u8 {
if a == 0 {
return if n == 0 { 1 } else { 0 };
}
let idx = (self.log[a as usize] as usize * n) % 255;
self.exp[idx]
}
#[inline]
pub fn exp_of(&self, i: usize) -> u8 {
self.exp[i % 255]
}
#[inline]
pub fn log_of(&self, a: u8) -> u8 {
assert!(a != 0, "GF(2^8) log of zero");
self.log[a as usize]
}
}