feanor_math/rings/multivariate/
multivariate_impl.rs

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