1use std::cmp::min;
2use std::fmt::Debug;
3
4use append_only_vec::AppendOnlyVec;
5
6use crate::computation::*;
7use crate::delegate::{UnwrapHom, WrapHom};
8use crate::divisibility::{DivisibilityRing, DivisibilityRingStore};
9use crate::field::Field;
10use crate::homomorphism::Homomorphism;
11use crate::local::{PrincipalLocalRing, PrincipalLocalRingStore};
12use crate::pid::PrincipalIdealRingStore;
13use crate::ring::*;
14use crate::rings::multivariate::*;
15use crate::seq::*;
16
17#[stability::unstable(feature = "enable")]
18#[derive(PartialEq, Clone, Eq, Hash)]
19pub enum SPoly {
20 Standard(usize, usize),
21 Nilpotent(
22 usize,
24 usize,
26 ),
27}
28
29impl Debug for SPoly {
30 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31 match self {
32 SPoly::Standard(i, j) => write!(f, "S({}, {})", i, j),
33 SPoly::Nilpotent(i, k) => write!(f, "p^{} F({})", k, i),
34 }
35 }
36}
37
38fn term_xlcm<P>(
39 ring: P,
40 (l_c, l_m): (&PolyCoeff<P>, &PolyMonomial<P>),
41 (r_c, r_m): (&PolyCoeff<P>, &PolyMonomial<P>),
42) -> (
43 (PolyCoeff<P>, PolyMonomial<P>),
44 (PolyCoeff<P>, PolyMonomial<P>),
45 (PolyCoeff<P>, PolyMonomial<P>),
46)
47where
48 P: RingStore,
49 P::Type: MultivariatePolyRing,
50 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: PrincipalLocalRing,
51{
52 let d_c = ring.base_ring().ideal_gen(l_c, r_c);
53 let m_m = ring.monomial_lcm(ring.clone_monomial(l_m), r_m);
54 let l_factor = ring.base_ring().checked_div(r_c, &d_c).unwrap();
55 let r_factor = ring.base_ring().checked_div(l_c, &d_c).unwrap();
56 let m_c = ring
57 .base_ring()
58 .mul_ref_snd(ring.base_ring().mul_ref_snd(d_c, &r_factor), &l_factor);
59 return (
60 (
61 l_factor,
62 ring.monomial_div(ring.clone_monomial(&m_m), l_m).ok().unwrap(),
63 ),
64 (
65 r_factor,
66 ring.monomial_div(ring.clone_monomial(&m_m), r_m).ok().unwrap(),
67 ),
68 (m_c, m_m),
69 );
70}
71
72fn term_lcm<P>(
73 ring: P,
74 (l_c, l_m): (&PolyCoeff<P>, &PolyMonomial<P>),
75 (r_c, r_m): (&PolyCoeff<P>, &PolyMonomial<P>),
76) -> (PolyCoeff<P>, PolyMonomial<P>)
77where
78 P: RingStore,
79 P::Type: MultivariatePolyRing,
80 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: PrincipalLocalRing,
81{
82 let d_c = ring.base_ring().ideal_gen(l_c, r_c);
83 let m_m = ring.monomial_lcm(ring.clone_monomial(l_m), r_m);
84 let l_factor = ring.base_ring().checked_div(r_c, &d_c).unwrap();
85 let r_factor = ring.base_ring().checked_div(l_c, &d_c).unwrap();
86 let m_c = ring
87 .base_ring()
88 .mul_ref_snd(ring.base_ring().mul_ref_snd(d_c, &r_factor), &l_factor);
89 return (m_c, m_m);
90}
91
92impl SPoly {
93 #[stability::unstable(feature = "enable")]
94 pub fn lcm_term<P, O>(&self, ring: P, basis: &[El<P>], order: O) -> (PolyCoeff<P>, PolyMonomial<P>)
95 where
96 P: RingStore + Copy,
97 P::Type: MultivariatePolyRing,
98 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: PrincipalLocalRing,
99 O: MonomialOrder + Copy,
100 {
101 match self {
102 SPoly::Standard(i, j) => term_lcm(
103 &ring,
104 ring.LT(&basis[*i], order).unwrap(),
105 ring.LT(&basis[*j], order).unwrap(),
106 ),
107 Self::Nilpotent(i, k) => {
108 let (c, m) = ring.LT(&basis[*i], order).unwrap();
109 (
110 ring.base_ring().mul_ref_fst(
111 c,
112 ring.base_ring()
113 .pow(ring.base_ring().clone_el(ring.base_ring().max_ideal_gen()), *k),
114 ),
115 ring.clone_monomial(m),
116 )
117 }
118 }
119 }
120
121 #[stability::unstable(feature = "enable")]
122 pub fn poly<P, O>(&self, ring: P, basis: &[El<P>], order: O) -> El<P>
123 where
124 P: RingStore + Copy,
125 P::Type: MultivariatePolyRing,
126 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: PrincipalLocalRing,
127 O: MonomialOrder + Copy,
128 {
129 match self {
130 SPoly::Standard(i, j) => {
131 let (f1_factor, f2_factor, _) = term_xlcm(
132 &ring,
133 ring.LT(&basis[*i], order).unwrap(),
134 ring.LT(&basis[*j], order).unwrap(),
135 );
136 let mut f1_scaled = ring.clone_el(&basis[*i]);
137 ring.mul_assign_monomial(&mut f1_scaled, f1_factor.1);
138 ring.inclusion().mul_assign_map(&mut f1_scaled, f1_factor.0);
139 let mut f2_scaled = ring.clone_el(&basis[*j]);
140 ring.mul_assign_monomial(&mut f2_scaled, f2_factor.1);
141 ring.inclusion().mul_assign_map(&mut f2_scaled, f2_factor.0);
142 return ring.sub(f1_scaled, f2_scaled);
143 }
144 SPoly::Nilpotent(i, k) => {
145 let mut result = ring.clone_el(&basis[*i]);
146 ring.inclusion().mul_assign_map(
147 &mut result,
148 ring.base_ring()
149 .pow(ring.base_ring().clone_el(ring.base_ring().max_ideal_gen()), *k),
150 );
151 return result;
152 }
153 }
154 }
155}
156
157#[inline(never)]
158fn find_reducer<'a, 'b, P, O, I>(
159 ring: P,
160 f: &El<P>,
161 reducers: I,
162 order: O,
163) -> Option<(usize, &'a El<P>, PolyCoeff<P>, PolyMonomial<P>)>
164where
165 P: RingStore + Copy,
166 P::Type: MultivariatePolyRing,
167 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: DivisibilityRing,
168 O: MonomialOrder + Copy,
169 I: Iterator<Item = (&'a El<P>, &'b ExpandedMonomial)>,
170{
171 if ring.is_zero(f) {
172 return None;
173 }
174 let (f_lc, f_lm) = ring.LT(f, order).unwrap();
175 let f_lm_expanded = ring.expand_monomial(f_lm);
176 reducers
177 .enumerate()
178 .filter_map(|(i, (reducer, reducer_lm_expanded))| {
179 if (0..ring.indeterminate_count()).all(|j| reducer_lm_expanded[j] <= f_lm_expanded[j]) {
180 let (r_lc, r_lm) = ring.LT(reducer, order).unwrap();
181 let quo_m = ring.monomial_div(ring.clone_monomial(f_lm), r_lm).ok().unwrap();
182 if let Some(quo_c) = ring.base_ring().checked_div(f_lc, r_lc) {
183 return Some((i, reducer, quo_c, quo_m));
184 }
185 }
186 return None;
187 })
188 .next()
189}
190
191#[inline(never)]
192fn filter_spoly<P, O>(ring: P, new_spoly: SPoly, basis: &[El<P>], order: O) -> Option<usize>
193where
194 P: RingStore + Copy,
195 P::Type: MultivariatePolyRing,
196 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: PrincipalLocalRing,
197 O: MonomialOrder + Copy,
198{
199 match new_spoly {
200 SPoly::Standard(i, k) => {
201 assert!(i < k);
202 let (bi_c, bi_m) = ring.LT(&basis[i], order).unwrap();
203 let (bk_c, bk_m) = ring.LT(&basis[k], order).unwrap();
204 let (S_c, S_m) = term_lcm(ring, (bi_c, bi_m), (bk_c, bk_m));
205 let S_c_val = ring.base_ring().valuation(&S_c).unwrap();
206
207 if S_c_val == 0
208 && order.eq_mon(
209 ring,
210 &ring.monomial_div(ring.clone_monomial(&S_m), bi_m).ok().unwrap(),
211 bk_m,
212 )
213 {
214 return Some(usize::MAX);
215 }
216
217 (0..k)
218 .filter_map(|j| {
219 if j == i {
220 return None;
221 }
222 let (bj_c, bj_m) = ring.LT(&basis[j], order).unwrap();
226 let (f_c, f_m) = term_lcm(ring, (bj_c, bj_m), (bk_c, bk_m));
227 let f_c_val = ring.base_ring().valuation(&f_c).unwrap();
228
229 if j < i && order.eq_mon(ring, &f_m, &S_m) && f_c_val <= S_c_val {
230 return Some(j);
231 }
232 if let Ok(quo) = ring.monomial_div(ring.clone_monomial(&S_m), &f_m)
233 && f_c_val <= S_c_val
234 && (f_c_val < S_c_val || ring.monomial_deg(&quo) > 0)
235 {
236 return Some(j);
237 }
238 return None;
239 })
240 .next()
241 }
242 SPoly::Nilpotent(i, k) => {
243 let nilpotent_power = ring.base_ring().nilpotent_power().unwrap();
244 let f = &basis[i];
245
246 let mut smallest_elim_coeff_valuation = usize::MAX;
247 let mut current = ring.LT(f, order).unwrap();
248 while ring.base_ring().valuation(current.0).unwrap() + k >= nilpotent_power {
249 smallest_elim_coeff_valuation = min(
250 smallest_elim_coeff_valuation,
251 ring.base_ring().valuation(current.0).unwrap(),
252 );
253 let next = ring.largest_term_lt(f, order, current.1);
254 if next.is_none() {
255 return Some(usize::MAX);
256 }
257 current = next.unwrap();
258 }
259 assert!(
260 smallest_elim_coeff_valuation == usize::MAX || smallest_elim_coeff_valuation + k >= nilpotent_power
261 );
262 if smallest_elim_coeff_valuation == usize::MAX || smallest_elim_coeff_valuation + k > nilpotent_power {
263 return Some(usize::MAX);
264 } else {
265 return None;
266 }
267 }
268 }
269}
270
271#[stability::unstable(feature = "enable")]
272pub fn default_sort_fn<P, O>(ring: P, order: O) -> impl FnMut(&mut [SPoly], &[El<P>])
273where
274 P: RingStore + Copy,
275 P::Type: MultivariatePolyRing,
276 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: PrincipalLocalRing,
277 O: MonomialOrder + Copy,
278{
279 move |open, basis| {
280 open.sort_by_key(|spoly| {
281 let (lc, lm) = spoly.lcm_term(ring, basis, order);
282 (
283 -(ring.base_ring().valuation(&lc).unwrap_or(0) as i64),
284 -(ring.monomial_deg(&lm) as i64),
285 )
286 })
287 }
288}
289
290#[stability::unstable(feature = "enable")]
291pub type ExpandedMonomial = Vec<usize>;
292
293fn augment_lm<P, O>(ring: P, f: El<P>, order: O) -> (El<P>, ExpandedMonomial)
294where
295 P: RingStore + Copy,
296 P::Type: MultivariatePolyRing,
297 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: PrincipalLocalRing,
298 O: MonomialOrder,
299{
300 let exponents = ring.expand_monomial(ring.LT(&f, order).unwrap().1);
301 return (f, exponents);
302}
303
304#[stability::unstable(feature = "enable")]
340pub fn buchberger<P, O, Controller, SortFn, AbortFn>(
341 ring: P,
342 input_basis: Vec<El<P>>,
343 order: O,
344 mut sort_spolys: SortFn,
345 mut abort_early_if: AbortFn,
346 controller: Controller,
347) -> Result<Vec<El<P>>, Controller::Abort>
348where
349 P: RingStore + Copy + Send + Sync,
350 El<P>: Send + Sync,
351 P::Type: MultivariatePolyRing,
352 <P::Type as RingExtension>::BaseRing: Sync,
353 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: PrincipalLocalRing,
354 O: MonomialOrder + Copy + Send + Sync,
355 PolyCoeff<P>: Send + Sync,
356 Controller: ComputationController,
357 SortFn: FnMut(&mut [SPoly], &[El<P>]),
358 AbortFn: FnMut(&[(El<P>, ExpandedMonomial)]) -> bool,
359{
360 controller.run_computation(
361 format_args!(
362 "buchberger(len={}, vars={})",
363 input_basis.len(),
364 ring.indeterminate_count()
365 ),
366 |controller| {
367 let input_basis = inter_reduce(
370 &ring,
371 input_basis.into_iter().map(|f| augment_lm(ring, f, order)).collect(),
372 order,
373 )
374 .into_iter()
375 .map(|(f, _)| f)
376 .collect::<Vec<_>>();
377 debug_assert!(input_basis.iter().all(|f| !ring.is_zero(f)));
378
379 let nilpotent_power = ring
380 .base_ring()
381 .nilpotent_power()
382 .and_then(|e| if e != 0 { Some(e) } else { None });
383 assert!(
384 nilpotent_power.is_none()
385 || ring.base_ring().is_zero(&ring.base_ring().pow(
386 ring.base_ring().clone_el(ring.base_ring().max_ideal_gen()),
387 nilpotent_power.unwrap()
388 ))
389 );
390
391 let sort_reducers = |reducers: &mut [(El<P>, ExpandedMonomial)]| {
392 reducers.sort_by(|(lf, _), (rf, _)| {
394 order
395 .compare(ring, ring.LT(lf, order).unwrap().1, ring.LT(rf, order).unwrap().1)
396 .then_with(|| ring.terms(lf).count().cmp(&ring.terms(rf).count()))
397 })
398 };
399
400 let mut reducers: Vec<(El<P>, ExpandedMonomial)> = input_basis
404 .iter()
405 .map(|f| augment_lm(ring, ring.clone_el(f), order))
406 .collect::<Vec<_>>();
407 sort_reducers(&mut reducers);
408
409 let mut open = Vec::new();
410 let mut basis = Vec::new();
411 update_basis(
412 ring,
413 input_basis.into_iter(),
414 &mut basis,
415 &mut open,
416 order,
417 nilpotent_power,
418 &mut 0,
419 &mut sort_spolys,
420 );
421
422 let mut current_deg = 0;
423 let mut filtered_spolys = 0;
424 let mut changed = false;
425 loop {
426 let spolys_to_reduce_index = open
429 .iter()
430 .enumerate()
431 .rev()
432 .find(|(_, spoly)| ring.monomial_deg(&spoly.lcm_term(ring, &basis, order).1) > current_deg)
433 .map(|(i, _)| i + 1)
434 .unwrap_or(0);
435 let spolys_to_reduce = &open[spolys_to_reduce_index..];
436
437 let computation = ShortCircuitingComputation::new();
438 let new_polys = AppendOnlyVec::new();
439 let new_polys_ref = &new_polys;
440 let basis_ref = &basis;
441 let reducers_ref = &reducers;
442
443 computation
444 .handle(controller.clone())
445 .join_many(spolys_to_reduce.as_fn().map_fn(move |spoly| {
446 move |handle: ShortCircuitingComputationHandle<(), _>| {
447 let mut f = spoly.poly(ring, basis_ref, order);
448
449 reduce_poly(
450 ring,
451 &mut f,
452 || reducers_ref.iter().chain(new_polys_ref.iter()).map(|(f, lmf)| (f, lmf)),
453 order,
454 );
455
456 if !ring.is_zero(&f) {
457 log_progress!(handle, "s");
458 _ = new_polys_ref.push(augment_lm(ring, f, order));
459 } else {
460 log_progress!(handle, "-");
461 }
462
463 checkpoint!(handle);
464 return Ok(None);
465 }
466 }));
467
468 drop(open.drain(spolys_to_reduce_index..));
469 let new_polys = new_polys.into_vec();
470 _ = computation.finish()?;
471
472 if new_polys.is_empty() && open.is_empty() {
474 if changed {
475 log_progress!(controller, "!");
476 return buchberger::<P, O, _, _, _>(
481 ring,
482 reducers.into_iter().map(|(f, _)| f).collect(),
483 order,
484 sort_spolys,
485 abort_early_if,
486 controller,
487 );
488 } else {
489 return Ok(reducers.into_iter().map(|(f, _)| f).collect());
490 }
491 } else if new_polys.is_empty() {
492 current_deg = ring.monomial_deg(&open.last().unwrap().lcm_term(ring, &basis, order).1);
493 log_progress!(controller, "{{{}}}", current_deg);
494 } else {
495 changed = true;
496 current_deg = 0;
497 update_basis(
498 ring,
499 new_polys.iter().map(|(f, _)| ring.clone_el(f)),
500 &mut basis,
501 &mut open,
502 order,
503 nilpotent_power,
504 &mut filtered_spolys,
505 &mut sort_spolys,
506 );
507 log_progress!(
508 controller,
509 "(b={})(S={})(f={})",
510 basis.len(),
511 open.len(),
512 filtered_spolys
513 );
514
515 reducers.extend(new_polys);
516 reducers = inter_reduce(ring, reducers, order);
517 sort_reducers(&mut reducers);
518 log_progress!(controller, "(r={})", reducers.len());
519 if abort_early_if(&reducers) {
520 log_progress!(controller, "(early_abort)");
521 return Ok(reducers.into_iter().map(|(f, _)| f).collect());
522 }
523 }
524
525 if open.len() + filtered_spolys
527 > reducers.len() * reducers.len() / 2 + reducers.len() * nilpotent_power.unwrap_or(0) + 1
528 {
529 log_progress!(controller, "!");
530 return buchberger::<P, O, _, _, _>(
531 ring,
532 reducers.into_iter().map(|(f, _)| f).collect(),
533 order,
534 sort_spolys,
535 abort_early_if,
536 controller,
537 );
538 }
539 }
540 },
541 )
542}
543
544fn update_basis<I, P, O, SortFn>(
545 ring: P,
546 new_polys: I,
547 basis: &mut Vec<El<P>>,
548 open: &mut Vec<SPoly>,
549 order: O,
550 nilpotent_power: Option<usize>,
551 filtered_spolys: &mut usize,
552 sort_spolys: &mut SortFn,
553) where
554 P: RingStore + Copy,
555 P::Type: MultivariatePolyRing,
556 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: PrincipalLocalRing,
557 O: MonomialOrder + Copy,
558 SortFn: FnMut(&mut [SPoly], &[El<P>]),
559 I: Iterator<Item = El<P>>,
560{
561 for new_poly in new_polys {
562 basis.push(new_poly);
563 for i in 0..(basis.len() - 1) {
564 let spoly = SPoly::Standard(i, basis.len() - 1);
565 if filter_spoly(ring, spoly.clone(), &*basis, order).is_none() {
566 open.push(spoly);
567 } else {
568 *filtered_spolys += 1;
569 }
570 }
571 if let Some(e) = nilpotent_power {
572 for k in 1..e {
573 let spoly = SPoly::Nilpotent(basis.len() - 1, k);
574 if filter_spoly(ring, spoly.clone(), &*basis, order).is_none() {
575 open.push(spoly);
576 } else {
577 *filtered_spolys += 1;
578 }
579 }
580 }
581 }
582 sort_spolys(&mut *open, &*basis);
583}
584
585fn reduce_poly<'a, 'b, F, I, P, O>(ring: P, to_reduce: &mut El<P>, mut reducers: F, order: O)
586where
587 P: 'a + RingStore + Copy,
588 P::Type: MultivariatePolyRing,
589 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: DivisibilityRing,
590 O: MonomialOrder + Copy,
591 F: FnMut() -> I,
592 I: Iterator<Item = (&'a El<P>, &'b ExpandedMonomial)>,
593{
594 while let Some((_, reducer, quo_c, quo_m)) = find_reducer(ring, to_reduce, reducers(), order) {
595 let prev_lm = ring.clone_monomial(ring.LT(to_reduce, order).unwrap().1);
596 let mut scaled_reducer = ring.clone_el(reducer);
597 ring.mul_assign_monomial(&mut scaled_reducer, ring.clone_monomial(&quo_m));
598 ring.inclusion().mul_assign_ref_map(&mut scaled_reducer, &quo_c);
599 debug_assert!(
600 order.compare(
601 ring,
602 ring.LT(&scaled_reducer, order).unwrap().1,
603 ring.LT(to_reduce, order).unwrap().1
604 ) == std::cmp::Ordering::Equal
605 );
606 ring.sub_assign(to_reduce, scaled_reducer);
607 debug_assert!(
608 ring.is_zero(to_reduce)
609 || order.compare(ring, ring.LT(to_reduce, order).unwrap().1, &prev_lm) == std::cmp::Ordering::Less
610 );
611 }
612}
613
614#[stability::unstable(feature = "enable")]
615pub fn multivariate_division<'a, P, O, I>(ring: P, mut f: El<P>, reducers: I, order: O) -> El<P>
616where
617 P: 'a + RingStore + Copy,
618 P::Type: MultivariatePolyRing,
619 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: DivisibilityRing,
620 O: MonomialOrder + Copy,
621 I: Clone + Iterator<Item = &'a El<P>>,
622{
623 let lms = reducers
624 .clone()
625 .map(|f| ring.expand_monomial(ring.LT(f, order).unwrap().1))
626 .collect::<Vec<_>>();
627 reduce_poly(ring, &mut f, || reducers.clone().zip(lms.iter()), order);
628 return f;
629}
630
631fn inter_reduce<P, O>(ring: P, mut polys: Vec<(El<P>, ExpandedMonomial)>, order: O) -> Vec<(El<P>, ExpandedMonomial)>
632where
633 P: RingStore + Copy,
634 P::Type: MultivariatePolyRing,
635 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: DivisibilityRing,
636 O: MonomialOrder + Copy,
637{
638 let mut changed = true;
639 while changed {
640 changed = false;
641 let mut i = 0;
642 while i < polys.len() {
643 let last_i = polys.len() - 1;
644 polys.swap(i, last_i);
645 let (reducers, to_reduce) = polys.split_at_mut(last_i);
646 let to_reduce = &mut to_reduce[0];
647
648 reduce_poly(
649 ring,
650 &mut to_reduce.0,
651 || reducers.iter().map(|(f, lmf)| (f, lmf)),
652 order,
653 );
654
655 if !ring.is_zero(&to_reduce.0) {
657 to_reduce.1 = ring.expand_monomial(ring.LT(&to_reduce.0, order).unwrap().1);
658 polys.swap(i, last_i);
659 i += 1;
660 } else {
661 _ = polys.pop().unwrap();
662 }
663 }
664 }
665 return polys;
666}
667
668use crate::rings::local::AsLocalPIR;
669use crate::rings::multivariate::multivariate_impl::MultivariatePolyRingImpl;
670
671pub fn buchberger_simple<P, O>(ring: P, input_basis: Vec<El<P>>, order: O) -> Vec<El<P>>
676where
677 P: RingStore + Copy + Send + Sync,
678 El<P>: Send + Sync,
679 P::Type: MultivariatePolyRing,
680 <P::Type as RingExtension>::BaseRing: Sync,
681 <<P::Type as RingExtension>::BaseRing as RingStore>::Type: Field,
682 O: MonomialOrder + Copy + Send + Sync,
683 PolyCoeff<P>: Send + Sync,
684{
685 let as_local_pir = AsLocalPIR::from_field(ring.base_ring());
686 let new_poly_ring = MultivariatePolyRingImpl::new(&as_local_pir, ring.indeterminate_count());
687 let from_ring = new_poly_ring.lifted_hom(ring, WrapHom::to_delegate_ring(as_local_pir.get_ring()));
688 let result = buchberger::<_, _, _, _, _>(
689 &new_poly_ring,
690 input_basis.into_iter().map(|f| from_ring.map(f)).collect(),
691 order,
692 default_sort_fn(&new_poly_ring, order),
693 |_| false,
694 DontObserve,
695 )
696 .unwrap_or_else(no_error);
697 let to_ring = ring.lifted_hom(&new_poly_ring, UnwrapHom::from_delegate_ring(as_local_pir.get_ring()));
698 return result.into_iter().map(|f| to_ring.map(f)).collect();
699}
700
701#[cfg(test)]
702use crate::integer::BigIntRing;
703#[cfg(test)]
704use crate::rings::poly::{PolyRingStore, dense_poly};
705#[cfg(test)]
706use crate::rings::rational::RationalField;
707#[cfg(test)]
708use crate::rings::zn::zn_static;
709
710#[test]
711fn test_buchberger_small() {
712 let base = zn_static::F17;
713 let ring = MultivariatePolyRingImpl::new(base, 2);
714
715 let f1 = ring.from_terms(
716 [
717 (1, ring.create_monomial([2, 0])),
718 (1, ring.create_monomial([0, 2])),
719 (16, ring.create_monomial([0, 0])),
720 ]
721 .into_iter(),
722 );
723 let f2 = ring.from_terms([(1, ring.create_monomial([1, 1])), (15, ring.create_monomial([0, 0]))].into_iter());
724
725 let actual = buchberger(
726 &ring,
727 vec![ring.clone_el(&f1), ring.clone_el(&f2)],
728 DegRevLex,
729 default_sort_fn(&ring, DegRevLex),
730 |_| false,
731 TEST_LOG_PROGRESS,
732 )
733 .unwrap_or_else(no_error);
734
735 let expected = ring.from_terms(
736 [
737 (16, ring.create_monomial([0, 3])),
738 (15, ring.create_monomial([1, 0])),
739 (1, ring.create_monomial([0, 1])),
740 ]
741 .into_iter(),
742 );
743
744 assert_eq!(3, actual.len());
745 assert_el_eq!(
746 ring,
747 ring.zero(),
748 multivariate_division(&ring, f1, actual.iter(), DegRevLex)
749 );
750 assert_el_eq!(
751 ring,
752 ring.zero(),
753 multivariate_division(&ring, f2, actual.iter(), DegRevLex)
754 );
755 assert_el_eq!(
756 ring,
757 ring.zero(),
758 multivariate_division(&ring, expected, actual.iter(), DegRevLex)
759 );
760}
761
762#[test]
763fn test_buchberger_larger() {
764 let base = zn_static::F17;
765 let ring = MultivariatePolyRingImpl::new(base, 3);
766
767 let f1 = ring.from_terms(
768 [
769 (1, ring.create_monomial([2, 1, 1])),
770 (1, ring.create_monomial([0, 2, 0])),
771 (1, ring.create_monomial([1, 0, 1])),
772 (2, ring.create_monomial([1, 0, 0])),
773 (1, ring.create_monomial([0, 0, 0])),
774 ]
775 .into_iter(),
776 );
777 let f2 = ring.from_terms(
778 [
779 (1, ring.create_monomial([0, 3, 1])),
780 (1, ring.create_monomial([0, 0, 3])),
781 (1, ring.create_monomial([1, 1, 0])),
782 ]
783 .into_iter(),
784 );
785 let f3 = ring.from_terms(
786 [
787 (1, ring.create_monomial([1, 0, 2])),
788 (1, ring.create_monomial([1, 0, 1])),
789 (2, ring.create_monomial([0, 1, 1])),
790 (7, ring.create_monomial([0, 0, 0])),
791 ]
792 .into_iter(),
793 );
794
795 let actual = buchberger(
796 &ring,
797 vec![ring.clone_el(&f1), ring.clone_el(&f2), ring.clone_el(&f3)],
798 DegRevLex,
799 default_sort_fn(&ring, DegRevLex),
800 |_| false,
801 TEST_LOG_PROGRESS,
802 )
803 .unwrap_or_else(no_error);
804
805 let g1 = ring.from_terms(
806 [
807 (1, ring.create_monomial([0, 4, 0])),
808 (8, ring.create_monomial([0, 3, 1])),
809 (12, ring.create_monomial([0, 1, 3])),
810 (6, ring.create_monomial([0, 0, 4])),
811 (1, ring.create_monomial([0, 3, 0])),
812 (13, ring.create_monomial([0, 2, 1])),
813 (11, ring.create_monomial([0, 1, 2])),
814 (10, ring.create_monomial([0, 0, 3])),
815 (11, ring.create_monomial([0, 2, 0])),
816 (12, ring.create_monomial([0, 1, 1])),
817 (6, ring.create_monomial([0, 0, 2])),
818 (6, ring.create_monomial([0, 1, 0])),
819 (13, ring.create_monomial([0, 0, 1])),
820 (9, ring.create_monomial([0, 0, 0])),
821 ]
822 .into_iter(),
823 );
824
825 assert_el_eq!(
826 ring,
827 ring.zero(),
828 multivariate_division(&ring, g1, actual.iter(), DegRevLex)
829 );
830}
831
832#[test]
833fn test_generic_computation() {
834 let base = zn_static::F17;
835 let ring = MultivariatePolyRingImpl::new(base, 6);
836 let poly_ring = dense_poly::DensePolyRing::new(&ring, "X");
837
838 let var_i = |i: usize| {
839 ring.create_term(
840 base.one(),
841 ring.create_monomial((0..ring.indeterminate_count()).map(|j| if i == j { 1 } else { 0 })),
842 )
843 };
844 let X1 = poly_ring.mul(
845 poly_ring.from_terms([(var_i(0), 0), (ring.one(), 1)].into_iter()),
846 poly_ring.from_terms([(var_i(1), 0), (ring.one(), 1)].into_iter()),
847 );
848 let X2 = poly_ring.mul(
849 poly_ring.add(
850 poly_ring.clone_el(&X1),
851 poly_ring.from_terms([(var_i(2), 0), (var_i(3), 1)].into_iter()),
852 ),
853 poly_ring.add(
854 poly_ring.clone_el(&X1),
855 poly_ring.from_terms([(var_i(4), 0), (var_i(5), 1)].into_iter()),
856 ),
857 );
858 let basis = vec![
859 ring.sub_ref_snd(ring.int_hom().map(1), poly_ring.coefficient_at(&X2, 0)),
860 ring.sub_ref_snd(ring.int_hom().map(1), poly_ring.coefficient_at(&X2, 1)),
861 ring.sub_ref_snd(ring.int_hom().map(1), poly_ring.coefficient_at(&X2, 2)),
862 ];
863
864 let start = std::time::Instant::now();
865 let gb1 = buchberger(
866 &ring,
867 basis.iter().map(|f| ring.clone_el(f)).collect(),
868 DegRevLex,
869 default_sort_fn(&ring, DegRevLex),
870 |_| false,
871 TEST_LOG_PROGRESS,
872 )
873 .unwrap_or_else(no_error);
874 let end = std::time::Instant::now();
875
876 println!("Computed GB in {} ms", (end - start).as_millis());
877
878 assert_eq!(11, gb1.len());
879}
880
881#[test]
882fn test_gb_local_ring() {
883 let base = AsLocalPIR::from_zn(zn_static::Zn::<16>::RING).unwrap();
884 let ring: MultivariatePolyRingImpl<_> = MultivariatePolyRingImpl::new(base, 1);
885
886 let f = ring.from_terms(
887 [
888 (base.int_hom().map(4), ring.create_monomial([1])),
889 (base.one(), ring.create_monomial([0])),
890 ]
891 .into_iter(),
892 );
893 let gb = buchberger(
894 &ring,
895 vec![f],
896 DegRevLex,
897 default_sort_fn(&ring, DegRevLex),
898 |_| false,
899 TEST_LOG_PROGRESS,
900 )
901 .unwrap_or_else(no_error);
902
903 assert_eq!(1, gb.len());
904 assert_el_eq!(ring, ring.one(), gb[0]);
905}
906
907#[test]
908fn test_gb_lex() {
909 let ZZ = BigIntRing::RING;
910 let QQ = AsLocalPIR::from_field(RationalField::new(ZZ));
911 let QQYX = MultivariatePolyRingImpl::new(&QQ, 2);
912 let [f, g] = QQYX.with_wrapped_indeterminates(|[Y, X]| {
913 [
914 1 + X.pow_ref(2) + 2 * Y + (1 + X) * Y.pow_ref(2),
915 3 + X + (2 + X) * Y + (1 + X + X.pow_ref(2)) * Y.pow_ref(2),
916 ]
917 });
918 let expected = QQYX.with_wrapped_indeterminates(|[Y, X]| {
919 [
920 X.pow_ref(8) + 2 * X.pow_ref(7) + 3 * X.pow_ref(6)
921 - 5 * X.pow_ref(5)
922 - 10 * X.pow_ref(4)
923 - 7 * X.pow_ref(3)
924 + 8 * X.pow_ref(2)
925 + 8 * X
926 + 4,
927 2 * Y + X.pow_ref(6) + 3 * X.pow_ref(5) + 6 * X.pow_ref(4) + X.pow_ref(3) - 7 * X.pow_ref(2) - 12 * X - 2,
928 ]
929 });
930
931 let mut gb = buchberger_simple(&QQYX, vec![f, g], Lex);
932
933 assert_eq!(2, gb.len());
934 gb.sort_unstable_by_key(|f| QQYX.appearing_indeterminates(f).len());
935 for (mut f, mut e) in gb.into_iter().zip(expected.into_iter()) {
936 let f_lc_inv = QQ.invert(QQYX.LT(&f, Lex).unwrap().0).unwrap();
937 QQYX.inclusion().mul_assign_map(&mut f, f_lc_inv);
938 let e_lc_inv = QQ.invert(QQYX.LT(&e, Lex).unwrap().0).unwrap();
939 QQYX.inclusion().mul_assign_map(&mut e, e_lc_inv);
940 assert_el_eq!(QQYX, e, f);
941 }
942}
943
944#[cfg(test)]
945#[cfg(feature = "parallel")]
946static TEST_COMPUTATION_CONTROLLER: ExecuteMultithreaded<LogProgress> = RunMultithreadedLogProgress;
947#[cfg(test)]
948#[cfg(not(feature = "parallel"))]
949static TEST_COMPUTATION_CONTROLLER: LogProgress = TEST_LOG_PROGRESS;
950
951#[ignore]
952#[test]
953fn test_expensive_gb_1() {
954 let base = AsLocalPIR::from_zn(zn_static::Zn::<16>::RING).unwrap();
955 let ring: MultivariatePolyRingImpl<_> = MultivariatePolyRingImpl::new(base, 12);
956
957 let system = ring.with_wrapped_indeterminates(|[Y0, Y1, Y2, Y3, Y4, Y5, Y6, Y7, Y8, Y9, Y10, Y11]| {
958 [
959 Y0 * Y1 * Y2 * Y3.pow_ref(2) * Y4.pow_ref(2)
960 + 4 * Y0 * Y1 * Y2 * Y3 * Y4 * Y4 * Y8
961 + Y0 * Y1 * Y2 * Y5.pow_ref(2) * Y8.pow_ref(2)
962 + Y0 * Y2 * Y3 * Y4 * Y6
963 + Y0 * Y1 * Y3 * Y4 * Y7
964 + Y0 * Y2 * Y5 * Y6 * Y8
965 + Y0 * Y1 * Y5 * Y7 * Y8
966 + Y0 * Y2 * Y3 * Y5 * Y10
967 + Y0 * Y1 * Y3 * Y5 * Y11
968 + Y0 * Y6 * Y7
969 + Y3 * Y5 * Y9
970 - 4,
971 2 * Y0 * Y1 * Y2 * Y3.pow_ref(2) * Y4 * Y5
972 + 2 * Y0 * Y1 * Y2 * Y3 * Y5.pow_ref(2) * Y8
973 + Y0 * Y2 * Y3 * Y5 * Y6
974 + Y0 * Y1 * Y3 * Y5 * Y7
975 + 8,
976 Y0 * Y1 * Y2 * Y3.pow_ref(2) * Y5.pow_ref(2) - 5,
977 ]
978 });
979
980 let part_of_result =
981 ring.with_wrapped_indeterminates(|[_Y0, Y1, Y2, _Y3, _Y4, _Y5, Y6, Y7, _Y8, _Y9, _Y10, _Y11]| {
982 [
983 4 * Y2.pow_ref(2) * Y6.pow_ref(2) - 4 * Y1.pow_ref(2) * Y7.pow_ref(2),
984 8 * Y2 * Y6 + 8 * Y1 * Y7.clone(),
985 ]
986 });
987
988 let start = std::time::Instant::now();
989 let gb = buchberger(
990 &ring,
991 system.iter().map(|f| ring.clone_el(f)).collect(),
992 DegRevLex,
993 default_sort_fn(&ring, DegRevLex),
994 |_| false,
995 TEST_COMPUTATION_CONTROLLER,
996 )
997 .unwrap_or_else(no_error);
998 let end = std::time::Instant::now();
999
1000 println!("Computed GB in {} ms", (end - start).as_millis());
1001
1002 for f in &part_of_result {
1003 assert!(ring.is_zero(&multivariate_division(&ring, ring.clone_el(f), gb.iter(), DegRevLex)));
1004 }
1005
1006 assert_eq!(108, gb.len());
1007}
1008
1009#[test]
1010#[ignore]
1011fn test_expensive_gb_2() {
1012 let base = zn_static::Fp::<7>::RING;
1013 let ring = MultivariatePolyRingImpl::new(base, 7);
1014
1015 let basis = ring.with_wrapped_indeterminates_dyn(|[X0, X1, X2, X3, X4, X5, X6]| {
1016 [
1017 6 + 2 * X5
1018 + 2 * X4
1019 + X6
1020 + 4 * X0
1021 + 5 * X6 * X5
1022 + X6 * X4
1023 + 3 * X0 * X4
1024 + 6 * X0 * X6
1025 + 2 * X0 * X3
1026 + X0 * X2
1027 + 4 * X0 * X1
1028 + 2 * X3 * X4 * X5
1029 + 4 * X0 * X6 * X5
1030 + 6 * X0 * X2 * X5
1031 + 5 * X0 * X6 * X4
1032 + 2 * X0 * X3 * X4
1033 + 4 * X0 * X1 * X4
1034 + X0 * X6.pow_ref(2)
1035 + 3 * X0 * X3 * X6
1036 + 5 * X0 * X2 * X6
1037 + 2 * X0 * X1 * X6
1038 + X0 * X3.pow_ref(2)
1039 + 2 * X0 * X2 * X3
1040 + 3 * X0 * X3 * X4 * X5
1041 + 4 * X0 * X3 * X6 * X5
1042 + 3 * X0 * X1 * X6 * X5
1043 + 3 * X0 * X2 * X3 * X5
1044 + 3 * X0 * X3 * X6 * X4
1045 + 2 * X0 * X1 * X6 * X4
1046 + 2 * X0 * X3.pow_ref(2) * X4
1047 + 2 * X0 * X2 * X3 * X4
1048 + 3 * X0 * X3.pow_ref(2) * X4 * X5
1049 + 4 * X0 * X1 * X3 * X4 * X5
1050 + X0 * X3.pow_ref(2) * X4.pow_ref(2),
1051 5 + 4 * X0
1052 + 6 * X4 * X5
1053 + 3 * X6 * X5
1054 + 4 * X0 * X4
1055 + 3 * X0 * X6
1056 + 6 * X0 * X3
1057 + 6 * X0 * X2
1058 + 6 * X6 * X4 * X5
1059 + 2 * X0 * X4 * X5
1060 + 4 * X0 * X6 * X5
1061 + 3 * X0 * X2 * X5
1062 + 3 * X0 * X6 * X4
1063 + 5 * X0 * X3 * X4
1064 + 6 * X0 * X2 * X4
1065 + 4 * X0 * X6.pow_ref(2)
1066 + 3 * X0 * X3 * X6
1067 + 3 * X0 * X2 * X6
1068 + 2 * X0 * X6 * X4 * X5
1069 + 6 * X0 * X3 * X4 * X5
1070 + 5 * X0 * X1 * X4 * X5
1071 + 6 * X0 * X6.pow_ref(2) * X5
1072 + 2 * X0 * X3 * X6 * X5
1073 + 2 * X0 * X2 * X6 * X5
1074 + 6 * X0 * X1 * X6 * X5
1075 + 6 * X0 * X2 * X3 * X5
1076 + 6 * X0 * X3 * X4.pow_ref(2)
1077 + 4 * X0 * X6.pow_ref(2) * X4
1078 + 6 * X0 * X3 * X6 * X4
1079 + 3 * X0 * X2 * X6 * X4
1080 + 4 * X0 * X3 * X6 * X4 * X5
1081 + 5 * X0 * X1 * X6 * X4 * X5
1082 + 6 * X0 * X3.pow_ref(2) * X4 * X5
1083 + 5 * X0 * X2 * X3 * X4 * X5
1084 + 3 * X0 * X3 * X6 * X4.pow_ref(2)
1085 + 6 * X0 * X3.pow_ref(2) * X4.pow_ref(2) * X5.clone(),
1086 2 + 2 * X0
1087 + 4 * X0 * X4
1088 + 2 * X0 * X6
1089 + 5 * X0 * X4 * X5
1090 + 2 * X0 * X6 * X5
1091 + 4 * X0 * X2 * X5
1092 + 2 * X0 * X4.pow_ref(2)
1093 + 4 * X0 * X6 * X4
1094 + 4 * X0 * X6.pow_ref(2)
1095 + 2 * X6 * X4 * X5.pow_ref(2)
1096 + 4 * X0 * X6 * X4 * X5
1097 + X0 * X3 * X4 * X5
1098 + X0 * X2 * X4 * X5
1099 + 3 * X0 * X6.pow_ref(2) * X5
1100 + 2 * X0 * X3 * X6 * X5
1101 + 4 * X0 * X2 * X6 * X5
1102 + 2 * X0 * X6 * X4.pow_ref(2)
1103 + X0 * X6.pow_ref(2) * X4
1104 + 3 * X0 * X6 * X4 * X5.pow_ref(2)
1105 + 2 * X0 * X6.pow_ref(2) * X5.pow_ref(2)
1106 + 3 * X0 * X2 * X6 * X5.pow_ref(2)
1107 + X0 * X3 * X4.pow_ref(2) * X5
1108 + X0 * X6.pow_ref(2) * X4 * X5
1109 + X0 * X3 * X6 * X4 * X5
1110 + 6 * X0 * X2 * X6 * X4 * X5
1111 + 4 * X0 * X6.pow_ref(2) * X4.pow_ref(2)
1112 + 6 * X0 * X3 * X6 * X4 * X5.pow_ref(2)
1113 + 4 * X0 * X1 * X6 * X4 * X5.pow_ref(2)
1114 + 4 * X0 * X2 * X3 * X4 * X5.pow_ref(2)
1115 + 6 * X0 * X3 * X6 * X4.pow_ref(2) * X5
1116 + 2 * X0 * X3.pow_ref(2) * X4.pow_ref(2) * X5.pow_ref(2),
1117 4 + 5 * X0 * X4 * X5
1118 + 6 * X0 * X6 * X5
1119 + 5 * X0 * X4.pow_ref(2) * X5
1120 + 3 * X0 * X6 * X4 * X5
1121 + 3 * X0 * X6.pow_ref(2) * X5
1122 + 6 * X0 * X6 * X4 * X5.pow_ref(2)
1123 + 5 * X0 * X2 * X4 * X5.pow_ref(2)
1124 + X0 * X6.pow_ref(2) * X5.pow_ref(2)
1125 + 6 * X0 * X2 * X6 * X5.pow_ref(2)
1126 + 4 * X0 * X6 * X4.pow_ref(2) * X5
1127 + 2 * X0 * X6.pow_ref(2) * X4 * X5
1128 + 5 * X0 * X3 * X4.pow_ref(2) * X5.pow_ref(2)
1129 + 3 * X0 * X6.pow_ref(2) * X4 * X5.pow_ref(2)
1130 + 5 * X0 * X3 * X6 * X4 * X5.pow_ref(2)
1131 + 4 * X0 * X2 * X6 * X4 * X5.pow_ref(2)
1132 + 6 * X0 * X6.pow_ref(2) * X4.pow_ref(2) * X5
1133 + 4 * X0 * X3 * X6 * X4.pow_ref(2) * X5.pow_ref(2),
1134 4 + 4 * X0 * X4.pow_ref(2) * X5.pow_ref(2)
1135 + X0 * X6 * X4 * X5.pow_ref(2)
1136 + X0 * X6.pow_ref(2) * X5.pow_ref(2)
1137 + 5 * X0 * X6 * X4.pow_ref(2) * X5.pow_ref(2)
1138 + 6 * X0 * X6.pow_ref(2) * X4 * X5.pow_ref(2)
1139 + 3 * X0 * X6.pow_ref(2) * X4 * X5.pow_ref(3)
1140 + 4 * X0 * X2 * X6 * X4 * X5.pow_ref(3)
1141 + 6 * X0 * X6.pow_ref(2) * X4.pow_ref(2) * X5.pow_ref(2)
1142 + 4 * X0 * X3 * X6 * X4.pow_ref(2) * X5.pow_ref(3),
1143 5 * X0 * X6 * X4.pow_ref(2) * X5.pow_ref(3)
1144 + 6 * X0 * X6.pow_ref(2) * X4 * X5.pow_ref(3)
1145 + 5 * X0 * X6.pow_ref(2) * X4.pow_ref(2) * X5.pow_ref(3),
1146 2 * X0 * X6.pow_ref(2) * X4.pow_ref(2) * X5.pow_ref(4),
1147 ]
1148 });
1149
1150 let start = std::time::Instant::now();
1151 let gb = buchberger(
1152 &ring,
1153 basis,
1154 DegRevLex,
1155 default_sort_fn(&ring, DegRevLex),
1156 |_| false,
1157 TEST_COMPUTATION_CONTROLLER,
1158 )
1159 .unwrap_or_else(no_error);
1160 let end = std::time::Instant::now();
1161
1162 println!("Computed GB in {} ms", (end - start).as_millis());
1163
1164 assert_eq!(130, gb.len());
1165}
1166
1167#[test]
1168#[ignore]
1169fn test_groebner_cyclic6() {
1170 let base = zn_static::Fp::<65537>::RING;
1171 let ring = MultivariatePolyRingImpl::new(base, 6);
1172
1173 let cyclic6 = ring.with_wrapped_indeterminates_dyn(|[x, y, z, t, u, v]| {
1174 [
1175 x + y + z + t + u + v,
1176 x * y + y * z + z * t + t * u + x * v + u * v,
1177 x * y * z + y * z * t + z * t * u + x * y * v + x * u * v + t * u * v,
1178 x * y * z * t + y * z * t * u + x * y * z * v + x * y * u * v + x * t * u * v + z * t * u * v,
1179 x * y * z * t * u
1180 + x * y * z * t * v
1181 + x * y * z * u * v
1182 + x * y * t * u * v
1183 + x * z * t * u * v
1184 + y * z * t * u * v,
1185 x * y * z * t * u * v - 1,
1186 ]
1187 });
1188
1189 let start = std::time::Instant::now();
1190 let gb = buchberger(
1191 &ring,
1192 cyclic6,
1193 DegRevLex,
1194 default_sort_fn(&ring, DegRevLex),
1195 |_| false,
1196 TEST_COMPUTATION_CONTROLLER,
1197 )
1198 .unwrap_or_else(no_error);
1199 let end = std::time::Instant::now();
1200
1201 println!("Computed GB in {} ms", (end - start).as_millis());
1202 assert_eq!(45, gb.len());
1203}
1204
1205#[test]
1206#[ignore]
1207fn test_groebner_cyclic7() {
1208 let base = zn_static::Fp::<65537>::RING;
1209 let ring = MultivariatePolyRingImpl::new(base, 7);
1210
1211 let cyclic7 = ring.with_wrapped_indeterminates_dyn(|[x, y, z, t, u, v, w]| {
1212 [
1213 x + y + z + t + u + v + w,
1214 x * y + y * z + z * t + t * u + u * v + x * w + v * w,
1215 x * y * z + y * z * t + z * t * u + t * u * v + x * y * w + x * v * w + u * v * w,
1216 x * y * z * t
1217 + y * z * t * u
1218 + z * t * u * v
1219 + x * y * z * w
1220 + x * y * v * w
1221 + x * u * v * w
1222 + t * u * v * w,
1223 x * y * z * t * u
1224 + y * z * t * u * v
1225 + x * y * z * t * w
1226 + x * y * z * v * w
1227 + x * y * u * v * w
1228 + x * t * u * v * w
1229 + z * t * u * v * w,
1230 x * y * z * t * u * v
1231 + x * y * z * t * u * w
1232 + x * y * z * t * v * w
1233 + x * y * z * u * v * w
1234 + x * y * t * u * v * w
1235 + x * z * t * u * v * w
1236 + y * z * t * u * v * w,
1237 x * y * z * t * u * v * w - 1,
1238 ]
1239 });
1240
1241 let start = std::time::Instant::now();
1242 let gb = buchberger(
1243 &ring,
1244 cyclic7,
1245 DegRevLex,
1246 default_sort_fn(&ring, DegRevLex),
1247 |_| false,
1248 TEST_COMPUTATION_CONTROLLER,
1249 )
1250 .unwrap_or_else(no_error);
1251 let end = std::time::Instant::now();
1252
1253 println!("Computed GB in {} ms", (end - start).as_millis());
1254 assert_eq!(209, gb.len());
1255}
1256
1257#[test]
1258#[ignore]
1259fn test_groebner_cyclic8() {
1260 let base = zn_static::Fp::<65537>::RING;
1261 let ring = MultivariatePolyRingImpl::new(base, 8);
1262
1263 let cyclic7 = ring.with_wrapped_indeterminates_dyn(|[x, y, z, s, t, u, v, w]| {
1264 [
1265 x + y + z + s + t + u + v + w,
1266 x * y + y * z + z * s + s * t + t * u + u * v + x * w + v * w,
1267 x * y * z + y * z * s + z * s * t + s * t * u + t * u * v + x * y * w + x * v * w + u * v * w,
1268 x * y * z * s
1269 + y * z * s * t
1270 + z * s * t * u
1271 + s * t * u * v
1272 + x * y * z * w
1273 + x * y * v * w
1274 + x * u * v * w
1275 + t * u * v * w,
1276 x * y * z * s * t
1277 + y * z * s * t * u
1278 + z * s * t * u * v
1279 + x * y * z * s * w
1280 + x * y * z * v * w
1281 + x * y * u * v * w
1282 + x * t * u * v * w
1283 + s * t * u * v * w,
1284 x * y * z * s * t * u
1285 + y * z * s * t * u * v
1286 + x * y * z * s * t * w
1287 + x * y * z * s * v * w
1288 + x * y * z * u * v * w
1289 + x * y * t * u * v * w
1290 + x * s * t * u * v * w
1291 + z * s * t * u * v * w,
1292 x * y * z * s * t * u * v
1293 + x * y * z * s * t * u * w
1294 + x * y * z * s * t * v * w
1295 + x * y * z * s * u * v * w
1296 + x * y * z * t * u * v * w
1297 + x * y * s * t * u * v * w
1298 + x * z * s * t * u * v * w
1299 + y * z * s * t * u * v * w,
1300 x * y * z * s * t * u * v * w - 1,
1301 ]
1302 });
1303
1304 let start = std::time::Instant::now();
1305 let gb = buchberger(
1306 &ring,
1307 cyclic7,
1308 DegRevLex,
1309 default_sort_fn(&ring, DegRevLex),
1310 |_| false,
1311 TEST_COMPUTATION_CONTROLLER,
1312 )
1313 .unwrap_or_else(no_error);
1314 let end = std::time::Instant::now();
1315
1316 println!("Computed GB in {} ms", (end - start).as_millis());
1317 assert_eq!(372, gb.len());
1318}