use crate::{finite_field::mul, Polynomial, PolynomialMulError};
impl Polynomial {
#[inline]
pub fn mul_xor_assign(&mut self, a: &Self, b: &Self) -> Result<(), PolynomialMulError> {
let a_deg = a.degree as usize;
let b_deg = b.degree as usize;
if a.coefficients[a_deg] == 0 || b.coefficients[b_deg] == 0 {
return Ok(());
}
let product_deg = a_deg + b_deg;
if product_deg > usize::from(Self::MAX_DEGREE) {
return Err(PolynomialMulError::DegreeOverflow);
}
for i in 0..=a_deg {
let a_coef = a.coefficients[i];
if a_coef == 0 {
continue;
}
for j in 0..=b_deg {
let b_coef = b.coefficients[j];
if b_coef == 0 {
continue;
}
self.coefficients[i + j] ^= mul(a_coef, b_coef);
}
}
#[allow(clippy::cast_possible_truncation)] if product_deg > self.degree as usize {
self.degree = product_deg as u8;
}
self.trim_degree();
Ok(())
}
}
#[cfg(test)]
#[allow(clippy::expect_used)]
mod tests {
use crate::{Polynomial, PolynomialMulError};
#[test]
fn mul_xor_assign_max_product_degree() {
let mut dest = Polynomial::default();
let mut a = Polynomial::default();
let mut b = Polynomial::default();
a.set(127, 1);
b.set(127, 1);
dest.mul_xor_assign(&a, &b).expect("product degree fits");
assert_eq!(dest.degree(), 254);
}
#[test]
fn mul_xor_assign_rejects_degree_overflow() {
let mut dest = Polynomial::try_from(&[7u8][..]).expect("valid polynomial");
let mut a = Polynomial::default();
let mut b = Polynomial::default();
a.set(128, 1);
b.set(127, 1);
let original = dest;
let result = dest.mul_xor_assign(&a, &b);
assert_eq!(result, Err(PolynomialMulError::DegreeOverflow));
assert_eq!(dest, original);
}
#[test]
fn mul_xor_assign_basic() {
let mut dest = Polynomial::try_from(&[1u8][..]).expect("valid polynomial");
let a = Polynomial::try_from(&[1u8, 1][..]).expect("valid polynomial");
let b = Polynomial::try_from(&[1u8, 1][..]).expect("valid polynomial");
dest.mul_xor_assign(&a, &b).expect("product degree fits");
assert_eq!(dest.coefficients(), &[0, 0, 1]);
}
#[test]
fn mul_xor_assign_with_zero_a() {
let mut dest = Polynomial::try_from(&[5u8, 3][..]).expect("valid polynomial");
let a = Polynomial::default(); let b = Polynomial::try_from(&[1u8, 2, 3][..]).expect("valid polynomial");
dest.mul_xor_assign(&a, &b).expect("product degree fits");
assert_eq!(dest.coefficients(), &[5, 3]);
}
#[test]
fn mul_xor_assign_with_zero_b() {
let mut dest = Polynomial::try_from(&[5u8, 3][..]).expect("valid polynomial");
let a = Polynomial::try_from(&[1u8, 2, 3][..]).expect("valid polynomial");
let b = Polynomial::default();
dest.mul_xor_assign(&a, &b).expect("product degree fits");
assert_eq!(dest.coefficients(), &[5, 3]);
}
#[test]
fn mul_xor_assign_accumulates() {
let mut dest = Polynomial::try_from(&[1u8, 0, 1][..]).expect("valid polynomial");
let a = Polynomial::try_from(&[1u8, 1][..]).expect("valid polynomial");
let b = Polynomial::try_from(&[1u8, 1][..]).expect("valid polynomial");
dest.mul_xor_assign(&a, &b).expect("product degree fits");
assert_eq!(dest.coefficients(), &[0]);
}
#[test]
fn mul_xor_assign_extends_degree() {
let mut dest = Polynomial::try_from(&[5u8][..]).expect("valid polynomial");
let a = Polynomial::try_from(&[1u8, 0, 1][..]).expect("valid polynomial"); let b = Polynomial::try_from(&[1u8, 1][..]).expect("valid polynomial");
dest.mul_xor_assign(&a, &b).expect("product degree fits");
assert_eq!(dest.coefficients(), &[4, 1, 1, 1]);
}
}