1use super::Polynomial;
2use super::polynomial_structure::*;
3use crate::polynomial::HomogeneousOfDegreeResult;
4use crate::polynomial::Monomial;
5use crate::polynomial::MultiPolynomial;
6use crate::polynomial::Term;
7use crate::polynomial::Variable;
8use crate::polynomial::VariablePower;
9use crate::structure::*;
10use algebraeon_nzq::*;
11use algebraeon_sets::structure::*;
12use std::borrow::Borrow;
13use std::collections::HashMap;
14use std::collections::HashSet;
15use std::fmt::Display;
16use std::marker::PhantomData;
17use std::rc::Rc;
18
19#[derive(Debug, Clone, PartialEq, Eq)]
20pub struct MultiPolynomialStructure<RS: RingEqSignature, RSB: BorrowedStructure<RS>> {
21 _coeff_ring: PhantomData<RS>,
22 coeff_ring: RSB,
23}
24
25impl<RS: RingEqSignature, RSB: BorrowedStructure<RS>> MultiPolynomialStructure<RS, RSB> {
26 fn new(coeff_ring: RSB) -> Self {
27 Self {
28 _coeff_ring: PhantomData,
29 coeff_ring,
30 }
31 }
32
33 pub fn coeff_ring(&self) -> &RS {
34 self.coeff_ring.borrow()
35 }
36}
37
38pub trait RingToMultiPolynomialRingSignature: RingEqSignature {
39 fn multivariable_polynomial_ring(&self) -> MultiPolynomialStructure<Self, &Self> {
40 MultiPolynomialStructure::new(self)
41 }
42
43 fn into_multivariable_polynomial_ring(self) -> MultiPolynomialStructure<Self, Self> {
44 MultiPolynomialStructure::new(self)
45 }
46}
47impl<RS: RingEqSignature> RingToMultiPolynomialRingSignature for RS {}
48
49impl<RS: RingEqSignature, RSB: BorrowedStructure<RS>> Signature
50 for MultiPolynomialStructure<RS, RSB>
51{
52}
53
54impl<RS: RingEqSignature + ToStringSignature, RSB: BorrowedStructure<RS>> ToStringSignature
55 for MultiPolynomialStructure<RS, RSB>
56{
57 fn to_string(&self, p: &Self::Set) -> String {
58 if p.terms.is_empty() {
59 "0".into()
60 } else {
61 let mut s = String::new();
62 for (idx, term) in p.terms.iter().enumerate() {
63 if idx != 0 {
64 s += "+";
65 }
66 if !self
67 .coeff_ring()
68 .equal(&term.coeff, &self.coeff_ring().one())
69 {
70 s += "(";
71 s += &self.coeff_ring().to_string(&term.coeff);
72 s += ")";
73 }
74 s += &term.monomial.to_string();
75 }
76 s
77 }
78 }
79}
80
81impl<RS: RingEqSignature, RSB: BorrowedStructure<RS>> SetSignature
82 for MultiPolynomialStructure<RS, RSB>
83{
84 type Set = MultiPolynomial<RS::Set>;
85
86 fn is_element(&self, _x: &Self::Set) -> Result<(), String> {
87 Ok(())
88 }
89}
90
91impl<RS: RingEqSignature, RSB: BorrowedStructure<RS>> EqSignature
92 for MultiPolynomialStructure<RS, RSB>
93{
94 fn equal(&self, a: &Self::Set, b: &Self::Set) -> bool {
95 let a = self.reduce(a.clone());
96 let b = self.reduce(b.clone());
97
98 let n = a.terms.len();
99 if n == b.terms.len() {
100 (0..n).all(|i| {
101 self.coeff_ring()
102 .equal(&a.terms[i].coeff, &b.terms[i].coeff)
103 && a.terms[i].monomial == b.terms[i].monomial
104 })
105 } else {
106 false
107 }
108 }
109}
110
111impl<RS: RingEqSignature, RSB: BorrowedStructure<RS>> RinglikeSpecializationSignature
112 for MultiPolynomialStructure<RS, RSB>
113{
114}
115
116impl<RS: RingEqSignature, RSB: BorrowedStructure<RS>> ZeroSignature
117 for MultiPolynomialStructure<RS, RSB>
118{
119 fn zero(&self) -> Self::Set {
120 MultiPolynomial { terms: vec![] }
121 }
122}
123
124impl<RS: RingEqSignature, RSB: BorrowedStructure<RS>> AdditionSignature
125 for MultiPolynomialStructure<RS, RSB>
126{
127 fn add(&self, a: &Self::Set, b: &Self::Set) -> Self::Set {
128 let mut a = a.clone();
129 let mut existing_monomials: HashMap<Monomial, usize> = HashMap::new(); for (
131 idx,
132 Term {
133 coeff: _coeff,
134 monomial,
135 },
136 ) in a.terms.clone().into_iter().enumerate()
137 {
138 existing_monomials.insert(monomial, idx);
139 }
140 for Term { coeff, monomial } in &b.terms {
141 if existing_monomials.contains_key(monomial) {
142 self.coeff_ring().add_mut(
143 &mut a.terms[*existing_monomials.get(monomial).unwrap()].coeff,
144 coeff,
145 );
146 } else {
147 a.terms.push(Term {
148 coeff: coeff.clone(),
149 monomial: monomial.clone(),
150 });
151 }
152 }
153 self.reduce(a) }
155}
156
157impl<RS: RingEqSignature, RSB: BorrowedStructure<RS>> CancellativeAdditionSignature
158 for MultiPolynomialStructure<RS, RSB>
159{
160 fn try_sub(&self, a: &Self::Set, b: &Self::Set) -> Option<Self::Set> {
161 Some(self.sub(a, b))
162 }
163}
164
165impl<RS: RingEqSignature, RSB: BorrowedStructure<RS>> TryNegateSignature
166 for MultiPolynomialStructure<RS, RSB>
167{
168 fn try_neg(&self, a: &Self::Set) -> Option<Self::Set> {
169 Some(self.neg(a))
170 }
171}
172
173impl<RS: RingEqSignature, RSB: BorrowedStructure<RS>> AdditiveMonoidSignature
174 for MultiPolynomialStructure<RS, RSB>
175{
176}
177
178impl<RS: RingEqSignature, RSB: BorrowedStructure<RS>> AdditiveGroupSignature
179 for MultiPolynomialStructure<RS, RSB>
180{
181 fn neg(&self, a: &Self::Set) -> Self::Set {
182 MultiPolynomial {
183 terms: a
184 .terms
185 .iter()
186 .map(
187 |Term {
188 coeff: c,
189 monomial: m,
190 }| Term {
191 coeff: self.coeff_ring().neg(c),
192 monomial: m.clone(),
193 },
194 )
195 .collect(),
196 }
197 }
198}
199
200impl<RS: RingEqSignature, RSB: BorrowedStructure<RS>> OneSignature
201 for MultiPolynomialStructure<RS, RSB>
202{
203 fn one(&self) -> Self::Set {
204 MultiPolynomial {
205 terms: vec![Term {
206 coeff: self.coeff_ring().one(),
207 monomial: Monomial::one(),
208 }],
209 }
210 }
211}
212
213impl<RS: RingEqSignature, RSB: BorrowedStructure<RS>> MultiplicationSignature
214 for MultiPolynomialStructure<RS, RSB>
215{
216 fn mul(&self, a: &Self::Set, b: &Self::Set) -> Self::Set {
217 let mut terms: HashMap<Monomial, RS::Set> = HashMap::new();
218 for Term {
219 coeff: a_coeff,
220 monomial: a_monomial,
221 } in &a.terms
222 {
223 for Term {
224 coeff: b_coeff,
225 monomial: b_monomial,
226 } in &b.terms
227 {
228 let mon = Monomial::mul(a_monomial, b_monomial);
229 let coeff = self.coeff_ring().mul(a_coeff, b_coeff);
230 self.coeff_ring()
231 .add_mut(terms.entry(mon).or_insert(self.coeff_ring().zero()), &coeff);
232 }
233 }
234 self.reduce(MultiPolynomial::new(
235 terms
236 .into_iter()
237 .map(|(monomial, coeff)| Term { coeff, monomial })
238 .collect(),
239 ))
240 }
241}
242
243impl<RS: RingEqSignature, RSB: BorrowedStructure<RS>> CommutativeMultiplicationSignature
244 for MultiPolynomialStructure<RS, RSB>
245{
246}
247
248impl<RS: RingEqSignature, RSB: BorrowedStructure<RS>> MultiplicativeMonoidSignature
249 for MultiPolynomialStructure<RS, RSB>
250{
251}
252
253impl<RS: RingEqSignature, RSB: BorrowedStructure<RS>> MultiplicativeAbsorptionMonoidSignature
254 for MultiPolynomialStructure<RS, RSB>
255{
256}
257
258impl<RS: RingEqSignature, RSB: BorrowedStructure<RS>> LeftDistributiveMultiplicationOverAddition
259 for MultiPolynomialStructure<RS, RSB>
260{
261}
262
263impl<RS: RingEqSignature, RSB: BorrowedStructure<RS>> RightDistributiveMultiplicationOverAddition
264 for MultiPolynomialStructure<RS, RSB>
265{
266}
267
268impl<RS: RingEqSignature, RSB: BorrowedStructure<RS>> SemiRingSignature
269 for MultiPolynomialStructure<RS, RSB>
270{
271}
272
273impl<RS: RingEqSignature, RSB: BorrowedStructure<RS>> RingSignature
274 for MultiPolynomialStructure<RS, RSB>
275{
276}
277
278impl<RS: CharacteristicSignature + RingEqSignature, RSB: BorrowedStructure<RS>>
279 CharacteristicSignature for MultiPolynomialStructure<RS, RSB>
280{
281 fn characteristic(&self) -> Natural {
282 self.coeff_ring().characteristic()
283 }
284}
285
286impl<RS: IntegralDomainSignature, RSB: BorrowedStructure<RS>> TryReciprocalSignature
287 for MultiPolynomialStructure<RS, RSB>
288{
289 fn try_reciprocal(&self, a: &Self::Set) -> Option<Self::Set> {
290 self.try_divide(&self.one(), a)
291 }
292}
293
294impl<RS: IntegralDomainSignature, RSB: BorrowedStructure<RS>> CancellativeMultiplicationSignature
295 for MultiPolynomialStructure<RS, RSB>
296{
297 fn try_divide(&self, a: &Self::Set, b: &Self::Set) -> Option<Self::Set> {
298 let mut vars = HashSet::new();
299 vars.extend(a.free_vars());
300 vars.extend(b.free_vars());
301 if vars.is_empty() {
302 debug_assert!(a.terms.len() <= 1);
304 debug_assert!(b.terms.len() <= 1);
305 if b.terms.is_empty() {
306 None
307 } else if a.terms.is_empty() {
308 Some(self.zero())
309 } else {
310 debug_assert!(a.terms.len() == 1);
311 debug_assert!(b.terms.len() == 1);
312 debug_assert!(!self.coeff_ring().is_zero(&b.terms[0].coeff));
313 Some(MultiPolynomial::constant(
314 self.coeff_ring()
315 .try_divide(&a.terms[0].coeff, &b.terms[0].coeff)?,
316 ))
317 }
318 } else {
319 let var = vars.iter().next().unwrap();
320 let a_poly = self.expand(a, var);
321 let b_poly = self.expand(b, var);
322 let poly_ring = self.polynomials();
323 Some(poly_ring.evaluate(
324 &poly_ring.try_divide(&a_poly, &b_poly)?,
325 &self.var(var.clone()),
326 ))
327 }
328 }
329}
330
331impl<RS: IntegralDomainSignature, RSB: BorrowedStructure<RS>> MultiplicativeIntegralMonoidSignature
332 for MultiPolynomialStructure<RS, RSB>
333{
334}
335
336impl<RS: IntegralDomainSignature, RSB: BorrowedStructure<RS>> IntegralDomainSignature
337 for MultiPolynomialStructure<RS, RSB>
338{
339}
340
341impl<RS: FavoriteAssociateSignature + IntegralDomainSignature, RSB: BorrowedStructure<RS>>
342 FavoriteAssociateSignature for MultiPolynomialStructure<RS, RSB>
343{
344 fn factor_fav_assoc(&self, mpoly: &Self::Set) -> (Self::Set, Self::Set) {
345 match mpoly.terms.first() {
346 None => {
347 debug_assert!(self.is_zero(mpoly));
348 (self.one(), self.zero())
349 }
350 Some(first_term) => {
351 debug_assert!(!self.is_zero(mpoly));
352 let (unit, _) = self.coeff_ring().factor_fav_assoc(&first_term.coeff);
353 let unit_inv =
354 MultiPolynomial::constant(self.coeff_ring().try_reciprocal(&unit).unwrap());
355 let unit = MultiPolynomial::constant(unit);
356 (unit, self.mul(&unit_inv, mpoly))
357 }
358 }
359 }
360}
361
362impl<RS: CharZeroRingSignature + EqSignature, RSB: BorrowedStructure<RS>> CharZeroRingSignature
363 for MultiPolynomialStructure<RS, RSB>
364{
365 fn try_to_int(&self, x: &Self::Set) -> Option<Integer> {
366 self.coeff_ring().try_to_int(&self.as_constant(x)?)
367 }
368}
369
370impl<
371 RS: EqSignature + IntegralDomainSignature + FiniteUnitsSignature,
372 RSB: BorrowedStructure<RS>,
373 B: BorrowedStructure<MultiPolynomialStructure<RS, RSB>>,
374> CountableSetSignature
375 for MultiplicativeMonoidUnitsStructure<MultiPolynomialStructure<RS, RSB>, B>
376{
377 fn generate_all_elements(&self) -> impl Iterator<Item = Self::Set> + Clone {
378 self.list_all_elements().into_iter()
379 }
380}
381
382impl<
383 RS: EqSignature + IntegralDomainSignature + FiniteUnitsSignature,
384 RSB: BorrowedStructure<RS>,
385 B: BorrowedStructure<MultiPolynomialStructure<RS, RSB>>,
386> FiniteSetSignature for MultiplicativeMonoidUnitsStructure<MultiPolynomialStructure<RS, RSB>, B>
387{
388 fn list_all_elements(&self) -> Vec<Self::Set> {
389 self.monoid()
390 .coeff_ring()
391 .all_units()
392 .into_iter()
393 .map(MultiPolynomial::constant)
394 .collect()
395 }
396}
397
398impl<
399 RS: GreatestCommonDivisorSignature,
400 RSB: BorrowedStructure<RS>,
401 MPB: BorrowedStructure<MultiPolynomialStructure<RS, RSB>>,
402> GreatestCommonDivisorSignature for PolynomialStructure<MultiPolynomialStructure<RS, RSB>, MPB>
403{
404 fn gcd(&self, x: &Self::Set, y: &Self::Set) -> Self::Set {
405 self.gcd_by_primitive_subresultant(x.clone(), y.clone())
406 }
407}
408
409impl<RS: GreatestCommonDivisorSignature, RSB: BorrowedStructure<RS>> GreatestCommonDivisorSignature
410 for MultiPolynomialStructure<RS, RSB>
411where
412 for<'a> PolynomialStructure<MultiPolynomialStructure<RS, RSB>, &'a Self>:
413 SetSignature<Set = Polynomial<MultiPolynomial<RS::Set>>>,
414{
415 fn gcd(&self, x: &Self::Set, y: &Self::Set) -> Self::Set {
416 if let Some(free_var) = x.free_vars().into_iter().chain(y.free_vars()).next() {
417 let poly_over_self = self.polynomials();
418 let x_poly = self.expand(x, &free_var);
419 let y_poly = self.expand(y, &free_var);
420 let g_poly = poly_over_self.gcd(&x_poly, &y_poly);
421 poly_over_self.evaluate(&g_poly, &self.var(free_var))
422 } else {
423 let x = self.as_constant(x).unwrap();
424 let y = self.as_constant(y).unwrap();
425 let g = self.coeff_ring().gcd(&x, &y);
426 MultiPolynomial::constant(g)
427 }
428 }
429}
430
431impl<RS: UniqueFactorizationMonoidSignature + IntegralDomainSignature, RSB: BorrowedStructure<RS>>
432 UniqueFactorizationMonoidSignature for MultiPolynomialStructure<RS, RSB>
433{
434 type FactoredExponent = NaturalCanonicalStructure;
435
436 fn factorization_exponents(&self) -> &Self::FactoredExponent {
437 Natural::structure_ref()
438 }
439
440 fn into_factorization_exponents(self) -> Self::FactoredExponent {
441 Natural::structure()
442 }
443
444 fn try_is_irreducible(&self, _a: &Self::Set) -> Option<bool> {
445 None
446 }
447
448 fn factorization_pow(&self, a: &Self::Set, k: &Natural) -> Self::Set {
449 self.nat_pow(a, k)
450 }
451}
452
453impl<
454 RS: UniqueFactorizationMonoidSignature<FactoredExponent = NaturalCanonicalStructure>
455 + GreatestCommonDivisorSignature
456 + CharZeroRingSignature
457 + FiniteUnitsSignature
458 + 'static,
459 RSB: BorrowedStructure<RS> + 'static,
460 MPB: BorrowedStructure<MultiPolynomialStructure<RS, RSB>>,
461> PolynomialStructure<MultiPolynomialStructure<RS, RSB>, MPB>
462where
463 PolynomialStructure<MultiPolynomialStructure<RS, RSB>, MPB>: SetSignature<Set = Polynomial<MultiPolynomial<RS::Set>>>
464 + UniqueFactorizationMonoidSignature<FactoredExponent = NaturalCanonicalStructure>,
465 PolynomialStructure<RS, RSB>: SetSignature<Set = Polynomial<RS::Set>>
466 + UniqueFactorizationMonoidSignature<FactoredExponent = NaturalCanonicalStructure>,
467 MultiPolynomialStructure<RS, RSB>: SetSignature<Set = MultiPolynomial<RS::Set>>
468 + UniqueFactorizationMonoidSignature<FactoredExponent = NaturalCanonicalStructure>
469 + GreatestCommonDivisorSignature,
470{
471 pub fn factor_by_yuns_and_kroneckers_inductively(
472 &self,
473 factor_poly: impl Fn(&Polynomial<RS::Set>) -> Factored<Polynomial<RS::Set>, Natural>,
474 factor_multipoly_coeff: impl Fn(
475 &MultiPolynomial<RS::Set>,
476 ) -> Factored<MultiPolynomial<RS::Set>, Natural>,
477 mpoly: &<Self as SetSignature>::Set,
478 ) -> Factored<Polynomial<MultiPolynomial<RS::Set>>, Natural> {
479 match |mpoly: &<Self as SetSignature>::Set| -> Option<Polynomial<RS::Set>> {
480 let mut const_coeffs = vec![];
481 for coeff in self.into_coeffs(mpoly.clone()) {
482 const_coeffs.push(self.coeff_ring().as_constant(&coeff)?);
483 }
484 Some(Polynomial::from_coeffs(const_coeffs))
485 }(mpoly)
486 {
487 Some(poly) => {
490 let (unit, factors) = factor_poly(&poly).into_unit_and_powers().unwrap();
491 self.factorizations().new_unit_and_powers_unchecked(
492 unit.apply_map_into(MultiPolynomial::constant),
493 factors
494 .into_iter()
495 .map(|(factor, power)| {
496 (factor.apply_map_into(MultiPolynomial::constant), power)
497 })
498 .collect(),
499 )
500 }
501 None => {
502 self.factorize_by_yuns_and_kroneckers_method(mpoly, |c| factor_multipoly_coeff(c))
503 }
504 }
505 }
506}
507
508impl<
509 RS: UniqueFactorizationMonoidSignature<FactoredExponent = NaturalCanonicalStructure>
510 + GreatestCommonDivisorSignature
511 + CharZeroRingSignature
512 + FiniteUnitsSignature
513 + 'static,
514 RSB: BorrowedStructure<RS> + 'static,
515> MultiPolynomialStructure<RS, RSB>
516where
517 MultiPolynomialStructure<RS, RSB>: SetSignature<Set = MultiPolynomial<RS::Set>>
518 + UniqueFactorizationMonoidSignature<FactoredExponent = NaturalCanonicalStructure>,
519 PolynomialStructure<RS, RSB>: SetSignature<Set = Polynomial<RS::Set>>
520 + UniqueFactorizationMonoidSignature<FactoredExponent = NaturalCanonicalStructure>,
521 for<'a> PolynomialStructure<Self, &'a Self>: SetSignature<Set = Polynomial<MultiPolynomial<RS::Set>>>
522 + UniqueFactorizationMonoidSignature<FactoredExponent = NaturalCanonicalStructure>,
523{
524 pub fn factor_by_yuns_and_kroneckers_inductively(
525 &self,
526 factor_coeff: Rc<dyn Fn(&RS::Set) -> Factored<RS::Set, Natural>>,
527 factor_poly: Rc<dyn Fn(&Polynomial<RS::Set>) -> Factored<Polynomial<RS::Set>, Natural>>,
528 mpoly: &<Self as SetSignature>::Set,
529 ) -> Factored<MultiPolynomial<RS::Set>, Natural> {
530 if self.is_zero(mpoly) {
531 Factored::Zero
532 } else {
533 #[allow(clippy::single_match_else)]
534 match mpoly.free_vars().into_iter().next() {
535 Some(free_var) => {
536 match mpoly.homogeneous_of_degree() {
537 HomogeneousOfDegreeResult::Homogeneous(_) => {
538 let dehom_mpoly = self.partial_evaluate(
540 mpoly,
541 HashMap::from([(free_var.clone(), self.coeff_ring().one())]),
542 );
543 let (unit, factors) = self
544 .factor_by_yuns_and_kroneckers_inductively(
545 factor_coeff.clone(),
546 factor_poly.clone(),
547 &dehom_mpoly,
548 )
549 .into_unit_and_powers()
550 .unwrap();
551
552 self.factorizations().new_unit_and_powers_unchecked(
553 self.homogenize(&unit, &free_var),
554 factors
555 .into_iter()
556 .map(|(factor, power)| {
557 (self.homogenize(&factor, &free_var), power)
558 })
559 .collect(),
560 )
561 }
562 HomogeneousOfDegreeResult::No => {
563 let poly_over_self = self.polynomials();
567 let expanded_poly = self.expand(mpoly, &free_var);
568 let free_var = self.var(free_var);
569 let (unit, factors) = poly_over_self
570 .factor_by_yuns_and_kroneckers_inductively(
571 factor_poly.as_ref(),
572 |c| {
573 self.factor_by_yuns_and_kroneckers_inductively(
574 factor_coeff.clone(),
575 factor_poly.clone(),
576 c,
577 )
578 },
579 &expanded_poly,
580 )
581 .into_unit_and_powers()
582 .unwrap();
583
584 self.factorizations().new_unit_and_powers_unchecked(
585 poly_over_self.evaluate(&unit, &free_var),
586 factors
587 .into_iter()
588 .map(|(factor, power)| {
589 (poly_over_self.evaluate(&factor, &free_var), power)
590 })
591 .collect(),
592 )
593 }
594 HomogeneousOfDegreeResult::Zero => unreachable!(),
595 }
596 }
597 None => {
598 let value = self.as_constant(mpoly).unwrap();
600 if let Some((unit, factors)) = factor_coeff(&value).into_unit_and_powers() {
601 self.factorizations().new_unit_and_powers_unchecked(
602 MultiPolynomial::constant(unit),
603 factors
604 .into_iter()
605 .map(|(factor, power)| (MultiPolynomial::constant(factor), power))
606 .collect(),
607 )
608 } else {
609 Factored::Zero
610 }
611 }
612 }
613 }
614 }
615}
616
617impl<RS: RingEqSignature, RSB: BorrowedStructure<RS>> MultiPolynomialStructure<RS, RSB> {
618 pub fn reduce(&self, p: MultiPolynomial<RS::Set>) -> MultiPolynomial<RS::Set> {
619 MultiPolynomial::new(
620 p.terms
621 .into_iter()
622 .filter(|Term { coeff, .. }| !self.coeff_ring().is_zero(coeff))
623 .collect(),
624 )
625 }
626
627 pub fn var_pow(&self, v: Variable, k: usize) -> MultiPolynomial<RS::Set> {
628 MultiPolynomial {
629 terms: vec![Term {
630 coeff: self.coeff_ring().one(),
631 monomial: Monomial::new(vec![VariablePower { var: v, pow: k }]),
632 }],
633 }
634 }
635
636 pub fn var(&self, v: Variable) -> MultiPolynomial<RS::Set> {
637 self.var_pow(v, 1)
638 }
639
640 pub fn as_constant(&self, p: &MultiPolynomial<RS::Set>) -> Option<RS::Set> {
641 if p.terms.is_empty() {
642 Some(self.coeff_ring().zero())
643 } else if p.terms.len() == 1 {
644 let Term { coeff, monomial } = &p.terms[0];
645 if monomial == &Monomial::one() {
646 Some(coeff.clone())
647 } else {
648 None
649 }
650 } else {
651 None
652 }
653 }
654
655 pub fn degree(&self, p: &MultiPolynomial<RS::Set>) -> Option<usize> {
656 p.terms.iter().map(Term::degree).max()
657 }
658
659 pub fn split_by_degree(
660 &self,
661 p: MultiPolynomial<RS::Set>,
662 ) -> HashMap<usize, MultiPolynomial<RS::Set>> {
663 let mut p_by_deg = HashMap::new();
664 for term in p.terms {
665 let deg = term.degree();
667 p_by_deg.entry(deg).or_insert_with(Vec::new);
668 p_by_deg.get_mut(°).unwrap().push(term);
669 }
670 p_by_deg
671 .into_iter()
672 .map(|(d, t)| (d, MultiPolynomial { terms: t }))
673 .collect()
674 }
675
676 pub fn homogenize(
677 &self,
678 p: &MultiPolynomial<RS::Set>,
679 v: &Variable,
680 ) -> MultiPolynomial<RS::Set> {
681 if self.is_zero(p) {
682 self.zero()
683 } else {
684 let d = self.degree(p).unwrap();
685 let h = MultiPolynomial::new(
686 p.terms
687 .iter()
688 .map(|Term { coeff, monomial }| Term {
689 coeff: coeff.clone(),
690 monomial: monomial.homogenize(d, v),
691 })
692 .collect(),
693 );
694 debug_assert!(h.check_invariants().is_ok());
695 h
696 }
697 }
698
699 pub fn expand(
700 &self,
701 p: &MultiPolynomial<RS::Set>,
702 v: &Variable,
703 ) -> Polynomial<MultiPolynomial<RS::Set>> {
704 let mut coeffs = vec![];
705 for Term { coeff, monomial } in &p.terms {
706 let k = monomial.get_var_pow(v);
707 while coeffs.len() <= k {
708 coeffs.push(self.zero());
709 }
710 self.add_mut(
711 &mut coeffs[k],
712 &MultiPolynomial {
713 terms: vec![Term {
714 coeff: coeff.clone(),
715 monomial: Monomial::new(
716 monomial
717 .clone()
718 .prod
719 .into_iter()
720 .filter(|VariablePower { var, pow: _pow }| var != v)
721 .collect(),
722 ),
723 }],
724 },
725 );
726 }
727 Polynomial::from_coeffs(coeffs)
728 }
729
730 pub fn partial_evaluate(
731 &self,
732 poly: &MultiPolynomial<RS::Set>,
733 values: HashMap<Variable, impl Borrow<RS::Set>>,
734 ) -> MultiPolynomial<RS::Set> {
735 let mut values = values
736 .into_iter()
737 .map(|(v, a)| (v, MultiPolynomial::constant(a.borrow().clone())))
738 .collect::<HashMap<_, _>>();
739
740 let free_vars = poly.free_vars();
741 for free_var in free_vars {
742 if !values.contains_key(&free_var) {
743 values.insert(free_var.clone(), self.var(free_var));
744 }
745 }
746 MultiPolynomialStructure::new(self.clone()).evaluate(
747 &poly.apply_map(|x| MultiPolynomial::constant(x.clone())),
748 values,
749 )
750 }
751
752 pub fn evaluate(
753 &self,
754 poly: &MultiPolynomial<RS::Set>,
755 values: HashMap<Variable, impl Borrow<RS::Set>>,
756 ) -> RS::Set {
757 self.coeff_ring().sum(
758 poly.terms
759 .iter()
760 .map(|Term { coeff, monomial }| {
761 self.coeff_ring()
762 .mul(coeff, &monomial.evaluate(self.coeff_ring(), &values))
763 })
764 .collect(),
765 )
766 }
767}
768
769impl<R: MetaType> MetaType for MultiPolynomial<R>
770where
771 R::Signature: RingEqSignature,
772{
773 type Signature = MultiPolynomialStructure<R::Signature, R::Signature>;
774
775 fn structure() -> Self::Signature {
776 MultiPolynomialStructure::new(R::structure())
777 }
778}
779
780impl<R: MetaType> Display for MultiPolynomial<R>
781where
782 R::Signature: RingEqSignature + ToStringSignature,
783{
784 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
785 write!(f, "{}", Self::structure().to_string(self))
786 }
787}
788
789impl<R: MetaType> PartialEq for MultiPolynomial<R>
790where
791 R::Signature: RingEqSignature,
792{
793 fn eq(&self, other: &Self) -> bool {
794 Self::structure().equal(self, other)
795 }
796}
797
798impl<R: MetaType> Eq for MultiPolynomial<R> where R::Signature: RingEqSignature {}
799
800impl<R: MetaType> MultiPolynomial<R>
801where
802 R::Signature: RingEqSignature,
803{
804 pub fn reduce(self) -> Self {
805 Self::structure().reduce(self)
806 }
807
808 pub fn var(v: Variable) -> Self {
809 Self::structure().var(v)
810 }
811
812 pub fn as_constant(&self) -> Option<R> {
813 Self::structure().as_constant(self)
814 }
815
816 pub fn degree(&self) -> Option<usize> {
817 Self::structure().degree(self)
818 }
819
820 pub fn homogenize(&self, v: &Variable) -> Self {
821 Self::structure().homogenize(self, v)
822 }
823
824 pub fn expand(&self, v: &Variable) -> Polynomial<Self> {
825 Self::structure().expand(self, v)
826 }
827
828 pub fn evaluate(&self, values: HashMap<Variable, impl Borrow<R>>) -> R {
829 Self::structure().evaluate(self, values)
830 }
831}
832
833#[cfg(test)]
834mod tests {
835 use crate::structure::IntoErgonomic;
836
837 use algebraeon_nzq::*;
838
839 use super::*;
840
841 #[test]
842 fn test_monomial_ordering() {
843 let xv = Variable::new(String::from("x"));
844 let yv = Variable::new(String::from("y"));
845
846 let xx = Monomial::new(vec![VariablePower {
847 var: xv.clone(),
848 pow: 2,
849 }]);
850 let yy = Monomial::new(vec![VariablePower {
851 var: yv.clone(),
852 pow: 2,
853 }]);
854 let xy = Monomial::new(vec![
855 VariablePower {
856 var: xv.clone(),
857 pow: 1,
858 },
859 VariablePower {
860 var: yv.clone(),
861 pow: 1,
862 },
863 ]);
864 let x = Monomial::new(vec![VariablePower {
865 var: xv.clone(),
866 pow: 1,
867 }]);
868 let y = Monomial::new(vec![VariablePower {
869 var: yv.clone(),
870 pow: 1,
871 }]);
872 let one = Monomial::one();
873
874 {
875 let terms = vec![
876 xx.clone(),
877 xy.clone(),
878 x.clone(),
879 yy.clone(),
880 y.clone(),
881 one.clone(),
882 ];
883 let mut sorted_terms = terms.clone();
884 sorted_terms.sort_by(Monomial::lexicographic_order);
885
886 assert_eq!(terms, sorted_terms);
887 }
888
889 {
890 let terms = vec![
891 xx.clone(),
892 xy.clone(),
893 yy.clone(),
894 x.clone(),
895 y.clone(),
896 one.clone(),
897 ];
898 let mut sorted_terms = terms.clone();
899 sorted_terms.sort_by(Monomial::graded_lexicographic_order);
900
901 assert_eq!(terms, sorted_terms);
902 }
903 }
904
905 #[test]
906 fn test_reduction() {
907 let x = MultiPolynomial::<Integer>::var(Variable::new(String::from("x")));
908 let f = MultiPolynomial::sum(vec![&x, &MultiPolynomial::neg(&x)]);
909 assert_eq!(f.terms.len(), 0);
910
911 let x = MultiPolynomial::<Integer>::var(Variable::new(String::from("x")));
912 let y = MultiPolynomial::<Integer>::var(Variable::new(String::from("y")));
913 let g = MultiPolynomial::sum(vec![&x, &y]);
914 let h = MultiPolynomial::sum(vec![&x, &MultiPolynomial::neg(&y)]);
915 let f = MultiPolynomial::product(vec![&g, &h]);
916 println!("g = {}", g);
917 println!("h = {}", h);
918 println!("f = {}", f);
919 assert_eq!(f.terms.len(), 2);
920 }
921
922 #[test]
923 fn test_divideision() {
924 let x = MultiPolynomial::<Integer>::var(Variable::new(String::from("x")));
925 let y = MultiPolynomial::<Integer>::var(Variable::new(String::from("y")));
926
927 let f = MultiPolynomial::sum(vec![
928 &MultiPolynomial::product(vec![&x, &x]),
929 &MultiPolynomial::neg(&MultiPolynomial::product(vec![&y, &y])),
930 ]);
931 let g = MultiPolynomial::sum(vec![&x, &MultiPolynomial::neg(&y)]);
932 match MultiPolynomial::try_divide(&f, &g) {
933 Some(h) => {
934 assert_eq!(f, MultiPolynomial::mul(&g, &h));
935 }
936 None => panic!(),
937 }
938
939 let f = MultiPolynomial::sum(vec![
940 &MultiPolynomial::product(vec![&x, &x]),
941 &MultiPolynomial::neg(&MultiPolynomial::product(vec![&y, &y])),
942 ]);
943 let g = MultiPolynomial::zero();
944 assert!(MultiPolynomial::try_divide(&f, &g).is_none());
945
946 let f = MultiPolynomial::sum(vec![
947 &MultiPolynomial::product(vec![&x, &x]),
948 &MultiPolynomial::neg(&MultiPolynomial::product(vec![&y, &y])),
949 ]);
950 let g = MultiPolynomial::sum(vec![&x]);
951 assert!(MultiPolynomial::try_divide(&f, &g).is_none());
952 }
953
954 #[test]
955 fn test_elems() {
956 let x = &MultiPolynomial::<Integer>::var(Variable::new("x")).into_ergonomic();
957 let y = &MultiPolynomial::<Integer>::var(Variable::new("y")).into_ergonomic();
958 let z = &MultiPolynomial::<Integer>::var(Variable::new("z")).into_ergonomic();
959
960 let f = x + y + z;
961 let g = x - y + z;
962
963 let h = (&f * &g) / &f;
964 h.into_verbose().check_invariants().unwrap();
965
966 println!("f = {}", f);
967 println!("g = {}", g);
968 println!("fg = {}", &f * &g);
969 println!("fg/f = {}", (&f * &g) / &f);
970
971 assert_eq!((&f * &g) / &f, g);
972 }
973
974 }