1use crate::divisibility::DivisibilityRing;
2use crate::divisibility::DivisibilityRingStore;
3use crate::divisibility::Domain;
4use crate::pid::*;
5use crate::ring::*;
6use crate::homomorphism::*;
7use crate::rings::finite::FiniteRing;
8use crate::rings::poly::*;
9
10#[stability::unstable(feature = "enable")]
26pub 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>
27 where P: RingStore,
28 P::Type: PolyRing,
29 F: FnMut(&El<<P::Type as RingExtension>::BaseRing>) -> Result<El<<P::Type as RingExtension>::BaseRing>, E>
30{
31 assert!(poly_ring.degree(rhs).is_some());
32
33 let rhs_deg = poly_ring.degree(rhs).unwrap();
34 if poly_ring.degree(&lhs).is_none() {
35 return Ok((poly_ring.zero(), lhs));
36 }
37 let lhs_deg = poly_ring.degree(&lhs).unwrap();
38 if lhs_deg < rhs_deg {
39 return Ok((poly_ring.zero(), lhs));
40 }
41 let mut result = poly_ring.zero();
42 for i in (0..(lhs_deg + 1 - rhs_deg)).rev() {
43 let quo = left_div_lc(poly_ring.coefficient_at(&lhs, i + rhs_deg))?;
44 if !poly_ring.base_ring().is_zero(&quo) {
45 poly_ring.get_ring().add_assign_from_terms(
46 &mut lhs,
47 poly_ring.terms(rhs)
48 .map(|(c, j)| {
49 (poly_ring.base_ring().negate(poly_ring.base_ring().mul_ref(&quo, c)), i + j)
50 })
51 );
52 }
53 poly_ring.get_ring().add_assign_from_terms(&mut result, std::iter::once((quo, i)));
54 }
55 return Ok((result, lhs));
56}
57
58#[stability::unstable(feature = "enable")]
77pub 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>
78 where P: RingStore,
79 P::Type: PolyRing,
80 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: DivisibilityRing,
81 F: FnMut(&El<<P::Type as RingExtension>::BaseRing>) -> Result<El<<P::Type as RingExtension>::BaseRing>, E>
82{
83 assert!(poly_ring.degree(rhs).is_some());
84
85 let rhs_deg = poly_ring.degree(rhs).unwrap();
86 if poly_ring.degree(&lhs).is_none() {
87 return Ok(poly_ring.zero());
88 }
89 let lhs_deg = poly_ring.degree(&lhs).unwrap();
90 if lhs_deg < rhs_deg {
91 return Ok(poly_ring.zero());
92 }
93 for i in (0..(lhs_deg + 1 - rhs_deg)).rev() {
94 let quo = left_div_lc(poly_ring.coefficient_at(&lhs, i + rhs_deg))?;
95 if !poly_ring.base_ring().is_zero(&quo) {
96 poly_ring.get_ring().add_assign_from_terms(
97 &mut lhs,
98 poly_ring.terms(rhs)
99 .map(|(c, j)| {
100 (poly_ring.base_ring().negate(poly_ring.base_ring().mul_ref(&quo, c)), i + j)
101 })
102 );
103 }
104 _ = poly_ring.balance_poly(&mut lhs);
105 }
106 return Ok(lhs);
107}
108
109#[stability::unstable(feature = "enable")]
115pub 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>)
116 where P: RingStore,
117 P::Type: PolyRing,
118 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: Domain + PrincipalIdealRing
119{
120 assert!(!ring.is_zero(rhs));
121 let d = ring.degree(rhs).unwrap();
122 let base_ring = ring.base_ring();
123 let rhs_lc = ring.lc(rhs).unwrap();
124
125 let mut current_scale = base_ring.one();
126 let mut terms = Vec::new();
127 while let Some(lhs_deg) = ring.degree(&lhs) {
128 if lhs_deg < d {
129 break;
130 }
131 let lhs_lc = base_ring.clone_el(ring.lc(&lhs).unwrap());
132 let gcd = base_ring.ideal_gen(&lhs_lc, &rhs_lc);
133 let additional_scale = base_ring.checked_div(&rhs_lc, &gcd).unwrap();
134
135 base_ring.mul_assign_ref(&mut current_scale, &additional_scale);
136 terms.iter_mut().for_each(|(c, _)| base_ring.mul_assign_ref(c, &additional_scale));
137 ring.inclusion().mul_assign_map(&mut lhs, additional_scale);
138
139 let factor = base_ring.checked_div(ring.lc(&lhs).unwrap(), rhs_lc).unwrap();
140 ring.get_ring().add_assign_from_terms(&mut lhs,
141 ring.terms(rhs).map(|(c, i)| (base_ring.negate(base_ring.mul_ref(c, &factor)), i + lhs_deg - d))
142 );
143 terms.push((factor, lhs_deg - d));
144 }
145 return (ring.from_terms(terms.into_iter()), lhs, current_scale);
146}
147
148#[stability::unstable(feature = "enable")]
152pub enum PolyDivRemReducedError<R>
153 where R: ?Sized + RingBase
154{
155 NotReduced(R::Element),
156 NotDivisibleByContent(R::Element)
157}
158
159#[stability::unstable(feature = "enable")]
219pub 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>>
220 where P: RingStore,
221 P::Type: PolyRing,
222 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: FiniteRing + PrincipalIdealRing
223{
224 if ring.is_zero(rhs) && ring.is_zero(&lhs) {
225 return Ok((ring.zero(), ring.zero()));
226 } else if ring.is_zero(rhs) {
227 return Err(PolyDivRemReducedError::NotDivisibleByContent(ring.base_ring().zero()));
228 }
229 let rhs_deg = ring.degree(rhs).unwrap();
230 let mut result = ring.zero();
231 let zero = ring.base_ring().zero();
232 while ring.degree(&lhs).is_some() && ring.degree(&lhs).unwrap() >= rhs_deg {
233 let lhs_deg = ring.degree(&lhs).unwrap();
234 let lcf = ring.lc(&lhs).unwrap();
235 let mut h = ring.zero();
236 let mut annihilator = ring.base_ring().one();
237 let mut i: i64 = rhs_deg as i64;
238 let mut d = ring.base_ring().zero();
239 while ring.base_ring().checked_div(lcf, &d).is_none() {
240 debug_assert!(ring.base_ring().eq_el(ring.lc(&ring.mul_ref(&h, rhs)).unwrap_or(&zero), &d));
241 if i == -1 {
242 return Err(PolyDivRemReducedError::NotDivisibleByContent(d));
243 }
244 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)));
245 ring.inclusion().mul_assign_map(&mut h, s);
246 ring.add_assign(&mut h, ring.from_terms([(ring.base_ring().mul_ref(&annihilator, &t), lhs_deg - i as usize)]));
247 annihilator = ring.base_ring().annihilator(&new_d);
248 d = new_d;
249 i = i - 1;
250 if !ring.base_ring().is_unit(&ring.base_ring().ideal_gen(&annihilator, &d)) {
251 let nilpotent = ring.base_ring().annihilator(&ring.base_ring().ideal_gen(&annihilator, &d));
252 debug_assert!(!ring.base_ring().is_zero(&nilpotent));
253 debug_assert!(ring.base_ring().is_zero(&ring.base_ring().mul_ref(&nilpotent, &nilpotent)));
254 return Err(PolyDivRemReducedError::NotReduced(nilpotent));
255 }
256 debug_assert!(ring.base_ring().eq_el(ring.lc(&ring.mul_ref(&h, rhs)).unwrap_or(&zero), &d));
257 }
258 let scale = ring.base_ring().checked_div(lcf, &d).unwrap();
259 ring.inclusion().mul_assign_map(&mut h, scale);
260 ring.sub_assign(&mut lhs, ring.mul_ref(&h, rhs));
261 ring.add_assign(&mut result, h);
262 debug_assert!(ring.degree(&lhs).map(|d| d + 1).unwrap_or(0) <= lhs_deg);
263 }
264 return Ok((result, lhs));
265}
266
267#[stability::unstable(feature = "enable")]
273pub 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>>
274 where P: RingStore + Copy,
275 P::Type: PolyRing,
276 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: FiniteRing + PrincipalIdealRing
277{
278 let mut result = ring.zero();
279 while !ring.is_zero(&lhs) {
280 match poly_div_rem_finite_reduced(ring, lhs, &rhs) {
281 Ok((q, r)) => {
282 ring.add_assign(&mut result, q);
283 lhs = r;
284 },
285 Err(PolyDivRemReducedError::NotReduced(nilpotent)) => {
286 return Err(nilpotent);
287 },
288 Err(PolyDivRemReducedError::NotDivisibleByContent(_)) => {
289 return Ok(None);
290 }
291 }
292 let annihilate_lc = ring.base_ring().annihilator(ring.lc(&rhs).unwrap());
293 ring.inclusion().mul_assign_map(&mut rhs, annihilate_lc);
294 }
295 return Ok(Some(result));
296}
297
298#[cfg(test)]
299use crate::rings::zn::zn_64::*;
300#[cfg(test)]
301use crate::rings::zn::*;
302#[cfg(test)]
303use crate::integer::*;
304#[cfg(test)]
305use crate::primitive_int::*;
306#[cfg(test)]
307use dense_poly::DensePolyRing;
308
309#[test]
310fn test_poly_div_rem_finite_reduced() {
311 let base_ring = Zn::new(5 * 7 * 11);
312 let ring = DensePolyRing::new(base_ring, "X");
313
314 let [f, g, _q, _r] = ring.with_wrapped_indeterminate(|X| [
315 X.pow_ref(2),
316 X.pow_ref(2) * 5 + X * 7 + 11,
317 X * (-77) + 108,
318 91 * X - 33
319 ]);
320 let (q, r) = poly_div_rem_finite_reduced(&ring, ring.clone_el(&f), &g).ok().unwrap();
321 assert_eq!(1, ring.degree(&r).unwrap());
322 assert_el_eq!(&ring, &f, ring.add(ring.mul(q, g), r));
323
324 let [f, g] = ring.with_wrapped_indeterminate(|X| [
325 5 * X.pow_ref(2),
326 X * 5 * 11 + X * 7 * 11,
327 ]);
328 if let Err(PolyDivRemReducedError::NotDivisibleByContent(content)) = poly_div_rem_finite_reduced(&ring, f, &g) {
329 assert!(base_ring.checked_div(&content, &base_ring.int_hom().map(11)).is_some());
330 assert!(base_ring.checked_div(&base_ring.int_hom().map(11), &content).is_some());
331 } else {
332 assert!(false);
333 }
334
335 let base_ring = Zn::new(5 * 5 * 11);
336 let ring = DensePolyRing::new(base_ring, "X");
337
338 let [g] = ring.with_wrapped_indeterminate(|X| [
340 1 - 5 * X - 5 * X.pow_ref(2)
341 ]);
342 let f = ring.from_terms([(base_ring.int_hom().map(11), 2)]);
343 if let Err(PolyDivRemReducedError::NotReduced(nilpotent)) = poly_div_rem_finite_reduced(&ring, ring.clone_el(&f), &g) {
344 assert!(!base_ring.is_zero(&nilpotent));
345 assert!(base_ring.is_zero(&base_ring.pow(nilpotent, 2)));
346 } else {
347 assert!(false);
348 }
349}
350
351#[test]
352fn test_poly_div_rem_finite_reduced_nonmonic() {
353 let base_ring = zn_big::Zn::new(BigIntRing::RING, int_cast(8589934594, BigIntRing::RING, StaticRing::<i64>::RING));
354 let poly_ring = DensePolyRing::new(&base_ring, "X");
355 let [f, g] = poly_ring.with_wrapped_indeterminate(|X| [
356 1431655767 + -1431655765 * X,
357 -2 + X + X.pow_ref(2)
358 ]);
359 let (q, r) = poly_div_rem_finite_reduced(&poly_ring, poly_ring.clone_el(&g), &f).ok().unwrap();
360 assert_eq!(0, poly_ring.degree(&r).unwrap_or(0));
361 assert_el_eq!(&poly_ring, &g, poly_ring.add(poly_ring.mul_ref(&q, &f), r));
362}