use crate::{EuclideanError, Polynomial};
pub fn euclidean(
syndromes: &Polynomial,
t: u8,
) -> Result<(Polynomial, Polynomial), EuclideanError> {
let two_t = usize::from(t) * 2;
if two_t > usize::from(Polynomial::MAX_DEGREE) {
return Err(EuclideanError::CapabilityTooHigh { t });
}
let mut r: [Polynomial; 2] = [Polynomial::default(), Polynomial::default()];
let mut t_poly: [Polynomial; 2] = [Polynomial::default(), Polynomial::default()];
#[allow(clippy::cast_possible_truncation)]
r[0].set(two_t as u8, 1);
r[1] = *syndromes;
t_poly[1].set(0, 1);
let mut q = Polynomial::default();
let mut idx: usize = 1;
while r[idx].degree() >= t {
let (r_left, r_right) = r.split_at_mut(1);
let (r_prev, r_curr) = if idx == 1 {
(&mut r_left[0], &r_right[0])
} else {
(&mut r_right[0], &r_left[0])
};
let (t_left, t_right) = t_poly.split_at_mut(1);
let (t_prev, t_curr) = if idx == 1 {
(&mut t_left[0], &t_right[0])
} else {
(&mut t_right[0], &t_left[0])
};
r_prev.div_rem_inplace(r_curr, &mut q)?;
t_prev.mul_xor_assign(&q, t_curr)?;
idx ^= 1;
}
Ok((t_poly[idx], r[idx]))
}
#[cfg(test)]
#[allow(clippy::expect_used)]
mod tests {
use super::*;
use crate::finite_field::{inv, mul, ANTILOG_TABLE};
#[test]
fn euclidean_zero_syndromes() {
let syndromes: Polynomial = [0u8; 4].into_iter().collect();
let t = 2;
let (sigma, omega) = euclidean(&syndromes, t).expect("should succeed");
assert_eq!(sigma.degree(), 0);
assert_eq!(omega.coefficients(), &[0]);
}
#[test]
fn euclidean_single_error_sigma_has_one_root() {
let syndromes: Polynomial = [1u8; 4].into_iter().collect(); let t = 2;
let (sigma, _omega) = euclidean(&syndromes, t).expect("should succeed");
assert!(sigma.degree() <= 1);
let x = ANTILOG_TABLE[255 % 255].get();
let sigma_at_x = Polynomial::eval_coefficients_at(sigma.coefficients(), x);
let scale = inv(sigma[0u8]).expect("sigma[0] should be non-zero").get();
let _normalized_eval = mul(sigma_at_x, scale);
assert_eq!(
Polynomial::eval_coefficients_at(sigma.coefficients(), 1),
0,
"sigma should have a root at alpha^0 for error at position 0"
);
}
#[test]
fn euclidean_verifies_degree_bounds() {
let syndromes: Polynomial = [3u8, 5, 7, 11].into_iter().collect(); let t = 2;
let (sigma, omega) = euclidean(&syndromes, t).expect("should succeed");
assert!(
sigma.degree() <= t,
"sigma degree {} exceeds t={}",
sigma.degree(),
t
);
assert!(
omega.degree() < t || omega.is_zero(),
"omega degree {} should be < t={}",
omega.degree(),
t
);
}
#[test]
fn euclidean_with_t_equals_one() {
let syndromes: Polynomial = [42u8, 0].into_iter().collect(); let t = 1;
let (sigma, omega) = euclidean(&syndromes, t).expect("should succeed");
assert!(sigma.degree() <= t);
assert!(omega.degree() < t || omega.is_zero());
}
#[test]
fn euclidean_with_large_t() {
let syndromes: Polynomial = [1u8; 20].into_iter().collect(); let t = 10;
let (sigma, omega) = euclidean(&syndromes, t).expect("should succeed");
assert!(sigma.degree() <= t);
assert!(omega.degree() < t || omega.is_zero());
}
#[test]
fn euclidean_minimal_valid_case() {
let syndromes: Polynomial = [42u8, 17].into_iter().collect();
let t = 1;
let (sigma, omega) = euclidean(&syndromes, t).expect("should succeed");
assert!(sigma.degree() <= t);
assert!(omega.degree() < t || omega.is_zero());
}
#[test]
fn euclidean_syndrome_with_trailing_zeros() {
let syndromes: Polynomial = [1u8, 2, 0, 0].into_iter().collect(); let t = 2;
let (sigma, omega) = euclidean(&syndromes, t).expect("should succeed");
assert!(sigma.degree() <= t);
assert!(
omega.degree() < t || omega.is_zero(),
"omega degree {} should be < t={}",
omega.degree(),
t
);
}
#[test]
fn euclidean_rejects_capability_above_127() {
let syndromes: Polynomial = [1u8; 4].into_iter().collect();
for t in [128, 200, 255] {
assert_eq!(
euclidean(&syndromes, t),
Err(EuclideanError::CapabilityTooHigh { t })
);
}
}
#[test]
fn euclidean_max_valid_length() {
let syndromes: Polynomial = [0u8; 255].into_iter().collect();
let t = 127;
let result = euclidean(&syndromes, t);
assert!(result.is_ok());
}
#[test]
fn euclidean_output_is_deterministic() {
let syndromes: Polynomial = [7u8, 13, 19, 23, 29, 31].into_iter().collect();
let t = 3;
let (sigma1, omega1) = euclidean(&syndromes, t).expect("should succeed");
let (sigma2, omega2) = euclidean(&syndromes, t).expect("should succeed");
assert_eq!(sigma1, sigma2);
assert_eq!(omega1, omega2);
}
#[test]
fn euclidean_integration_with_rs_syndromes() {
use crate::ReedSolomon;
let rs = ReedSolomon::new(2).expect("valid RS");
let message = b"test";
let mut encoded = rs.encode(message).expect("encoding succeeds");
encoded[0] ^= 0x42;
let syndromes = ReedSolomon::compute_syndromes(rs.parity_bytes(), &encoded);
let t = rs.parity_bytes() / 2;
let (sigma, omega) = euclidean(&syndromes, t).expect("euclidean succeeds");
assert!(sigma.degree() <= t, "sigma degree should be <= t");
assert!(
omega.degree() < t || omega.is_zero(),
"omega degree {} should be < t={}",
omega.degree(),
t
);
}
}