Skip to main content

feanor_math/rings/multivariate/
multivariate_impl.rs

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
21/// Computes the "cumulative binomial function" `sum_(0 <= l <= k) binomial(n + l, n)`
22fn 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
30/// Returns the index of the given monomial within the list of all degree-d monomials, ordered by
31/// DegRevLex
32fn 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
50/// Inverse to [`enumeration_index_degrevlex()`].
51fn 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/// Stores a reference to a monomial w.r.t. a given [`MultivariatePolyRingImplBase`].
83#[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
124/// An element of [`MultivariatePolyRingImpl`].
125pub 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
142/// Implementation of multivariate polynomial rings.
143pub struct MultivariatePolyRingImplBase<R, A = Global>
144where
145    R: RingStore,
146    A: Clone + Allocator + Send,
147{
148    base_ring: R,
149    variable_count: usize,
150    /// why do I use a RefCell here? Note that in `create_monomial`, we are putting values into
151    /// `tmp_monomial` from an iterator, which means that theoretically, the `next()` of the
152    /// iterator might call `create_monomial()` again. Using a `RefCell` at least leads to a
153    /// panic in this crazy unlikely scenario, while using `Cell` would silently give wrong
154    /// results.
155    tmp_monomials: ThreadLocal<(RefCell<Box<[Exponent]>>, RefCell<Box<[Exponent]>>)>,
156    /// indices are [lhs_deg][rhs_deg][lhs_index][rhs_index]
157    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
165/// [`RingStore`] corresponding to [`MultivariatePolyRingImplBase`]
166pub type MultivariatePolyRingImpl<R, A = Global> = RingValue<MultivariatePolyRingImplBase<R, A>>;
167
168impl<R> MultivariatePolyRingImpl<R>
169where
170    R: RingStore,
171{
172    /// Creates a new instance of the ring `base_ring[X0, ..., Xn]` where `n = variable_count - 1`.
173    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    /// Creates a new instance of the ring `base_ring[X0, ..., Xn]` where `n = variable_count - 1`.
184    ///
185    /// The can represent all monomials up to the given degree, and will panic should an operation
186    /// produce a monomial that exceeds this degree.
187    ///
188    /// Furthermore, `max_multiplication_table = (d1, d2)` configures for which monomials a
189    /// multiplication table is precomputed. In particular, a multiplication table is
190    /// precomputed for all products where one summand has degree `<= d2` and the other summand
191    /// has degree `<= d1`. Note that `d1 <= d2` is required.
192    #[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        // the largest degree for which we have an order-preserving embedding of same-degree
204        // monomials into OrderIdx
205        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            // ensure that cum_binomial() always fits within an u64
214            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    /// We have to temporary monomials that are used as follows:
288    ///  - `tmp_monomial2` for `create_monomial()`
289    ///  - both for bivariate operations in monomials (e.g. `monomial_mul()`)
290    ///
291    /// We use `#[inline(always)]` in the hope that the compiler can optimize out runtime
292    /// checks of `RefCell`.
293    #[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    /// See [`MultivariatePolyRingImplBase::tmp_monomial1()`]
315    #[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        // `multiset_combinations()` iterates through the values in descending lex order, so by
396        // reversing the variable order, we get degrevlex
397        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    /// Computes the sum of two elements; rhs may contain zero elements, but must be sorted and not
456    /// contain equal monomials
457    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                    &current,
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            // we duplicate it to work better with noncommutative rings (not that this is currently
608            // of relevance...)
609            lhs.data.iter().fold(self.zero(), |mut current, (l_c, l_m)| {
610                let mut new = self.add_terms(
611                    &current,
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
695/// Iterator over the terms of a [`MultivariatePolyRingEl`].
696pub 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, &lt_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 &deg3_monomials {
1264        for rhs in &deg4_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}