#[derive(Debug, Clone)]
pub struct Gf {
exp: Vec<u16>,
log: Vec<u16>,
size: usize,
base: usize,
}
impl Gf {
pub fn new(size: usize, primitive: usize, base: usize) -> Gf {
let order = size - 1;
let mut exp = vec![0u16; order];
let mut log = vec![0u16; size];
let mut x = 1usize;
for (i, e) in exp.iter_mut().enumerate() {
*e = x as u16;
log[x] = i as u16;
x <<= 1;
if x & size != 0 {
x ^= primitive;
}
}
Gf {
exp,
log,
size,
base,
}
}
pub fn gf16() -> Gf {
Gf::new(16, 0b1_0011, 1)
}
pub fn for_word_size(word_size: usize) -> Option<Gf> {
Some(match word_size {
4 => Gf::gf16(),
6 => Gf::new(64, 0b100_0011, 1), 8 => Gf::new(256, 0b1_0010_1101, 1), 10 => Gf::new(1024, 0b100_0000_1001, 1), _ => return None,
})
}
fn order(&self) -> usize {
self.size - 1
}
pub fn base(&self) -> usize {
self.base
}
pub fn size(&self) -> usize {
self.size
}
pub fn exp(&self, n: i64) -> u16 {
let o = self.order() as i64;
let m = ((n % o) + o) % o;
self.exp[m as usize]
}
pub fn log(&self, a: u16) -> usize {
self.log[a as usize] as usize
}
pub fn mul(&self, a: u16, b: u16) -> u16 {
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]
}
pub fn inv(&self, a: u16) -> u16 {
debug_assert!(a != 0, "inverse of zero in GF");
self.exp((self.order() - self.log[a as usize] as usize) as i64)
}
}
fn add(a: u16, b: u16) -> u16 {
a ^ b
}
fn degree(p: &[u16]) -> usize {
p.len().saturating_sub(1)
}
fn normalize(mut p: Vec<u16>) -> Vec<u16> {
let mut lead = 0;
while lead + 1 < p.len() && p[lead] == 0 {
lead += 1;
}
if lead > 0 {
p.drain(0..lead);
}
p
}
fn is_zero(p: &[u16]) -> bool {
p.iter().all(|&c| c == 0)
}
fn eval(gf: &Gf, p: &[u16], x: u16) -> u16 {
let mut acc = 0u16;
for &c in p {
acc = add(gf.mul(acc, x), c);
}
acc
}
fn poly_mul(gf: &Gf, a: &[u16], b: &[u16]) -> Vec<u16> {
if is_zero(a) || is_zero(b) {
return vec![0];
}
let mut out = vec![0u16; a.len() + b.len() - 1];
for (i, &av) in a.iter().enumerate() {
if av == 0 {
continue;
}
for (j, &bv) in b.iter().enumerate() {
out[i + j] ^= gf.mul(av, bv);
}
}
normalize(out)
}
fn poly_scale(gf: &Gf, a: &[u16], s: u16) -> Vec<u16> {
normalize(a.iter().map(|&c| gf.mul(c, s)).collect())
}
fn poly_add(a: &[u16], b: &[u16]) -> Vec<u16> {
let (long, short) = if a.len() >= b.len() { (a, b) } else { (b, a) };
let mut out = long.to_vec();
let off = long.len() - short.len();
for (i, &c) in short.iter().enumerate() {
out[off + i] ^= c;
}
normalize(out)
}
fn monomial(s: u16, shift: usize) -> Vec<u16> {
if s == 0 {
return vec![0];
}
let mut p = vec![0u16; shift + 1];
p[0] = s;
p
}
fn generator(gf: &Gf, ec: usize) -> Vec<u16> {
let mut g = vec![1u16];
for i in 0..ec {
g = poly_mul(gf, &g, &[1, gf.exp((gf.base + i) as i64)]);
}
g
}
pub fn rs_encode(gf: &Gf, data: &[u16], ec: usize) -> Vec<u16> {
let g = generator(gf, ec);
let mut rem = vec![0u16; ec];
for &d in data {
let factor = d ^ rem[0];
rem.remove(0);
rem.push(0);
if factor != 0 {
for i in 1..g.len() {
rem[i - 1] ^= gf.mul(g[i], factor);
}
}
}
rem
}
pub fn rs_decode(gf: &Gf, received: &mut [u16], ec: usize) -> bool {
let mut syndromes = vec![0u16; ec];
let mut no_error = true;
for i in 0..ec {
let s = eval(gf, received, gf.exp((gf.base + i) as i64));
syndromes[ec - 1 - i] = s;
if s != 0 {
no_error = false;
}
}
if no_error {
return true;
}
let syndromes = normalize(syndromes);
let (sigma, omega) = match run_euclidean(gf, monomial(1, ec), syndromes, ec) {
Some(v) => v,
None => return false,
};
let locations = match find_error_locations(gf, &sigma) {
Some(v) => v,
None => return false,
};
let magnitudes = find_error_magnitudes(gf, &omega, &locations);
let n = received.len();
for (loc, mag) in locations.iter().zip(magnitudes.iter()) {
let pos = match n.checked_sub(1 + gf.log(*loc)) {
Some(p) if p < n => p,
_ => return false,
};
received[pos] ^= *mag;
}
true
}
fn run_euclidean(gf: &Gf, a: Vec<u16>, b: Vec<u16>, ec: usize) -> Option<(Vec<u16>, Vec<u16>)> {
let (mut r_last, mut r) = if degree(&a) < degree(&b) {
(b, a)
} else {
(a, b)
};
let mut t_last = vec![0u16];
let mut t = vec![1u16];
while 2 * degree(&r) >= ec {
let r_last_last = r_last;
let t_last_last = t_last;
r_last = r;
t_last = t;
if is_zero(&r_last) {
return None;
}
r = r_last_last;
let mut q = vec![0u16];
let dlt = r_last[0];
let dlt_inv = gf.inv(dlt);
while degree(&r) >= degree(&r_last) && !is_zero(&r) {
let deg_diff = degree(&r) - degree(&r_last);
let scale = gf.mul(r[0], dlt_inv);
q = poly_add(&q, &monomial(scale, deg_diff));
let term = poly_mul(gf, &r_last, &monomial(scale, deg_diff));
r = poly_add(&r, &term);
}
t = poly_add(&poly_mul(gf, &q, &t_last), &t_last_last);
if degree(&r) >= degree(&r_last) {
return None;
}
}
let sigma_tilde_at_zero = *t.last().unwrap();
if sigma_tilde_at_zero == 0 {
return None;
}
let inverse = gf.inv(sigma_tilde_at_zero);
let sigma = poly_scale(gf, &t, inverse);
let omega = poly_scale(gf, &r, inverse);
Some((sigma, omega))
}
fn find_error_locations(gf: &Gf, sigma: &[u16]) -> Option<Vec<u16>> {
let num_errors = degree(sigma);
if num_errors == 0 {
return None;
}
if num_errors == 1 {
return Some(vec![sigma[0]]);
}
let mut result = Vec::with_capacity(num_errors);
for i in 1..gf.size() as u16 {
if eval(gf, sigma, i) == 0 {
result.push(gf.inv(i));
if result.len() == num_errors {
break;
}
}
}
if result.len() != num_errors {
return None;
}
Some(result)
}
fn find_error_magnitudes(gf: &Gf, omega: &[u16], locations: &[u16]) -> Vec<u16> {
let s = locations.len();
let mut result = vec![0u16; s];
for i in 0..s {
let xi_inverse = gf.inv(locations[i]);
let mut denominator = 1u16;
for (j, &loc) in locations.iter().enumerate() {
if i != j {
let term = gf.mul(loc, xi_inverse);
let term_plus_1 = if term & 1 == 0 { term | 1 } else { term & !1 };
denominator = gf.mul(denominator, term_plus_1);
}
}
let mut magnitude = gf.mul(eval(gf, omega, xi_inverse), gf.inv(denominator));
if gf.base() != 0 {
magnitude = gf.mul(magnitude, xi_inverse);
}
result[i] = magnitude;
}
result
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn gf16_known_products() {
let gf = Gf::gf16();
assert_eq!(gf.mul(2, 2), 4);
assert_eq!(gf.mul(2, 4), 8);
assert_eq!(gf.mul(2, 8), 3); assert_eq!(gf.mul(3, 3), 5); assert_eq!(gf.mul(7, 7), 6);
for a in 1u16..16 {
assert_eq!(gf.mul(a, gf.inv(a)), 1, "inverse of {a}");
}
}
#[test]
fn rs_roundtrip_and_correction_gf64() {
let gf = Gf::for_word_size(6).unwrap();
let data: Vec<u16> = vec![1, 2, 3, 4, 5, 6, 7, 8];
let ec = 7;
let check = rs_encode(&gf, &data, ec);
let mut block = data.clone();
block.extend_from_slice(&check);
let clean = block.clone();
let mut b0 = block.clone();
assert!(rs_decode(&gf, &mut b0, ec));
assert_eq!(b0, clean);
block[0] ^= 0x2a;
block[4] ^= 0x11;
block[9] ^= 0x3f;
assert!(rs_decode(&gf, &mut block, ec));
assert_eq!(block, clean);
}
#[test]
fn rs_detects_uncorrectable() {
let gf = Gf::for_word_size(8).unwrap();
let data: Vec<u16> = (0..16).collect();
let ec = 6; let check = rs_encode(&gf, &data, ec);
let mut block = data.clone();
block.extend_from_slice(&check);
for i in [0, 3, 7, 15] {
block[i] ^= 0x55;
}
let before = block.clone();
let ok = rs_decode(&gf, &mut block, ec);
if ok {
assert_ne!(block, data, "should not have recovered original data");
} else {
assert_eq!(block, before);
}
}
}