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
20fn 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
27fn 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
47fn 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#[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
122pub struct MultivariatePolyRingEl<R, A = Global>
126 where R: RingStore,
127 A: Allocator + Clone
128{
129 data: Vec<(El<R>, MonomialIdentifier), A>
130}
131
132pub struct MultivariatePolyRingImplBase<R, A = Global>
136 where R: RingStore,
137 A: Clone + Allocator + Send
138{
139 base_ring: R,
140 variable_count: usize,
141 tmp_monomials: ThreadLocal<(RefCell<Box<[Exponent]>>, RefCell<Box<[Exponent]>>)>,
146 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
155pub type MultivariatePolyRingImpl<R, A = Global> = RingValue<MultivariatePolyRingImplBase<R, A>>;
159
160impl<R> MultivariatePolyRingImpl<R>
161 where R: RingStore
162{
163 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 #[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 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 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 #[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 #[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 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 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(¤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())));
440 std::mem::swap(&mut new, &mut current);
441 std::mem::swap(&mut new.data, &mut tmp);
442 current
443 })
444 } else {
445 lhs.data.iter().fold(self.zero(), |mut current, (l_c, l_m)| {
447 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())));
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, <_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 °3_monomials {
950 for rhs in °4_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}