use super::PolyOverZ;
use flint_sys::{
fmpz::{fmpz, fmpz_is_one, fmpz_is_zero, fmpz_submul, fmpz_zero},
fmpz_mod::{fmpz_mod_ctx_struct, fmpz_mod_set_fmpz},
fmpz_mod_poly::fmpz_mod_poly_struct,
fmpz_poly::{_fmpz_poly_normalise, fmpz_poly_struct},
};
use std::cmp::min;
pub(crate) unsafe fn reduce_fmpz_poly_by_poly_over_z(
poly: &mut fmpz_poly_struct,
modulus: &PolyOverZ,
) {
let modulus_nr_coeff = modulus.get_degree() as usize;
assert!(
1 == unsafe { fmpz_is_one(modulus.poly.coeffs.add(modulus_nr_coeff)) },
"In order to reduce, the modulus must have a leading coefficient of 1."
);
let non_zero: Vec<usize> = (0..modulus_nr_coeff)
.filter(|i| unsafe { 0 == fmpz_is_zero(modulus.poly.coeffs.add(*i)) })
.collect();
unsafe {
reduce_fmpz_poly_by_fmpz_poly_sparse(
poly.coeffs,
poly.length as usize,
modulus.poly.coeffs,
modulus.poly.length as usize,
&non_zero,
);
}
poly.length = min(poly.length, modulus.poly.length - 1);
unsafe { _fmpz_poly_normalise(poly) };
}
pub(crate) unsafe fn reduce_fmpz_poly_by_fmpz_mod_poly_sparse(
poly: &mut fmpz_poly_struct,
modulus_poly: &fmpz_mod_poly_struct,
non_zero: &Vec<usize>,
modulus: &fmpz_mod_ctx_struct,
) {
let poly_len = poly.length as usize;
let modulus_poly_len = modulus_poly.length as usize;
unsafe {
reduce_fmpz_poly_by_fmpz_poly_sparse(
poly.coeffs,
poly_len,
modulus_poly.coeffs,
modulus_poly_len,
non_zero,
)
};
for i in 0..min(poly_len, modulus_poly_len - 1) {
unsafe { fmpz_mod_set_fmpz(poly.coeffs.add(i), poly.coeffs.add(i), modulus) };
}
poly.length = min(poly_len, modulus_poly_len - 1) as i64;
unsafe { _fmpz_poly_normalise(poly) };
}
pub(crate) unsafe fn reduce_fmpz_poly_by_fmpz_poly_sparse(
poly_coeffs: *mut fmpz,
poly_len: usize,
modulus_polynomial_coeffs: *mut fmpz,
modulus_polynomial_len: usize,
non_zero: &Vec<usize>,
) {
let (poly_len, modulus_polynomial_len) = (poly_len - 1, modulus_polynomial_len - 1);
if poly_len >= modulus_polynomial_len {
for i in (modulus_polynomial_len..=poly_len).rev() {
for k in non_zero {
unsafe {
fmpz_submul(
poly_coeffs.add((i - modulus_polynomial_len) + k),
poly_coeffs.add(i),
modulus_polynomial_coeffs.add(*k),
)
};
}
unsafe { fmpz_zero(poly_coeffs.add(i)) };
}
}
}
#[cfg(test)]
mod test_reduce_fmpz_poly_by_poly_over_z {
use crate::integer::{
PolyOverZ, poly_over_z::fmpz_poly_helpers::reduce_fmpz_poly_by_poly_over_z,
};
use std::str::FromStr;
#[test]
fn reduce_more_than_once() {
let mut a = PolyOverZ::from_str("4 0 1 2 3").unwrap();
let modulus = PolyOverZ::from_str("3 0 1 1").unwrap();
unsafe { reduce_fmpz_poly_by_poly_over_z(&mut a.poly, &modulus) }
assert_eq!(PolyOverZ::from_str("2 0 2").unwrap(), a);
}
#[test]
#[should_panic]
fn no_leading_zero() {
let mut a = PolyOverZ::from_str("3 1 2 3").unwrap();
let modulus = PolyOverZ::from_str("2 1 2").unwrap();
unsafe { reduce_fmpz_poly_by_poly_over_z(&mut a.poly, &modulus) }
}
#[test]
fn large_coefficients() {
let mut a = PolyOverZ::from_str(&format!("3 1 -{} -1", u64::MAX)).unwrap();
let modulus = PolyOverZ::from_str(&format!("2 {} 1", u64::MAX)).unwrap();
unsafe { reduce_fmpz_poly_by_poly_over_z(&mut a.poly, &modulus) }
assert_eq!(PolyOverZ::from(1), a);
}
}
#[cfg(test)]
mod test_reduce_fmpz_poly_by_fmpz_poly {
use crate::{
integer::{MatPolyOverZ, fmpz_poly_helpers::reduce_fmpz_poly_by_fmpz_poly_sparse},
integer_mod_q::ModulusPolynomialRingZq,
};
use flint_sys::{fmpz_poly::_fmpz_poly_normalise, fmpz_poly_mat::fmpz_poly_mat_entry};
use std::str::FromStr;
#[test]
#[allow(unused_mut)] fn working_example() {
let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap();
let mut poly_mat = MatPolyOverZ::from_str("[[4 -1 0 1 1, 1 42],[0, 2 1 2]]").unwrap();
let entry = unsafe { fmpz_poly_mat_entry(&poly_mat.matrix, 0, 0) };
if (unsafe { *entry }).length > 0 {
unsafe {
reduce_fmpz_poly_by_fmpz_poly_sparse(
(*entry).coeffs,
(*entry).length as usize,
modulus.modulus.poly.coeffs,
(modulus.get_degree() + 1) as usize,
&modulus.non_zero,
)
}
unsafe {
_fmpz_poly_normalise(entry);
};
}
let cmp_poly_mat = MatPolyOverZ::from_str("[[3 -2 0 1, 1 42],[0, 2 1 2]]").unwrap();
assert_eq!(cmp_poly_mat, poly_mat)
}
}
#[cfg(test)]
mod test_reduce_fmpz_poly_by_fmpz_mod_poly {
use crate::{
integer::{MatPolyOverZ, fmpz_poly_helpers::reduce_fmpz_poly_by_fmpz_poly_sparse},
integer_mod_q::{MatPolynomialRingZq, ModulusPolynomialRingZq},
};
use flint_sys::{fmpz_poly::_fmpz_poly_normalise, fmpz_poly_mat::fmpz_poly_mat_entry};
use std::str::FromStr;
#[test]
#[allow(unused_mut)] fn working_example() {
let modulus = ModulusPolynomialRingZq::from_str("4 1 0 0 1 mod 17").unwrap();
let poly_mat = MatPolyOverZ::from_str("[[4 -1 0 1 1, 1 42],[0, 2 1 2]]").unwrap();
let mut poly_ring_mat = MatPolynomialRingZq::from((&poly_mat, &modulus));
let entry = unsafe { fmpz_poly_mat_entry(&poly_ring_mat.matrix.matrix, 0, 0) };
if (unsafe { *entry }).length > 0 {
unsafe {
reduce_fmpz_poly_by_fmpz_poly_sparse(
(*entry).coeffs,
(*entry).length as usize,
poly_ring_mat.modulus.modulus.poly.coeffs,
(poly_ring_mat.modulus.get_degree() + 1) as usize,
&poly_ring_mat.modulus.non_zero,
)
}
unsafe {
_fmpz_poly_normalise(entry);
};
}
let cmp_poly_mat = MatPolyOverZ::from_str("[[3 15 0 1, 1 8],[0, 2 1 2]]").unwrap();
assert_eq!(
cmp_poly_mat,
poly_ring_mat.get_representative_least_nonnegative_residue()
)
}
}