use crate::divisibility::*;
use crate::computation::*;
use crate::pid::*;
use crate::ring::*;
use crate::homomorphism::*;
use crate::rings::finite::FiniteRing;
use crate::rings::poly::*;
use std::cmp::max;
#[stability::unstable(feature = "enable")]
pub fn poly_div_rem<P, F, E>(poly_ring: P, mut lhs: El<P>, rhs: &El<P>, mut left_div_lc: F) -> Result<(El<P>, El<P>), E>
where P: RingStore,
P::Type: PolyRing,
F: FnMut(&El<<P::Type as RingExtension>::BaseRing>) -> Result<El<<P::Type as RingExtension>::BaseRing>, E>
{
assert!(poly_ring.degree(rhs).is_some());
let rhs_deg = poly_ring.degree(rhs).unwrap();
if poly_ring.degree(&lhs).is_none() {
return Ok((poly_ring.zero(), lhs));
}
let lhs_deg = poly_ring.degree(&lhs).unwrap();
if lhs_deg < rhs_deg {
return Ok((poly_ring.zero(), lhs));
}
let result = poly_ring.try_from_terms(
(0..(lhs_deg + 1 - rhs_deg)).rev().map(|i| {
let quo = left_div_lc(poly_ring.coefficient_at(&lhs, i + rhs_deg))?;
let neg_quo = poly_ring.base_ring().negate(quo);
if !poly_ring.base_ring().is_zero(&neg_quo) {
poly_ring.get_ring().add_assign_from_terms(
&mut lhs,
poly_ring.terms(rhs).map(|(c, j)|
(poly_ring.base_ring().mul_ref(&neg_quo, c), i + j)
)
);
}
Ok((poly_ring.base_ring().negate(neg_quo), i))
})
)?;
return Ok((result, lhs));
}
const FAST_POLY_DIV_THRESHOLD: usize = 32;
pub fn fast_poly_div_rem<P, F, E, Controller>(poly_ring: P, f: El<P>, g: &El<P>, mut left_div_lc: F, controller: Controller)-> Result<(El<P>, El<P>), E>
where P: RingStore + Copy,
P::Type: PolyRing,
F: FnMut(&El<<P::Type as RingExtension>::BaseRing>) -> Result<El<<P::Type as RingExtension>::BaseRing>, E>,
Controller: ComputationController
{
fn fast_poly_div_impl<P, F, E, Controller>(poly_ring: P, f: El<P>, g: &El<P>, left_div_lc: &mut F, controller: Controller)-> Result<(El<P>, El<P>), E>
where P: RingStore + Copy,
P::Type: PolyRing,
F: FnMut(&El<<P::Type as RingExtension>::BaseRing>) -> Result<El<<P::Type as RingExtension>::BaseRing>, E>,
Controller: ComputationController
{
let deg_g = poly_ring.degree(g).unwrap();
if poly_ring.degree(&f).is_none() || poly_ring.degree(&f).unwrap() < deg_g {
return Ok((poly_ring.zero(), f));
}
let deg_f = poly_ring.degree(&f).unwrap();
if deg_g < FAST_POLY_DIV_THRESHOLD || (deg_f - deg_g) < FAST_POLY_DIV_THRESHOLD {
return poly_div_rem(poly_ring, f, g, left_div_lc);
}
let (split_degree_f, split_degree_g) = if deg_f >= 3 * deg_g {
(deg_f / 3, 0)
} else if 2 * (deg_f / 3) < deg_g {
(deg_g / 2, deg_g / 2)
} else {
(deg_f / 3, deg_g - deg_f / 3)
};
assert!(split_degree_f >= split_degree_g);
assert!(split_degree_f <= deg_f);
assert!(split_degree_g <= deg_g);
let f_upper = poly_ring.from_terms(poly_ring.terms(&f).filter(|(_, i)| *i >= split_degree_f).map(|(c, i)| (poly_ring.base_ring().clone_el(c), i - split_degree_f)));
let mut f_lower = f;
poly_ring.truncate_monomials(&mut f_lower, split_degree_f);
let g_upper = poly_ring.from_terms(poly_ring.terms(&g).filter(|(_, i)| *i >= split_degree_g).map(|(c, i)| (poly_ring.base_ring().clone_el(c), i - split_degree_g)));
let mut g_lower = poly_ring.clone_el(g);
poly_ring.truncate_monomials(&mut g_lower, split_degree_g);
let (q_upper, r) = fast_poly_div_impl(poly_ring, poly_ring.clone_el(&f_upper), &g_upper, &mut *left_div_lc, controller.clone())?;
debug_assert!(poly_ring.degree(&q_upper).is_none() || poly_ring.degree(&q_upper).unwrap() <= deg_f + split_degree_g - split_degree_f - deg_g);
debug_assert!(poly_ring.degree(&r).is_none() || poly_ring.degree(&r).unwrap() <= deg_g - split_degree_g - 1);
poly_ring.get_ring().add_assign_from_terms(&mut f_lower, poly_ring.terms(&r).map(|(c, i)| (poly_ring.base_ring().clone_el(c), i + split_degree_f)));
debug_assert!(poly_ring.degree(&f_lower).is_none() || poly_ring.degree(&f_lower).unwrap() <= deg_g + split_degree_f - split_degree_g);
poly_ring.mul_assign_ref(&mut g_lower, &q_upper);
poly_ring.get_ring().add_assign_from_terms(&mut f_lower, poly_ring.terms(&g_lower).map(|(c, i)| (poly_ring.base_ring().negate(poly_ring.base_ring().clone_el(c)), i + split_degree_f - split_degree_g)));
debug_assert!(poly_ring.degree(&f_lower).is_none() || poly_ring.degree(&f_lower).unwrap() <= max(deg_f + split_degree_g - deg_g, deg_g + split_degree_f - split_degree_g));
let (mut q_lower, r) = fast_poly_div_impl(poly_ring, poly_ring.clone_el(&f_lower), g, &mut *left_div_lc, controller)?;
poly_ring.get_ring().add_assign_from_terms(&mut q_lower, poly_ring.terms(&q_upper).map(|(c, i)| (poly_ring.base_ring().clone_el(c), i + split_degree_f - split_degree_g)));
return Ok((q_lower, r));
}
assert!(!poly_ring.is_zero(g));
if poly_ring.is_zero(&f) {
return Ok((poly_ring.zero(), f));
}
controller.run_computation(format_args!("fast_poly_div(ldeg={}, rdeg={})", poly_ring.degree(&f).unwrap(), poly_ring.degree(g).unwrap()), |controller|
fast_poly_div_impl(poly_ring, f, g, &mut left_div_lc, controller)
)
}
#[stability::unstable(feature = "enable")]
pub fn poly_div_rem_domain<P>(ring: P, mut lhs: El<P>, rhs: &El<P>) -> (El<P>, El<P>, El<<P::Type as RingExtension>::BaseRing>)
where P: RingStore,
P::Type: PolyRing,
<<P::Type as RingExtension>::BaseRing as RingStore>::Type: Domain + PrincipalIdealRing
{
assert!(!ring.is_zero(rhs));
let d = ring.degree(rhs).unwrap();
let base_ring = ring.base_ring();
let rhs_lc = ring.lc(rhs).unwrap();
let mut current_scale = base_ring.one();
let mut terms = Vec::new();
while let Some(lhs_deg) = ring.degree(&lhs) {
if lhs_deg < d {
break;
}
let lhs_lc = base_ring.clone_el(ring.lc(&lhs).unwrap());
let gcd = base_ring.ideal_gen(&lhs_lc, &rhs_lc);
let additional_scale = base_ring.checked_div(&rhs_lc, &gcd).unwrap();
base_ring.mul_assign_ref(&mut current_scale, &additional_scale);
terms.iter_mut().for_each(|(c, _)| base_ring.mul_assign_ref(c, &additional_scale));
ring.inclusion().mul_assign_map(&mut lhs, additional_scale);
let factor = base_ring.checked_div(ring.lc(&lhs).unwrap(), rhs_lc).unwrap();
ring.get_ring().add_assign_from_terms(&mut lhs,
ring.terms(rhs).map(|(c, i)| (base_ring.negate(base_ring.mul_ref(c, &factor)), i + lhs_deg - d))
);
terms.push((factor, lhs_deg - d));
}
return (ring.from_terms(terms.into_iter()), lhs, current_scale);
}
#[stability::unstable(feature = "enable")]
pub enum PolyDivRemReducedError<R>
where R: ?Sized + RingBase
{
NotReduced(R::Element),
NotDivisibleByContent(R::Element)
}
#[stability::unstable(feature = "enable")]
pub fn poly_div_rem_finite_reduced<P>(ring: P, mut lhs: El<P>, rhs: &El<P>) -> Result<(El<P>, El<P>), PolyDivRemReducedError<<<P::Type as RingExtension>::BaseRing as RingStore>::Type>>
where P: RingStore,
P::Type: PolyRing,
<<P::Type as RingExtension>::BaseRing as RingStore>::Type: FiniteRing + PrincipalIdealRing
{
if ring.is_zero(rhs) && ring.is_zero(&lhs) {
return Ok((ring.zero(), ring.zero()));
} else if ring.is_zero(rhs) {
return Err(PolyDivRemReducedError::NotDivisibleByContent(ring.base_ring().zero()));
}
let rhs_deg = ring.degree(rhs).unwrap();
let mut result = ring.zero();
let zero = ring.base_ring().zero();
while ring.degree(&lhs).is_some() && ring.degree(&lhs).unwrap() >= rhs_deg {
let lhs_deg = ring.degree(&lhs).unwrap();
let lcf = ring.lc(&lhs).unwrap();
let mut h = ring.zero();
let mut annihilator = ring.base_ring().one();
let mut i: i64 = rhs_deg.try_into().unwrap();
let mut d = ring.base_ring().zero();
while ring.base_ring().checked_div(lcf, &d).is_none() {
debug_assert!(ring.base_ring().eq_el(ring.lc(&ring.mul_ref(&h, rhs)).unwrap_or(&zero), &d));
if i == -1 {
return Err(PolyDivRemReducedError::NotDivisibleByContent(d));
}
let (s, t, new_d) = ring.base_ring().extended_ideal_gen(&d, &ring.base_ring().mul_ref(&annihilator, ring.coefficient_at(&rhs, i as usize)));
ring.inclusion().mul_assign_map(&mut h, s);
ring.add_assign(&mut h, ring.from_terms([(ring.base_ring().mul_ref(&annihilator, &t), lhs_deg - i as usize)]));
annihilator = ring.base_ring().annihilator(&new_d);
d = new_d;
i = i - 1;
if !ring.base_ring().is_unit(&ring.base_ring().ideal_gen(&annihilator, &d)) {
let nilpotent = ring.base_ring().annihilator(&ring.base_ring().ideal_gen(&annihilator, &d));
debug_assert!(!ring.base_ring().is_zero(&nilpotent));
debug_assert!(ring.base_ring().is_zero(&ring.base_ring().mul_ref(&nilpotent, &nilpotent)));
return Err(PolyDivRemReducedError::NotReduced(nilpotent));
}
debug_assert!(ring.base_ring().eq_el(ring.lc(&ring.mul_ref(&h, rhs)).unwrap_or(&zero), &d));
}
let scale = ring.base_ring().checked_div(lcf, &d).unwrap();
ring.inclusion().mul_assign_map(&mut h, scale);
ring.sub_assign(&mut lhs, ring.mul_ref(&h, rhs));
ring.add_assign(&mut result, h);
debug_assert!(ring.degree(&lhs).map(|d| d + 1).unwrap_or(0) <= lhs_deg);
}
return Ok((result, lhs));
}
#[stability::unstable(feature = "enable")]
pub fn poly_checked_div_finite_reduced<P>(ring: P, mut lhs: El<P>, mut rhs: El<P>) -> Result<Option<El<P>>, El<<P::Type as RingExtension>::BaseRing>>
where P: RingStore + Copy,
P::Type: PolyRing,
<<P::Type as RingExtension>::BaseRing as RingStore>::Type: FiniteRing + PrincipalIdealRing
{
let mut result = ring.zero();
while !ring.is_zero(&lhs) {
match poly_div_rem_finite_reduced(ring, lhs, &rhs) {
Ok((q, r)) => {
ring.add_assign(&mut result, q);
lhs = r;
},
Err(PolyDivRemReducedError::NotReduced(nilpotent)) => {
return Err(nilpotent);
},
Err(PolyDivRemReducedError::NotDivisibleByContent(_)) => {
return Ok(None);
}
}
let annihilate_lc = ring.base_ring().annihilator(ring.lc(&rhs).unwrap());
ring.inclusion().mul_assign_map(&mut rhs, annihilate_lc);
}
return Ok(Some(result));
}
#[cfg(test)]
use crate::rings::zn::zn_64::*;
#[cfg(test)]
use crate::rings::zn::*;
#[cfg(test)]
use crate::integer::*;
#[cfg(test)]
use crate::primitive_int::*;
#[cfg(test)]
use dense_poly::DensePolyRing;
#[test]
fn test_poly_div_rem_finite_reduced() {
let base_ring = Zn::new(5 * 7 * 11);
let ring = DensePolyRing::new(base_ring, "X");
let [f, g, _q, _r] = ring.with_wrapped_indeterminate(|X| [
X.pow_ref(2),
X.pow_ref(2) * 5 + X * 7 + 11,
X * (-77) + 108,
91 * X - 33
]);
let (q, r) = poly_div_rem_finite_reduced(&ring, ring.clone_el(&f), &g).ok().unwrap();
assert_eq!(1, ring.degree(&r).unwrap());
assert_el_eq!(&ring, &f, ring.add(ring.mul(q, g), r));
let [f, g] = ring.with_wrapped_indeterminate(|X| [
5 * X.pow_ref(2),
X * 5 * 11 + X * 7 * 11,
]);
if let Err(PolyDivRemReducedError::NotDivisibleByContent(content)) = poly_div_rem_finite_reduced(&ring, f, &g) {
assert!(base_ring.checked_div(&content, &base_ring.int_hom().map(11)).is_some());
assert!(base_ring.checked_div(&base_ring.int_hom().map(11), &content).is_some());
} else {
assert!(false);
}
let base_ring = Zn::new(5 * 5 * 11);
let ring = DensePolyRing::new(base_ring, "X");
let [g] = ring.with_wrapped_indeterminate(|X| [
1 - 5 * X - 5 * X.pow_ref(2)
]);
let f = ring.from_terms([(base_ring.int_hom().map(11), 2)]);
if let Err(PolyDivRemReducedError::NotReduced(nilpotent)) = poly_div_rem_finite_reduced(&ring, ring.clone_el(&f), &g) {
assert!(!base_ring.is_zero(&nilpotent));
assert!(base_ring.is_zero(&base_ring.pow(nilpotent, 2)));
} else {
assert!(false);
}
}
#[test]
fn test_poly_div_rem_finite_reduced_nonmonic() {
let base_ring = zn_big::Zn::new(BigIntRing::RING, int_cast(8589934594, BigIntRing::RING, StaticRing::<i64>::RING));
let poly_ring = DensePolyRing::new(&base_ring, "X");
let [f, g] = poly_ring.with_wrapped_indeterminate(|X| [
1431655767 + -1431655765 * X,
-2 + X + X.pow_ref(2)
]);
let (q, r) = poly_div_rem_finite_reduced(&poly_ring, poly_ring.clone_el(&g), &f).ok().unwrap();
assert_eq!(0, poly_ring.degree(&r).unwrap_or(0));
assert_el_eq!(&poly_ring, &g, poly_ring.add(poly_ring.mul_ref(&q, &f), r));
}
#[test]
fn test_fast_poly_div() {
let ZZ = BigIntRing::RING;
let ZZX = DensePolyRing::new(ZZ, "X");
let [f, g] = ZZX.with_wrapped_indeterminate(|X| [X.pow_ref(80) - 1, X.pow_ref(40) - 2 * X.pow_ref(33) + X.pow_ref(21) - X + 10]);
assert_el_eq!(&ZZX, ZZX.div_rem_monic(ZZX.clone_el(&f), &g).0, fast_poly_div_rem(&ZZX, ZZX.clone_el(&f), &g, |c| Ok(ZZ.clone_el(c)), LOG_PROGRESS).unwrap_or_else(no_error).0);
}