1use std::alloc::{Allocator, Global};
2use std::cell::{RefCell, RefMut};
3use std::fmt::Debug;
4
5use atomicbox::AtomicOptionBox;
6use thread_local::ThreadLocal;
7
8use crate::algorithms::int_bisect;
9use crate::homomorphism::*;
10use crate::integer::*;
11use crate::iters::multiset_combinations;
12use crate::ordered::OrderedRingStore;
13use crate::primitive_int::StaticRing;
14use crate::ring::*;
15use crate::rings::multivariate::*;
16use crate::seq::{VectorFn, VectorView};
17
18type Exponent = u16;
19type OrderIdx = u64;
20
21fn compute_cum_binomial(n: usize, k: usize) -> u64 {
23 StaticRing::<i64>::RING.sum((0..(k + 1)).map(|l| {
24 binomial((n + l) as i128, &(n as i128), StaticRing::<i128>::RING)
25 .try_into()
26 .unwrap()
27 })) as u64
28}
29
30fn enumeration_index_degrevlex<V>(d: Exponent, mon: V, cum_binomial_lookup_table: &[Vec<u64>]) -> u64
33where
34 V: VectorFn<Exponent>,
35{
36 debug_assert!(d == mon.iter().sum::<Exponent>());
37 let n = mon.len();
38 let mut remaining_degree_minus_one: i64 = Into::<i64>::into(d) - 1;
39 let mut result = 0;
40 for i in 0..(n - 1) {
41 remaining_degree_minus_one -= Into::<i64>::into(mon.at(n - 1 - i));
42 if remaining_degree_minus_one < 0 {
43 return result;
44 }
45 result += cum_binomial_lookup_table[n - i - 2][remaining_degree_minus_one as usize];
46 }
47 return result;
48}
49
50fn nth_monomial_degrevlex<F>(n: usize, d: Exponent, mut index: u64, cum_binomial_lookup_table: &[Vec<u64>], mut out: F)
52where
53 F: FnMut(usize, Exponent),
54{
55 for i in 0..n {
56 out(i, 0);
57 }
58 let mut check_degree = 0;
59 let mut remaining_degree = d as usize;
60 for i in 0..(n - 1) {
61 if index == 0 {
62 out(n - 1 - i, remaining_degree as Exponent);
63 check_degree += remaining_degree as Exponent;
64 debug_assert!(d == check_degree);
65 return;
66 }
67 let remaining_degree_minus_one = match cum_binomial_lookup_table[n - i - 2].binary_search(&index) {
68 Ok(idx) => idx,
69 Err(idx) => idx - 1,
70 };
71 index -= cum_binomial_lookup_table[n - i - 2][remaining_degree_minus_one];
72 let new_remaining_degree = remaining_degree_minus_one + 1;
73 out(n - 1 - i, (remaining_degree - new_remaining_degree) as Exponent);
74 check_degree += (remaining_degree - new_remaining_degree) as Exponent;
75 remaining_degree = new_remaining_degree;
76 }
77 out(0, remaining_degree as Exponent);
78 check_degree += remaining_degree as Exponent;
79 debug_assert!(d == check_degree);
80}
81
82#[repr(transparent)]
84pub struct MonomialIdentifier {
85 data: InternalMonomialIdentifier,
86}
87
88impl Debug for MonomialIdentifier {
89 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
90 f.debug_struct("MonomialIdentifier")
91 .field("deg", &self.data.deg)
92 .field("idx", &self.data.order)
93 .finish()
94 }
95}
96
97#[derive(Clone, Debug)]
98struct InternalMonomialIdentifier {
99 deg: Exponent,
100 order: OrderIdx,
101}
102
103impl InternalMonomialIdentifier {
104 fn wrap(self) -> MonomialIdentifier { MonomialIdentifier { data: self } }
105}
106
107impl PartialEq for InternalMonomialIdentifier {
108 fn eq(&self, other: &Self) -> bool {
109 let res = self.deg == other.deg && self.order == other.order;
110 return res;
111 }
112}
113
114impl Eq for InternalMonomialIdentifier {}
115
116impl Ord for InternalMonomialIdentifier {
117 fn cmp(&self, other: &Self) -> Ordering { self.deg.cmp(&other.deg).then_with(|| self.order.cmp(&other.order)) }
118}
119
120impl PartialOrd for InternalMonomialIdentifier {
121 fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
122}
123
124pub struct MultivariatePolyRingEl<R, A = Global>
126where
127 R: RingStore,
128 A: Allocator + Clone,
129{
130 data: Vec<(El<R>, MonomialIdentifier), A>,
131}
132
133impl<R, A> Debug for MultivariatePolyRingEl<R, A>
134where
135 R: RingStore,
136 El<R>: Debug,
137 A: Allocator + Clone,
138{
139 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{:?}", self.data) }
140}
141
142pub struct MultivariatePolyRingImplBase<R, A = Global>
144where
145 R: RingStore,
146 A: Clone + Allocator + Send,
147{
148 base_ring: R,
149 variable_count: usize,
150 tmp_monomials: ThreadLocal<(RefCell<Box<[Exponent]>>, RefCell<Box<[Exponent]>>)>,
156 monomial_multiplication_table: Vec<Vec<Vec<Vec<u64>>>>,
158 zero: El<R>,
159 cum_binomial_lookup_table: Vec<Vec<u64>>,
160 max_supported_deg: Exponent,
161 allocator: A,
162 tmp_poly: AtomicOptionBox<Vec<(El<R>, MonomialIdentifier)>>,
163}
164
165pub type MultivariatePolyRingImpl<R, A = Global> = RingValue<MultivariatePolyRingImplBase<R, A>>;
167
168impl<R> MultivariatePolyRingImpl<R>
169where
170 R: RingStore,
171{
172 pub fn new(base_ring: R, variable_count: usize) -> Self {
174 Self::new_with_mult_table(base_ring, variable_count, 64, (6, 8), Global)
175 }
176}
177
178impl<R, A> MultivariatePolyRingImpl<R, A>
179where
180 R: RingStore,
181 A: Clone + Allocator + Send,
182{
183 #[stability::unstable(feature = "enable")]
193 pub fn new_with_mult_table(
194 base_ring: R,
195 variable_count: usize,
196 max_supported_deg: Exponent,
197 max_multiplication_table: (Exponent, Exponent),
198 allocator: A,
199 ) -> Self {
200 assert!(variable_count >= 1);
201 assert!(max_multiplication_table.0 <= max_multiplication_table.1);
202 assert!(max_multiplication_table.0 + max_multiplication_table.1 <= max_supported_deg);
203 let max_degree_for_orderidx = if variable_count == 1 || variable_count == 2 {
206 usize::MAX
207 } else {
208 let k = int_cast(
209 TryInto::<i64>::try_into(variable_count).unwrap() - 1,
210 BigIntRing::RING,
211 StaticRing::<i64>::RING,
212 );
213 int_bisect::find_root_floor(StaticRing::<i64>::RING, 0, |d| {
215 if BigIntRing::RING.is_lt(
216 &BigIntRing::RING.mul(
217 binomial(
218 int_cast(
219 d + TryInto::<i64>::try_into(variable_count).unwrap() - 1,
220 BigIntRing::RING,
221 StaticRing::<i64>::RING,
222 ),
223 &k,
224 BigIntRing::RING,
225 ),
226 int_cast(*d, BigIntRing::RING, StaticRing::<i64>::RING),
227 ),
228 &BigIntRing::RING.power_of_two(u64::BITS as usize),
229 ) {
230 -1
231 } else {
232 1
233 }
234 }) as usize
235 };
236 assert!(
237 max_degree_for_orderidx >= max_supported_deg as usize,
238 "currently only degrees are supported for which the total number of this-degree monomials fits in a u64"
239 );
240
241 let cum_binomial_lookup_table = (0..(variable_count - 1))
242 .map(|n| {
243 (0..=max_supported_deg)
244 .map(|k| compute_cum_binomial(n, k as usize))
245 .collect::<Vec<_>>()
246 })
247 .collect::<Vec<_>>();
248 let monomial_multipliation_table = (0..max_multiplication_table.0)
249 .map(|lhs_deg| {
250 (lhs_deg..max_multiplication_table.1)
251 .map(|rhs_deg| {
252 MultivariatePolyRingImplBase::<R, A>::create_multiplication_table(
253 variable_count,
254 lhs_deg,
255 rhs_deg,
256 &cum_binomial_lookup_table,
257 )
258 })
259 .collect::<Vec<_>>()
260 })
261 .collect::<Vec<_>>();
262 RingValue::from(MultivariatePolyRingImplBase {
263 zero: base_ring.zero(),
264 base_ring,
265 variable_count,
266 max_supported_deg,
267 monomial_multiplication_table: monomial_multipliation_table,
268 tmp_monomials: ThreadLocal::new(),
269 cum_binomial_lookup_table,
270 tmp_poly: AtomicOptionBox::none(),
271 allocator,
272 })
273 }
274}
275
276impl<R, A> MultivariatePolyRingImplBase<R, A>
277where
278 R: RingStore,
279 A: Clone + Allocator + Send,
280{
281 #[stability::unstable(feature = "enable")]
282 pub fn allocator(&self) -> &A { &self.allocator }
283
284 #[inline(always)]
285 fn tmp_monomials(&self) -> (RefMut<'_, [u16]>, RefMut<'_, [u16]>) { (self.tmp_monomial1(), self.tmp_monomial2()) }
286
287 #[inline(always)]
294 fn tmp_monomial1(&self) -> RefMut<'_, [u16]> {
295 let (mon, _) = self.tmp_monomials.get_or(|| {
296 (
297 RefCell::new(
298 (0..self.variable_count)
299 .map(|_| 0)
300 .collect::<Vec<_>>()
301 .into_boxed_slice(),
302 ),
303 RefCell::new(
304 (0..self.variable_count)
305 .map(|_| 0)
306 .collect::<Vec<_>>()
307 .into_boxed_slice(),
308 ),
309 )
310 });
311 RefMut::map(mon.borrow_mut(), |mon| &mut **mon)
312 }
313
314 #[inline(always)]
316 fn tmp_monomial2(&self) -> RefMut<'_, [u16]> {
317 let (_, mon) = self.tmp_monomials.get_or(|| {
318 (
319 RefCell::new(
320 (0..self.variable_count)
321 .map(|_| 0)
322 .collect::<Vec<_>>()
323 .into_boxed_slice(),
324 ),
325 RefCell::new(
326 (0..self.variable_count)
327 .map(|_| 0)
328 .collect::<Vec<_>>()
329 .into_boxed_slice(),
330 ),
331 )
332 });
333 RefMut::map(mon.borrow_mut(), |mon| &mut **mon)
334 }
335
336 #[inline(always)]
337 fn exponent_wise_bivariate_monomial_operation<F>(
338 &self,
339 lhs: InternalMonomialIdentifier,
340 rhs: InternalMonomialIdentifier,
341 mut f: F,
342 ) -> MonomialIdentifier
343 where
344 F: FnMut(Exponent, Exponent) -> Exponent,
345 {
346 let (mut lhs_mon, mut rhs_mon) = self.tmp_monomials();
347 nth_monomial_degrevlex(
348 self.variable_count,
349 lhs.deg,
350 lhs.order,
351 &self.cum_binomial_lookup_table,
352 |i, x| lhs_mon[i] = x,
353 );
354 nth_monomial_degrevlex(
355 self.variable_count,
356 rhs.deg,
357 rhs.order,
358 &self.cum_binomial_lookup_table,
359 |i, x| rhs_mon[i] = x,
360 );
361 let mut res_deg = 0;
362 for i in 0..self.variable_count {
363 lhs_mon[i] = f(lhs_mon[i], rhs_mon[i]);
364 res_deg += lhs_mon[i];
365 }
366 assert!(
367 res_deg <= self.max_supported_deg,
368 "Polynomial ring was configured to support monomials up to degree {}, but operation resulted in degree {}",
369 self.max_supported_deg,
370 res_deg
371 );
372 return MonomialIdentifier {
373 data: InternalMonomialIdentifier {
374 deg: res_deg,
375 order: enumeration_index_degrevlex(
376 res_deg,
377 (*lhs_mon).clone_els_by(|x| *x),
378 &self.cum_binomial_lookup_table,
379 ),
380 },
381 };
382 }
383
384 fn create_multiplication_table(
385 variable_count: usize,
386 lhs_deg: Exponent,
387 rhs_deg: Exponent,
388 cum_binomial_lookup_table: &[Vec<u64>],
389 ) -> Vec<Vec<u64>> {
390 debug_assert!(lhs_deg <= rhs_deg);
391 let lhs_max_deg = (0..variable_count).map(|_| lhs_deg as usize).collect::<Vec<_>>();
392 let rhs_max_deg = (0..variable_count).map(|_| rhs_deg as usize).collect::<Vec<_>>();
393 let mut lhs_i = 0;
394 let mut rhs_i = 0;
395 let rev = |i| variable_count - 1 - i;
398 multiset_combinations(&lhs_max_deg, lhs_deg as usize, |lhs| {
399 let result = multiset_combinations(&rhs_max_deg, rhs_deg as usize, |rhs| {
400 let result_index = enumeration_index_degrevlex(
401 lhs_deg + rhs_deg,
402 (0..variable_count).map_fn(|i| (lhs[rev(i)] + rhs[rev(i)]) as u16),
403 cum_binomial_lookup_table,
404 );
405 rhs_i += 1;
406 return result_index;
407 })
408 .collect::<Vec<_>>();
409 lhs_i += 1;
410 return result;
411 })
412 .collect::<Vec<_>>()
413 }
414
415 fn try_get_multiplication_table(&self, lhs_deg: Exponent, rhs_deg: Exponent) -> Option<&Vec<Vec<u64>>> {
416 debug_assert!(lhs_deg <= rhs_deg);
417 if lhs_deg as usize >= self.monomial_multiplication_table.len()
418 || rhs_deg as usize >= self.monomial_multiplication_table[lhs_deg as usize].len()
419 {
420 return None;
421 }
422 Some(&self.monomial_multiplication_table[lhs_deg as usize][rhs_deg as usize])
423 }
424
425 fn compare_degrevlex(&self, lhs: &InternalMonomialIdentifier, rhs: &InternalMonomialIdentifier) -> Ordering {
426 let res = lhs.deg.cmp(&rhs.deg).then_with(|| lhs.order.cmp(&rhs.order));
427 debug_assert!(res == DegRevLex.compare(RingRef::new(self), &lhs.clone().wrap(), &rhs.clone().wrap()));
428 return res;
429 }
430
431 fn is_valid(&self, el: &[(El<R>, MonomialIdentifier)]) -> bool {
432 for i in 1..el.len() {
433 if self.compare_degrevlex(&el[i - 1].1.data, &el[i].1.data) != Ordering::Less {
434 return false;
435 }
436 }
437 if !self.base_ring().get_ring().is_approximate() {
438 for i in 0..el.len() {
439 if self.base_ring().is_zero(&el[i].0) {
440 return false;
441 }
442 }
443 }
444 return true;
445 }
446
447 fn remove_zeros(&self, el: &mut Vec<(El<R>, MonomialIdentifier), A>) {
448 if self.base_ring().get_ring().is_approximate() {
449 return;
450 }
451 el.retain(|(c, _)| !self.base_ring().is_zero(c));
452 debug_assert!(self.is_valid(el));
453 }
454
455 fn add_terms<I>(
458 &self,
459 lhs: &<Self as RingBase>::Element,
460 rhs_sorted: I,
461 out: Vec<(El<R>, MonomialIdentifier), A>,
462 ) -> <Self as RingBase>::Element
463 where
464 I: Iterator<Item = (El<R>, MonomialIdentifier)>,
465 {
466 debug_assert!(self.is_valid(&lhs.data));
467
468 let mut result = out;
469 result.clear();
470 result.reserve(lhs.data.len() + rhs_sorted.size_hint().0);
471
472 let mut lhs_it = lhs.data.iter().peekable();
473 let mut rhs_it = rhs_sorted.peekable();
474
475 while let (Some((_, l_m)), Some((_, r_m))) = (lhs_it.peek(), rhs_it.peek()) {
476 let next_element = match self.compare_degrevlex(&l_m.data, &r_m.data) {
477 Ordering::Equal => {
478 let (l_c, _l_m) = lhs_it.next().unwrap();
479 let (r_c, r_m) = rhs_it.next().unwrap();
480 (self.base_ring().add_ref_fst(l_c, r_c), r_m)
481 }
482 Ordering::Less => {
483 let (l_c, l_m) = lhs_it.next().unwrap();
484 (self.base_ring().clone_el(l_c), l_m.data.clone().wrap())
485 }
486 Ordering::Greater => {
487 let (r_c, r_m) = rhs_it.next().unwrap();
488 (r_c, r_m)
489 }
490 };
491 result.push(next_element);
492 }
493 result.extend(lhs_it.map(|(c, m)| (self.base_ring().clone_el(c), m.data.clone().wrap())));
494 result.extend(rhs_it);
495 self.remove_zeros(&mut result);
496 debug_assert!(self.is_valid(&result));
497 return MultivariatePolyRingEl { data: result };
498 }
499}
500
501impl<R, A> Debug for MultivariatePolyRingImplBase<R, A>
502where
503 R: RingStore,
504 R::Type: Debug,
505 A: Clone + Allocator + Send,
506{
507 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
508 f.debug_struct("MultivariatePolyRingImplBase")
509 .field("base_ring", &self.base_ring.get_ring())
510 .field("variable_count", &self.variable_count)
511 .field("max_supported_deg", &self.max_supported_deg)
512 .finish()
513 }
514}
515
516impl<R, A> PartialEq for MultivariatePolyRingImplBase<R, A>
517where
518 R: RingStore,
519 A: Clone + Allocator + Send,
520{
521 fn eq(&self, other: &Self) -> bool {
522 self.base_ring.get_ring() == other.base_ring.get_ring()
523 && self.variable_count == other.variable_count
524 && self.max_supported_deg == other.max_supported_deg
525 }
526}
527
528impl<R, A> RingBase for MultivariatePolyRingImplBase<R, A>
529where
530 R: RingStore,
531 A: Clone + Allocator + Send,
532{
533 type Element = MultivariatePolyRingEl<R, A>;
534
535 fn clone_el(&self, val: &Self::Element) -> Self::Element {
536 let mut data = Vec::with_capacity_in(val.data.len(), self.allocator.clone());
537 data.extend(
538 val.data
539 .iter()
540 .map(|(c, m)| (self.base_ring().clone_el(c), self.clone_monomial(m))),
541 );
542 MultivariatePolyRingEl { data }
543 }
544
545 fn add_ref(&self, lhs: &Self::Element, rhs: &Self::Element) -> Self::Element {
546 debug_assert!(self.is_valid(&rhs.data));
547 self.add_terms(
548 lhs,
549 rhs.data
550 .iter()
551 .map(|(c, m)| (self.base_ring().clone_el(c), m.data.clone().wrap())),
552 Vec::new_in(self.allocator.clone()),
553 )
554 }
555
556 fn add_assign_ref(&self, lhs: &mut Self::Element, rhs: &Self::Element) { *lhs = self.add_ref(lhs, rhs); }
557
558 fn add_assign(&self, lhs: &mut Self::Element, rhs: Self::Element) {
559 debug_assert!(self.is_valid(&rhs.data));
560 *lhs = self.add_terms(lhs, rhs.data.into_iter(), Vec::new_in(self.allocator.clone()));
561 }
562
563 fn sub_assign_ref(&self, lhs: &mut Self::Element, rhs: &Self::Element) {
564 debug_assert!(self.is_valid(&rhs.data));
565 *lhs = self.add_terms(
566 lhs,
567 rhs.data.iter().map(|(c, m)| {
568 (
569 self.base_ring().negate(self.base_ring.clone_el(c)),
570 m.data.clone().wrap(),
571 )
572 }),
573 Vec::new_in(self.allocator.clone()),
574 );
575 }
576
577 fn negate_inplace(&self, lhs: &mut Self::Element) {
578 debug_assert!(self.is_valid(&lhs.data));
579 for (c, _) in &mut lhs.data {
580 self.base_ring().negate_inplace(c);
581 }
582 }
583
584 fn mul_assign(&self, lhs: &mut Self::Element, rhs: Self::Element) { self.mul_assign_ref(lhs, &rhs); }
585
586 fn mul_assign_ref(&self, lhs: &mut Self::Element, rhs: &Self::Element) { *lhs = self.mul_ref(lhs, rhs); }
587
588 fn mul_ref(&self, lhs: &Self::Element, rhs: &Self::Element) -> Self::Element {
589 let mut tmp = Vec::new_in(self.allocator.clone());
590 if lhs.data.len() > rhs.data.len() {
591 rhs.data.iter().fold(self.zero(), |mut current, (r_c, r_m)| {
592 let mut new = self.add_terms(
593 ¤t,
594 lhs.data.iter().map(|(c, m)| {
595 (
596 self.base_ring().mul_ref(c, r_c),
597 self.monomial_mul(m.data.clone().wrap(), r_m),
598 )
599 }),
600 std::mem::replace(&mut tmp, Vec::new_in(self.allocator.clone())),
601 );
602 std::mem::swap(&mut new, &mut current);
603 std::mem::swap(&mut new.data, &mut tmp);
604 current
605 })
606 } else {
607 lhs.data.iter().fold(self.zero(), |mut current, (l_c, l_m)| {
610 let mut new = self.add_terms(
611 ¤t,
612 rhs.data.iter().map(|(c, m)| {
613 (
614 self.base_ring().mul_ref(l_c, c),
615 self.monomial_mul(m.data.clone().wrap(), l_m),
616 )
617 }),
618 std::mem::replace(&mut tmp, Vec::new_in(self.allocator.clone())),
619 );
620 std::mem::swap(&mut new, &mut current);
621 std::mem::swap(&mut new.data, &mut tmp);
622 current
623 })
624 }
625 }
626
627 fn zero(&self) -> Self::Element {
628 MultivariatePolyRingEl {
629 data: Vec::new_in(self.allocator.clone()),
630 }
631 }
632
633 fn from_int(&self, value: i32) -> Self::Element { self.from(self.base_ring().get_ring().from_int(value)) }
634
635 fn eq_el(&self, lhs: &Self::Element, rhs: &Self::Element) -> bool {
636 debug_assert!(self.is_valid(&lhs.data));
637 debug_assert!(self.is_valid(&rhs.data));
638 if lhs.data.len() != rhs.data.len() {
639 return false;
640 }
641 for i in 0..lhs.data.len() {
642 if lhs.data.at(i).1.data != rhs.data.at(i).1.data
643 || !self.base_ring.eq_el(&lhs.data.at(i).0, &rhs.data.at(i).0)
644 {
645 return false;
646 }
647 }
648 return true;
649 }
650
651 fn is_zero(&self, value: &Self::Element) -> bool { value.data.is_empty() }
652
653 fn is_one(&self, value: &Self::Element) -> bool {
654 debug_assert!(self.is_valid(&value.data));
655 if value.data.len() != 1 {
656 return false;
657 }
658 value.data[0].1.data.deg == 0 && self.base_ring().is_one(&value.data[0].0)
659 }
660
661 fn is_neg_one(&self, value: &Self::Element) -> bool {
662 debug_assert!(self.is_valid(&value.data));
663 if value.data.len() != 1 {
664 return false;
665 }
666 value.data[0].1.data.deg == 0 && self.base_ring().is_neg_one(&value.data[0].0)
667 }
668
669 fn is_commutative(&self) -> bool { self.base_ring().is_commutative() }
670 fn is_noetherian(&self) -> bool { self.base_ring().is_noetherian() }
671
672 fn dbg(&self, value: &Self::Element, out: &mut std::fmt::Formatter) -> std::fmt::Result {
673 self.dbg_within(value, out, EnvBindingStrength::Weakest)
674 }
675
676 fn dbg_within<'a>(
677 &self,
678 value: &Self::Element,
679 out: &mut std::fmt::Formatter<'a>,
680 env: EnvBindingStrength,
681 ) -> std::fmt::Result {
682 super::generic_impls::print(RingRef::new(self), value, out, env)
683 }
684
685 fn characteristic<I: IntegerRingStore + Copy>(&self, ZZ: I) -> Option<El<I>>
686 where
687 I::Type: IntegerRing,
688 {
689 self.base_ring().characteristic(ZZ)
690 }
691
692 fn is_approximate(&self) -> bool { self.base_ring().get_ring().is_approximate() }
693}
694
695pub struct TermIterImpl<'a, R>
697where
698 R: RingStore,
699{
700 base_iter: std::slice::Iter<'a, (El<R>, MonomialIdentifier)>,
701}
702
703impl<'a, R> Iterator for TermIterImpl<'a, R>
704where
705 R: RingStore,
706{
707 type Item = (&'a El<R>, &'a MonomialIdentifier);
708
709 fn next(&mut self) -> Option<Self::Item> { self.base_iter.next().map(|(c, m)| (c, m)) }
710}
711
712impl<R, A> RingExtension for MultivariatePolyRingImplBase<R, A>
713where
714 R: RingStore,
715 A: Clone + Allocator + Send,
716{
717 type BaseRing = R;
718
719 fn base_ring(&self) -> &Self::BaseRing { &self.base_ring }
720
721 fn from(&self, x: El<Self::BaseRing>) -> Self::Element {
722 if !self.base_ring().get_ring().is_approximate() && self.base_ring().is_zero(&x) {
723 return self.zero();
724 } else {
725 let mut data = Vec::with_capacity_in(1, self.allocator.clone());
726 data.push((x, self.create_monomial((0..self.variable_count).map(|_| 0))));
727 return MultivariatePolyRingEl { data };
728 }
729 }
730
731 fn mul_assign_base(&self, lhs: &mut Self::Element, rhs: &El<Self::BaseRing>) {
732 for (c, _) in &mut lhs.data {
733 self.base_ring.mul_assign_ref(c, rhs);
734 }
735 lhs.data.retain(|(c, _)| !self.base_ring.is_zero(c));
736 }
737}
738
739impl<R, A> MultivariatePolyRing for MultivariatePolyRingImplBase<R, A>
740where
741 R: RingStore,
742 A: Clone + Allocator + Send,
743{
744 type Monomial = MonomialIdentifier;
745 type TermIter<'a>
746 = TermIterImpl<'a, R>
747 where
748 Self: 'a;
749
750 fn indeterminate_count(&self) -> usize { self.variable_count }
751
752 fn create_monomial<I>(&self, exponents: I) -> Self::Monomial
753 where
754 I: IntoIterator<Item = usize>,
755 I::IntoIter: ExactSizeIterator,
756 {
757 let exponents = exponents.into_iter();
758 assert_eq!(exponents.len(), self.indeterminate_count());
759
760 let mut tmp_monomial = self.tmp_monomial2();
761 let mut deg = 0;
762 for (i, e) in exponents.enumerate() {
763 deg += e as Exponent;
764 tmp_monomial[i] = e as Exponent;
765 }
766 assert!(
767 deg <= self.max_supported_deg,
768 "Polynomial ring was configured to support monomials up to degree {}, but create_monomial() was called for degree {}",
769 self.max_supported_deg,
770 deg
771 );
772 return MonomialIdentifier {
773 data: InternalMonomialIdentifier {
774 deg,
775 order: enumeration_index_degrevlex(
776 deg,
777 (*tmp_monomial).clone_els_by(|x| *x),
778 &self.cum_binomial_lookup_table,
779 ),
780 },
781 };
782 }
783
784 fn clone_monomial(&self, mon: &Self::Monomial) -> Self::Monomial { mon.data.clone().wrap() }
785
786 fn add_assign_from_terms<I>(&self, lhs: &mut Self::Element, terms: I)
787 where
788 I: IntoIterator<Item = (El<Self::BaseRing>, Self::Monomial)>,
789 {
790 let terms = terms.into_iter();
791 let mut rhs = self
792 .tmp_poly
793 .swap(None, std::sync::atomic::Ordering::AcqRel)
794 .map(|b| *b)
795 .unwrap_or_default();
796 debug_assert!(rhs.is_empty());
797 rhs.extend(terms);
798 rhs.sort_unstable_by(|l, r| self.compare_degrevlex(&l.1.data, &r.1.data));
799 rhs.dedup_by(|(snd_c, snd_m), (fst_c, fst_m)| {
800 if self.compare_degrevlex(&fst_m.data, &snd_m.data) == Ordering::Equal {
801 self.base_ring().add_assign_ref(fst_c, snd_c);
802 return true;
803 } else {
804 return false;
805 }
806 });
807 *lhs = self.add_terms(lhs, rhs.drain(..), Vec::new_in(self.allocator.clone()));
808 _ = self
809 .tmp_poly
810 .swap(Some(Box::new(rhs)), std::sync::atomic::Ordering::AcqRel);
811 }
812
813 fn mul_assign_monomial(&self, f: &mut Self::Element, rhs: Self::Monomial) {
814 let rhs_deg = rhs.data.deg;
815 let (mut lhs_mon, mut rhs_mon) = self.tmp_monomials();
816 nth_monomial_degrevlex(
817 self.variable_count,
818 rhs_deg,
819 rhs.data.order,
820 &self.cum_binomial_lookup_table,
821 |i, x| rhs_mon[i] = x,
822 );
823
824 for (_, lhs) in &mut f.data {
825 let lhs_deg = lhs.data.deg;
826 let mut fallback = || {
827 let res_deg = lhs.data.deg + rhs.data.deg;
828 assert!(
829 res_deg <= self.max_supported_deg,
830 "Polynomial ring was configured to support monomials up to degree {}, but multiplication resulted in degree {}",
831 self.max_supported_deg,
832 res_deg
833 );
834 nth_monomial_degrevlex(
835 self.variable_count,
836 lhs.data.deg,
837 lhs.data.order,
838 &self.cum_binomial_lookup_table,
839 |i, x| lhs_mon[i] = x,
840 );
841 for i in 0..self.variable_count {
842 lhs_mon[i] += rhs_mon[i];
843 }
844 MonomialIdentifier {
845 data: InternalMonomialIdentifier {
846 deg: res_deg,
847 order: enumeration_index_degrevlex(
848 res_deg,
849 (*lhs_mon).clone_els_by(|x| *x),
850 &self.cum_binomial_lookup_table,
851 ),
852 },
853 }
854 };
855 let new_val = if lhs_deg <= rhs_deg {
856 if let Some(table) = self.try_get_multiplication_table(lhs_deg, rhs_deg) {
857 MonomialIdentifier {
858 data: InternalMonomialIdentifier {
859 deg: lhs_deg + rhs_deg,
860 order: table[lhs.data.order as usize][rhs.data.order as usize],
861 },
862 }
863 } else {
864 fallback()
865 }
866 } else {
867 if let Some(table) = self.try_get_multiplication_table(rhs_deg, lhs_deg) {
868 MonomialIdentifier {
869 data: InternalMonomialIdentifier {
870 deg: lhs_deg + rhs_deg,
871 order: table[rhs.data.order as usize][lhs.data.order as usize],
872 },
873 }
874 } else {
875 fallback()
876 }
877 };
878 debug_assert!(new_val.data == fallback().data);
879 *lhs = new_val;
880 }
881 }
882
883 fn coefficient_at<'a>(&'a self, f: &'a Self::Element, m: &Self::Monomial) -> &'a El<Self::BaseRing> {
884 match f
885 .data
886 .binary_search_by(|(_, fm)| self.compare_degrevlex(&fm.data, &m.data))
887 {
888 Ok(i) => &f.data.at(i).0,
889 Err(_) => &self.zero,
890 }
891 }
892
893 fn expand_monomial_to(&self, m: &Self::Monomial, out: &mut [usize]) {
894 nth_monomial_degrevlex(
895 self.variable_count,
896 m.data.deg,
897 m.data.order,
898 &self.cum_binomial_lookup_table,
899 |i, x| out[i] = x as usize,
900 );
901 }
902
903 fn exponent_at(&self, m: &Self::Monomial, var_index: usize) -> usize {
904 let mut output = 0;
905 nth_monomial_degrevlex(
906 self.variable_count,
907 m.data.deg,
908 m.data.order,
909 &self.cum_binomial_lookup_table,
910 |i, x| {
911 if i == var_index {
912 output = x
913 }
914 },
915 );
916 return output as usize;
917 }
918
919 fn terms<'a>(&'a self, f: &'a Self::Element) -> Self::TermIter<'a> {
920 TermIterImpl {
921 base_iter: f.data.iter(),
922 }
923 }
924
925 fn monomial_deg(&self, mon: &Self::Monomial) -> usize { mon.data.deg as usize }
926
927 fn LT<'a, O: MonomialOrder>(
928 &'a self,
929 f: &'a Self::Element,
930 order: O,
931 ) -> Option<(&'a El<Self::BaseRing>, &'a Self::Monomial)> {
932 if order.is_same(&DegRevLex) {
933 f.data.last().map(|(c, m)| (c, m))
934 } else {
935 self.terms(f).max_by(|l, r| order.compare(RingRef::new(self), l.1, r.1))
936 }
937 }
938
939 fn largest_term_lt<'a, O: MonomialOrder>(
940 &'a self,
941 f: &'a Self::Element,
942 order: O,
943 lt_than: &Self::Monomial,
944 ) -> Option<(&'a El<Self::BaseRing>, &'a Self::Monomial)> {
945 if order.is_same(&DegRevLex) {
946 let res = match f
947 .data
948 .binary_search_by(|(_, mon)| self.compare_degrevlex(&mon.data, <_than.data))
949 {
950 Ok(0) => None,
951 Ok(i) => Some((&f.data[i - 1].0, &f.data[i - 1].1)),
952 Err(0) => None,
953 Err(i) => Some((&f.data[i - 1].0, &f.data[i - 1].1)),
954 };
955 assert!({
956 let expected = self
957 .terms(f)
958 .filter(|(_, m)| order.compare(RingRef::new(self), m, lt_than) == Ordering::Less)
959 .max_by(|l, r| order.compare(RingRef::new(self), l.1, r.1));
960 (res.is_none() && expected.is_none()) || std::ptr::eq(res.unwrap().0, expected.unwrap().0)
961 });
962 return res;
963 } else {
964 self.terms(f)
965 .filter(|(_, m)| order.compare(RingRef::new(self), m, lt_than) == Ordering::Less)
966 .max_by(|l, r| order.compare(RingRef::new(self), l.1, r.1))
967 }
968 }
969
970 fn monomial_mul(&self, lhs: Self::Monomial, rhs: &Self::Monomial) -> Self::Monomial {
971 let lhs_deg = lhs.data.deg;
972 let rhs_deg = rhs.data.deg;
973 if lhs_deg <= rhs_deg {
974 if let Some(table) = self.try_get_multiplication_table(lhs_deg, rhs_deg) {
975 return MonomialIdentifier {
976 data: InternalMonomialIdentifier {
977 deg: lhs_deg + rhs_deg,
978 order: table[lhs.data.order as usize][rhs.data.order as usize],
979 },
980 };
981 }
982 } else {
983 if let Some(table) = self.try_get_multiplication_table(rhs_deg, lhs_deg) {
984 return MonomialIdentifier {
985 data: InternalMonomialIdentifier {
986 deg: lhs_deg + rhs_deg,
987 order: table[rhs.data.order as usize][lhs.data.order as usize],
988 },
989 };
990 }
991 }
992 return self.exponent_wise_bivariate_monomial_operation(lhs.data, rhs.data.clone(), |a, b| a + b);
993 }
994
995 fn monomial_lcm(&self, lhs: Self::Monomial, rhs: &Self::Monomial) -> Self::Monomial {
996 self.exponent_wise_bivariate_monomial_operation(lhs.data, rhs.data.clone(), max)
997 }
998
999 fn monomial_div(&self, lhs: Self::Monomial, rhs: &Self::Monomial) -> Result<Self::Monomial, Self::Monomial> {
1000 let mut failed = false;
1001 let result = self.exponent_wise_bivariate_monomial_operation(lhs.data, rhs.data.clone(), |a, b| {
1002 match a.checked_sub(b) {
1003 Some(x) => x,
1004 None => {
1005 failed = true;
1006 0
1007 }
1008 }
1009 });
1010 if failed { Err(result) } else { Ok(result) }
1011 }
1012
1013 fn evaluate<S, V, H>(&self, f: &Self::Element, values: V, hom: H) -> S::Element
1014 where
1015 S: ?Sized + RingBase,
1016 H: Homomorphism<<Self::BaseRing as RingStore>::Type, S>,
1017 V: VectorFn<S::Element>,
1018 {
1019 assert!(hom.domain().get_ring() == self.base_ring().get_ring());
1020 assert_eq!(values.len(), self.indeterminate_count());
1021 let new_ring = RingValue::from(MultivariatePolyRingImplBase {
1022 zero: hom.codomain().zero(),
1023 base_ring: hom.codomain(),
1024 variable_count: self.variable_count,
1025 max_supported_deg: self.max_supported_deg,
1026 monomial_multiplication_table: self.monomial_multiplication_table.clone(),
1027 tmp_monomials: ThreadLocal::new(),
1028 cum_binomial_lookup_table: self.cum_binomial_lookup_table.clone(),
1029 tmp_poly: AtomicOptionBox::new(None),
1030 allocator: self.allocator.clone(),
1031 });
1032 let mut result = new_ring.from_terms(self.terms(f).map(|(c, m)| {
1033 (
1034 hom.map_ref(c),
1035 new_ring.create_monomial((0..self.indeterminate_count()).map(|i| self.exponent_at(m, i))),
1036 )
1037 }));
1038 for i in 0..self.indeterminate_count() {
1039 result = new_ring.specialize(&result, i, &new_ring.inclusion().map(values.at(i)));
1040 }
1041 debug_assert!(result.data.len() <= 1);
1042 if result.data.is_empty() {
1043 return hom.codomain().zero();
1044 } else {
1045 debug_assert!(result.data[0].1.data.deg == 0);
1046 return result.data.into_iter().next().unwrap().0;
1047 }
1048 }
1049}
1050
1051impl<P, R, A> CanHomFrom<P> for MultivariatePolyRingImplBase<R, A>
1052where
1053 R: RingStore,
1054 A: Clone + Allocator + Send,
1055 P: MultivariatePolyRing,
1056 R::Type: CanHomFrom<<P::BaseRing as RingStore>::Type>,
1057{
1058 type Homomorphism = <R::Type as CanHomFrom<<P::BaseRing as RingStore>::Type>>::Homomorphism;
1059
1060 fn has_canonical_hom(&self, from: &P) -> Option<Self::Homomorphism> {
1061 if self.indeterminate_count() >= from.indeterminate_count() {
1062 self.base_ring()
1063 .get_ring()
1064 .has_canonical_hom(from.base_ring().get_ring())
1065 } else {
1066 None
1067 }
1068 }
1069
1070 fn map_in(&self, from: &P, el: <P as RingBase>::Element, hom: &Self::Homomorphism) -> Self::Element {
1071 self.map_in_ref(from, &el, hom)
1072 }
1073
1074 fn map_in_ref(&self, from: &P, el: &<P as RingBase>::Element, hom: &Self::Homomorphism) -> Self::Element {
1075 RingRef::new(self).from_terms(from.terms(el).map(|(c, m)| {
1076 (
1077 self.base_ring()
1078 .get_ring()
1079 .map_in_ref(from.base_ring().get_ring(), c, hom),
1080 self.create_monomial((0..self.indeterminate_count()).map(|i| {
1081 if i < from.indeterminate_count() {
1082 from.exponent_at(m, i)
1083 } else {
1084 0
1085 }
1086 })),
1087 )
1088 }))
1089 }
1090}
1091
1092impl<P, R, A> CanIsoFromTo<P> for MultivariatePolyRingImplBase<R, A>
1093where
1094 R: RingStore,
1095 A: Clone + Allocator + Send,
1096 P: MultivariatePolyRing,
1097 R::Type: CanIsoFromTo<<P::BaseRing as RingStore>::Type>,
1098{
1099 type Isomorphism = <R::Type as CanIsoFromTo<<P::BaseRing as RingStore>::Type>>::Isomorphism;
1100
1101 fn has_canonical_iso(&self, from: &P) -> Option<Self::Isomorphism> {
1102 if self.indeterminate_count() == from.indeterminate_count() {
1103 self.base_ring()
1104 .get_ring()
1105 .has_canonical_iso(from.base_ring().get_ring())
1106 } else {
1107 None
1108 }
1109 }
1110
1111 fn map_out(&self, from: &P, el: Self::Element, iso: &Self::Isomorphism) -> <P as RingBase>::Element {
1112 RingRef::new(from).from_terms(self.terms(&el).map(|(c, m)| {
1113 (
1114 self.base_ring()
1115 .get_ring()
1116 .map_out(from.base_ring().get_ring(), self.base_ring().clone_el(c), iso),
1117 from.create_monomial((0..self.indeterminate_count()).map(|i| self.exponent_at(m, i))),
1118 )
1119 }))
1120 }
1121}
1122
1123#[cfg(test)]
1124use crate::rings::approx_real::float::Real64;
1125#[cfg(test)]
1126use crate::rings::zn::zn_static;
1127#[cfg(test)]
1128use crate::rings::zn::zn_static::F17;
1129#[cfg(test)]
1130use crate::rings::zn::*;
1131
1132#[cfg(test)]
1133fn ring_and_elements() -> (
1134 MultivariatePolyRingImpl<zn_static::Fp<17>>,
1135 Vec<MultivariatePolyRingEl<zn_static::Fp<17>>>,
1136) {
1137 let ring = MultivariatePolyRingImpl::new(F17, 3);
1138 let els = vec![
1139 ring.from_terms([]),
1140 ring.from_terms([(ring.base_ring().one(), ring.create_monomial([0, 0, 0]))]),
1141 ring.from_terms([(ring.base_ring().neg_one(), ring.create_monomial([0, 0, 0]))]),
1142 ring.from_terms([(ring.base_ring().one(), ring.create_monomial([1, 0, 0]))]),
1143 ring.from_terms([(ring.base_ring().neg_one(), ring.create_monomial([1, 0, 0]))]),
1144 ring.from_terms([
1145 (ring.base_ring().one(), ring.create_monomial([1, 0, 0])),
1146 (ring.base_ring().one(), ring.create_monomial([0, 1, 0])),
1147 ]),
1148 ring.from_terms([
1149 (ring.base_ring().one(), ring.create_monomial([2, 0, 0])),
1150 (ring.base_ring().neg_one(), ring.create_monomial([1, 0, 0])),
1151 ]),
1152 ring.from_terms([
1153 (ring.base_ring().one(), ring.create_monomial([1, 0, 0])),
1154 (ring.base_ring().neg_one(), ring.create_monomial([0, 1, 1])),
1155 (ring.base_ring().one(), ring.create_monomial([0, 0, 2])),
1156 ]),
1157 ];
1158 return (ring, els);
1159}
1160
1161#[test]
1162fn test_ring_axioms() {
1163 let (ring, els) = ring_and_elements();
1164 crate::ring::generic_tests::test_ring_axioms(&ring, els.into_iter());
1165}
1166
1167#[test]
1168fn test_multivariate_axioms() {
1169 let (ring, _els) = ring_and_elements();
1170 crate::rings::multivariate::generic_tests::test_poly_ring_axioms(
1171 &ring,
1172 [F17.one(), F17.zero(), F17.int_hom().map(2), F17.neg_one()].into_iter(),
1173 );
1174}
1175
1176#[test]
1177fn test_enumeration_index_degrevlex() {
1178 let cum_binomial_lookup_table = (0..4)
1179 .map(|n| (0..7).map(|k| compute_cum_binomial(n, k)).collect::<Vec<_>>())
1180 .collect::<Vec<_>>();
1181
1182 assert_eq!(
1183 0,
1184 enumeration_index_degrevlex(0, [0, 0, 0, 0].clone_els_by(|x| *x), &cum_binomial_lookup_table)
1185 );
1186 let mut check = [0, 0, 0, 0];
1187 nth_monomial_degrevlex(4, 0, 0, &cum_binomial_lookup_table, |i, x| check[i] = x);
1188 assert_eq!(&[0, 0, 0, 0], &check);
1189
1190 assert_eq!(
1191 0,
1192 enumeration_index_degrevlex(1, [0, 0, 0, 1].clone_els_by(|x| *x), &cum_binomial_lookup_table)
1193 );
1194 let mut check = [0, 0, 0, 0];
1195 nth_monomial_degrevlex(4, 1, 0, &cum_binomial_lookup_table, |i, x| check[i] = x);
1196 assert_eq!(&[0, 0, 0, 1], &check);
1197
1198 assert_eq!(
1199 3,
1200 enumeration_index_degrevlex(1, [1, 0, 0, 0].clone_els_by(|x| *x), &cum_binomial_lookup_table)
1201 );
1202 let mut check = [0, 0, 0, 0];
1203 nth_monomial_degrevlex(4, 1, 3, &cum_binomial_lookup_table, |i, x| check[i] = x);
1204 assert_eq!(&[1, 0, 0, 0], &check);
1205
1206 fn degrevlex_cmp(lhs: &[u16; 4], rhs: &[u16; 4]) -> Ordering {
1207 let lhs_deg = lhs[0] + lhs[1] + lhs[2] + lhs[3];
1208 let rhs_deg = rhs[0] + rhs[1] + rhs[2] + rhs[3];
1209 if lhs_deg < rhs_deg {
1210 return Ordering::Less;
1211 } else if lhs_deg > rhs_deg {
1212 return Ordering::Greater;
1213 } else {
1214 for i in (0..4).rev() {
1215 if lhs[i] > rhs[i] {
1216 return Ordering::Less;
1217 } else if lhs[i] < rhs[i] {
1218 return Ordering::Greater;
1219 }
1220 }
1221 return Ordering::Equal;
1222 }
1223 }
1224
1225 let all_monomials = multiset_combinations(&[6, 6, 6, 6], 6, |slice| {
1226 std::array::from_fn::<_, 4, _>(|i| slice[3 - i] as u16)
1227 })
1228 .collect::<Vec<_>>();
1229 assert!(all_monomials.is_sorted_by(|l, r| degrevlex_cmp(l, r) == Ordering::Less));
1230
1231 for i in 0..all_monomials.len() {
1232 assert_eq!(
1233 i as u64,
1234 enumeration_index_degrevlex(6, (&all_monomials[i]).clone_els_by(|x| *x), &cum_binomial_lookup_table)
1235 );
1236 let mut check = [0, 0, 0, 0];
1237 nth_monomial_degrevlex(4, 6, i as u64, &cum_binomial_lookup_table, |i, x| check[i] = x);
1238 assert_eq!(&all_monomials[i], &check);
1239 }
1240}
1241
1242#[test]
1243fn test_create_multiplication_table() {
1244 let cum_binomial_lookup_table = (0..3)
1245 .map(|n| (0..7).map(|k| compute_cum_binomial(n, k)).collect::<Vec<_>>())
1246 .collect::<Vec<_>>();
1247 let mul_table = MultivariatePolyRingImplBase::<StaticRing<i64>>::create_multiplication_table(
1248 3,
1249 3,
1250 4,
1251 &cum_binomial_lookup_table,
1252 );
1253
1254 let deg3_monomials = multiset_combinations(&[3, 3, 3], 3, |slice| {
1255 std::array::from_fn::<_, 3, _>(|i| slice[2 - i] as u16)
1256 })
1257 .collect::<Vec<_>>();
1258 let deg4_monomials = multiset_combinations(&[4, 4, 4], 4, |slice| {
1259 std::array::from_fn::<_, 3, _>(|i| slice[2 - i] as u16)
1260 })
1261 .collect::<Vec<_>>();
1262
1263 for lhs in °3_monomials {
1264 for rhs in °4_monomials {
1265 assert_eq!(
1266 enumeration_index_degrevlex(7, (0..3).map_fn(|i| lhs[i] + rhs[i]), &cum_binomial_lookup_table),
1267 mul_table
1268 [enumeration_index_degrevlex(3, (0..3).map_fn(|i| lhs[i]), &cum_binomial_lookup_table) as usize]
1269 [enumeration_index_degrevlex(4, (0..3).map_fn(|i| rhs[i]), &cum_binomial_lookup_table) as usize]
1270 );
1271 }
1272 }
1273}
1274
1275#[test]
1276fn test_monomial_small() {
1277 assert_eq!(16, size_of::<MonomialIdentifier>());
1278}
1279
1280#[test]
1281fn test_new_many_variables() {
1282 for m in 1..32 {
1283 let ring = MultivariatePolyRingImpl::new_with_mult_table(StaticRing::<i64>::RING, m, 32, (2, 3), Global);
1284 assert_eq!(m, ring.indeterminate_count());
1285 }
1286}
1287
1288#[test]
1289fn test_evaluate_approximate_ring() {
1290 let ring = MultivariatePolyRingImpl::new(Real64::RING, 2);
1291 let [f] = ring.with_wrapped_indeterminates(|[X, Y]| [X * X * Y - Y * Y]);
1292 let x = 0.47312;
1293 let y = -1.43877;
1294 assert!(
1295 Real64::RING
1296 .abs((x * x * y - y * y) - ring.evaluate(&f, [x, y].clone_els_by(|x| *x), &Real64::RING.identity()))
1297 <= 0.000000001
1298 );
1299}
1300
1301#[test]
1302fn test_evaluate_many_variables() {
1303 let ring = MultivariatePolyRingImpl::new_with_mult_table(StaticRing::<i64>::RING, 20, 16, (4, 6), Global);
1304 let [f] = ring.with_wrapped_indeterminates(|X: [_; 20]| [X[0] + X[5] + X[19]]);
1305 assert_eq!(
1306 1 + 6 + 20,
1307 ring.evaluate(&f, (1..21).map_fn(|x| x as i64), StaticRing::<i64>::RING.identity())
1308 );
1309}
1310
1311#[test]
1312fn test_appearing_indeterminates() {
1313 let F7 = zn_64::Zn::new(7).as_field().ok().unwrap();
1314 let F7XY = MultivariatePolyRingImpl::new(&F7, 2);
1315 let [f, g] = F7XY.with_wrapped_indeterminates(|[X, Y]| [5 + 4 * X, 6 + 2 * Y]);
1316 assert_eq!(vec![(0, 1)], F7XY.appearing_indeterminates(&f));
1317 assert_eq!(vec![(1, 1)], F7XY.appearing_indeterminates(&g));
1318}