Skip to main content

feanor_math/rings/poly/
mod.rs

1use std::collections::HashMap;
2
3use crate::divisibility::*;
4use crate::homomorphism::*;
5use crate::ring::*;
6use crate::wrapper::RingElementWrapper;
7
8/// Contains [`dense_poly::DensePolyRing`], an implementation of univariate polynomials
9/// based on dense coefficient storage.
10pub mod dense_poly;
11/// Contains [`sparse_poly::SparsePolyRing`], an implementation of univariate polynomials
12/// based on sparse coefficient storage.
13pub mod sparse_poly;
14
15/// Trait for all rings that represent the polynomial ring `R[X]` with
16/// any base ring R.
17///
18/// Currently, the two implementations of this type of ring are [`dense_poly::DensePolyRing`]
19/// and [`sparse_poly::SparsePolyRing`].
20pub trait PolyRing: RingExtension {
21    /// Type of the iterator over all non-zero terms of a polynomial in this ring,
22    /// as returned by [`PolyRing::terms()`].
23    type TermsIterator<'a>: Iterator<Item = (&'a El<Self::BaseRing>, usize)>
24    where
25        Self: 'a;
26
27    /// Returns the indeterminate `X` generating this polynomial ring.
28    fn indeterminate(&self) -> Self::Element;
29
30    /// Returns all the nonzero terms of the given polynomial.
31    ///
32    /// If the base ring is only approximate, it is valid to return "zero" terms,
33    /// whatever that actually means.
34    fn terms<'a>(&'a self, f: &'a Self::Element) -> Self::TermsIterator<'a>;
35
36    /// Adds the given terms to the given polynomial.
37    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    /// Multiplies the given polynomial with `X^rhs_power`.
52    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    /// Returns the coefficient of `f` that corresponds to the monomial `X^i`.
57    fn coefficient_at<'a>(&'a self, f: &'a Self::Element, i: usize) -> &'a El<Self::BaseRing>;
58
59    /// Returns the degree of the polynomial `f`, i.e. the value `d` such that `f` can be written as
60    /// `f(X) = a0 + a1 * X + a2 * X^2 + ... + ad * X^d`. Returns `None` if `f` is zero.
61    fn degree(&self, f: &Self::Element) -> Option<usize>;
62
63    /// Compute the euclidean division by a monic polynomial `rhs`.
64    ///
65    /// Concretely, if `rhs` is a monic polynomial (polynomial with highest coefficient equal to 1),
66    /// then there exist unique `q, r` such that `lhs = rhs * q + r` and `deg(r) < deg(rhs)`.
67    /// These are returned. This function panics if `rhs` is not monic.
68    fn div_rem_monic(&self, lhs: Self::Element, rhs: &Self::Element) -> (Self::Element, Self::Element);
69
70    /// Truncates the monomials of the given polynomial from the given position on, i.e.
71    /// computes the remainder of the polynomial division of `lhs` by `X^truncated_at_inclusive`.
72    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    /// Computes the polynomial whose coefficients are the images of the coefficients of `el`
81    /// under the given homomorphism.
82    ///
83    /// This is used to implement [`PolyRingStore::lifted_hom()`].
84    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    /// Possibly divides all coefficients of the polynomial by a common factor,
95    /// in order to make them "smaller".
96    ///
97    /// In cases where we mainly care about polynomials up to scaling by non-zero
98    /// divisors of the base ring, balancing intermediate polynomials can improve performance.
99    ///
100    /// For more details on balancing, see [`DivisibilityRing::balance_factor()`].
101    #[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    /// Evaluates the given polynomial at the given values.
120    ///
121    /// # Example
122    /// ```rust
123    /// # use feanor_math::ring::*;
124    /// # use feanor_math::rings::poly::*;
125    /// # use feanor_math::primitive_int::*;
126    /// let ring = dense_poly::DensePolyRing::new(StaticRing::<i32>::RING, "X");
127    /// let x = ring.indeterminate();
128    /// let poly = ring.add(ring.clone_el(&x), ring.pow(x, 2));
129    /// assert_eq!(
130    ///     12,
131    ///     ring.evaluate(&poly, &3, &StaticRing::<i32>::RING.identity())
132    /// );
133    /// ```
134    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
146/// [`RingStore`] corresponding to [`PolyRing`].
147pub 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    /// See [`PolyRing::coefficient_at()`].
158    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    /// See [`PolyRing::terms()`].
163    fn terms<'a>(&'a self, f: &'a El<Self>) -> <Self::Type as PolyRing>::TermsIterator<'a> { self.get_ring().terms(f) }
164
165    /// Computes the polynomial from the given terms.
166    ///
167    /// If the iterator gives a term for the same monomial `X^i` multiple times,
168    /// the corresponding coefficients will be summed up.
169    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    /// Computes the polynomial from the given terms.
179    ///
180    /// As opposed to [`PolyRingStore::from_terms()`], the iterator may return errors,
181    /// in which case the computation is aborted and the first error is returned.
182    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    /// Returns a reference to the leading coefficient of the given polynomial, or `None` if the
209    /// polynomial is zero.
210    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    /// Divides each coefficient of this polynomial by its leading coefficient, thus making it
215    /// monic.
216    ///
217    /// Panics if the leading coefficient is not a unit.
218    ///
219    /// # Example
220    /// ```rust
221    /// # use feanor_math::ring::*;
222    /// # use feanor_math::rings::poly::*;
223    /// # use feanor_math::assert_el_eq;
224    /// # use feanor_math::rings::poly::dense_poly::*;
225    /// # use feanor_math::primitive_int::*;
226    /// let P = DensePolyRing::new(StaticRing::<i64>::RING, "X");
227    /// let f = P.from_terms([(6, 0), (3, 1)].into_iter());
228    /// assert_el_eq!(
229    ///     P,
230    ///     P.from_terms([(2, 0), (1, 1)].into_iter()),
231    ///     P.normalize(f)
232    /// );
233    /// ```
234    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    /// See [`PolyRing::evaluate()`].
253    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    /// Lifts the given homomorphism of base rings `S -> R` to the corresponding
262    /// homomorphism of polynomial rings `S[X] -> R[X]`.
263    ///
264    /// As opposed to [`PolyRingStore::lifted_hom()`], this transfers the ownership
265    /// of `self` into the homomorphism object.
266    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    /// Lifts the given homomorphism of base rings `S -> R` to the corresponding
279    /// homomorphism of polynomial rings `S[X] -> R[X]`.
280    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    /// Invokes the function with a wrapped version of the indeterminate of this poly ring.
293    /// Use for convenient creation of polynomials.
294    ///
295    /// Note however that [`PolyRingStore::from_terms()`] might be more performant.
296    ///
297    /// If the number of polynomials to be created is known at compile time, it is more convenient
298    /// to instead use [`PolyRingStore::with_wrapped_indeterminate()`].
299    ///
300    /// # Example
301    /// ```rust
302    /// use feanor_math::assert_el_eq;
303    /// use feanor_math::homomorphism::*;
304    /// use feanor_math::ring::*;
305    /// use feanor_math::rings::poly::dense_poly::*;
306    /// use feanor_math::rings::poly::*;
307    /// use feanor_math::rings::zn::zn_64::*;
308    /// let base_ring = Zn::new(7);
309    /// let poly_ring = DensePolyRing::new(base_ring, "X");
310    /// let f_version1 = poly_ring.from_terms(
311    ///     [
312    ///         (base_ring.int_hom().map(3), 0),
313    ///         (base_ring.int_hom().map(2), 1),
314    ///         (base_ring.one(), 3),
315    ///     ]
316    ///     .into_iter(),
317    /// );
318    /// let f_version2 = poly_ring
319    ///     .with_wrapped_indeterminate_dyn(|x| [3 + 2 * x + x.pow_ref(3)])
320    ///     .into_iter()
321    ///     .next()
322    ///     .unwrap();
323    /// assert_el_eq!(poly_ring, f_version1, f_version2);
324    /// ```
325    #[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    /// Invokes the function with a wrapped version of the indeterminate of this poly ring.
336    /// Use for convenient creation of polynomials.
337    ///
338    /// Note however that [`PolyRingStore::from_terms()`] might be more performant.
339    ///
340    /// If the number of polynomials to be created is not known at compile time, instead
341    /// use [`PolyRingStore::with_wrapped_indeterminate_dyn()`].
342    ///
343    /// # Example
344    /// ```rust
345    /// use feanor_math::assert_el_eq;
346    /// use feanor_math::homomorphism::*;
347    /// use feanor_math::ring::*;
348    /// use feanor_math::rings::poly::dense_poly::*;
349    /// use feanor_math::rings::poly::*;
350    /// use feanor_math::rings::zn::zn_64::*;
351    /// let base_ring = Zn::new(7);
352    /// let poly_ring = DensePolyRing::new(base_ring, "X");
353    /// let f_version1 = poly_ring.from_terms(
354    ///     [
355    ///         (base_ring.int_hom().map(3), 0),
356    ///         (base_ring.int_hom().map(2), 1),
357    ///         (base_ring.one(), 3),
358    ///     ]
359    ///     .into_iter(),
360    /// );
361    /// let [f_version2] = poly_ring.with_wrapped_indeterminate(|x| [3 + 2 * x + x.pow_ref(3)]);
362    /// assert_el_eq!(poly_ring, f_version1, f_version2);
363    /// ```
364    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    /// See [`PolyRing::balance_poly()`].
374    #[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
383/// Homomorphism between two polynomial rings, induced by a homomorphism between their coefficient
384/// rings.
385///
386/// This is the type returned by [`PolyRingStore::lifted_hom()`] and
387/// [`PolyRingStore::into_lifted_hom()`], which should be used to create an instance of this type.
388pub 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
431/// Computes the formal derivative of a polynomial.
432///
433/// The formal derivative is the linear map defined by
434/// ```text
435///   X^k  ->  k * X^(k - 1)
436/// ```
437pub 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                // print nothing
554                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
599/// Constructs the homomorphism `R[X][Y] -> S[X][Y], X -> Y, Y -> X` that swaps the
600/// the two indeterminates of a polynomial ring in two indeterminates.
601///
602/// Coefficients are mapped from `R` to `S` using the given homomorphism.
603pub 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        // test linear independence of X
643        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        // elementwise addition follows trivially from the ring axioms
664
665        // technically, convoluted multiplication follows from the ring axioms too, but test it
666        // anyway
667        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        // test terms(), from_terms()
705        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        // test div_rem_monic()
736        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        // test truncate_monomials()
771        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        // test evaluate()
793        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}