1use std::cmp::max;
2
3use crate::computation::*;
4use crate::divisibility::*;
5use crate::homomorphism::*;
6use crate::pid::*;
7use crate::ring::*;
8use crate::rings::finite::FiniteRing;
9use crate::rings::poly::*;
10
11#[stability::unstable(feature = "enable")]
25pub 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>
26where
27 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 result = poly_ring.try_from_terms((0..(lhs_deg + 1 - rhs_deg)).rev().map(|i| {
42 let quo = left_div_lc(poly_ring.coefficient_at(&lhs, i + rhs_deg))?;
43 let neg_quo = poly_ring.base_ring().negate(quo);
44 if !poly_ring.base_ring().is_zero(&neg_quo) {
45 poly_ring.get_ring().add_assign_from_terms(
46 &mut lhs,
47 poly_ring
48 .terms(rhs)
49 .map(|(c, j)| (poly_ring.base_ring().mul_ref(&neg_quo, c), i + j)),
50 );
51 }
52 Ok((poly_ring.base_ring().negate(neg_quo), i))
53 }))?;
54 return Ok((result, lhs));
55}
56
57const FAST_POLY_DIV_THRESHOLD: usize = 32;
58
59pub fn fast_poly_div_rem<P, F, E, Controller>(
65 poly_ring: P,
66 f: El<P>,
67 g: &El<P>,
68 mut left_div_lc: F,
69 controller: Controller,
70) -> Result<(El<P>, El<P>), E>
71where
72 P: RingStore + Copy,
73 P::Type: PolyRing,
74 F: FnMut(&El<<P::Type as RingExtension>::BaseRing>) -> Result<El<<P::Type as RingExtension>::BaseRing>, E>,
75 Controller: ComputationController,
76{
77 fn fast_poly_div_impl<P, F, E, Controller>(
78 poly_ring: P,
79 f: El<P>,
80 g: &El<P>,
81 left_div_lc: &mut F,
82 controller: Controller,
83 ) -> Result<(El<P>, El<P>), E>
84 where
85 P: RingStore + Copy,
86 P::Type: PolyRing,
87 F: FnMut(&El<<P::Type as RingExtension>::BaseRing>) -> Result<El<<P::Type as RingExtension>::BaseRing>, E>,
88 Controller: ComputationController,
89 {
90 let deg_g = poly_ring.degree(g).unwrap();
91 if poly_ring.degree(&f).is_none() || poly_ring.degree(&f).unwrap() < deg_g {
92 return Ok((poly_ring.zero(), f));
93 }
94 let deg_f = poly_ring.degree(&f).unwrap();
95 if deg_g < FAST_POLY_DIV_THRESHOLD || (deg_f - deg_g) < FAST_POLY_DIV_THRESHOLD {
96 return poly_div_rem(poly_ring, f, g, left_div_lc);
97 }
98
99 let (split_degree_f, split_degree_g) = if deg_f >= 3 * deg_g {
100 (deg_f / 3, 0)
101 } else if 2 * (deg_f / 3) < deg_g {
102 (deg_g / 2, deg_g / 2)
103 } else {
104 (deg_f / 3, deg_g - deg_f / 3)
105 };
106 assert!(split_degree_f >= split_degree_g);
107 assert!(split_degree_f <= deg_f);
108 assert!(split_degree_g <= deg_g);
109
110 let f_upper = poly_ring.from_terms(
111 poly_ring
112 .terms(&f)
113 .filter(|(_, i)| *i >= split_degree_f)
114 .map(|(c, i)| (poly_ring.base_ring().clone_el(c), i - split_degree_f)),
115 );
116 let mut f_lower = f;
117 poly_ring.truncate_monomials(&mut f_lower, split_degree_f);
118 let g_upper = poly_ring.from_terms(
119 poly_ring
120 .terms(g)
121 .filter(|(_, i)| *i >= split_degree_g)
122 .map(|(c, i)| (poly_ring.base_ring().clone_el(c), i - split_degree_g)),
123 );
124 let mut g_lower = poly_ring.clone_el(g);
125 poly_ring.truncate_monomials(&mut g_lower, split_degree_g);
126
127 let (q_upper, r) = fast_poly_div_impl(
128 poly_ring,
129 poly_ring.clone_el(&f_upper),
130 &g_upper,
131 &mut *left_div_lc,
132 controller.clone(),
133 )?;
134 debug_assert!(
135 poly_ring.degree(&q_upper).is_none()
136 || poly_ring.degree(&q_upper).unwrap() <= deg_f + split_degree_g - split_degree_f - deg_g
137 );
138 debug_assert!(poly_ring.degree(&r).is_none() || poly_ring.degree(&r).unwrap() < deg_g - split_degree_g);
139
140 poly_ring.get_ring().add_assign_from_terms(
141 &mut f_lower,
142 poly_ring
143 .terms(&r)
144 .map(|(c, i)| (poly_ring.base_ring().clone_el(c), i + split_degree_f)),
145 );
146 debug_assert!(
147 poly_ring.degree(&f_lower).is_none()
148 || poly_ring.degree(&f_lower).unwrap() <= deg_g + split_degree_f - split_degree_g
149 );
150 poly_ring.mul_assign_ref(&mut g_lower, &q_upper);
151 poly_ring.get_ring().add_assign_from_terms(
152 &mut f_lower,
153 poly_ring.terms(&g_lower).map(|(c, i)| {
154 (
155 poly_ring.base_ring().negate(poly_ring.base_ring().clone_el(c)),
156 i + split_degree_f - split_degree_g,
157 )
158 }),
159 );
160 debug_assert!(
161 poly_ring.degree(&f_lower).is_none()
162 || poly_ring.degree(&f_lower).unwrap()
163 <= max(deg_f + split_degree_g - deg_g, deg_g + split_degree_f - split_degree_g)
164 );
165
166 let (mut q_lower, r) = fast_poly_div_impl(
167 poly_ring,
168 poly_ring.clone_el(&f_lower),
169 g,
170 &mut *left_div_lc,
171 controller,
172 )?;
173
174 poly_ring.get_ring().add_assign_from_terms(
175 &mut q_lower,
176 poly_ring
177 .terms(&q_upper)
178 .map(|(c, i)| (poly_ring.base_ring().clone_el(c), i + split_degree_f - split_degree_g)),
179 );
180 return Ok((q_lower, r));
181 }
182
183 assert!(!poly_ring.is_zero(g));
184 if poly_ring.is_zero(&f) {
185 return Ok((poly_ring.zero(), f));
186 }
187 controller.run_computation(
188 format_args!(
189 "fast_poly_div(ldeg={}, rdeg={})",
190 poly_ring.degree(&f).unwrap(),
191 poly_ring.degree(g).unwrap()
192 ),
193 |controller| fast_poly_div_impl(poly_ring, f, g, &mut left_div_lc, controller),
194 )
195}
196
197#[stability::unstable(feature = "enable")]
201pub fn poly_div_rem_domain<P>(
202 ring: P,
203 mut lhs: El<P>,
204 rhs: &El<P>,
205) -> (El<P>, El<P>, El<<P::Type as RingExtension>::BaseRing>)
206where
207 P: RingStore,
208 P::Type: PolyRing,
209 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: Domain + PrincipalIdealRing,
210{
211 assert!(!ring.is_zero(rhs));
212 let d = ring.degree(rhs).unwrap();
213 let base_ring = ring.base_ring();
214 let rhs_lc = ring.lc(rhs).unwrap();
215
216 let mut current_scale = base_ring.one();
217 let mut terms = Vec::new();
218 while let Some(lhs_deg) = ring.degree(&lhs) {
219 if lhs_deg < d {
220 break;
221 }
222 let lhs_lc = base_ring.clone_el(ring.lc(&lhs).unwrap());
223 let gcd = base_ring.ideal_gen(&lhs_lc, rhs_lc);
224 let additional_scale = base_ring.checked_div(rhs_lc, &gcd).unwrap();
225
226 base_ring.mul_assign_ref(&mut current_scale, &additional_scale);
227 terms
228 .iter_mut()
229 .for_each(|(c, _)| base_ring.mul_assign_ref(c, &additional_scale));
230 ring.inclusion().mul_assign_map(&mut lhs, additional_scale);
231
232 let factor = base_ring.checked_div(ring.lc(&lhs).unwrap(), rhs_lc).unwrap();
233 ring.get_ring().add_assign_from_terms(
234 &mut lhs,
235 ring.terms(rhs)
236 .map(|(c, i)| (base_ring.negate(base_ring.mul_ref(c, &factor)), i + lhs_deg - d)),
237 );
238 terms.push((factor, lhs_deg - d));
239 }
240 return (ring.from_terms(terms), lhs, current_scale);
241}
242
243#[stability::unstable(feature = "enable")]
245pub enum PolyDivRemReducedError<R>
246where
247 R: ?Sized + RingBase,
248{
249 NotReduced(R::Element),
250 NotDivisibleByContent(R::Element),
251}
252
253#[stability::unstable(feature = "enable")]
312pub fn poly_div_rem_finite_reduced<P>(
313 ring: P,
314 mut lhs: El<P>,
315 rhs: &El<P>,
316) -> Result<(El<P>, El<P>), PolyDivRemReducedError<<<P::Type as RingExtension>::BaseRing as RingStore>::Type>>
317where
318 P: RingStore,
319 P::Type: PolyRing,
320 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: FiniteRing + PrincipalIdealRing,
321{
322 if ring.is_zero(rhs) && ring.is_zero(&lhs) {
323 return Ok((ring.zero(), ring.zero()));
324 } else if ring.is_zero(rhs) {
325 return Err(PolyDivRemReducedError::NotDivisibleByContent(ring.base_ring().zero()));
326 }
327 let rhs_deg = ring.degree(rhs).unwrap();
328 let mut result = ring.zero();
329 let zero = ring.base_ring().zero();
330 while ring.degree(&lhs).is_some() && ring.degree(&lhs).unwrap() >= rhs_deg {
331 let lhs_deg = ring.degree(&lhs).unwrap();
332 let lcf = ring.lc(&lhs).unwrap();
333 let mut h = ring.zero();
334 let mut annihilator = ring.base_ring().one();
335 let mut i: i64 = rhs_deg.try_into().unwrap();
336 let mut d = ring.base_ring().zero();
337 while ring.base_ring().checked_div(lcf, &d).is_none() {
338 debug_assert!(
339 ring.base_ring()
340 .eq_el(ring.lc(&ring.mul_ref(&h, rhs)).unwrap_or(&zero), &d)
341 );
342 if i == -1 {
343 return Err(PolyDivRemReducedError::NotDivisibleByContent(d));
344 }
345 let (s, t, new_d) = ring.base_ring().extended_ideal_gen(
346 &d,
347 &ring
348 .base_ring()
349 .mul_ref(&annihilator, ring.coefficient_at(rhs, i as usize)),
350 );
351 ring.inclusion().mul_assign_map(&mut h, s);
352 ring.add_assign(
353 &mut h,
354 ring.from_terms([(ring.base_ring().mul_ref(&annihilator, &t), lhs_deg - i as usize)]),
355 );
356 annihilator = ring.base_ring().annihilator(&new_d);
357 d = new_d;
358 i -= 1;
359 if !ring.base_ring().is_unit(&ring.base_ring().ideal_gen(&annihilator, &d)) {
360 let nilpotent = ring
361 .base_ring()
362 .annihilator(&ring.base_ring().ideal_gen(&annihilator, &d));
363 debug_assert!(!ring.base_ring().is_zero(&nilpotent));
364 debug_assert!(
365 ring.base_ring()
366 .is_zero(&ring.base_ring().mul_ref(&nilpotent, &nilpotent))
367 );
368 return Err(PolyDivRemReducedError::NotReduced(nilpotent));
369 }
370 debug_assert!(
371 ring.base_ring()
372 .eq_el(ring.lc(&ring.mul_ref(&h, rhs)).unwrap_or(&zero), &d)
373 );
374 }
375 let scale = ring.base_ring().checked_div(lcf, &d).unwrap();
376 ring.inclusion().mul_assign_map(&mut h, scale);
377 ring.sub_assign(&mut lhs, ring.mul_ref(&h, rhs));
378 ring.add_assign(&mut result, h);
379 debug_assert!(ring.degree(&lhs).map(|d| d + 1).unwrap_or(0) <= lhs_deg);
380 }
381 return Ok((result, lhs));
382}
383
384#[stability::unstable(feature = "enable")]
388pub fn poly_checked_div_finite_reduced<P>(
389 ring: P,
390 mut lhs: El<P>,
391 mut rhs: El<P>,
392) -> Result<Option<El<P>>, El<<P::Type as RingExtension>::BaseRing>>
393where
394 P: RingStore + Copy,
395 P::Type: PolyRing,
396 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: FiniteRing + PrincipalIdealRing,
397{
398 let mut result = ring.zero();
399 while !ring.is_zero(&lhs) {
400 match poly_div_rem_finite_reduced(ring, lhs, &rhs) {
401 Ok((q, r)) => {
402 ring.add_assign(&mut result, q);
403 lhs = r;
404 }
405 Err(PolyDivRemReducedError::NotReduced(nilpotent)) => {
406 return Err(nilpotent);
407 }
408 Err(PolyDivRemReducedError::NotDivisibleByContent(_)) => {
409 return Ok(None);
410 }
411 }
412 let annihilate_lc = ring.base_ring().annihilator(ring.lc(&rhs).unwrap());
413 ring.inclusion().mul_assign_map(&mut rhs, annihilate_lc);
414 }
415 return Ok(Some(result));
416}
417
418#[cfg(test)]
419use dense_poly::DensePolyRing;
420
421#[cfg(test)]
422use crate::integer::*;
423#[cfg(test)]
424use crate::primitive_int::*;
425#[cfg(test)]
426use crate::rings::zn::zn_64::*;
427#[cfg(test)]
428use crate::rings::zn::*;
429
430#[test]
431fn test_poly_div_rem_finite_reduced() {
432 let base_ring = Zn::new(5 * 7 * 11);
433 let ring = DensePolyRing::new(base_ring, "X");
434
435 let [f, g, _q, _r] = ring.with_wrapped_indeterminate(|X| {
436 [
437 X.pow_ref(2),
438 X.pow_ref(2) * 5 + X * 7 + 11,
439 X * (-77) + 108,
440 91 * X - 33,
441 ]
442 });
443 let (q, r) = poly_div_rem_finite_reduced(&ring, ring.clone_el(&f), &g).ok().unwrap();
444 assert_eq!(1, ring.degree(&r).unwrap());
445 assert_el_eq!(&ring, &f, ring.add(ring.mul(q, g), r));
446
447 let [f, g] = ring.with_wrapped_indeterminate(|X| [5 * X.pow_ref(2), X * 5 * 11 + X * 7 * 11]);
448 if let Err(PolyDivRemReducedError::NotDivisibleByContent(content)) = poly_div_rem_finite_reduced(&ring, f, &g) {
449 assert!(base_ring.checked_div(&content, &base_ring.int_hom().map(11)).is_some());
450 assert!(base_ring.checked_div(&base_ring.int_hom().map(11), &content).is_some());
451 } else {
452 assert!(false);
453 }
454
455 let base_ring = Zn::new(5 * 5 * 11);
456 let ring = DensePolyRing::new(base_ring, "X");
457
458 let [g] = ring.with_wrapped_indeterminate(|X| [1 - 5 * X - 5 * X.pow_ref(2)]);
460 let f = ring.from_terms([(base_ring.int_hom().map(11), 2)]);
461 if let Err(PolyDivRemReducedError::NotReduced(nilpotent)) =
462 poly_div_rem_finite_reduced(&ring, ring.clone_el(&f), &g)
463 {
464 assert!(!base_ring.is_zero(&nilpotent));
465 assert!(base_ring.is_zero(&base_ring.pow(nilpotent, 2)));
466 } else {
467 assert!(false);
468 }
469}
470
471#[test]
472fn test_poly_div_rem_finite_reduced_nonmonic() {
473 let base_ring = zn_big::Zn::new(
474 BigIntRing::RING,
475 int_cast(8589934594, BigIntRing::RING, StaticRing::<i64>::RING),
476 );
477 let poly_ring = DensePolyRing::new(&base_ring, "X");
478 let [f, g] = poly_ring.with_wrapped_indeterminate(|X| [1431655767 + -1431655765 * X, -2 + X + X.pow_ref(2)]);
479 let (q, r) = poly_div_rem_finite_reduced(&poly_ring, poly_ring.clone_el(&g), &f)
480 .ok()
481 .unwrap();
482 assert_eq!(0, poly_ring.degree(&r).unwrap_or(0));
483 assert_el_eq!(&poly_ring, &g, poly_ring.add(poly_ring.mul_ref(&q, &f), r));
484}
485
486#[test]
487fn test_fast_poly_div() {
488 let ZZ = BigIntRing::RING;
489 let ZZX = DensePolyRing::new(ZZ, "X");
490 let [f, g] = ZZX.with_wrapped_indeterminate(|X| {
491 [
492 X.pow_ref(80) - 1,
493 X.pow_ref(40) - 2 * X.pow_ref(33) + X.pow_ref(21) - X + 10,
494 ]
495 });
496 assert_el_eq!(
497 &ZZX,
498 ZZX.div_rem_monic(ZZX.clone_el(&f), &g).0,
499 fast_poly_div_rem(&ZZX, ZZX.clone_el(&f), &g, |c| Ok(ZZ.clone_el(c)), LOG_PROGRESS)
500 .unwrap_or_else(no_error)
501 .0
502 );
503}