1use std::collections::HashMap;
2
3use crate::divisibility::*;
4use crate::homomorphism::*;
5use crate::ring::*;
6use crate::wrapper::RingElementWrapper;
7
8pub mod dense_poly;
11pub mod sparse_poly;
14
15pub trait PolyRing: RingExtension {
21 type TermsIterator<'a>: Iterator<Item = (&'a El<Self::BaseRing>, usize)>
24 where
25 Self: 'a;
26
27 fn indeterminate(&self) -> Self::Element;
29
30 fn terms<'a>(&'a self, f: &'a Self::Element) -> Self::TermsIterator<'a>;
35
36 fn add_assign_from_terms<I>(&self, lhs: &mut Self::Element, rhs: I)
38 where
39 I: IntoIterator<Item = (El<Self::BaseRing>, usize)>,
40 {
41 let self_ring = RingRef::new(self);
42 self.add_assign(
43 lhs,
44 self_ring.sum(
45 rhs.into_iter()
46 .map(|(c, i)| self.mul(self.from(c), self_ring.pow(self.indeterminate(), i))),
47 ),
48 );
49 }
50
51 fn mul_assign_monomial(&self, lhs: &mut Self::Element, rhs_power: usize) {
53 self.mul_assign(lhs, RingRef::new(self).pow(self.indeterminate(), rhs_power));
54 }
55
56 fn coefficient_at<'a>(&'a self, f: &'a Self::Element, i: usize) -> &'a El<Self::BaseRing>;
58
59 fn degree(&self, f: &Self::Element) -> Option<usize>;
62
63 fn div_rem_monic(&self, lhs: Self::Element, rhs: &Self::Element) -> (Self::Element, Self::Element);
69
70 fn truncate_monomials(&self, lhs: &mut Self::Element, truncated_at_inclusive: usize) {
73 *lhs = RingRef::new(self).from_terms(
74 self.terms(lhs)
75 .filter(|(_, i)| *i < truncated_at_inclusive)
76 .map(|(c, i)| (self.base_ring().clone_el(c), i)),
77 )
78 }
79
80 fn map_terms<P, H>(&self, from: &P, el: &P::Element, hom: H) -> Self::Element
85 where
86 P: ?Sized + PolyRing,
87 H: Homomorphism<<P::BaseRing as RingStore>::Type, <Self::BaseRing as RingStore>::Type>,
88 {
89 assert!(self.base_ring().get_ring() == hom.codomain().get_ring());
90 assert!(from.base_ring().get_ring() == hom.domain().get_ring());
91 RingRef::new(self).from_terms(from.terms(el).map(|(c, i)| (hom.map_ref(c), i)))
92 }
93
94 #[stability::unstable(feature = "enable")]
102 fn balance_poly(&self, f: &mut Self::Element) -> Option<El<Self::BaseRing>>
103 where
104 <Self::BaseRing as RingStore>::Type: DivisibilityRing,
105 {
106 let balance_factor = self
107 .base_ring()
108 .get_ring()
109 .balance_factor(self.terms(f).map(|(c, _)| c));
110 if let Some(balance_factor) = &balance_factor {
111 *f = RingRef::new(self).from_terms(
112 self.terms(f)
113 .map(|(c, i)| (self.base_ring().checked_div(c, balance_factor).unwrap(), i)),
114 );
115 }
116 return balance_factor;
117 }
118
119 fn evaluate<R, H>(&self, f: &Self::Element, value: &R::Element, hom: H) -> R::Element
135 where
136 R: ?Sized + RingBase,
137 H: Homomorphism<<Self::BaseRing as RingStore>::Type, R>,
138 {
139 hom.codomain().sum(self.terms(f).map(|(c, i)| {
140 let result = hom.codomain().pow(hom.codomain().clone_el(value), i);
141 hom.mul_ref_snd_map(result, c)
142 }))
143 }
144}
145
146pub trait PolyRingStore: RingStore
148where
149 Self::Type: PolyRing,
150{
151 delegate! { PolyRing, fn indeterminate(&self) -> El<Self> }
152 delegate! { PolyRing, fn degree(&self, f: &El<Self>) -> Option<usize> }
153 delegate! { PolyRing, fn mul_assign_monomial(&self, lhs: &mut El<Self>, rhs_power: usize) -> () }
154 delegate! { PolyRing, fn div_rem_monic(&self, lhs: El<Self>, rhs: &El<Self>) -> (El<Self>, El<Self>) }
155 delegate! { PolyRing, fn truncate_monomials(&self, lhs: &mut El<Self>, truncated_at_degree: usize) -> () }
156
157 fn coefficient_at<'a>(&'a self, f: &'a El<Self>, i: usize) -> &'a El<<Self::Type as RingExtension>::BaseRing> {
159 self.get_ring().coefficient_at(f, i)
160 }
161
162 fn terms<'a>(&'a self, f: &'a El<Self>) -> <Self::Type as PolyRing>::TermsIterator<'a> { self.get_ring().terms(f) }
164
165 fn from_terms<I>(&self, iter: I) -> El<Self>
170 where
171 I: IntoIterator<Item = (El<<Self::Type as RingExtension>::BaseRing>, usize)>,
172 {
173 let mut result = self.zero();
174 self.get_ring().add_assign_from_terms(&mut result, iter);
175 return result;
176 }
177
178 fn try_from_terms<E, I>(&self, iter: I) -> Result<El<Self>, E>
183 where
184 I: IntoIterator<Item = Result<(El<<Self::Type as RingExtension>::BaseRing>, usize), E>>,
185 {
186 let mut result = self.zero();
187 let mut error = None;
188 self.get_ring().add_assign_from_terms(
189 &mut result,
190 iter.into_iter()
191 .map(|t| match t {
192 Ok(t) => Some(t),
193 Err(e) => {
194 error = Some(e);
195 None
196 }
197 })
198 .take_while(|t| t.is_some())
199 .map(|t| t.unwrap()),
200 );
201 if let Some(e) = error {
202 return Err(e);
203 } else {
204 return Ok(result);
205 }
206 }
207
208 fn lc<'a>(&'a self, f: &'a El<Self>) -> Option<&'a El<<Self::Type as RingExtension>::BaseRing>> {
211 Some(self.coefficient_at(f, self.degree(f)?))
212 }
213
214 fn normalize(&self, mut f: El<Self>) -> El<Self>
235 where
236 <<Self::Type as RingExtension>::BaseRing as RingStore>::Type: DivisibilityRing + Domain,
237 {
238 if self.is_zero(&f) {
239 return f;
240 } else if let Some(inv_lc) = self.base_ring().invert(self.lc(&f).unwrap()) {
241 self.inclusion().mul_assign_ref_map(&mut f, &inv_lc);
242 return f;
243 } else {
244 let lc = self.lc(&f).unwrap();
245 return self.from_terms(
246 self.terms(&f)
247 .map(|(c, i)| (self.base_ring().checked_div(c, lc).unwrap(), i)),
248 );
249 }
250 }
251
252 fn evaluate<R, H>(&self, f: &El<Self>, value: &R::Element, hom: H) -> R::Element
254 where
255 R: ?Sized + RingBase,
256 H: Homomorphism<<<Self::Type as RingExtension>::BaseRing as RingStore>::Type, R>,
257 {
258 self.get_ring().evaluate(f, value, hom)
259 }
260
261 fn into_lifted_hom<P, H>(self, from: P, hom: H) -> CoefficientHom<P, Self, H>
267 where
268 P: RingStore,
269 P::Type: PolyRing,
270 H: Homomorphism<
271 <<P::Type as RingExtension>::BaseRing as RingStore>::Type,
272 <<Self::Type as RingExtension>::BaseRing as RingStore>::Type,
273 >,
274 {
275 CoefficientHom { from, to: self, hom }
276 }
277
278 fn lifted_hom<P, H>(&self, from: P, hom: H) -> CoefficientHom<P, &Self, H>
281 where
282 P: RingStore,
283 P::Type: PolyRing,
284 H: Homomorphism<
285 <<P::Type as RingExtension>::BaseRing as RingStore>::Type,
286 <<Self::Type as RingExtension>::BaseRing as RingStore>::Type,
287 >,
288 {
289 self.into_lifted_hom(from, hom)
290 }
291
292 #[stability::unstable(feature = "enable")]
326 fn with_wrapped_indeterminate_dyn<'a, F, T>(&'a self, f: F) -> Vec<El<Self>>
327 where
328 F: FnOnce(&RingElementWrapper<&'a Self>) -> T,
329 T: IntoIterator<Item = RingElementWrapper<&'a Self>>,
330 {
331 let wrapped_indet = RingElementWrapper::new(self, self.indeterminate());
332 f(&wrapped_indet).into_iter().map(|f| f.unwrap()).collect()
333 }
334
335 fn with_wrapped_indeterminate<'a, F, const M: usize>(&'a self, f: F) -> [El<Self>; M]
365 where
366 F: FnOnce(&RingElementWrapper<&'a Self>) -> [RingElementWrapper<&'a Self>; M],
367 {
368 let wrapped_indet = RingElementWrapper::new(self, self.indeterminate());
369 let mut result_it = f(&wrapped_indet).into_iter();
370 return std::array::from_fn(|_| result_it.next().unwrap().unwrap());
371 }
372
373 #[stability::unstable(feature = "enable")]
375 fn balance_poly(&self, f: &mut El<Self>) -> Option<El<<Self::Type as RingExtension>::BaseRing>>
376 where
377 <<Self::Type as RingExtension>::BaseRing as RingStore>::Type: DivisibilityRing,
378 {
379 self.get_ring().balance_poly(f)
380 }
381}
382
383pub struct CoefficientHom<PFrom, PTo, H>
389where
390 PFrom: RingStore,
391 PTo: RingStore,
392 PFrom::Type: PolyRing,
393 PTo::Type: PolyRing,
394 H: Homomorphism<
395 <<PFrom::Type as RingExtension>::BaseRing as RingStore>::Type,
396 <<PTo::Type as RingExtension>::BaseRing as RingStore>::Type,
397 >,
398{
399 from: PFrom,
400 to: PTo,
401 hom: H,
402}
403
404impl<PFrom, PTo, H> Homomorphism<PFrom::Type, PTo::Type> for CoefficientHom<PFrom, PTo, H>
405where
406 PFrom: RingStore,
407 PTo: RingStore,
408 PFrom::Type: PolyRing,
409 PTo::Type: PolyRing,
410 H: Homomorphism<
411 <<PFrom::Type as RingExtension>::BaseRing as RingStore>::Type,
412 <<PTo::Type as RingExtension>::BaseRing as RingStore>::Type,
413 >,
414{
415 type DomainStore = PFrom;
416 type CodomainStore = PTo;
417
418 fn codomain(&self) -> &Self::CodomainStore { &self.to }
419
420 fn domain(&self) -> &Self::DomainStore { &self.from }
421
422 fn map(&self, x: <PFrom::Type as RingBase>::Element) -> <PTo::Type as RingBase>::Element { self.map_ref(&x) }
423
424 fn map_ref(&self, x: &<PFrom::Type as RingBase>::Element) -> <PTo::Type as RingBase>::Element {
425 self.to.get_ring().map_terms(self.from.get_ring(), x, &self.hom)
426 }
427}
428
429impl<R: RingStore> PolyRingStore for R where R::Type: PolyRing {}
430
431pub fn derive_poly<P>(poly_ring: P, poly: &El<P>) -> El<P>
438where
439 P: PolyRingStore,
440 P::Type: PolyRing,
441{
442 poly_ring.from_terms(poly_ring.terms(poly).filter(|(_, i)| *i > 0).map(|(c, i)| {
443 (
444 poly_ring
445 .base_ring()
446 .int_hom()
447 .mul_ref_fst_map(c, i.try_into().unwrap()),
448 i - 1,
449 )
450 }))
451}
452
453pub mod generic_impls {
454 use super::PolyRing;
455 use crate::homomorphism::*;
456 use crate::ring::*;
457
458 #[allow(type_alias_bounds)]
459 #[stability::unstable(feature = "enable")]
460 pub type Homomorphism<P1: PolyRing, P2: PolyRing> =
461 <<P2::BaseRing as RingStore>::Type as CanHomFrom<<P1::BaseRing as RingStore>::Type>>::Homomorphism;
462
463 #[stability::unstable(feature = "enable")]
464 pub fn has_canonical_hom<P1: PolyRing, P2: PolyRing>(from: &P1, to: &P2) -> Option<Homomorphism<P1, P2>>
465 where
466 <P2::BaseRing as RingStore>::Type: CanHomFrom<<P1::BaseRing as RingStore>::Type>,
467 {
468 to.base_ring().get_ring().has_canonical_hom(from.base_ring().get_ring())
469 }
470
471 #[stability::unstable(feature = "enable")]
472 pub fn map_in<P1: PolyRing, P2: PolyRing>(
473 from: &P1,
474 to: &P2,
475 el: P1::Element,
476 hom: &Homomorphism<P1, P2>,
477 ) -> P2::Element
478 where
479 <P2::BaseRing as RingStore>::Type: CanHomFrom<<P1::BaseRing as RingStore>::Type>,
480 {
481 let mut result = to.zero();
482 to.add_assign_from_terms(
483 &mut result,
484 from.terms(&el).map(|(c, i)| {
485 (
486 to.base_ring()
487 .get_ring()
488 .map_in(from.base_ring().get_ring(), from.base_ring().clone_el(c), hom),
489 i,
490 )
491 }),
492 );
493 return result;
494 }
495
496 #[allow(type_alias_bounds)]
497 #[stability::unstable(feature = "enable")]
498 pub type Isomorphism<P1: PolyRing, P2: PolyRing> =
499 <<P2::BaseRing as RingStore>::Type as CanIsoFromTo<<P1::BaseRing as RingStore>::Type>>::Isomorphism;
500
501 #[stability::unstable(feature = "enable")]
502 pub fn map_out<P1: PolyRing, P2: PolyRing>(
503 from: &P1,
504 to: &P2,
505 el: P2::Element,
506 iso: &Isomorphism<P1, P2>,
507 ) -> P1::Element
508 where
509 <P2::BaseRing as RingStore>::Type: CanIsoFromTo<<P1::BaseRing as RingStore>::Type>,
510 {
511 let mut result = from.zero();
512 from.add_assign_from_terms(
513 &mut result,
514 to.terms(&el).map(|(c, i)| {
515 (
516 to.base_ring()
517 .get_ring()
518 .map_out(from.base_ring().get_ring(), to.base_ring().clone_el(c), iso),
519 i,
520 )
521 }),
522 );
523 return result;
524 }
525
526 #[stability::unstable(feature = "enable")]
527 pub fn dbg_poly<P: PolyRing>(
528 ring: &P,
529 el: &P::Element,
530 out: &mut std::fmt::Formatter,
531 unknown_name: &str,
532 env: EnvBindingStrength,
533 ) -> std::fmt::Result {
534 let mut terms = ring.terms(el).fuse();
535 let first_term = terms.next();
536 if first_term.is_none() {
537 return ring
538 .base_ring()
539 .get_ring()
540 .dbg_within(&ring.base_ring().zero(), out, env);
541 }
542 let second_term = terms.next();
543 let use_parenthesis = (env > EnvBindingStrength::Sum && second_term.is_some())
544 || (env > EnvBindingStrength::Product
545 && !(ring.base_ring().is_one(first_term.as_ref().unwrap().0) && first_term.as_ref().unwrap().1 == 1))
546 || env == EnvBindingStrength::Strongest;
547 let mut terms = first_term.into_iter().chain(second_term).chain(terms);
548 if use_parenthesis {
549 write!(out, "(")?;
550 }
551 let print_unknown = |i: usize, out: &mut std::fmt::Formatter| {
552 if i == 0 {
553 Ok(())
555 } else if i == 1 {
556 write!(out, "{}", unknown_name)
557 } else {
558 write!(out, "{}^{}", unknown_name, i)
559 }
560 };
561 if let Some((c, i)) = terms.next() {
562 if ring.base_ring().get_ring().is_approximate() || !ring.base_ring().is_one(c) || i == 0 {
563 ring.base_ring().get_ring().dbg_within(
564 c,
565 out,
566 if i == 0 {
567 EnvBindingStrength::Sum
568 } else {
569 EnvBindingStrength::Product
570 },
571 )?;
572 }
573 print_unknown(i, out)?;
574 } else {
575 write!(out, "0")?;
576 }
577 for (c, i) in terms {
578 write!(out, " + ")?;
579 if ring.base_ring().get_ring().is_approximate() || !ring.base_ring().is_one(c) || i == 0 {
580 ring.base_ring().get_ring().dbg_within(
581 c,
582 out,
583 if i == 0 {
584 EnvBindingStrength::Sum
585 } else {
586 EnvBindingStrength::Product
587 },
588 )?;
589 }
590 print_unknown(i, out)?;
591 }
592 if use_parenthesis {
593 write!(out, ")")?;
594 }
595 return Ok(());
596 }
597}
598
599pub fn transpose_indeterminates<P1, P2, H>(from: P1, to: P2, base_hom: H) -> impl Homomorphism<P1::Type, P2::Type>
604 where P1: RingStore, P1::Type: PolyRing,
605 P2: RingStore, P2::Type: PolyRing,
606 <<P1::Type as RingExtension>::BaseRing as RingStore>::Type: PolyRing,
607 <<P2::Type as RingExtension>::BaseRing as RingStore>::Type: PolyRing,
608 H: Homomorphism<<<<<P1::Type as RingExtension>::BaseRing as RingStore>::Type as RingExtension>::BaseRing as RingStore>::Type,
609 <<<<P2::Type as RingExtension>::BaseRing as RingStore>::Type as RingExtension>::BaseRing as RingStore>::Type>
610{
611 LambdaHom::new(from, to, move |from, to, x| {
612 let mut result_terms: HashMap<usize, Vec<(_, usize)>> = HashMap::new();
613 for (f, i) in from.terms(x) {
614 for (c, j) in from.base_ring().terms(f) {
615 match result_terms.entry(j) {
616 std::collections::hash_map::Entry::Occupied(mut e) => {
617 e.get_mut().push((base_hom.map_ref(c), i));
618 }
619 std::collections::hash_map::Entry::Vacant(e) => {
620 _ = e.insert(vec![(base_hom.map_ref(c), i)]);
621 }
622 }
623 }
624 }
625 return to.from_terms(result_terms.into_iter().map(|(j, f)| (to.base_ring().from_terms(f), j)));
626 })
627}
628
629#[cfg(any(test, feature = "generic_tests"))]
630pub mod generic_tests {
631 use super::*;
632
633 pub fn test_poly_ring_axioms<R: PolyRingStore, I: Iterator<Item = El<<R::Type as RingExtension>::BaseRing>>>(
634 ring: R,
635 interesting_base_ring_elements: I,
636 ) where
637 R::Type: PolyRing,
638 {
639 let x = ring.indeterminate();
640 let elements = interesting_base_ring_elements.collect::<Vec<_>>();
641
642 for a in &elements {
644 for b in &elements {
645 for c in &elements {
646 for d in &elements {
647 let a_bx = ring.add(
648 ring.inclusion().map_ref(a),
649 ring.mul_ref_snd(ring.inclusion().map_ref(b), &x),
650 );
651 let c_dx = ring.add(
652 ring.inclusion().map_ref(c),
653 ring.mul_ref_snd(ring.inclusion().map_ref(d), &x),
654 );
655 assert!(
656 ring.eq_el(&a_bx, &c_dx) == (ring.base_ring().eq_el(a, c) && ring.base_ring().eq_el(b, d))
657 );
658 }
659 }
660 }
661 }
662
663 for a in &elements {
668 for b in &elements {
669 for c in &elements {
670 for d in &elements {
671 let a_bx = ring.add(
672 ring.inclusion().map_ref(a),
673 ring.mul_ref_snd(ring.inclusion().map_ref(b), &x),
674 );
675 let c_dx = ring.add(
676 ring.inclusion().map_ref(c),
677 ring.mul_ref_snd(ring.inclusion().map_ref(d), &x),
678 );
679 let result = <_ as RingStore>::sum(
680 &ring,
681 [
682 ring.mul(ring.inclusion().map_ref(a), ring.inclusion().map_ref(c)),
683 ring.mul(
684 ring.inclusion().map_ref(a),
685 ring.mul_ref_snd(ring.inclusion().map_ref(d), &x),
686 ),
687 ring.mul(
688 ring.inclusion().map_ref(b),
689 ring.mul_ref_snd(ring.inclusion().map_ref(c), &x),
690 ),
691 ring.mul(
692 ring.inclusion().map_ref(b),
693 ring.mul(ring.inclusion().map_ref(d), ring.pow(ring.clone_el(&x), 2)),
694 ),
695 ]
696 .into_iter(),
697 );
698 assert_el_eq!(ring, result, ring.mul(a_bx, c_dx));
699 }
700 }
701 }
702 }
703
704 for a in &elements {
706 for b in &elements {
707 for c in &elements {
708 let f = <_ as RingStore>::sum(
709 &ring,
710 [
711 ring.inclusion().map_ref(a),
712 ring.mul_ref_snd(ring.inclusion().map_ref(b), &x),
713 ring.mul(ring.inclusion().map_ref(c), ring.pow(ring.clone_el(&x), 3)),
714 ]
715 .into_iter(),
716 );
717 let actual = ring.from_terms(
718 [
719 (ring.base_ring().clone_el(a), 0),
720 (ring.base_ring().clone_el(c), 3),
721 (ring.base_ring().clone_el(b), 1),
722 ]
723 .into_iter(),
724 );
725 assert_el_eq!(ring, f, actual);
726 assert_el_eq!(
727 ring,
728 f,
729 ring.from_terms(ring.terms(&f).map(|(c, i)| (ring.base_ring().clone_el(c), i)))
730 );
731 }
732 }
733 }
734
735 for a in &elements {
737 for b in &elements {
738 for c in &elements {
739 let f = ring
740 .from_terms([(ring.base_ring().clone_el(a), 0), (ring.base_ring().clone_el(b), 3)].into_iter());
741 let g = ring.from_terms(
742 [
743 (ring.base_ring().negate(ring.base_ring().clone_el(c)), 0),
744 (ring.base_ring().one(), 1),
745 ]
746 .into_iter(),
747 );
748
749 let (quo, rem) = ring.div_rem_monic(ring.clone_el(&f), &g);
750 assert_el_eq!(
751 &ring,
752 &ring.from_terms(
753 [(
754 ring.base_ring().add_ref_fst(
755 a,
756 ring.base_ring()
757 .mul_ref_fst(b, ring.base_ring().pow(ring.base_ring().clone_el(c), 3))
758 ),
759 0
760 )]
761 .into_iter()
762 ),
763 &rem
764 );
765 assert_el_eq!(&ring, &f, &ring.add(rem, ring.mul(quo, g)));
766 }
767 }
768 }
769
770 for a in &elements {
772 for b in &elements {
773 for i in 2..5 {
774 let a = ring.from_terms([
775 (ring.base_ring().clone_el(a), 0),
776 (ring.base_ring().one(), 3),
777 (ring.base_ring().clone_el(b), 4),
778 (ring.base_ring().one(), 5),
779 ]);
780 let mut a_trunc = ring.clone_el(&a);
781 ring.truncate_monomials(&mut a_trunc, i);
782 assert_el_eq!(
783 &ring,
784 ring.div_rem_monic(ring.clone_el(&a), &ring.from_terms([(ring.base_ring().one(), i)]))
785 .1,
786 a_trunc
787 );
788 }
789 }
790 }
791
792 let hom = ring.base_ring().int_hom();
794 let base_ring = hom.codomain();
795 let f = ring.from_terms([(hom.map(1), 0), (hom.map(3), 1), (hom.map(7), 3)].into_iter());
796 for a in &elements {
797 assert_el_eq!(
798 &base_ring,
799 &base_ring.add(
800 base_ring.one(),
801 base_ring.add(
802 base_ring.mul_ref_snd(hom.map(3), a),
803 base_ring.mul(hom.map(7), base_ring.pow(base_ring.clone_el(a), 3))
804 )
805 ),
806 &ring.evaluate(&f, a, &base_ring.identity())
807 )
808 }
809 }
810}
811
812#[cfg(test)]
813use std::fmt::{Display, Formatter};
814
815#[cfg(test)]
816use dense_poly::DensePolyRing;
817
818#[cfg(test)]
819use crate::primitive_int::StaticRing;
820
821#[test]
822fn test_dbg_poly() {
823 let ring = DensePolyRing::new(StaticRing::<i64>::RING, "X");
824 let [f1, f2, f3, f4] = ring.with_wrapped_indeterminate(|X| [X.clone(), X + 1, 2 * X + 1, 2 * X]);
825
826 fn to_str(
827 ring: &DensePolyRing<StaticRing<i64>>,
828 f: &El<DensePolyRing<StaticRing<i64>>>,
829 env: EnvBindingStrength,
830 ) -> String {
831 struct DisplayEl<'a> {
832 ring: &'a DensePolyRing<StaticRing<i64>>,
833 f: &'a El<DensePolyRing<StaticRing<i64>>>,
834 env: EnvBindingStrength,
835 }
836 impl<'a> Display for DisplayEl<'a> {
837 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
838 generic_impls::dbg_poly(self.ring.get_ring(), self.f, f, "X", self.env)
839 }
840 }
841 return format!("{}", DisplayEl { ring, f, env });
842 }
843
844 assert_eq!("X", to_str(&ring, &f1, EnvBindingStrength::Sum));
845 assert_eq!("X", to_str(&ring, &f1, EnvBindingStrength::Product));
846 assert_eq!("X", to_str(&ring, &f1, EnvBindingStrength::Power));
847 assert_eq!("X + 1", to_str(&ring, &f2, EnvBindingStrength::Sum));
848 assert_eq!("(X + 1)", to_str(&ring, &f2, EnvBindingStrength::Product));
849 assert_eq!("(X + 1)", to_str(&ring, &f2, EnvBindingStrength::Power));
850 assert_eq!("2X + 1", to_str(&ring, &f3, EnvBindingStrength::Sum));
851 assert_eq!("(2X + 1)", to_str(&ring, &f3, EnvBindingStrength::Product));
852 assert_eq!("(2X + 1)", to_str(&ring, &f3, EnvBindingStrength::Power));
853 assert_eq!("2X", to_str(&ring, &f4, EnvBindingStrength::Sum));
854 assert_eq!("2X", to_str(&ring, &f4, EnvBindingStrength::Product));
855 assert_eq!("(2X)", to_str(&ring, &f4, EnvBindingStrength::Power));
856}