use crate::{BITS, Gf2Poly, Limb, limbs};
const fn const_byte_inverse(orig: u8) -> u8 {
let mut product = orig;
let mut result = 1;
let mut i = 1;
while i < 8 {
if (product >> i) & 1 != 0 {
product ^= orig << i;
result |= 1 << i;
}
i += 1;
}
result
}
const BYTE_INVERSE: [Limb; 128] = {
let mut res = [0; 128];
let mut i = 0u8;
while i < 128 {
res[i as usize] = const_byte_inverse(2 * i + 1) as Limb;
i += 1;
}
res
};
const BYTE_DEG: [u64; 128] = {
let mut res = [0; 128];
let mut i = 0;
while i < 128 {
res[i] = BITS - BYTE_INVERSE[i].leading_zeros() as u64 - 1;
i += 1;
}
res
};
fn inverse_mod_power(f: &Gf2Poly, degree: u64) -> Gf2Poly {
if degree == 0 {
return Gf2Poly::zero();
}
if !f.eval(false) {
panic!("f may not be divisible by x!");
}
let reduced = f.truncated(degree);
let mut res = inverse_mod_power_impl(&reduced, degree);
res.truncate_mut(degree);
res
}
fn inverse_mod_power_impl(f: &Gf2Poly, degree: u64) -> Gf2Poly {
if degree <= 8 {
let half = (f.limbs()[0] / 2) as usize;
let inverse = BYTE_INVERSE[half];
let deg = BYTE_DEG[half];
return Gf2Poly {
deg,
limbs: limbs![inverse],
};
}
let hdeg = degree.div_ceil(2);
let lo = f.truncated(hdeg);
let lo_inv = inverse_mod_power_impl(&lo, hdeg);
let mut inv = f * lo_inv.square();
inv.truncate_mut(degree);
inv
}
pub(crate) fn divmod_base(mut lhs: Limb, mut rhs: Limb, ldeg: u8, rdeg: u8) -> (Limb, Limb) {
if rdeg > ldeg {
return (0, lhs);
}
let mut result = 0;
let mut diff = ldeg - rdeg;
rhs <<= diff;
loop {
let reduced = lhs ^ rhs;
if lhs > reduced {
lhs = reduced;
result |= 1 << diff;
}
if diff == 0 {
break (result, lhs);
}
rhs >>= 1;
diff -= 1;
}
}
fn rem_base(mut lhs: Limb, mut rhs: Limb, ldeg: u8, rdeg: u8) -> Limb {
if rdeg > ldeg {
return lhs;
}
let mut diff = ldeg - rdeg;
rhs <<= diff;
loop {
lhs = lhs.min(lhs ^ rhs);
if diff == 0 {
break lhs;
}
rhs >>= 1;
diff -= 1;
}
}
impl Gf2Poly {
fn div_rev(&self, rhs: &Gf2Poly) -> Gf2Poly {
let rev_lhs = self.reverse();
let rev_rhs = rhs.reverse();
let result_deg = self.deg() - rhs.deg();
let rev_inv_rhs = inverse_mod_power(&rev_rhs, result_deg + 1);
let mut rev_result = rev_lhs * rev_inv_rhs;
rev_result.truncate_mut(result_deg + 1);
let padding = result_deg - rev_result.deg();
rev_result.reverse() << padding
}
fn divide(&self, rhs: &Gf2Poly) -> Gf2Poly {
if rhs.is_zero() {
panic!("Division by zero.");
}
if self.is_zero() || self.deg() < rhs.deg() {
return Gf2Poly::zero();
}
if rhs.is_one() {
return self.clone();
}
if self.deg() == rhs.deg() {
return Gf2Poly::one();
}
if let ([lhs_limb], [rhs_limb]) = (self.limbs(), rhs.limbs()) {
let lhs_deg = self.deg();
let rhs_deg = rhs.deg();
let (result, _) = divmod_base(*lhs_limb, *rhs_limb, lhs_deg as u8, rhs_deg as u8);
return Gf2Poly {
deg: lhs_deg - rhs_deg,
limbs: limbs![result],
};
}
self.div_rev(rhs)
}
}
impl core::ops::Div for &Gf2Poly {
type Output = Gf2Poly;
fn div(self, rhs: Self) -> Self::Output {
self.divide(rhs)
}
}
impl core::ops::Div for Gf2Poly {
type Output = Gf2Poly;
fn div(self, rhs: Self) -> Self::Output {
&self / &rhs
}
}
impl core::ops::Div<Gf2Poly> for &Gf2Poly {
type Output = Gf2Poly;
fn div(self, rhs: Gf2Poly) -> Self::Output {
self / &rhs
}
}
impl core::ops::Div<&Gf2Poly> for Gf2Poly {
type Output = Gf2Poly;
fn div(self, rhs: &Gf2Poly) -> Self::Output {
&self / rhs
}
}
impl core::ops::DivAssign<&Gf2Poly> for Gf2Poly {
fn div_assign(&mut self, rhs: &Gf2Poly) {
*self = &*self / rhs;
}
}
impl core::ops::DivAssign<Gf2Poly> for Gf2Poly {
fn div_assign(&mut self, rhs: Gf2Poly) {
*self = &*self / &rhs;
}
}
impl core::ops::Rem for &Gf2Poly {
type Output = Gf2Poly;
fn rem(self, rhs: Self) -> Self::Output {
if rhs.is_zero() {
return self.clone();
}
if rhs.is_one() {
return Gf2Poly::zero();
}
if let ([lhs_limb], [rhs_limb]) = (self.limbs(), rhs.limbs()) {
let lhs_deg = self.deg();
let rhs_deg = rhs.deg();
let result = rem_base(*lhs_limb, *rhs_limb, lhs_deg as u8, rhs_deg as u8);
return Gf2Poly::from_limbs(&[result]);
}
self + rhs * &(self / rhs)
}
}
impl core::ops::Rem for Gf2Poly {
type Output = Gf2Poly;
fn rem(self, rhs: Self) -> Self::Output {
if rhs.is_zero() {
return self;
}
&self % &rhs
}
}
impl core::ops::Rem<Gf2Poly> for &Gf2Poly {
type Output = Gf2Poly;
fn rem(self, rhs: Gf2Poly) -> Self::Output {
self % &rhs
}
}
impl core::ops::Rem<&Gf2Poly> for Gf2Poly {
type Output = Gf2Poly;
fn rem(self, rhs: &Gf2Poly) -> Self::Output {
if rhs.is_zero() {
return self;
}
(&self / rhs) * rhs + self
}
}
impl core::ops::RemAssign<&Gf2Poly> for Gf2Poly {
fn rem_assign(&mut self, rhs: &Gf2Poly) {
if rhs.is_zero() {
return;
}
*self = &*self % rhs;
}
}
impl core::ops::RemAssign<Gf2Poly> for Gf2Poly {
fn rem_assign(&mut self, rhs: Gf2Poly) {
if rhs.is_zero() {
return;
}
*self = &*self % &rhs;
}
}
impl Gf2Poly {
pub fn divmod(&self, by: &Self) -> (Self, Self) {
let quotient = self / by;
let remainder = self + "ient * by;
(quotient, remainder)
}
pub fn divides(&self, rhs: &Self) -> bool {
if rhs.is_zero() {
return true;
}
(rhs % self).is_zero()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::prop_assert_poly_eq;
use proptest::prelude::*;
#[test]
fn zero_divisibility() {
assert!(Gf2Poly::zero().divides(&Gf2Poly::zero()));
assert!(Gf2Poly::one().divides(&Gf2Poly::zero()));
assert!(!Gf2Poly::zero().divides(&Gf2Poly::one()));
}
proptest! {
#[test]
fn mod_inverse(mut a: Gf2Poly, degree in 1u64..128) {
if !a.eval(false) {
a += &Gf2Poly::one();
}
let a_inv = inverse_mod_power(&a, degree);
prop_assert!(a_inv.deg() < degree);
prop_assert!(a_inv.is_normalized());
let one = (a * a_inv).truncated(degree);
prop_assert_poly_eq!(one, Gf2Poly::one());
}
#[test]
fn remainder_deg(a: Gf2Poly, b: Gf2Poly) {
prop_assume!(!b.is_zero());
let r = &a % &b;
prop_assert!(r.is_zero() || r.deg() < b.deg());
}
#[test]
fn remainder_and_quotient(a: Gf2Poly, b: Gf2Poly) {
prop_assume!(!b.is_zero());
let q = &a / &b;
prop_assert!(q.is_normalized());
let rem1 = &a + &q * &b;
let rem2 = &a % &b;
prop_assert_poly_eq!(rem1, rem2);
}
}
}