use std::cmp::max;
use crate::computation::*;
use crate::divisibility::*;
use crate::homomorphism::*;
use crate::pid::*;
use crate::ring::*;
use crate::rings::finite::FiniteRing;
use crate::rings::poly::*;
#[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);
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), 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 -= 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 dense_poly::DensePolyRing;
#[cfg(test)]
use crate::integer::*;
#[cfg(test)]
use crate::primitive_int::*;
#[cfg(test)]
use crate::rings::zn::zn_64::*;
#[cfg(test)]
use crate::rings::zn::*;
#[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
);
}