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::iters::multiset_combinations;
10use crate::primitive_int::StaticRing;
11use crate::ring::*;
12use crate::rings::multivariate::*;
13use crate::integer::*;
14use crate::seq::{VectorFn, VectorView};
15use crate::homomorphism::*;
16use crate::ordered::OrderedRingStore;
17
18type Exponent = u16;
19type OrderIdx = u64;
20
21///
22/// Computes the "cumulative binomial function" `sum_(0 <= l <= k) binomial(n + l, n)`
23/// 
24fn compute_cum_binomial(n: usize, k: usize) -> u64 {
25    StaticRing::<i64>::RING.sum((0..(k + 1)).map(|l| binomial((n + l) as i128, &(n as i128), StaticRing::<i128>::RING).try_into().unwrap())) as u64
26}
27
28///
29/// Returns the index of the given monomial within the list of all degree-d monomials, ordered by DegRevLex
30/// 
31fn enumeration_index_degrevlex<V>(d: Exponent, mon: V, cum_binomial_lookup_table: &[Vec<u64>]) -> u64
32    where V: VectorFn<Exponent>
33{
34    debug_assert!(d == mon.iter().sum::<Exponent>());
35    let n = mon.len();
36    let mut remaining_degree_minus_one: i64 = TryInto::<i64>::try_into(d).unwrap() - 1;
37    let mut result = 0;
38    for i in 0..(n - 1) {
39        remaining_degree_minus_one -= TryInto::<i64>::try_into(mon.at(n - 1 - i)).unwrap();
40        if remaining_degree_minus_one < 0 {
41            return result;
42        }
43        result += cum_binomial_lookup_table[n - i - 2][remaining_degree_minus_one as usize];
44    }
45    return result;
46}
47
48///
49/// Inverse to [`enumeration_index_degrevlex()`].
50/// 
51fn nth_monomial_degrevlex<F>(n: usize, d: Exponent, mut index: u64, cum_binomial_lookup_table: &[Vec<u64>], mut out: F)
52    where F: FnMut(usize, Exponent)
53{
54    for i in 0..n {
55        out(i, 0);
56    }
57    let mut check_degree = 0;
58    let mut remaining_degree = d as usize;
59    for i in 0..(n - 1) {
60        if index == 0 {
61            out(n - 1 - i, remaining_degree as Exponent);
62            check_degree += remaining_degree as Exponent;
63            debug_assert!(d == check_degree);
64            return;
65        }
66        let remaining_degree_minus_one = match cum_binomial_lookup_table[n - i - 2].binary_search(&index) {
67            Ok(idx) => idx,
68            Err(idx) => idx - 1
69        };
70        index -= cum_binomial_lookup_table[n - i - 2][remaining_degree_minus_one];
71        let new_remaining_degree = remaining_degree_minus_one + 1;
72        out(n - 1 - i, (remaining_degree - new_remaining_degree) as Exponent);
73        check_degree += (remaining_degree - new_remaining_degree) as Exponent;
74        remaining_degree = new_remaining_degree;
75    }
76    out(0, remaining_degree as Exponent);
77    check_degree += remaining_degree as Exponent;
78    debug_assert!(d == check_degree);
79}
80
81///
82/// Stores a reference to a monomial w.r.t. a given [`MultivariatePolyRingImplBase`].
83/// 
84#[repr(transparent)]
85pub struct MonomialIdentifier {
86    data: InternalMonomialIdentifier
87}
88
89impl Debug for MonomialIdentifier {
90    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91        f.debug_struct("MonomialIdentifier")
92            .field("deg", &self.data.deg)
93            .field("idx", &self.data.order)
94            .finish()
95    }
96}
97
98#[derive(Clone, Debug)]
99struct InternalMonomialIdentifier {
100    deg: Exponent,
101    order: OrderIdx
102}
103
104impl InternalMonomialIdentifier {
105
106    fn wrap(self) -> MonomialIdentifier {
107        MonomialIdentifier { data: self }
108    }
109}
110
111impl PartialEq for InternalMonomialIdentifier {
112    fn eq(&self, other: &Self) -> bool {
113        let res = self.deg == other.deg && self.order == other.order;
114        return res;
115    }
116}
117
118impl Eq for InternalMonomialIdentifier {}
119
120impl Ord for InternalMonomialIdentifier {
121    fn cmp(&self, other: &Self) -> Ordering {
122        self.deg.cmp(&other.deg).then_with(|| self.order.cmp(&other.order))
123    }
124}
125
126impl PartialOrd for InternalMonomialIdentifier {
127    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
128        Some(self.cmp(other))
129    }
130}
131
132///
133/// An element of [`MultivariatePolyRingImpl`].
134/// 
135pub struct MultivariatePolyRingEl<R, A = Global>
136    where R: RingStore,
137        A: Allocator + Clone
138{
139    data: Vec<(El<R>, MonomialIdentifier), A>
140}
141
142impl<R, A> Debug for MultivariatePolyRingEl<R, A>
143    where R: RingStore,
144        El<R>: Debug,
145        A: Allocator + Clone
146{
147    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
148        write!(f, "{:?}", self.data)
149    }
150}
151
152///
153/// Implementation of multivariate polynomial rings.
154/// 
155pub struct MultivariatePolyRingImplBase<R, A = Global>
156    where R: RingStore,
157        A: Clone + Allocator + Send
158{
159    base_ring: R,
160    variable_count: usize,
161    /// why do I use a RefCell here? Note that in `create_monomial`, we are putting values into `tmp_monomial`
162    /// from an iterator, which means that theoretically, the `next()` of the iterator might call `create_monomial()`
163    /// again. Using a `RefCell` at least leads to a panic in this crazy unlikely scenario, while using `Cell` would
164    /// silently give wrong results.
165    tmp_monomials: ThreadLocal<(RefCell<Box<[Exponent]>>, RefCell<Box<[Exponent]>>)>,
166    /// indices are [lhs_deg][rhs_deg][lhs_index][rhs_index]
167    monomial_multiplication_table: Vec<Vec<Vec<Vec<u64>>>>,
168    zero: El<R>,
169    cum_binomial_lookup_table: Vec<Vec<u64>>,
170    max_supported_deg: Exponent,
171    allocator: A,
172    tmp_poly: AtomicOptionBox<Vec<(El<R>, MonomialIdentifier)>>
173}
174
175///
176/// [`RingStore`] corresponding to [`MultivariatePolyRingImplBase`]
177/// 
178pub type MultivariatePolyRingImpl<R, A = Global> = RingValue<MultivariatePolyRingImplBase<R, A>>;
179
180impl<R> MultivariatePolyRingImpl<R>
181    where R: RingStore
182{
183    ///
184    /// Creates a new instance of the ring `base_ring[X0, ..., Xn]` where `n = variable_count - 1`.
185    /// 
186    pub fn new(base_ring: R, variable_count: usize) -> Self {
187        Self::new_with_mult_table(base_ring, variable_count, 64, (6, 8), Global)
188    }
189}
190
191impl<R, A> MultivariatePolyRingImpl<R, A>
192    where R: RingStore,
193        A: Clone + Allocator + Send
194{
195    ///
196    /// Creates a new instance of the ring `base_ring[X0, ..., Xn]` where `n = variable_count - 1`.
197    /// 
198    /// The can represent all monomials up to the given degree, and will panic should an operation
199    /// produce a monomial that exceeds this degree. 
200    /// 
201    /// Furthermore, `max_multiplication_table = (d1, d2)` configures for which monomials a multiplication 
202    /// table is precomputed. In particular, a multiplication table is precomputed for all products where
203    /// one summand has degree `<= d2` and the other summand has degree `<= d1`. Note that `d1 <= d2` is
204    /// required.
205    /// 
206    #[stability::unstable(feature = "enable")]
207    pub fn new_with_mult_table(base_ring: R, variable_count: usize, max_supported_deg: Exponent, max_multiplication_table: (Exponent, Exponent), allocator: A) -> Self {
208        assert!(variable_count >= 1);
209        assert!(max_multiplication_table.0 <= max_multiplication_table.1);
210        assert!(max_multiplication_table.0 + max_multiplication_table.1 <= max_supported_deg);
211        // the largest degree for which we have an order-preserving embedding of same-degree monomials into OrderIdx
212        let max_degree_for_orderidx = if variable_count == 1 || variable_count == 2 {
213            usize::MAX
214        } else {
215            let k = int_cast(TryInto::<i64>::try_into(variable_count).unwrap() - 1, BigIntRing::RING, StaticRing::<i64>::RING);
216            // ensure that cum_binomial() always fits within an u64
217            int_bisect::find_root_floor(StaticRing::<i64>::RING, 0, |d| if BigIntRing::RING.is_lt(&BigIntRing::RING.mul(
218                binomial(int_cast(d + TryInto::<i64>::try_into(variable_count).unwrap() - 1, BigIntRing::RING, StaticRing::<i64>::RING), &k, BigIntRing::RING),
219                int_cast(*d, BigIntRing::RING, StaticRing::<i64>::RING)
220            ), &BigIntRing::RING.power_of_two(u64::BITS as usize)) { -1 } else { 1 }) as usize
221        };
222        assert!(max_degree_for_orderidx >= max_supported_deg as usize, "currently only degrees are supported for which the total number of this-degree monomials fits in a u64");
223
224        let cum_binomial_lookup_table = (0..(variable_count - 1)).map(|n| (0..=max_supported_deg).map(|k| compute_cum_binomial(n, k as usize)).collect::<Vec<_>>()).collect::<Vec<_>>();
225        let monomial_multipliation_table = (0..max_multiplication_table.0).map(|lhs_deg| (lhs_deg..max_multiplication_table.1).map(|rhs_deg| MultivariatePolyRingImplBase::<R, A>::create_multiplication_table(variable_count, lhs_deg, rhs_deg, &cum_binomial_lookup_table)).collect::<Vec<_>>()).collect::<Vec<_>>();
226        RingValue::from(MultivariatePolyRingImplBase {
227            zero: base_ring.zero(),
228            base_ring: base_ring,
229            variable_count: variable_count,
230            max_supported_deg: max_supported_deg,
231            monomial_multiplication_table: monomial_multipliation_table,
232            tmp_monomials: ThreadLocal::new(),
233            cum_binomial_lookup_table: cum_binomial_lookup_table,
234            tmp_poly: AtomicOptionBox::none(),
235            allocator: allocator,
236        })
237    }
238}
239
240impl<R, A> MultivariatePolyRingImplBase<R, A>
241    where R: RingStore,
242        A: Clone + Allocator + Send
243{
244    #[stability::unstable(feature = "enable")]
245    pub fn allocator(&self) -> &A {
246        &self.allocator
247    }
248
249    #[inline(always)]
250    fn tmp_monomials(&self) -> (RefMut<'_, [u16]>, RefMut<'_, [u16]>) {
251        (self.tmp_monomial1(), self.tmp_monomial2())
252    }
253
254    ///
255    /// We have to temporary monomials that are used as follows:
256    ///  - `tmp_monomial2` for `create_monomial()`
257    ///  - both for bivariate operations in monomials (e.g. `monomial_mul()`)
258    /// 
259    /// We use `#[inline(always)]` in the hope that the compiler can optimize out runtime 
260    /// checks of `RefCell`.
261    /// 
262    #[inline(always)]
263    fn tmp_monomial1(&self) -> RefMut<'_, [u16]> {
264        let (mon, _) = self.tmp_monomials.get_or(|| (RefCell::new((0..self.variable_count).map(|_| 0).collect::<Vec<_>>().into_boxed_slice()), RefCell::new((0..self.variable_count).map(|_| 0).collect::<Vec<_>>().into_boxed_slice())));
265        RefMut::map(mon.borrow_mut(), |mon| &mut**mon)
266    }
267
268    ///
269    /// See [`MultivariatePolyRingImplBase::tmp_monomial1()`]
270    /// 
271    #[inline(always)]
272    fn tmp_monomial2(&self) -> RefMut<'_, [u16]> {
273        let (_, mon) = self.tmp_monomials.get_or(|| (RefCell::new((0..self.variable_count).map(|_| 0).collect::<Vec<_>>().into_boxed_slice()), RefCell::new((0..self.variable_count).map(|_| 0).collect::<Vec<_>>().into_boxed_slice())));
274        RefMut::map(mon.borrow_mut(), |mon| &mut**mon)
275    }
276
277    #[inline(always)]
278    fn exponent_wise_bivariate_monomial_operation<F>(&self, lhs: InternalMonomialIdentifier, rhs: InternalMonomialIdentifier, mut f: F) -> MonomialIdentifier
279        where F: FnMut(Exponent, Exponent) -> Exponent
280    {
281        let (mut lhs_mon, mut rhs_mon) = self.tmp_monomials();
282        nth_monomial_degrevlex(self.variable_count, lhs.deg, lhs.order, &self.cum_binomial_lookup_table, |i, x| lhs_mon[i] = x);
283        nth_monomial_degrevlex(self.variable_count, rhs.deg, rhs.order, &self.cum_binomial_lookup_table, |i, x| rhs_mon[i] = x);
284        let mut res_deg = 0;
285        for i in 0..self.variable_count {
286            lhs_mon[i] = f(lhs_mon[i], rhs_mon[i]);
287            res_deg += lhs_mon[i];
288        }
289        assert!(res_deg <= self.max_supported_deg, "Polynomial ring was configured to support monomials up to degree {}, but operation resulted in degree {}", self.max_supported_deg, res_deg);
290        return MonomialIdentifier {
291            data: InternalMonomialIdentifier {
292                deg: res_deg,
293                order: enumeration_index_degrevlex(res_deg, (&*lhs_mon).clone_els_by(|x| *x), &self.cum_binomial_lookup_table)
294            }
295        }
296    }
297
298    fn create_multiplication_table(variable_count: usize, lhs_deg: Exponent, rhs_deg: Exponent, cum_binomial_lookup_table: &[Vec<u64>]) -> Vec<Vec<u64>> {
299        debug_assert!(lhs_deg <= rhs_deg);
300        let lhs_max_deg = (0..variable_count).map(|_| lhs_deg as usize).collect::<Vec<_>>();
301        let rhs_max_deg = (0..variable_count).map(|_| rhs_deg as usize).collect::<Vec<_>>();
302        let mut lhs_i = 0;
303        let mut rhs_i = 0;
304        // `multiset_combinations()` iterates through the values in descending lex order, so by reversing the variable order, we get degrevlex
305        let rev = |i| variable_count - 1 - i;
306        multiset_combinations(&lhs_max_deg, lhs_deg as usize, |lhs| {
307            let result = multiset_combinations(&rhs_max_deg, rhs_deg as usize, |rhs| {
308                let result_index = enumeration_index_degrevlex(lhs_deg + rhs_deg, (0..variable_count).map_fn(|i| (lhs[rev(i)] + rhs[rev(i)]) as u16), cum_binomial_lookup_table);
309                rhs_i += 1;
310                return result_index;
311            }).collect::<Vec<_>>();
312            lhs_i += 1;
313            return result;
314        }).collect::<Vec<_>>()
315    }
316
317    fn try_get_multiplication_table<'a>(&'a self, lhs_deg: Exponent, rhs_deg: Exponent) -> Option<&'a Vec<Vec<u64>>> {
318        debug_assert!(lhs_deg <= rhs_deg);
319        if lhs_deg as usize >= self.monomial_multiplication_table.len() || rhs_deg as usize >= self.monomial_multiplication_table[lhs_deg as usize].len() {
320            return None;
321        }
322        Some(&self.monomial_multiplication_table[lhs_deg as usize][rhs_deg as usize])
323    }
324
325    fn compare_degrevlex(&self, lhs: &InternalMonomialIdentifier, rhs: &InternalMonomialIdentifier) -> Ordering {
326        let res = lhs.deg.cmp(&rhs.deg).then_with(|| lhs.order.cmp(&rhs.order));
327        debug_assert!(res == DegRevLex.compare(RingRef::new(self), &lhs.clone().wrap(), &rhs.clone().wrap()));
328        return res;
329    }
330
331    fn is_valid(&self, el: &[(El<R>, MonomialIdentifier)]) -> bool {
332        for i in 1..el.len() {
333            if self.compare_degrevlex(&el[i - 1].1.data, &el[i].1.data) != Ordering::Less {
334                return false;
335            }
336        }
337        if !self.base_ring().get_ring().is_approximate() {
338            for i in 0..el.len() {
339                if self.base_ring().is_zero(&el[i].0) {
340                    return false;
341                }
342            }
343        }
344        return true;
345    }
346
347    fn remove_zeros(&self, el: &mut Vec<(El<R>, MonomialIdentifier), A>) {
348        if self.base_ring().get_ring().is_approximate() {
349            return;
350        }
351        el.retain(|(c, _)| !self.base_ring().is_zero(c));
352        debug_assert!(self.is_valid(&el));
353    }
354
355    ///
356    /// Computes the sum of two elements; rhs may contain zero elements, but must be sorted and not contain equal monomials
357    ///
358    fn add_terms<I>(&self, lhs: &<Self as RingBase>::Element, rhs_sorted: I, out: Vec<(El<R>, MonomialIdentifier), A>) -> <Self as RingBase>::Element
359        where I: Iterator<Item = (El<R>, MonomialIdentifier)>
360    {
361        debug_assert!(self.is_valid(&lhs.data));
362        
363        let mut result = out;
364        result.clear();
365        result.reserve(lhs.data.len() + rhs_sorted.size_hint().0);
366        
367        let mut lhs_it = lhs.data.iter().peekable();
368        let mut rhs_it = rhs_sorted.peekable();
369
370        while let (Some((_, l_m)), Some((_, r_m))) = (lhs_it.peek(), rhs_it.peek()) {
371            let next_element = match self.compare_degrevlex(&l_m.data, &r_m.data) {
372                Ordering::Equal => {
373                    let (l_c, _l_m) = lhs_it.next().unwrap();
374                    let (r_c, r_m) = rhs_it.next().unwrap();
375                    (self.base_ring().add_ref_fst(l_c, r_c), r_m)
376                },
377                Ordering::Less => {
378                    let (l_c, l_m) = lhs_it.next().unwrap();
379                    (self.base_ring().clone_el(l_c), l_m.data.clone().wrap())
380                },
381                Ordering::Greater => {
382                    let (r_c, r_m) = rhs_it.next().unwrap();
383                    (r_c, r_m)
384                }
385            };
386            result.push(next_element);
387        }
388        result.extend(lhs_it.map(|(c, m)| (self.base_ring().clone_el(c), m.data.clone().wrap())));
389        result.extend(rhs_it);
390        self.remove_zeros(&mut result);
391        debug_assert!(self.is_valid(&result));
392        return MultivariatePolyRingEl {
393            data: result
394        };
395    }
396}
397
398impl<R, A> Debug for MultivariatePolyRingImplBase<R, A>
399    where R: RingStore,
400        R::Type: Debug,
401        A: Clone + Allocator + Send
402{
403    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
404        f.debug_struct("MultivariatePolyRingImplBase")
405            .field("base_ring", &self.base_ring.get_ring())
406            .field("variable_count", &self.variable_count)
407            .field("max_supported_deg", &self.max_supported_deg)
408            .finish()
409    }
410}
411
412impl<R, A> PartialEq for MultivariatePolyRingImplBase<R, A>
413    where R: RingStore,
414        A: Clone + Allocator + Send
415{
416    fn eq(&self, other: &Self) -> bool {
417        self.base_ring.get_ring() == other.base_ring.get_ring() && self.variable_count == other.variable_count && self.max_supported_deg == other.max_supported_deg
418    }
419}
420
421impl<R, A> RingBase for MultivariatePolyRingImplBase<R, A>
422    where R: RingStore,
423        A: Clone + Allocator + Send
424{
425    type Element = MultivariatePolyRingEl<R, A>;
426
427    fn clone_el(&self, val: &Self::Element) -> Self::Element {
428        let mut data = Vec::with_capacity_in(val.data.len(), self.allocator.clone());
429        data.extend(val.data.iter().map(|(c, m)| (self.base_ring().clone_el(c), self.clone_monomial(m))));
430        MultivariatePolyRingEl {
431            data: data
432        }
433    }
434
435    fn add_ref(&self, lhs: &Self::Element, rhs: &Self::Element) -> Self::Element {
436        debug_assert!(self.is_valid(&rhs.data));
437        self.add_terms(lhs, rhs.data.iter().map(|(c, m)| (self.base_ring().clone_el(c), m.data.clone().wrap())), Vec::new_in(self.allocator.clone()))
438    }
439
440    fn add_assign_ref(&self, lhs: &mut Self::Element, rhs: &Self::Element) {
441        *lhs = self.add_ref(lhs, rhs);
442    }
443
444    fn add_assign(&self, lhs: &mut Self::Element, rhs: Self::Element) {
445        debug_assert!(self.is_valid(&rhs.data));
446        *lhs = self.add_terms(&lhs, rhs.data.into_iter(), Vec::new_in(self.allocator.clone()));
447    }
448
449    fn sub_assign_ref(&self, lhs: &mut Self::Element, rhs: &Self::Element) {
450        debug_assert!(self.is_valid(&rhs.data));
451        *lhs = self.add_terms(&lhs, rhs.data.iter().map(|(c, m)| (self.base_ring().negate(self.base_ring.clone_el(c)), m.data.clone().wrap())), Vec::new_in(self.allocator.clone()));
452    }
453    
454    fn negate_inplace(&self, lhs: &mut Self::Element) {
455        debug_assert!(self.is_valid(&lhs.data));
456        for (c, _) in &mut lhs.data {
457            self.base_ring().negate_inplace(c);
458        }
459    }
460
461    fn mul_assign(&self, lhs: &mut Self::Element, rhs: Self::Element) {
462        self.mul_assign_ref(lhs, &rhs);
463    }
464
465    fn mul_assign_ref(&self, lhs: &mut Self::Element, rhs: &Self::Element) {
466        *lhs = self.mul_ref(lhs, rhs);
467    }
468
469    fn mul_ref(&self, lhs: &Self::Element, rhs: &Self::Element) -> Self::Element {
470        let mut tmp = Vec::new_in(self.allocator.clone());
471        if lhs.data.len() > rhs.data.len() {
472            rhs.data.iter().fold(self.zero(), |mut current, (r_c, r_m)| {
473                let mut new = self.add_terms(&current, lhs.data.iter().map(|(c, m)| (self.base_ring().mul_ref(c, r_c), self.monomial_mul(m.data.clone().wrap(), r_m))), std::mem::replace(&mut tmp, Vec::new_in(self.allocator.clone())));
474                std::mem::swap(&mut new, &mut current);
475                std::mem::swap(&mut new.data, &mut tmp);
476                current
477            })
478        } else {
479            // we duplicate it to work better with noncommutative rings (not that this is currently of relevance...)
480            lhs.data.iter().fold(self.zero(), |mut current, (l_c, l_m)| {
481                let mut new = self.add_terms(&current, rhs.data.iter().map(|(c, m)| (self.base_ring().mul_ref(l_c, c), self.monomial_mul(m.data.clone().wrap(), l_m))), std::mem::replace(&mut tmp, Vec::new_in(self.allocator.clone())));
482                std::mem::swap(&mut new, &mut current);
483                std::mem::swap(&mut new.data, &mut tmp);
484                current
485            })
486        }
487    }
488
489    fn zero(&self) -> Self::Element {
490        MultivariatePolyRingEl {
491            data: Vec::new_in(self.allocator.clone())
492        }
493    }
494
495    fn from_int(&self, value: i32) -> Self::Element {
496        self.from(self.base_ring().get_ring().from_int(value))
497    }
498
499    fn eq_el(&self, lhs: &Self::Element, rhs: &Self::Element) -> bool {
500        debug_assert!(self.is_valid(&lhs.data));
501        debug_assert!(self.is_valid(&rhs.data));
502        if lhs.data.len() != rhs.data.len() {
503            return false;
504        }
505        for i in 0..lhs.data.len() {
506            if lhs.data.at(i).1.data != rhs.data.at(i).1.data || !self.base_ring.eq_el(&lhs.data.at(i).0, &rhs.data.at(i).0) {
507                return false;
508            }
509        }
510        return true;
511    }
512
513    fn is_zero(&self, value: &Self::Element) -> bool {
514        value.data.len() == 0
515    }
516
517    fn is_one(&self, value: &Self::Element) -> bool {
518        debug_assert!(self.is_valid(&value.data));
519        if value.data.len() != 1 {
520            return false;
521        }
522        value.data[0].1.data.deg == 0 && self.base_ring().is_one(&value.data[0].0)
523    }
524
525    fn is_neg_one(&self, value: &Self::Element) -> bool {
526        debug_assert!(self.is_valid(&value.data));
527        if value.data.len() != 1 {
528            return false;
529        }
530        value.data[0].1.data.deg == 0 && self.base_ring().is_neg_one(&value.data[0].0)
531    }
532
533    fn is_commutative(&self) -> bool { self.base_ring().is_commutative() }
534    fn is_noetherian(&self) -> bool { self.base_ring().is_noetherian() }
535
536    fn dbg(&self, value: &Self::Element, out: &mut std::fmt::Formatter) -> std::fmt::Result {
537        self.dbg_within(value, out, EnvBindingStrength::Weakest)
538    }
539
540    fn dbg_within<'a>(&self, value: &Self::Element, out: &mut std::fmt::Formatter<'a>, env: EnvBindingStrength) -> std::fmt::Result {
541        super::generic_impls::print(RingRef::new(self), value, out, env)
542    }
543
544    fn characteristic<I: IntegerRingStore + Copy>(&self, ZZ: I) -> Option<El<I>>
545         where I::Type: IntegerRing 
546    {
547        self.base_ring().characteristic(ZZ)
548    }
549
550    fn is_approximate(&self) -> bool {
551        self.base_ring().get_ring().is_approximate()
552    }
553}
554
555/// 
556/// Iterator over the terms of a [`MultivariatePolyRingEl`].
557/// 
558pub struct TermIterImpl<'a, R>
559    where R: RingStore
560{
561    base_iter: std::slice::Iter<'a, (El<R>, MonomialIdentifier)>
562}
563
564impl<'a, R> Iterator for TermIterImpl<'a, R>
565    where R: RingStore
566{
567    type Item = (&'a El<R>, &'a MonomialIdentifier);
568
569    fn next(&mut self) -> Option<Self::Item> {
570        self.base_iter.next().map(|(c, m)| (c, m))
571    }
572}
573
574impl<R, A> RingExtension for MultivariatePolyRingImplBase<R, A>
575    where R: RingStore,
576        A: Clone + Allocator + Send
577{
578    type BaseRing = R;
579
580    fn base_ring<'b>(&'b self) -> &'b Self::BaseRing {
581        &self.base_ring
582    }
583
584    fn from(&self, x: El<Self::BaseRing>) -> Self::Element {
585        if !self.base_ring().get_ring().is_approximate() && self.base_ring().is_zero(&x) {
586            return self.zero();
587        } else {
588            let mut data = Vec::with_capacity_in(1, self.allocator.clone());
589            data.push((x, self.create_monomial((0..self.variable_count).map(|_| 0))));
590            return MultivariatePolyRingEl { data };
591        }
592    }
593
594    fn mul_assign_base(&self, lhs: &mut Self::Element, rhs: &El<Self::BaseRing>) {
595        for (c, _) in &mut lhs.data {
596            self.base_ring.mul_assign_ref(c, rhs);
597        }
598        lhs.data.retain(|(c, _)| !self.base_ring.is_zero(c));
599    }
600}
601
602impl<R, A> MultivariatePolyRing for MultivariatePolyRingImplBase<R, A>
603    where R: RingStore,
604        A: Clone + Allocator + Send
605{
606    type Monomial = MonomialIdentifier;
607    type TermIter<'a> = TermIterImpl<'a, R>
608        where Self: 'a;
609        
610    fn indeterminate_count(&self) -> usize {
611        self.variable_count
612    }
613
614    fn create_monomial<I>(&self, exponents: I) -> Self::Monomial
615        where I: IntoIterator<Item = usize>,
616            I::IntoIter: ExactSizeIterator
617    {
618        let exponents = exponents.into_iter();
619        assert_eq!(exponents.len(), self.indeterminate_count());
620
621        let mut tmp_monomial = self.tmp_monomial2();
622        let mut deg = 0;
623        for (i, e) in exponents.enumerate() {
624            deg += e as Exponent;
625            tmp_monomial[i] = e as Exponent;
626        }
627        assert!(deg <= self.max_supported_deg, "Polynomial ring was configured to support monomials up to degree {}, but create_monomial() was called for degree {}", self.max_supported_deg, deg);
628        return MonomialIdentifier {
629            data: InternalMonomialIdentifier {
630                deg: deg,
631                order: enumeration_index_degrevlex(deg, (&*tmp_monomial).clone_els_by(|x| *x), &self.cum_binomial_lookup_table)
632            }
633        }
634    }
635
636    fn clone_monomial(&self, mon: &Self::Monomial) -> Self::Monomial {
637        mon.data.clone().wrap()
638    }
639
640    fn add_assign_from_terms<I>(&self, lhs: &mut Self::Element, terms: I)
641        where I: IntoIterator<Item = (El<Self::BaseRing>, Self::Monomial)>
642    {
643        let terms = terms.into_iter();
644        let mut rhs = self.tmp_poly.swap(None, std::sync::atomic::Ordering::AcqRel).map(|b| *b).unwrap_or(Vec::new());
645        debug_assert!(rhs.len() == 0);
646        rhs.extend(terms.into_iter());
647        rhs.sort_unstable_by(|l, r| self.compare_degrevlex(&l.1.data, &r.1.data));
648        rhs.dedup_by(|(snd_c, snd_m), (fst_c, fst_m)| {
649            if self.compare_degrevlex(&fst_m.data, &snd_m.data) == Ordering::Equal {
650                self.base_ring().add_assign_ref(fst_c, snd_c);
651                return true;
652            } else {
653                return false;
654            }
655        });
656        *lhs = self.add_terms(&lhs, rhs.drain(..), Vec::new_in(self.allocator.clone()));
657        _ = self.tmp_poly.swap(Some(Box::new(rhs)), std::sync::atomic::Ordering::AcqRel);
658    }
659
660    fn mul_assign_monomial(&self, f: &mut Self::Element, rhs: Self::Monomial) {
661        let rhs_deg = rhs.data.deg;
662        let (mut lhs_mon, mut rhs_mon) = self.tmp_monomials();
663        nth_monomial_degrevlex(self.variable_count, rhs_deg, rhs.data.order, &self.cum_binomial_lookup_table, |i, x| rhs_mon[i] = x);
664        
665        for (_, lhs) in &mut f.data {
666            let lhs_deg = lhs.data.deg;
667            let mut fallback = || {
668                let res_deg = lhs.data.deg + rhs.data.deg;
669                assert!(res_deg <= self.max_supported_deg, "Polynomial ring was configured to support monomials up to degree {}, but multiplication resulted in degree {}", self.max_supported_deg, res_deg);
670                nth_monomial_degrevlex(self.variable_count, lhs.data.deg, lhs.data.order, &self.cum_binomial_lookup_table, |i, x| lhs_mon[i] = x);
671                for i in 0..self.variable_count {
672                    lhs_mon[i] += rhs_mon[i];
673                }
674                MonomialIdentifier {
675                    data: InternalMonomialIdentifier {
676                        deg: res_deg,
677                        order: enumeration_index_degrevlex(res_deg, (&*lhs_mon).clone_els_by(|x| *x), &self.cum_binomial_lookup_table)
678                    }
679                }
680            };
681            let new_val = if lhs_deg <= rhs_deg {
682                if let Some(table) = self.try_get_multiplication_table(lhs_deg, rhs_deg) {
683                    MonomialIdentifier {
684                        data: InternalMonomialIdentifier {
685                            deg: lhs_deg + rhs_deg,
686                            order: table[lhs.data.order as usize][rhs.data.order as usize]
687                        }
688                    }
689                } else {
690                    fallback()
691                }
692            } else {
693                if let Some(table) = self.try_get_multiplication_table(rhs_deg, lhs_deg) {
694                    MonomialIdentifier {
695                        data: InternalMonomialIdentifier {
696                            deg: lhs_deg + rhs_deg,
697                            order: table[rhs.data.order as usize][lhs.data.order as usize]
698                        }
699                    }
700                } else {
701                    fallback()
702                }
703            };
704            debug_assert!(new_val.data == fallback().data);
705            *lhs = new_val;
706        }
707    }
708
709    fn coefficient_at<'a>(&'a self, f: &'a Self::Element, m: &Self::Monomial) -> &'a El<Self::BaseRing> {
710        match f.data.binary_search_by(|(_, fm)| self.compare_degrevlex(&fm.data, &m.data)) {
711            Ok(i) => &f.data.at(i).0,
712            Err(_) => &self.zero
713        }
714    }
715
716    fn expand_monomial_to(&self, m: &Self::Monomial, out: &mut [usize]) {
717        nth_monomial_degrevlex(self.variable_count, m.data.deg, m.data.order, &self.cum_binomial_lookup_table, |i, x| out[i] = x as usize);
718    }
719
720    fn exponent_at(&self, m: &Self::Monomial, var_index: usize) -> usize {
721        let mut output = 0;
722        nth_monomial_degrevlex(self.variable_count, m.data.deg, m.data.order, &self.cum_binomial_lookup_table, |i, x| if i == var_index { output = x });
723        return output as usize;
724    }
725
726    fn terms<'a>(&'a self, f: &'a Self::Element) -> Self::TermIter<'a> {
727        TermIterImpl {
728            base_iter: f.data.iter()
729        }
730    }
731
732    fn monomial_deg(&self, mon: &Self::Monomial) -> usize {
733        mon.data.deg as usize
734    }
735
736    fn LT<'a, O: MonomialOrder>(&'a self, f: &'a Self::Element, order: O) -> Option<(&'a El<Self::BaseRing>, &'a Self::Monomial)> {
737        if order.is_same(&DegRevLex) {
738            f.data.last().map(|(c, m)| (c, m))
739        } else {
740            self.terms(f).max_by(|l, r| order.compare(RingRef::new(self), &l.1, &r.1))
741        }
742    }
743
744    fn largest_term_lt<'a, O: MonomialOrder>(&'a self, f: &'a Self::Element, order: O, lt_than: &Self::Monomial) -> Option<(&'a El<Self::BaseRing>, &'a Self::Monomial)> {
745        if order.is_same(&DegRevLex) {
746            let res = match f.data.binary_search_by(|(_, mon)| self.compare_degrevlex(&mon.data, &lt_than.data)) {
747                Ok(0) => None,
748                Ok(i) => Some((&f.data[i - 1].0, &f.data[i - 1].1)),
749                Err(0) => None,
750                Err(i) => Some((&f.data[i - 1].0, &f.data[i - 1].1))
751            };
752            assert!({
753                let expected = self.terms(f).filter(|(_, m)| order.compare(RingRef::new(self), m, lt_than) == Ordering::Less).max_by(|l, r| order.compare(RingRef::new(self), &l.1, &r.1));
754                (res.is_none() && expected.is_none()) || std::ptr::eq(res.unwrap().0, expected.unwrap().0)
755            });
756            return res;
757        } else {
758            self.terms(f).filter(|(_, m)| order.compare(RingRef::new(self), m, lt_than) == Ordering::Less).max_by(|l, r| order.compare(RingRef::new(self), &l.1, &r.1))
759        }
760    }
761
762    fn monomial_mul(&self, lhs: Self::Monomial, rhs: &Self::Monomial) -> Self::Monomial {
763        let lhs_deg = lhs.data.deg;
764        let rhs_deg = rhs.data.deg;
765        if lhs_deg <= rhs_deg {
766            if let Some(table) = self.try_get_multiplication_table(lhs_deg, rhs_deg) {
767                return MonomialIdentifier {
768                    data: InternalMonomialIdentifier {
769                        deg: lhs_deg + rhs_deg,
770                        order: table[lhs.data.order as usize][rhs.data.order as usize]
771                    }
772                };
773            }
774        } else {
775            if let Some(table) = self.try_get_multiplication_table(rhs_deg, lhs_deg) {
776                return MonomialIdentifier {
777                    data: InternalMonomialIdentifier {
778                        deg: lhs_deg + rhs_deg,
779                        order: table[rhs.data.order as usize][lhs.data.order as usize]
780                    }
781                };
782            }
783        }
784        return self.exponent_wise_bivariate_monomial_operation(lhs.data, rhs.data.clone(), |a, b| a + b);
785    }
786
787    fn monomial_lcm(&self, lhs: Self::Monomial, rhs: &Self::Monomial) -> Self::Monomial {
788        self.exponent_wise_bivariate_monomial_operation(lhs.data, rhs.data.clone(), |a, b| max(a, b))
789    }
790
791    fn monomial_div(&self, lhs: Self::Monomial, rhs: &Self::Monomial) -> Result<Self::Monomial, Self::Monomial> {
792        let mut failed = false;
793        let result = self.exponent_wise_bivariate_monomial_operation(lhs.data, rhs.data.clone(), |a, b| match a.checked_sub(b) {
794            Some(x) => x,
795            None => {
796                failed = true;
797                0
798            }
799        });
800        if failed {
801            Err(result)
802        } else {
803            Ok(result)
804        }
805    }
806
807    fn evaluate<S, V, H>(&self, f: &Self::Element, values: V, hom: H) -> S::Element
808        where S: ?Sized + RingBase,
809            H: Homomorphism<<Self::BaseRing as RingStore>::Type, S>,
810            V: VectorFn<S::Element>
811    {
812        assert!(hom.domain().get_ring() == self.base_ring().get_ring());
813        assert_eq!(values.len(), self.indeterminate_count());
814        let new_ring = RingValue::from(MultivariatePolyRingImplBase {
815            zero: hom.codomain().zero(),
816            base_ring: hom.codomain(),
817            variable_count: self.variable_count,
818            max_supported_deg: self.max_supported_deg,
819            monomial_multiplication_table: self.monomial_multiplication_table.clone(),
820            tmp_monomials: ThreadLocal::new(),
821            cum_binomial_lookup_table: self.cum_binomial_lookup_table.clone(),
822            tmp_poly: AtomicOptionBox::new(None),
823            allocator: self.allocator.clone()
824        });
825        let mut result = new_ring.from_terms(self.terms(f).map(|(c, m)| (hom.map_ref(c), new_ring.create_monomial((0..self.indeterminate_count()).map(|i| self.exponent_at(m, i))))));
826        for i in 0..self.indeterminate_count() {
827            result = new_ring.specialize(&result, i, &new_ring.inclusion().map(values.at(i)));
828        }
829        debug_assert!(result.data.len() <= 1);
830        if result.data.len() == 0 {
831            return hom.codomain().zero();
832        } else {
833            debug_assert!(result.data[0].1.data.deg == 0);
834            return result.data.into_iter().next().unwrap().0;
835        }
836    }
837}
838
839impl<P, R, A> CanHomFrom<P> for MultivariatePolyRingImplBase<R, A> 
840    where R: RingStore,
841        A: Clone + Allocator + Send,
842        P: MultivariatePolyRing,
843        R::Type: CanHomFrom<<P::BaseRing as RingStore>::Type>
844{
845    type Homomorphism = <R::Type as CanHomFrom<<P::BaseRing as RingStore>::Type>>::Homomorphism;
846
847    fn has_canonical_hom(&self, from: &P) -> Option<Self::Homomorphism> {
848        if self.indeterminate_count() >= from.indeterminate_count() {
849            self.base_ring().get_ring().has_canonical_hom(from.base_ring().get_ring())
850        } else {
851            None
852        }
853    }
854
855    fn map_in(&self, from: &P, el: <P as RingBase>::Element, hom: &Self::Homomorphism) -> Self::Element {
856        self.map_in_ref(from, &el, hom)
857    }
858
859    fn map_in_ref(&self, from: &P, el: &<P as RingBase>::Element, hom: &Self::Homomorphism) -> Self::Element {
860        RingRef::new(self).from_terms(from.terms(el).map(|(c, m)| (
861            self.base_ring().get_ring().map_in_ref(from.base_ring().get_ring(), c, hom),
862            self.create_monomial((0..self.indeterminate_count()).map(|i| if i < from.indeterminate_count() { from.exponent_at(m, i) } else { 0 }))
863        )))
864    }
865}
866
867impl<P, R, A> CanIsoFromTo<P> for MultivariatePolyRingImplBase<R, A> 
868    where R: RingStore,
869        A: Clone + Allocator + Send,
870        P: MultivariatePolyRing,
871        R::Type: CanIsoFromTo<<P::BaseRing as RingStore>::Type>
872{
873    type Isomorphism = <R::Type as CanIsoFromTo<<P::BaseRing as RingStore>::Type>>::Isomorphism;
874
875    fn has_canonical_iso(&self, from: &P) -> Option<Self::Isomorphism> {
876        if self.indeterminate_count() == from.indeterminate_count() {
877            self.base_ring().get_ring().has_canonical_iso(from.base_ring().get_ring())
878        } else {
879            None
880        }
881    }
882
883    fn map_out(&self, from: &P, el: Self::Element, iso: &Self::Isomorphism) -> <P as RingBase>::Element {
884        RingRef::new(from).from_terms(self.terms(&el).map(|(c, m)| (
885            self.base_ring().get_ring().map_out(from.base_ring().get_ring(), self.base_ring().clone_el(c), iso),
886            from.create_monomial((0..self.indeterminate_count()).map(|i| self.exponent_at(m, i)))
887        )))
888    }
889}
890
891#[cfg(test)]
892use crate::rings::zn::zn_static;
893#[cfg(test)]
894use crate::rings::zn::zn_static::F17;
895#[cfg(test)]
896use crate::rings::zn::*;
897#[cfg(test)]
898use crate::rings::approx_real::float::Real64;
899
900#[cfg(test)]
901fn ring_and_elements() -> (MultivariatePolyRingImpl<zn_static::Fp<17>>, Vec<MultivariatePolyRingEl<zn_static::Fp<17>>>) {
902    let ring = MultivariatePolyRingImpl::new(F17, 3);
903    let els = vec![
904        ring.from_terms([]),
905        ring.from_terms([(ring.base_ring().one(), ring.create_monomial([0, 0, 0]))]),
906        ring.from_terms([(ring.base_ring().neg_one(), ring.create_monomial([0, 0, 0]))]),
907        ring.from_terms([(ring.base_ring().one(), ring.create_monomial([1, 0, 0]))]),
908        ring.from_terms([(ring.base_ring().neg_one(), ring.create_monomial([1, 0, 0]))]),
909        ring.from_terms([(ring.base_ring().one(), ring.create_monomial([1, 0, 0])), (ring.base_ring().one(), ring.create_monomial([0, 1, 0]))]),
910        ring.from_terms([(ring.base_ring().one(), ring.create_monomial([2, 0, 0])), (ring.base_ring().neg_one(), ring.create_monomial([1, 0, 0]))]),
911        ring.from_terms([(ring.base_ring().one(), ring.create_monomial([1, 0, 0])), (ring.base_ring().neg_one(), ring.create_monomial([0, 1, 1])), (ring.base_ring().one(), ring.create_monomial([0, 0, 2]))]),
912    ];
913    return (ring, els);
914}
915
916#[test]
917fn test_ring_axioms() {
918    let (ring, els) = ring_and_elements();
919    crate::ring::generic_tests::test_ring_axioms(&ring, els.into_iter());
920}
921
922#[test]
923fn test_multivariate_axioms() {
924    let (ring, _els) = ring_and_elements();
925    crate::rings::multivariate::generic_tests::test_poly_ring_axioms(&ring, [F17.one(), F17.zero(), F17.int_hom().map(2), F17.neg_one()].into_iter());
926}
927
928#[test]
929fn test_enumeration_index_degrevlex() {
930
931    let cum_binomial_lookup_table = (0..4).map(|n| (0..7).map(|k| compute_cum_binomial(n, k)).collect::<Vec<_>>()).collect::<Vec<_>>();
932
933    assert_eq!(0, enumeration_index_degrevlex(0, [0, 0, 0, 0].clone_els_by(|x| *x), &cum_binomial_lookup_table));
934    let mut check = [0, 0, 0, 0];
935    nth_monomial_degrevlex(4, 0, 0, &cum_binomial_lookup_table, |i, x| check[i] = x);
936    assert_eq!(&[0, 0, 0, 0], &check);
937
938    assert_eq!(0, enumeration_index_degrevlex(1, [0, 0, 0, 1].clone_els_by(|x| *x), &cum_binomial_lookup_table));
939    let mut check = [0, 0, 0, 0];
940    nth_monomial_degrevlex(4, 1, 0, &cum_binomial_lookup_table, |i, x| check[i] = x);
941    assert_eq!(&[0, 0, 0, 1], &check);
942
943    assert_eq!(3, enumeration_index_degrevlex(1, [1, 0, 0, 0].clone_els_by(|x| *x), &cum_binomial_lookup_table));
944    let mut check = [0, 0, 0, 0];
945    nth_monomial_degrevlex(4, 1, 3, &cum_binomial_lookup_table, |i, x| check[i] = x);
946    assert_eq!(&[1, 0, 0, 0], &check);
947
948    fn degrevlex_cmp(lhs: &[u16; 4], rhs: &[u16; 4]) -> Ordering {
949        let lhs_deg = lhs[0] + lhs[1] + lhs[2] + lhs[3];
950        let rhs_deg = rhs[0] + rhs[1] + rhs[2] + rhs[3];
951        if lhs_deg < rhs_deg {
952            return Ordering::Less;
953        } else if lhs_deg > rhs_deg {
954            return Ordering::Greater;
955        } else {
956            for i in (0..4).rev() {
957                if lhs[i] > rhs[i] {
958                    return Ordering::Less
959                } else if lhs[i] < rhs[i] {
960                    return Ordering::Greater;
961                }
962            }
963            return Ordering::Equal;
964        }
965    }
966
967    let all_monomials = multiset_combinations(&[6, 6, 6, 6], 6, |slice| std::array::from_fn::<_, 4, _>(|i| slice[3 - i] as u16)).collect::<Vec<_>>();
968    assert!(all_monomials.is_sorted_by(|l, r| degrevlex_cmp(l, r) == Ordering::Less));
969
970    for i in 0..all_monomials.len() {
971        assert_eq!(i as u64, enumeration_index_degrevlex(6, (&all_monomials[i]).clone_els_by(|x| *x), &cum_binomial_lookup_table));
972        let mut check = [0, 0, 0, 0];
973        nth_monomial_degrevlex(4, 6, i as u64, &cum_binomial_lookup_table, |i, x| check[i] = x);
974        assert_eq!(&all_monomials[i], &check);
975    }
976}
977
978#[test]
979fn test_create_multiplication_table() {
980    let cum_binomial_lookup_table = (0..3).map(|n| (0..7).map(|k| compute_cum_binomial(n, k)).collect::<Vec<_>>()).collect::<Vec<_>>();
981    let mul_table = MultivariatePolyRingImplBase::<StaticRing<i64>>::create_multiplication_table(3, 3, 4, &cum_binomial_lookup_table);
982
983    let deg3_monomials = multiset_combinations(&[3, 3, 3], 3, |slice| std::array::from_fn::<_, 3, _>(|i| slice[2 - i] as u16)).collect::<Vec<_>>();
984    let deg4_monomials = multiset_combinations(&[4, 4, 4], 4, |slice| std::array::from_fn::<_, 3, _>(|i| slice[2 - i] as u16)).collect::<Vec<_>>();
985
986    for lhs in &deg3_monomials {
987        for rhs in &deg4_monomials {
988            assert_eq!(
989                enumeration_index_degrevlex(7, (0..3).map_fn(|i| lhs[i] + rhs[i]), &cum_binomial_lookup_table),
990                mul_table[
991                    enumeration_index_degrevlex(3, (0..3).map_fn(|i| lhs[i]), &cum_binomial_lookup_table) as usize
992                ][
993                    enumeration_index_degrevlex(4, (0..3).map_fn(|i| rhs[i]), &cum_binomial_lookup_table) as usize
994                ]
995            );
996        }
997    }
998}
999
1000#[test]
1001fn test_monomial_small() {
1002    assert_eq!(16, size_of::<MonomialIdentifier>());
1003}
1004
1005#[test]
1006fn test_new_many_variables() {
1007    for m in 1..32 {
1008        let ring = MultivariatePolyRingImpl::new_with_mult_table(StaticRing::<i64>::RING, m, 32, (2, 3), Global);
1009        assert_eq!(m, ring.indeterminate_count());
1010    }
1011}
1012
1013#[test]
1014fn test_evaluate_approximate_ring() {
1015    let ring = MultivariatePolyRingImpl::new(Real64::RING, 2);
1016    let [f] = ring.with_wrapped_indeterminates(|[X, Y]| [X * X * Y - Y * Y]);
1017    let x = 0.47312;
1018    let y = -1.43877;
1019    assert!(Real64::RING.abs((x * x * y - y * y) - ring.evaluate(&f, [x, y].clone_els_by(|x| *x), &Real64::RING.identity())) <= 0.000000001);
1020}
1021
1022#[test]
1023fn test_evaluate_many_variables() {
1024    let ring = MultivariatePolyRingImpl::new_with_mult_table(StaticRing::<i64>::RING, 20, 16, (4, 6), Global);
1025    let [f] = ring.with_wrapped_indeterminates(|X: [_; 20]| [X[0] + X[5] + X[19]]);
1026    assert_eq!(1 + 6 + 20, ring.evaluate(&f, (1..21).map_fn(|x| x as i64), StaticRing::<i64>::RING.identity()));
1027}
1028
1029#[test]
1030fn test_appearing_indeterminates() {
1031    let F7 = zn_64::Zn::new(7).as_field().ok().unwrap();
1032    let F7XY = MultivariatePolyRingImpl::new(&F7, 2);
1033    let [f, g] = F7XY.with_wrapped_indeterminates(|[X, Y]| [5 + 4 * X, 6 + 2 * Y]);
1034    assert_eq!(vec![(0, 1)], F7XY.appearing_indeterminates(&f));
1035    assert_eq!(vec![(1, 1)], F7XY.appearing_indeterminates(&g));
1036}