use crate::symbolic::error_correction_helper::gf256_add;
use crate::symbolic::error_correction_helper::gf256_div;
use crate::symbolic::error_correction_helper::gf256_exp;
use crate::symbolic::error_correction_helper::gf256_inv;
use crate::symbolic::error_correction_helper::gf256_mul;
use crate::symbolic::error_correction_helper::poly_add_gf256;
use crate::symbolic::error_correction_helper::poly_div_gf256;
use crate::symbolic::error_correction_helper::poly_eval_gf256;
use crate::symbolic::error_correction_helper::poly_mul_gf256;
#[must_use]
pub fn hamming_distance(
a: &[u8],
b: &[u8],
) -> Option<usize> {
if a.len() != b.len() {
return None;
}
Some(a.iter().zip(b.iter()).filter(|&(&x, &y)| x != y).count())
}
#[must_use]
pub fn hamming_weight(data: &[u8]) -> usize {
data.iter().filter(|&&x| x != 0).count()
}
#[must_use]
pub fn hamming_encode(data: &[u8]) -> Option<Vec<u8>> {
if data.len() != 4 {
return None;
}
let d3 = data[0];
let d5 = data[1];
let d6 = data[2];
let d7 = data[3];
let p1 = d3 ^ d5 ^ d7;
let p2 = d3 ^ d6 ^ d7;
let p4 = d5 ^ d6 ^ d7;
Some(vec![p1, p2, d3, p4, d5, d6, d7])
}
#[must_use]
pub fn hamming_check(codeword: &[u8]) -> bool {
if codeword.len() != 7 {
return false;
}
let p1_in = codeword[0];
let p2_in = codeword[1];
let d3_in = codeword[2];
let p4_in = codeword[3];
let d5_in = codeword[4];
let d6_in = codeword[5];
let d7_in = codeword[6];
let p1_calc = d3_in ^ d5_in ^ d7_in;
let p2_calc = d3_in ^ d6_in ^ d7_in;
let p4_calc = d5_in ^ d6_in ^ d7_in;
p1_in == p1_calc && p2_in == p2_calc && p4_in == p4_calc
}
pub fn hamming_decode(codeword: &[u8]) -> Result<(Vec<u8>, Option<usize>), String> {
if codeword.len() != 7 {
return Err("Codeword length \
must be 7"
.to_string());
}
let p1_in = codeword[0];
let p2_in = codeword[1];
let d3_in = codeword[2];
let p4_in = codeword[3];
let d5_in = codeword[4];
let d6_in = codeword[5];
let d7_in = codeword[6];
let p1_calc = d3_in ^ d5_in ^ d7_in;
let p2_calc = d3_in ^ d6_in ^ d7_in;
let p4_calc = d5_in ^ d6_in ^ d7_in;
let c1 = p1_in ^ p1_calc;
let c2 = p2_in ^ p2_calc;
let c4 = p4_in ^ p4_calc;
let error_pos = (c4 << 2) | (c2 << 1) | c1;
let mut corrected_codeword = codeword.to_vec();
let error_index = if error_pos != 0 {
let index = error_pos as usize - 1;
if index < corrected_codeword.len() {
corrected_codeword[index] ^= 1;
}
Some(error_pos as usize)
} else {
None
};
let corrected_data = vec![
corrected_codeword[2],
corrected_codeword[4],
corrected_codeword[5],
corrected_codeword[6],
];
Ok((corrected_data, error_index))
}
pub(crate) fn rs_generator_poly(n_sym: usize) -> Result<Vec<u8>, String> {
if n_sym == 0 {
return Err("Number of symbols must \
be positive"
.to_string());
}
let mut g = vec![1];
for i in 0..n_sym {
let p = vec![1, gf256_exp(i as u8)];
g = poly_mul_gf256(&g, &p);
}
Ok(g)
}
pub fn rs_encode(
data: &[u8],
n_sym: usize,
) -> Result<Vec<u8>, String> {
if data.len() + n_sym > 255 {
return Err("Message length \
+ number of \
symbols cannot \
exceed 255"
.to_string());
}
let gen_poly = rs_generator_poly(n_sym)?;
let mut message_poly = data.to_vec();
message_poly.extend(vec![0; n_sym]);
let remainder = poly_div_gf256(message_poly, &gen_poly)?;
let mut codeword = data.to_vec();
codeword.extend(remainder);
Ok(codeword)
}
pub(crate) fn rs_calc_syndromes(
codeword_poly: &[u8],
n_sym: usize,
) -> Vec<u8> {
let mut syndromes = vec![0; n_sym];
for (i, syndrome) in syndromes.iter_mut().enumerate().take(n_sym) {
*syndrome = poly_eval_gf256(codeword_poly, gf256_exp(i as u8));
}
syndromes
}
#[must_use]
pub fn rs_check(
codeword: &[u8],
n_sym: usize,
) -> bool {
let syndromes = rs_calc_syndromes(codeword, n_sym);
syndromes.iter().all(|&s| s == 0)
}
#[must_use]
pub fn rs_error_count(
codeword: &[u8],
n_sym: usize,
) -> usize {
let syndromes = rs_calc_syndromes(codeword, n_sym);
if syndromes.iter().all(|&s| s == 0) {
return 0;
}
let sigma = rs_find_error_locator_poly(&syndromes);
sigma.len() - 1 }
#[allow(clippy::cast_possible_wrap)]
pub(crate) fn rs_find_error_locator_poly(syndromes: &[u8]) -> Vec<u8> {
let mut sigma = vec![1];
let mut prev_sigma = vec![1];
let mut l = 0;
let mut m = -1;
let mut b = 1;
for n in 0..syndromes.len() {
let mut d = syndromes[n];
for i in 1..=l {
d = gf256_add(d, gf256_mul(sigma[sigma.len() - 1 - i], syndromes[n - i]));
}
if d != 0 {
let t = sigma.clone();
let mut correction = vec![b];
correction.extend(vec![0; i64::from(n as i32 - m).try_into().unwrap_or(0)]);
correction = poly_mul_gf256(&correction, &prev_sigma);
sigma = poly_add_gf256(&sigma, &correction);
if 2 * l <= n {
l = n + 1 - l;
m = n as i32;
prev_sigma = t;
b = d;
}
}
}
sigma
}
pub(crate) fn rs_find_error_locations(
sigma: &[u8],
codeword_len: usize,
) -> Result<Vec<usize>, String> {
let mut error_locs = Vec::new();
let err_poly_degree = sigma.len() - 1;
for i in 0..codeword_len {
let x = gf256_exp((255 - i) as u8);
if poly_eval_gf256(sigma, x) == 0 {
error_locs.push(i);
}
}
if error_locs.len() != err_poly_degree {
return Err("Failed to find \
the correct \
number of error \
locations."
.to_string());
}
Ok(error_locs)
}
pub fn rs_decode(
codeword: &[u8],
n_sym: usize,
) -> Result<Vec<u8>, String> {
let mut codeword_poly = codeword.to_vec();
let syndromes = rs_calc_syndromes(&codeword_poly, n_sym);
if syndromes.iter().all(|&s| s == 0) {
return Ok(codeword[..codeword.len() - n_sym].to_vec());
}
let sigma = rs_find_error_locator_poly(&syndromes);
let error_locs = rs_find_error_locations(&sigma, codeword.len())?;
let mut omega = poly_mul_gf256(&syndromes, &sigma);
omega.truncate(n_sym);
for &err_loc in &error_locs {
let x_inv = gf256_inv(gf256_exp((codeword.len() - 1 - err_loc) as u8));
let mut sigma_prime_eval = 0;
for i in (1..sigma.len()).step_by(2) {
sigma_prime_eval = gf256_add(sigma_prime_eval, sigma[i]);
}
let y = gf256_div(poly_eval_gf256(&omega, x_inv?), sigma_prime_eval);
codeword_poly[err_loc] = gf256_add(codeword_poly[err_loc], y?);
}
Ok(codeword_poly[..codeword.len() - n_sym].to_vec())
}
const CRC32_POLYNOMIAL: u32 = 0xEDB8_8320;
#[must_use]
pub fn crc32_compute(data: &[u8]) -> u32 {
let mut crc: u32 = 0xFFFF_FFFF;
for byte in data {
crc ^= u32::from(*byte);
for _ in 0..8 {
if crc & 1 != 0 {
crc = (crc >> 1) ^ CRC32_POLYNOMIAL;
} else {
crc >>= 1;
}
}
}
!crc
}
#[must_use]
pub fn crc32_verify(
data: &[u8],
expected_crc: u32,
) -> bool {
crc32_compute(data) == expected_crc
}
#[must_use]
pub fn crc32_update(
crc: u32,
data: &[u8],
) -> u32 {
let mut crc = crc;
for byte in data {
crc ^= u32::from(*byte);
for _ in 0..8 {
if crc & 1 != 0 {
crc = (crc >> 1) ^ CRC32_POLYNOMIAL;
} else {
crc >>= 1;
}
}
}
crc
}
#[must_use]
pub const fn crc32_finalize(crc: u32) -> u32 {
!crc
}