use crate::divisibility::DivisibilityRing;
use crate::divisibility::DivisibilityRingStore;
use crate::divisibility::Domain;
use crate::pid::*;
use crate::ring::*;
use crate::homomorphism::*;
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 mut result = poly_ring.zero();
for i in (0..(lhs_deg + 1 - rhs_deg)).rev() {
let quo = left_div_lc(poly_ring.coefficient_at(&lhs, i + rhs_deg))?;
if !poly_ring.base_ring().is_zero(&quo) {
poly_ring.get_ring().add_assign_from_terms(
&mut lhs,
poly_ring.terms(rhs)
.map(|(c, j)| {
(poly_ring.base_ring().negate(poly_ring.base_ring().mul_ref(&quo, c)), i + j)
})
);
}
poly_ring.get_ring().add_assign_from_terms(&mut result, std::iter::once((quo, i)));
}
return Ok((result, lhs));
}
#[stability::unstable(feature = "enable")]
pub fn poly_rem<P, F, E>(poly_ring: P, mut lhs: El<P>, rhs: &El<P>, mut left_div_lc: F) -> Result<El<P>, E>
where P: RingStore,
P::Type: PolyRing,
<<P::Type as RingExtension>::BaseRing as RingStore>::Type: DivisibilityRing,
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());
}
let lhs_deg = poly_ring.degree(&lhs).unwrap();
if lhs_deg < rhs_deg {
return Ok(poly_ring.zero());
}
for i in (0..(lhs_deg + 1 - rhs_deg)).rev() {
let quo = left_div_lc(poly_ring.coefficient_at(&lhs, i + rhs_deg))?;
if !poly_ring.base_ring().is_zero(&quo) {
poly_ring.get_ring().add_assign_from_terms(
&mut lhs,
poly_ring.terms(rhs)
.map(|(c, j)| {
(poly_ring.base_ring().negate(poly_ring.base_ring().mul_ref(&quo, c)), i + j)
})
);
}
_ = poly_ring.balance_poly(&mut lhs);
}
return Ok(lhs);
}
#[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 as i64;
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));
}