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
21fn 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
28fn 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
48fn 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#[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
132pub 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
152pub struct MultivariatePolyRingImplBase<R, A = Global>
156 where R: RingStore,
157 A: Clone + Allocator + Send
158{
159 base_ring: R,
160 variable_count: usize,
161 tmp_monomials: ThreadLocal<(RefCell<Box<[Exponent]>>, RefCell<Box<[Exponent]>>)>,
166 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
175pub type MultivariatePolyRingImpl<R, A = Global> = RingValue<MultivariatePolyRingImplBase<R, A>>;
179
180impl<R> MultivariatePolyRingImpl<R>
181 where R: RingStore
182{
183 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 #[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 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 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 #[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 #[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 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 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(¤t, 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 lhs.data.iter().fold(self.zero(), |mut current, (l_c, l_m)| {
481 let mut new = self.add_terms(¤t, 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
555pub 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, <_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 °3_monomials {
987 for rhs in °4_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}