Skip to main content

feanor_math/rings/poly/
sparse_poly.rs

1use std::alloc::Allocator;
2use std::cmp::max;
3use std::fmt::{Debug, Formatter, Result};
4use std::sync::Arc;
5
6use crate::algorithms;
7use crate::algorithms::convolution::ConvolutionAlgorithm;
8use crate::algorithms::poly_gcd::PolyTFracGCDRing;
9use crate::divisibility::*;
10use crate::field::Field;
11use crate::integer::{IntegerRing, IntegerRingStore};
12use crate::pid::*;
13use crate::ring::*;
14use crate::rings::poly::*;
15use crate::seq::sparse::*;
16use crate::seq::{VectorViewMut, *};
17
18/// The univariate polynomial ring `R[X]`. Polynomials are stored as sparse vectors of
19/// coefficients, thus giving improved performance in the case that most coefficients are
20/// zero.
21///
22/// Unless polynomials are very sparse, [`crate::rings::poly::dense_poly::DensePolyRing`] will
23/// provide better performance.
24///
25/// # Example
26/// ```rust
27/// # use feanor_math::ring::*;
28/// # use feanor_math::homomorphism::*;
29/// # use feanor_math::rings::poly::*;
30/// # use feanor_math::rings::poly::sparse_poly::*;
31/// # use feanor_math::primitive_int::*;
32///
33/// let ZZ = StaticRing::<i32>::RING;
34/// let P = SparsePolyRing::new(ZZ, "X");
35/// let x10_plus_1 = P.add(P.pow(P.indeterminate(), 10), P.int_hom().map(1));
36/// let power = P.pow(x10_plus_1, 10);
37/// assert_eq!(0, *P.coefficient_at(&power, 1));
38/// ```
39/// This ring has a [`CanIsoFromTo`] to [`dense_poly::DensePolyRingBase`].
40/// ```rust
41/// # use feanor_math::assert_el_eq;
42/// # use feanor_math::homomorphism::*;
43/// # use feanor_math::ring::*;
44/// # use feanor_math::rings::poly::*;
45/// # use feanor_math::rings::poly::dense_poly::*;
46/// # use feanor_math::rings::poly::sparse_poly::*;
47/// # use feanor_math::primitive_int::*;
48///
49/// let ZZ = StaticRing::<i32>::RING;
50/// let P = SparsePolyRing::new(ZZ, "X");
51/// let P2 = DensePolyRing::new(ZZ, "X");
52/// let high_power_of_x = P.pow(P.indeterminate(), 10);
53/// assert_el_eq!(
54///     P2,
55///     P2.pow(P2.indeterminate(), 10),
56///     &P.can_iso(&P2).unwrap().map(high_power_of_x)
57/// );
58/// ```
59pub struct SparsePolyRingBase<R: RingStore> {
60    base_ring: Arc<R>,
61    unknown_name: &'static str,
62    zero: El<R>,
63}
64
65impl<R: RingStore + Clone> Clone for SparsePolyRingBase<R> {
66    fn clone(&self) -> Self {
67        SparsePolyRingBase {
68            base_ring: self.base_ring.clone(),
69            unknown_name: self.unknown_name,
70            zero: self.base_ring.zero(),
71        }
72    }
73}
74
75impl<R: RingStore> Debug for SparsePolyRingBase<R>
76where
77    R::Type: Debug,
78{
79    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
80        f.debug_struct("SparsePolyRingBase")
81            .field("base_ring", &self.base_ring.get_ring())
82            .finish()
83    }
84}
85
86/// The univariate polynomial ring `R[X]`, with polynomials being stored as sparse vectors of
87/// coefficients. For details, see [`SparsePolyRingBase`].
88#[allow(type_alias_bounds)]
89pub type SparsePolyRing<R: RingStore> = RingValue<SparsePolyRingBase<R>>;
90
91impl<R: RingStore> SparsePolyRing<R> {
92    /// Creates a new sparse polynomial ring over the given base ring.
93    pub fn new(base_ring: R, unknown_name: &'static str) -> Self {
94        let zero = base_ring.zero();
95        Self::from(SparsePolyRingBase {
96            base_ring: Arc::new(base_ring),
97            unknown_name,
98            zero,
99        })
100    }
101}
102
103impl<R: RingStore> SparsePolyRingBase<R> {
104    fn degree_truncate(&self, el: &mut SparseMapVector<Arc<R>>) {
105        for i in (0..el.len()).rev() {
106            if !self.base_ring.is_zero(el.at(i)) {
107                el.set_len(i + 1);
108                return;
109            }
110        }
111        el.set_len(0);
112    }
113
114    fn poly_div<F>(
115        &self,
116        lhs: &mut <Self as RingBase>::Element,
117        rhs: &<Self as RingBase>::Element,
118        mut left_div_lc: F,
119    ) -> Option<<Self as RingBase>::Element>
120    where
121        F: FnMut(El<R>) -> Option<El<R>>,
122    {
123        let lhs_val = std::mem::replace(lhs, self.zero());
124        let (quo, rem) = algorithms::poly_div::poly_div_rem(RingRef::new(self), lhs_val, rhs, |x| {
125            left_div_lc(self.base_ring().clone_el(x)).ok_or(())
126        })
127        .ok()?;
128        *lhs = rem;
129        return Some(quo);
130    }
131}
132
133/// An element of a [`SparsePolyRing`].
134pub struct SparsePolyRingEl<R: RingStore> {
135    data: SparseMapVector<Arc<R>>,
136}
137
138impl<R: RingStore> Debug for SparsePolyRingEl<R>
139where
140    El<R>: Debug,
141{
142    fn fmt(&self, f: &mut Formatter<'_>) -> Result { self.data.fmt(f) }
143}
144
145impl<R: RingStore> RingBase for SparsePolyRingBase<R> {
146    type Element = SparsePolyRingEl<R>;
147
148    fn clone_el(&self, val: &Self::Element) -> Self::Element { SparsePolyRingEl { data: val.data.clone() } }
149
150    fn add_assign_ref(&self, lhs: &mut Self::Element, rhs: &Self::Element) {
151        lhs.data.set_len(max(lhs.data.len(), rhs.data.len()));
152        for (i, c) in rhs.data.nontrivial_entries() {
153            self.base_ring.add_assign_ref(lhs.data.at_mut(i), c);
154        }
155        self.degree_truncate(&mut lhs.data);
156    }
157
158    fn add_assign(&self, lhs: &mut Self::Element, rhs: Self::Element) { self.add_assign_ref(lhs, &rhs); }
159
160    fn sub_assign_ref(&self, lhs: &mut Self::Element, rhs: &Self::Element) {
161        lhs.data.set_len(max(lhs.data.len(), rhs.data.len()));
162        for (i, c) in rhs.data.nontrivial_entries() {
163            self.base_ring.sub_assign_ref(lhs.data.at_mut(i), c);
164        }
165        self.degree_truncate(&mut lhs.data);
166    }
167
168    fn negate_inplace(&self, lhs: &mut Self::Element) { lhs.data.scan(|_, c| self.base_ring.negate_inplace(c)); }
169
170    fn mul_assign(&self, lhs: &mut Self::Element, rhs: Self::Element) { self.mul_assign_ref(lhs, &rhs); }
171
172    fn mul_assign_ref(&self, lhs: &mut Self::Element, rhs: &Self::Element) { *lhs = self.mul_ref(lhs, rhs); }
173
174    fn zero(&self) -> Self::Element {
175        SparsePolyRingEl {
176            data: SparseMapVector::new(0, self.base_ring.clone()),
177        }
178    }
179
180    fn from_int(&self, value: i32) -> Self::Element { self.from(self.base_ring().get_ring().from_int(value)) }
181
182    fn eq_el(&self, lhs: &Self::Element, rhs: &Self::Element) -> bool {
183        if lhs.data.len() != rhs.data.len() {
184            return false;
185        }
186        for (i, c) in lhs.data.nontrivial_entries() {
187            if !self.base_ring.eq_el(rhs.data.at(i), c) {
188                return false;
189            }
190        }
191        for (i, c) in rhs.data.nontrivial_entries() {
192            if !self.base_ring.eq_el(lhs.data.at(i), c) {
193                return false;
194            }
195        }
196        return true;
197    }
198
199    fn is_commutative(&self) -> bool { self.base_ring.is_commutative() }
200
201    fn is_noetherian(&self) -> bool {
202        // by Hilbert's basis theorem
203        self.base_ring.is_noetherian()
204    }
205
206    fn dbg_within<'a>(&self, value: &Self::Element, out: &mut Formatter<'a>, env: EnvBindingStrength) -> Result {
207        super::generic_impls::dbg_poly(self, value, out, self.unknown_name, env)
208    }
209
210    fn dbg<'a>(&self, value: &Self::Element, out: &mut Formatter<'a>) -> Result {
211        self.dbg_within(value, out, EnvBindingStrength::Weakest)
212    }
213
214    fn square(&self, value: &mut Self::Element) { *value = self.mul_ref(value, value); }
215
216    fn mul_ref(&self, lhs: &Self::Element, rhs: &Self::Element) -> Self::Element {
217        if lhs.data.len() == 0 || rhs.data.len() == 0 {
218            return self.zero();
219        }
220        let mut result = SparseMapVector::new(lhs.data.len() + rhs.data.len() - 1, self.base_ring.clone());
221        for (i, c1) in lhs.data.nontrivial_entries() {
222            for (j, c2) in rhs.data.nontrivial_entries() {
223                self.base_ring
224                    .add_assign(result.at_mut(i + j), self.base_ring.mul_ref(c1, c2));
225            }
226        }
227        // if the ring is not zero-divisor free
228        self.degree_truncate(&mut result);
229        return SparsePolyRingEl { data: result };
230    }
231
232    fn mul_assign_int(&self, lhs: &mut Self::Element, rhs: i32) {
233        if rhs == 0 {
234            *lhs = self.zero();
235        } else {
236            lhs.data.scan(|_, c| self.base_ring.int_hom().mul_assign_map(c, rhs));
237        }
238    }
239
240    fn characteristic<I: IntegerRingStore + Copy>(&self, ZZ: I) -> Option<El<I>>
241    where
242        I::Type: IntegerRing,
243    {
244        self.base_ring().characteristic(ZZ)
245    }
246
247    fn is_approximate(&self) -> bool { self.base_ring().get_ring().is_approximate() }
248}
249
250impl<R> PartialEq for SparsePolyRingBase<R>
251where
252    R: RingStore,
253{
254    fn eq(&self, other: &Self) -> bool { self.base_ring.get_ring() == other.base_ring.get_ring() }
255}
256
257impl<R: RingStore> RingExtension for SparsePolyRingBase<R> {
258    type BaseRing = R;
259
260    fn base_ring(&self) -> &Self::BaseRing { &self.base_ring }
261
262    fn from(&self, x: El<Self::BaseRing>) -> Self::Element {
263        let mut result = self.zero();
264        if !self.base_ring().is_zero(&x) {
265            result.data.set_len(1);
266            *result.data.at_mut(0) = x;
267        }
268        return result;
269    }
270}
271
272pub trait ImplGenericCanIsoFromToMarker: PolyRing {}
273
274impl<R, A, C> ImplGenericCanIsoFromToMarker for dense_poly::DensePolyRingBase<R, A, C>
275where
276    R: RingStore,
277    A: Allocator + Clone,
278    C: ConvolutionAlgorithm<R::Type>,
279{
280}
281
282impl<R, P> CanHomFrom<P> for SparsePolyRingBase<R>
283where
284    R: RingStore,
285    R::Type: CanHomFrom<<P::BaseRing as RingStore>::Type>,
286    P: ImplGenericCanIsoFromToMarker,
287{
288    type Homomorphism = super::generic_impls::Homomorphism<P, Self>;
289
290    fn has_canonical_hom(&self, from: &P) -> Option<Self::Homomorphism> {
291        super::generic_impls::has_canonical_hom(from, self)
292    }
293
294    fn map_in(&self, from: &P, el: P::Element, hom: &Self::Homomorphism) -> Self::Element {
295        super::generic_impls::map_in(from, self, el, hom)
296    }
297}
298
299impl<R1, R2> CanHomFrom<SparsePolyRingBase<R1>> for SparsePolyRingBase<R2>
300where
301    R1: RingStore,
302    R2: RingStore,
303    R2::Type: CanHomFrom<R1::Type>,
304{
305    type Homomorphism = <R2::Type as CanHomFrom<R1::Type>>::Homomorphism;
306
307    fn has_canonical_hom(&self, from: &SparsePolyRingBase<R1>) -> Option<Self::Homomorphism> {
308        self.base_ring()
309            .get_ring()
310            .has_canonical_hom(from.base_ring().get_ring())
311    }
312
313    fn map_in_ref(
314        &self,
315        from: &SparsePolyRingBase<R1>,
316        el: &SparsePolyRingEl<R1>,
317        hom: &Self::Homomorphism,
318    ) -> Self::Element {
319        let mut result = SparseMapVector::new(el.data.len(), self.base_ring.clone());
320        for (j, c) in el.data.nontrivial_entries() {
321            *result.at_mut(j) = self
322                .base_ring()
323                .get_ring()
324                .map_in_ref(from.base_ring().get_ring(), c, hom);
325        }
326        return SparsePolyRingEl { data: result };
327    }
328
329    fn map_in(
330        &self,
331        from: &SparsePolyRingBase<R1>,
332        el: <SparsePolyRingBase<R1> as RingBase>::Element,
333        hom: &Self::Homomorphism,
334    ) -> Self::Element {
335        self.map_in_ref(from, &el, hom)
336    }
337}
338
339impl<R, P> CanIsoFromTo<P> for SparsePolyRingBase<R>
340where
341    R: RingStore,
342    R::Type: CanIsoFromTo<<P::BaseRing as RingStore>::Type>,
343    P: ImplGenericCanIsoFromToMarker,
344{
345    type Isomorphism = super::generic_impls::Isomorphism<P, Self>;
346
347    fn has_canonical_iso(&self, from: &P) -> Option<Self::Isomorphism> {
348        self.base_ring()
349            .get_ring()
350            .has_canonical_iso(from.base_ring().get_ring())
351    }
352
353    fn map_out(&self, from: &P, el: Self::Element, iso: &Self::Isomorphism) -> P::Element {
354        super::generic_impls::map_out(from, self, el, iso)
355    }
356}
357
358impl<R1, R2> CanIsoFromTo<SparsePolyRingBase<R1>> for SparsePolyRingBase<R2>
359where
360    R1: RingStore,
361    R2: RingStore,
362    R2::Type: CanIsoFromTo<R1::Type>,
363{
364    type Isomorphism = <R2::Type as CanIsoFromTo<R1::Type>>::Isomorphism;
365
366    fn has_canonical_iso(&self, from: &SparsePolyRingBase<R1>) -> Option<Self::Isomorphism> {
367        self.base_ring()
368            .get_ring()
369            .has_canonical_iso(from.base_ring().get_ring())
370    }
371
372    fn map_out(
373        &self,
374        from: &SparsePolyRingBase<R1>,
375        el: Self::Element,
376        iso: &Self::Isomorphism,
377    ) -> SparsePolyRingEl<R1> {
378        let mut result = SparseMapVector::new(el.data.len(), from.base_ring.clone());
379        for (j, c) in el.data.nontrivial_entries() {
380            *result.at_mut(j) =
381                self.base_ring()
382                    .get_ring()
383                    .map_out(from.base_ring().get_ring(), self.base_ring().clone_el(c), iso);
384        }
385        return SparsePolyRingEl { data: result };
386    }
387}
388
389pub struct TermIterator<'a, R>
390where
391    R: RingStore,
392{
393    iter: SparseMapVectorIter<'a, Arc<R>>,
394}
395
396impl<'a, R> Iterator for TermIterator<'a, R>
397where
398    R: RingStore,
399{
400    type Item = (&'a El<R>, usize);
401
402    fn next(&mut self) -> Option<Self::Item> {
403        if let Some((i, c)) = self.iter.next() {
404            Some((c, i))
405        } else {
406            None
407        }
408    }
409}
410
411impl<R> PolyRing for SparsePolyRingBase<R>
412where
413    R: RingStore,
414{
415    type TermsIterator<'a>
416        = TermIterator<'a, R>
417    where
418        Self: 'a;
419
420    fn indeterminate(&self) -> Self::Element {
421        let mut result = self.zero();
422        result.data.set_len(2);
423        *result.data.at_mut(1) = self.base_ring.one();
424        return result;
425    }
426
427    fn terms<'a>(&'a self, f: &'a Self::Element) -> TermIterator<'a, R> {
428        TermIterator {
429            iter: f.data.nontrivial_entries(),
430        }
431    }
432
433    fn add_assign_from_terms<I>(&self, lhs: &mut Self::Element, rhs: I)
434    where
435        I: IntoIterator<Item = (El<Self::BaseRing>, usize)>,
436    {
437        for (c, i) in rhs {
438            lhs.data.set_len(max(lhs.data.len(), i + 1));
439            self.base_ring().add_assign(lhs.data.at_mut(i), c);
440        }
441        // if a previously set entry is then set to zero or adds up to zero, this might be not
442        // truncated
443        self.degree_truncate(&mut lhs.data);
444    }
445
446    fn coefficient_at<'a>(&'a self, f: &'a Self::Element, i: usize) -> &'a El<Self::BaseRing> {
447        if i < f.data.len() {
448            return f.data.at(i);
449        } else {
450            return &self.zero;
451        }
452    }
453
454    fn degree(&self, f: &Self::Element) -> Option<usize> { f.data.len().checked_sub(1) }
455
456    fn div_rem_monic(&self, mut lhs: Self::Element, rhs: &Self::Element) -> (Self::Element, Self::Element) {
457        assert!(
458            self.base_ring()
459                .is_one(self.coefficient_at(rhs, self.degree(rhs).unwrap()))
460        );
461        let quo = self.poly_div(&mut lhs, rhs, Some).unwrap();
462        return (quo, lhs);
463    }
464}
465
466impl<R> Domain for SparsePolyRingBase<R>
467where
468    R: RingStore,
469    R::Type: Domain,
470{
471}
472
473impl<R> DivisibilityRing for SparsePolyRingBase<R>
474where
475    R: RingStore,
476    R::Type: DivisibilityRing + Domain,
477{
478    fn checked_left_div(&self, lhs: &Self::Element, rhs: &Self::Element) -> Option<Self::Element> {
479        if let Some(d) = self.degree(rhs) {
480            let lc = rhs.data.at(d);
481            let mut lhs_copy = self.clone_el(lhs);
482            let quo = self.poly_div(&mut lhs_copy, rhs, |x| self.base_ring().checked_left_div(&x, lc))?;
483            if self.is_zero(&lhs_copy) { Some(quo) } else { None }
484        } else if self.is_zero(lhs) {
485            Some(self.zero())
486        } else {
487            None
488        }
489    }
490}
491
492impl<R> PrincipalIdealRing for SparsePolyRingBase<R>
493where
494    R: RingStore,
495    R::Type: Field + PolyTFracGCDRing,
496{
497    fn checked_div_min(&self, lhs: &Self::Element, rhs: &Self::Element) -> Option<Self::Element> {
498        // base ring is a field, so everything is fine
499        if self.is_zero(rhs) && self.is_zero(lhs) {
500            return Some(self.one());
501        } else if self.is_zero(rhs) {
502            return None;
503        }
504        let (quo, rem) = self.euclidean_div_rem(self.clone_el(lhs), rhs);
505        if self.is_zero(&rem) {
506            return Some(quo);
507        } else {
508            return None;
509        }
510    }
511
512    fn extended_ideal_gen(
513        &self,
514        lhs: &Self::Element,
515        rhs: &Self::Element,
516    ) -> (Self::Element, Self::Element, Self::Element) {
517        algorithms::eea::eea(self.clone_el(lhs), self.clone_el(rhs), RingRef::new(self))
518    }
519
520    fn ideal_gen(&self, lhs: &Self::Element, rhs: &Self::Element) -> Self::Element {
521        <_ as PolyTFracGCDRing>::gcd(RingRef::new(self), lhs, rhs)
522    }
523}
524
525impl<R> EuclideanRing for SparsePolyRingBase<R>
526where
527    R: RingStore,
528    R::Type: Field + PolyTFracGCDRing,
529{
530    fn euclidean_div_rem(&self, mut lhs: Self::Element, rhs: &Self::Element) -> (Self::Element, Self::Element) {
531        let lc_inv = self.base_ring.invert(rhs.data.at(self.degree(rhs).unwrap())).unwrap();
532        let quo = self
533            .poly_div(&mut lhs, rhs, |x| Some(self.base_ring().mul_ref_snd(x, &lc_inv)))
534            .unwrap();
535        return (quo, lhs);
536    }
537
538    fn euclidean_deg(&self, val: &Self::Element) -> Option<usize> {
539        return Some(self.degree(val).map(|x| x + 1).unwrap_or(0));
540    }
541}
542
543#[cfg(test)]
544use super::dense_poly::DensePolyRing;
545#[cfg(test)]
546use crate::primitive_int::StaticRing;
547#[cfg(test)]
548use crate::rings::finite::FiniteRingStore;
549#[cfg(test)]
550use crate::rings::zn::zn_static::{Fp, Zn};
551#[cfg(test)]
552use crate::rings::zn::*;
553
554#[cfg(test)]
555fn edge_case_elements<P: PolyRingStore>(poly_ring: P) -> impl Iterator<Item = El<P>>
556where
557    P::Type: PolyRing,
558{
559    let base_ring = poly_ring.base_ring();
560    vec![
561        poly_ring.from_terms([].into_iter()),
562        poly_ring.from_terms([(base_ring.int_hom().map(1), 0)].into_iter()),
563        poly_ring.from_terms([(base_ring.int_hom().map(1), 1)].into_iter()),
564        poly_ring.from_terms([(base_ring.int_hom().map(1), 0), (base_ring.int_hom().map(1), 1)].into_iter()),
565        poly_ring.from_terms([(base_ring.int_hom().map(-1), 0)].into_iter()),
566        poly_ring.from_terms([(base_ring.int_hom().map(-1), 1)].into_iter()),
567        poly_ring.from_terms([(base_ring.int_hom().map(-1), 0), (base_ring.int_hom().map(1), 1)].into_iter()),
568        poly_ring.from_terms([(base_ring.int_hom().map(1), 0), (base_ring.int_hom().map(-1), 1)].into_iter()),
569        poly_ring.from_terms([(base_ring.int_hom().map(-1), 0), (base_ring.int_hom().map(1), 2)].into_iter()),
570        poly_ring.from_terms([(base_ring.int_hom().map(1), 0), (base_ring.int_hom().map(-1), 2)].into_iter()),
571        poly_ring.from_terms(
572            [
573                (base_ring.int_hom().map(1), 0),
574                (base_ring.int_hom().map(-1), 2),
575                (base_ring.int_hom().map(0), 2),
576            ]
577            .into_iter(),
578        ),
579    ]
580    .into_iter()
581}
582
583#[test]
584fn test_ring_axioms() {
585    let poly_ring = SparsePolyRing::new(Zn::<7>::RING, "X");
586    crate::ring::generic_tests::test_ring_axioms(&poly_ring, edge_case_elements(&poly_ring));
587}
588
589#[test]
590fn test_poly_ring_axioms() {
591    let poly_ring = SparsePolyRing::new(Zn::<7>::RING, "X");
592    super::generic_tests::test_poly_ring_axioms(poly_ring, Zn::<7>::RING.elements());
593}
594
595#[test]
596fn test_canonical_iso_axioms_different_base_ring() {
597    let poly_ring1 = SparsePolyRing::new(zn_big::Zn::new(StaticRing::<i128>::RING, 7), "X");
598    let poly_ring2 = SparsePolyRing::new(zn_64::Zn::new(7), "X");
599    crate::ring::generic_tests::test_hom_axioms(&poly_ring1, &poly_ring2, edge_case_elements(&poly_ring1));
600    crate::ring::generic_tests::test_iso_axioms(&poly_ring1, &poly_ring2, edge_case_elements(&poly_ring1));
601}
602
603#[test]
604fn test_canonical_iso_dense_poly_ring() {
605    let poly_ring1 = SparsePolyRing::new(zn_64::Zn::new(7), "X");
606    let poly_ring2 = DensePolyRing::new(zn_64::Zn::new(7), "X");
607    crate::ring::generic_tests::test_hom_axioms(&poly_ring2, &poly_ring1, edge_case_elements(&poly_ring2));
608    crate::ring::generic_tests::test_iso_axioms(&poly_ring2, &poly_ring1, edge_case_elements(&poly_ring2));
609}
610
611#[test]
612fn test_divisibility_ring_axioms() {
613    let poly_ring = SparsePolyRing::new(Fp::<7>::RING, "X");
614    crate::divisibility::generic_tests::test_divisibility_axioms(&poly_ring, edge_case_elements(&poly_ring));
615}
616
617#[test]
618fn test_euclidean_ring_axioms() {
619    let poly_ring = SparsePolyRing::new(Fp::<7>::RING, "X");
620    crate::pid::generic_tests::test_euclidean_ring_axioms(&poly_ring, edge_case_elements(&poly_ring));
621}
622
623#[test]
624fn test_principal_ideal_ring_axioms() {
625    let poly_ring = SparsePolyRing::new(Fp::<7>::RING, "X");
626    crate::pid::generic_tests::test_principal_ideal_ring_axioms(&poly_ring, edge_case_elements(&poly_ring));
627}