#[derive(Debug, Clone)]
pub struct GaloisField {
exp: Vec<u8>,
log: Vec<u8>,
order: usize,
}
impl GaloisField {
pub fn new(primitive: u16, size: usize) -> Self {
let order = size - 1;
let mut exp = vec![0u8; order];
let mut log = vec![0u8; size];
let mut x: u16 = 1;
for (i, e) in exp.iter_mut().enumerate() {
*e = x as u8;
log[x as usize] = i as u8;
x <<= 1;
if x & size as u16 != 0 {
x ^= primitive;
}
}
GaloisField { exp, log, order }
}
pub fn data() -> Self {
GaloisField::new(0x163, 256)
}
pub fn function() -> Self {
GaloisField::new(0x13, 16)
}
fn exp_of(&self, n: usize) -> u8 {
self.exp[n % self.order]
}
fn mul(&self, a: u8, b: u8) -> u8 {
if a == 0 || b == 0 {
return 0;
}
let idx = (self.log[a as usize] as usize + self.log[b as usize] as usize) % self.order;
self.exp[idx]
}
fn div(&self, a: u8, b: u8) -> u8 {
assert!(b != 0, "division by zero in GF");
if a == 0 {
return 0;
}
let idx = (self.log[a as usize] as usize + self.order - self.log[b as usize] as usize)
% self.order;
self.exp[idx]
}
fn inv(&self, a: u8) -> u8 {
self.div(1, a)
}
fn pow(&self, base: u8, exp: i32) -> u8 {
if base == 0 {
return 0;
}
let l = self.log[base as usize] as i64;
let o = self.order as i64;
let mut idx = (l * exp as i64) % o;
if idx < 0 {
idx += o;
}
self.exp[idx as usize]
}
fn poly_mul(&self, a: &[u8], b: &[u8]) -> Vec<u8> {
let mut out = vec![0u8; a.len() + b.len() - 1];
for (i, &av) in a.iter().enumerate() {
for (j, &bv) in b.iter().enumerate() {
out[i + j] ^= self.mul(av, bv);
}
}
out
}
fn generator(&self, nsym: usize, fcr: usize) -> Vec<u8> {
let mut g = vec![1u8];
for i in 0..nsym {
g = self.poly_mul(&g, &[1, self.exp_of(fcr + i)]);
}
g
}
pub fn rs_encode(&self, data: &[u8], nsym: usize, fcr: usize) -> Vec<u8> {
let gen_poly = self.generator(nsym, fcr);
let mut rem = vec![0u8; nsym];
for &d in data {
let factor = d ^ rem[0];
rem.remove(0);
rem.push(0);
if factor != 0 {
for (i, &g) in gen_poly.iter().enumerate().skip(1) {
rem[i - 1] ^= self.mul(g, factor);
}
}
}
rem
}
pub fn rs_decode(&self, received: &[u8], nsym: usize, fcr: usize) -> Option<Vec<u8>> {
let n = received.len();
let mut syn = vec![0u8; nsym];
let mut all_zero = true;
for (i, s) in syn.iter_mut().enumerate() {
let x = self.exp_of(fcr + i);
let mut acc = 0u8;
for &c in received {
acc = self.mul(acc, x) ^ c;
}
*s = acc;
if acc != 0 {
all_zero = false;
}
}
if all_zero {
return Some(received.to_vec());
}
let mut sigma = vec![1u8];
let mut prev = vec![1u8];
let mut l = 0usize;
let mut m = 1usize;
let mut b = 1u8;
for it in 0..nsym {
let mut delta = syn[it];
for i in 1..=l {
if i < sigma.len() {
delta ^= self.mul(sigma[i], syn[it - i]);
}
}
if delta == 0 {
m += 1;
} else if 2 * l <= it {
let t = sigma.clone();
self.sub_shift(&mut sigma, &prev, self.div(delta, b), m);
l = it + 1 - l;
prev = t;
b = delta;
m = 1;
} else {
self.sub_shift(&mut sigma, &prev, self.div(delta, b), m);
m += 1;
}
}
while sigma.len() > 1 && *sigma.last().unwrap() == 0 {
sigma.pop();
}
let num_errors = sigma.len() - 1;
if num_errors > nsym / 2 || num_errors == 0 {
return None;
}
let mut positions = Vec::new();
for j in 0..n {
let x_inv = self.exp_of((self.order - (j % self.order)) % self.order);
let mut val = 0u8;
let mut pw = 1u8;
for &c in &sigma {
val ^= self.mul(c, pw);
pw = self.mul(pw, x_inv);
}
if val == 0 {
positions.push(n - 1 - j);
}
}
if positions.len() != num_errors {
return None;
}
let mut omega = vec![0u8; nsym];
for (i, &s) in syn.iter().enumerate() {
for (k, &sg) in sigma.iter().enumerate() {
if i + k < nsym {
omega[i + k] ^= self.mul(s, sg);
}
}
}
let mut corrected = received.to_vec();
for &pos in &positions {
let p = n - 1 - pos;
let x = self.exp_of(p % self.order);
let x_inv = self.inv(x);
let mut omega_val = 0u8;
let mut pw = 1u8;
for &c in &omega {
omega_val ^= self.mul(c, pw);
pw = self.mul(pw, x_inv);
}
let mut deriv = 0u8;
let mut pw = 1u8;
for (k, &c) in sigma.iter().enumerate() {
if k % 2 == 1 {
deriv ^= self.mul(c, pw);
pw = self.mul(pw, self.mul(x_inv, x_inv));
}
}
if deriv == 0 {
return None;
}
let scale = self.pow(x, 1 - fcr as i32);
corrected[pos] ^= self.mul(scale, self.div(omega_val, deriv));
}
for i in 0..nsym {
let x = self.exp_of(fcr + i);
let mut acc = 0u8;
for &c in &corrected {
acc = self.mul(acc, x) ^ c;
}
if acc != 0 {
return None;
}
}
Some(corrected)
}
fn sub_shift(&self, dst: &mut Vec<u8>, src: &[u8], scale: u8, shift: usize) {
let needed = src.len() + shift;
if dst.len() < needed {
dst.resize(needed, 0);
}
for (i, &s) in src.iter().enumerate() {
dst[i + shift] ^= self.mul(scale, s);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn data_field_axioms() {
let gf = GaloisField::data();
assert_eq!(gf.mul(0, 5), 0);
assert_eq!(gf.mul(1, 5), 5);
for a in 1u8..=255 {
assert_eq!(gf.mul(a, gf.inv(a)), 1, "inverse of {a}");
}
assert_eq!(gf.exp_of(255), 1);
}
#[test]
fn function_field_axioms() {
let gf = GaloisField::function();
for a in 1u8..=15 {
assert_eq!(gf.mul(a, gf.inv(a)), 1, "inverse of {a}");
}
assert_eq!(gf.exp_of(15), 1);
}
#[test]
fn rs_roundtrip_clean() {
let gf = GaloisField::data();
let data = [0x11u8, 0xEC, 0x04, 0xFF, 0x40, 0x00, 0x00, 0x00, 0x00];
let ec = gf.rs_encode(&data, 16, 1);
let mut block = data.to_vec();
block.extend_from_slice(&ec);
assert_eq!(
gf.rs_decode(&block, 16, 1).as_deref(),
Some(block.as_slice())
);
}
#[test]
fn rs_corrects_errors() {
let gf = GaloisField::data();
let data = [0x11u8, 0xEC, 0x04, 0xFF, 0x40, 0x00, 0x00, 0x00, 0x00];
let ec = gf.rs_encode(&data, 16, 1);
let mut block = data.to_vec();
block.extend_from_slice(&ec);
let clean = block.clone();
block[0] ^= 0xFF;
block[4] ^= 0x0F;
block[9] ^= 0xA5;
block[14] ^= 0x33;
block[20] ^= 0x01;
assert_eq!(
gf.rs_decode(&block, 16, 1).as_deref(),
Some(clean.as_slice())
);
}
#[test]
fn function_rs_roundtrip() {
let gf = GaloisField::function();
let data = [0x2, 0x1, 0x0];
let ec = gf.rs_encode(&data, 4, 1);
assert_eq!(ec.len(), 4);
let mut block = data.to_vec();
block.extend_from_slice(&ec);
assert_eq!(
gf.rs_decode(&block, 4, 1).as_deref(),
Some(block.as_slice())
);
}
}