const GF16_EXP: [u8; 31] = [
1, 2, 4, 8, 3, 6, 12, 11, 5, 10, 7, 14, 15, 13, 9, 1, 2, 4, 8, 3, 6, 12, 11, 5, 10, 7, 14, 15,
13, 9, 1,
];
const GF16_LOG: [i8; 16] = [-1, 0, 1, 4, 2, 8, 5, 10, 3, 14, 9, 7, 6, 13, 11, 12];
fn gf16_mul(a: u32, b: u32) -> u32 {
if a == 0 || b == 0 {
0
} else {
GF16_EXP[(GF16_LOG[a as usize] + GF16_LOG[b as usize]) as usize] as u32
}
}
fn gf16_div(a: u32, b: u32) -> u32 {
if a == 0 {
0
} else {
GF16_EXP[(GF16_LOG[a as usize] + 15 - GF16_LOG[b as usize]) as usize] as u32
}
}
fn gf16_hmul(a: u32, logb: u32) -> u32 {
if a == 0 {
0
} else {
GF16_EXP[(GF16_LOG[a as usize] as u32 + logb) as usize] as u32
}
}
fn bch15_5_calc_syndrome(y: u32) -> ([u32; 3], bool) {
let mut s = [0u32; 3];
let mut p = 0;
for (i, &exp) in GF16_EXP.iter().enumerate() {
if (y & (1 << i)) != 0 {
p ^= exp as u32;
}
}
s[0] = p;
p = 0;
for i in 0..3 {
for j in 0..5 {
if (y & (1 << (5 * i + j))) != 0 {
p ^= GF16_EXP[j * 3] as u32;
}
}
}
s[1] = p;
p = 0;
for i in 0..5 {
for j in 0..3 {
if (y & (1 << (3 * i + j))) != 0 {
p ^= GF16_EXP[j * 5] as u32;
}
}
}
s[2] = p;
let has_errors = s[0] != 0 || s[1] != 0 || s[2] != 0;
(s, has_errors)
}
fn bch15_5_calc_omega(s: &[u32; 3]) -> ([u32; 3], i32) {
let mut o = [0u32; 3];
o[0] = s[0];
let s02 = gf16_mul(s[0], s[0]);
let dd = s[1] ^ gf16_mul(s[0], s02);
let tt = s[2] ^ gf16_mul(s02, s[1]);
o[1] = if dd != 0 { gf16_div(tt, dd) } else { 0 };
o[2] = dd ^ gf16_mul(s[0], o[1]);
let mut d = 3;
while d > 0 && o[d - 1] == 0 {
d -= 1;
}
(o, d as i32)
}
fn bch15_5_calc_epos(s: &[u32; 3]) -> Result<([u32; 3], i32), i32> {
let (o, d) = bch15_5_calc_omega(s);
let mut epos = [0u32; 3];
let mut nerrors = 0i32;
if d == 1 {
epos[nerrors as usize] = GF16_LOG[o[0] as usize] as u32;
nerrors += 1;
} else if d > 0 {
for i in 0..15 {
let i2 = GF16_LOG[GF16_EXP[(i << 1) as usize] as usize] as u32;
if (GF16_EXP[(i + i2) as usize] as u32
^ gf16_hmul(o[0], i2)
^ gf16_hmul(o[1], i)
^ o[2])
== 0
{
epos[nerrors as usize] = i;
nerrors += 1;
}
}
if nerrors < d {
return Err(-1);
}
}
Ok((epos, nerrors))
}
pub(crate) fn bch15_5_correct(y: &mut u32) -> i32 {
let mut y_val = *y;
let (s, has_errors) = bch15_5_calc_syndrome(y_val);
if !has_errors {
return 0;
}
match bch15_5_calc_epos(&s) {
Ok((epos, nerrors)) if nerrors > 0 => {
for i in 0..nerrors {
y_val ^= 1 << epos[i as usize];
}
if bch15_5_encode(y_val >> 10) == y_val {
*y = y_val;
return nerrors;
}
-1
}
_ => {
-1
}
}
}
pub fn bch15_5_encode(x: u32) -> u32 {
(u32::wrapping_neg(x & 1) & 0x0537)
^ (u32::wrapping_neg((x >> 1) & 1) & 0x0A6E)
^ (u32::wrapping_neg((x >> 2) & 1) & 0x11EB)
^ (u32::wrapping_neg((x >> 3) & 1) & 0x23D6)
^ (u32::wrapping_neg((x >> 4) & 1) & 0x429B)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bch15_5_encode() {
assert_eq!(bch15_5_encode(0), 0x0000);
assert_eq!(bch15_5_encode(1), 0x0537);
assert_eq!(bch15_5_encode(2), 0x0A6E);
let encoded_31 = bch15_5_encode(31);
assert_eq!(encoded_31, 0x0537 ^ 0x0A6E ^ 0x11EB ^ 0x23D6 ^ 0x429B);
}
#[test]
fn test_bch15_5_correct_no_errors() {
let mut y = bch15_5_encode(15);
let nerrors = bch15_5_correct(&mut y);
assert_eq!(nerrors, 0);
assert_eq!(y, bch15_5_encode(15));
}
#[test]
fn test_bch15_5_correct_single_error() {
let original = bch15_5_encode(10);
let mut corrupted = original ^ (1 << 3);
let nerrors = bch15_5_correct(&mut corrupted);
assert_eq!(nerrors, 1);
assert_eq!(corrupted, original);
}
#[test]
fn test_bch15_5_correct_two_errors() {
let original = bch15_5_encode(5);
let mut corrupted = original ^ (1 << 1) ^ (1 << 7);
let nerrors = bch15_5_correct(&mut corrupted);
assert_eq!(nerrors, 2);
assert_eq!(corrupted, original);
}
#[test]
fn test_bch15_5_correct_three_errors() {
let original = bch15_5_encode(20);
let mut corrupted = original ^ (1 << 0) ^ (1 << 5) ^ (1 << 10);
let nerrors = bch15_5_correct(&mut corrupted);
assert_eq!(nerrors, 3);
assert_eq!(corrupted, original);
}
#[test]
fn test_bch15_5_correct_too_many_errors() {
let original = bch15_5_encode(7);
let mut corrupted = original ^ (1 << 0) ^ (1 << 3) ^ (1 << 6) ^ (1 << 9);
let nerrors = bch15_5_correct(&mut corrupted);
assert!(nerrors < 0);
}
#[test]
fn test_gf16_mul() {
assert_eq!(gf16_mul(0, 5), 0);
assert_eq!(gf16_mul(5, 0), 0);
assert_eq!(gf16_mul(2, 3), gf16_mul(3, 2)); }
}