extern crate binfield_matrix;
use binfield_matrix::{matrix_mul, matrix_mul_systematic};
pub fn encode(data: u16) -> u32 {
assert_eq!(data >> 9, 0);
matrix_mul_systematic(data, GEN)
}
pub fn decode(word: u32) -> Option<(u16, usize)> {
assert_eq!(word >> 17, 0);
let (fixed, word) = (0..17).fold((Some(0), word), |(fixed, word), _| {
let syndrome: u8 = matrix_mul(word, PAR);
if syndrome == 0 {
return (fixed, rotate_17(word));
}
match PATTERNS[syndrome as usize] {
0 => (None, rotate_17(word)),
pat => (Some(pat.count_ones() as usize), rotate_17(word ^ pat)),
}
});
fixed.map(|err| ((word >> 8) as u16, err))
}
const GEN: &[u16] = &[
0b100111100,
0b010011110,
0b001001111,
0b100011011,
0b110110001,
0b111100100,
0b011110010,
0b001111001,
];
const PAR: &[u32] = &[
0b10011110010000000,
0b01001111001000000,
0b00100111100100000,
0b10001101100010000,
0b11011000100001000,
0b11110010000000100,
0b01111001000000010,
0b00111100100000001,
];
const PATTERNS: [u32; 256] = [
0,
0b00000000000000001,
0,
0b00000000000000011,
0,
0b00000000000000101,
0, 0, 0,
0b00000000000001001,
0, 0, 0, 0, 0, 0, 0,
0b00000000000010001,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0b00000000000100001,
0, 0, 0, 0,
0b00100000000000001,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0b00000000100000001,
0, 0, 0, 0, 0, 0, 0, 0,
0b00000000001000001,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0b01000000000000001,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0,
0b00000001000000001,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0b00000000010000001,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0b00010000000000001,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0b10000000000000001,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0,
0b00001000000000001,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0b00000010000000001,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0b00000100000000001,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
];
fn rotate_17(word: u32) -> u32 {
let lsb = word & 1;
word >> 1 | lsb << 16
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_encode() {
assert_eq!(encode(0b000000000), 0b000000000_00000000);
assert_eq!(encode(0b111111111), 0b111111111_11111111);
assert_eq!(encode(0b100000001), 0b100000001_10100101);
assert_eq!(encode(0b000001001), 0b000001001_11001000);
assert_eq!(encode(0b000001011), 0b000001011_10111010);
assert_eq!(encode(0b000001010), 0b000001010_10000011);
assert_eq!(encode(0b000001000), 0b000001000_11110001);
}
#[test]
fn test_decode() {
for w in 0..1<<9 {
assert_eq!(decode(encode(w)), Some((w, 0)));
}
let w = encode(0b1010101);
assert_eq!(w, 0b1010101_00100001);
assert_eq!(decode(w), Some((0b1010101, 0)));
for i in 0..17 {
assert_eq!(decode(w ^ 1 << i), Some((0b1010101, 1)));
}
for (i, j) in (0..17).zip(0..17) {
if i != j {
assert_eq!(decode(w ^ (1 << i) ^ (1 << j)), Some((0b1010101, 2)));
}
}
}
#[test]
fn test_rotate_17() {
assert_eq!(rotate_17(0b00000000000000000), 0b00000000000000000);
assert_eq!(rotate_17(0b10000000000000000), 0b01000000000000000);
assert_eq!(rotate_17(0b01000000000000000), 0b00100000000000000);
assert_eq!(rotate_17(0b00100000000000000), 0b00010000000000000);
assert_eq!(rotate_17(0b00010000000000000), 0b00001000000000000);
assert_eq!(rotate_17(0b00001000000000000), 0b00000100000000000);
assert_eq!(rotate_17(0b00000100000000000), 0b00000010000000000);
assert_eq!(rotate_17(0b00000010000000000), 0b00000001000000000);
assert_eq!(rotate_17(0b00000001000000000), 0b00000000100000000);
assert_eq!(rotate_17(0b00000000100000000), 0b00000000010000000);
assert_eq!(rotate_17(0b00000000010000000), 0b00000000001000000);
assert_eq!(rotate_17(0b00000000001000000), 0b00000000000100000);
assert_eq!(rotate_17(0b00000000000100000), 0b00000000000010000);
assert_eq!(rotate_17(0b00000000000010000), 0b00000000000001000);
assert_eq!(rotate_17(0b00000000000001000), 0b00000000000000100);
assert_eq!(rotate_17(0b00000000000000100), 0b00000000000000010);
assert_eq!(rotate_17(0b00000000000000010), 0b00000000000000001);
assert_eq!(rotate_17(0b00000000000000001), 0b10000000000000000);
assert_eq!(rotate_17(0b01111111111111111), 0b10111111111111111);
let mut word = 0b11100011001010101;
for _ in 0..17 {
word = rotate_17(word);
}
assert_eq!(word, 0b11100011001010101);
}
}