Skip to main content

feanor_math/rings/poly/
dense_poly.rs

1use std::alloc::{Allocator, Global};
2use std::cmp::{max, min};
3use std::fmt::Debug;
4
5use feanor_serde::newtype_struct::*;
6use feanor_serde::seq::*;
7use serde::de::DeserializeSeed;
8use serde::{Deserializer, Serialize, Serializer};
9
10use crate::algorithms;
11use crate::algorithms::convolution::*;
12use crate::algorithms::interpolate::interpolate;
13use crate::algorithms::poly_div::fast_poly_div_rem;
14use crate::algorithms::poly_gcd::PolyTFracGCDRing;
15use crate::computation::{ComputationController, DontObserve, no_error};
16use crate::divisibility::*;
17use crate::field::Field;
18use crate::integer::*;
19use crate::pid::*;
20use crate::primitive_int::StaticRing;
21use crate::reduce_lift::poly_eval::{EvalPolyLocallyRing, InterpolationBaseRing, ToExtRingMap};
22use crate::ring::*;
23use crate::rings::poly::*;
24use crate::seq::*;
25use crate::serialization::*;
26use crate::specialization::{FiniteRingOperation, FiniteRingSpecializable};
27
28/// The univariate polynomial ring `R[X]`. Polynomials are stored as dense vectors of
29/// coefficients, allocated by the given memory provider.
30///
31/// If most of the coefficients will be zero, consider using [`sparse_poly::SparsePolyRingBase`]
32/// instead for improved performance.
33///
34/// # Example
35/// ```rust
36/// # use feanor_math::ring::*;
37/// # use feanor_math::homomorphism::*;
38/// # use feanor_math::rings::poly::*;
39/// # use feanor_math::rings::poly::dense_poly::*;
40/// # use feanor_math::primitive_int::*;
41/// let ZZ = StaticRing::<i32>::RING;
42/// let P = DensePolyRing::new(ZZ, "X");
43/// let x_plus_1 = P.add(P.indeterminate(), P.int_hom().map(1));
44/// let binomial_coefficients = P.pow(x_plus_1, 10);
45/// assert_eq!(
46///     10 * 9 * 8 * 7 * 6 / 120,
47///     *P.coefficient_at(&binomial_coefficients, 5)
48/// );
49/// ```
50/// This ring has a [`CanIsoFromTo`] to [`sparse_poly::SparsePolyRingBase`].
51/// ```rust
52/// # use feanor_math::assert_el_eq;
53/// # use feanor_math::ring::*;
54/// # use feanor_math::homomorphism::*;
55/// # use feanor_math::rings::poly::*;
56/// # use feanor_math::rings::poly::dense_poly::*;
57/// # use feanor_math::rings::poly::sparse_poly::*;
58/// # use feanor_math::primitive_int::*;
59/// let ZZ = StaticRing::<i32>::RING;
60/// let P = DensePolyRing::new(ZZ, "X");
61/// let P2 = SparsePolyRing::new(ZZ, "X");
62/// let high_power_of_x = P.pow(P.indeterminate(), 10);
63/// assert_el_eq!(
64///     P2,
65///     P2.pow(P2.indeterminate(), 10),
66///     &P.can_iso(&P2).unwrap().map(high_power_of_x)
67/// );
68/// ```
69pub struct DensePolyRingBase<
70    R: RingStore,
71    A: Allocator + Clone = Global,
72    C: ConvolutionAlgorithm<R::Type> = KaratsubaAlgorithm<Global>,
73> {
74    base_ring: R,
75    unknown_name: &'static str,
76    zero: El<R>,
77    element_allocator: A,
78    convolution_algorithm: C,
79}
80
81impl<R: RingStore + Clone, A: Allocator + Clone, C: ConvolutionAlgorithm<R::Type> + Clone> Clone
82    for DensePolyRingBase<R, A, C>
83{
84    fn clone(&self) -> Self {
85        DensePolyRingBase {
86            base_ring: <R as Clone>::clone(&self.base_ring),
87            unknown_name: self.unknown_name,
88            zero: self.base_ring.zero(),
89            element_allocator: self.element_allocator.clone(),
90            convolution_algorithm: self.convolution_algorithm.clone(),
91        }
92    }
93}
94
95impl<R: RingStore, A: Allocator + Clone, C: ConvolutionAlgorithm<R::Type>> Debug for DensePolyRingBase<R, A, C>
96where
97    R::Type: Debug,
98{
99    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100        f.debug_struct("DensePolyRing")
101            .field("base_ring", &self.base_ring.get_ring())
102            .finish()
103    }
104}
105
106/// The univariate polynomial ring `R[X]`, with polynomials being stored as dense vectors of
107/// coefficients. For details, see [`DensePolyRingBase`].
108pub type DensePolyRing<R, A = Global, C = KaratsubaAlgorithm> = RingValue<DensePolyRingBase<R, A, C>>;
109
110impl<R: RingStore> DensePolyRing<R> {
111    pub fn new(base_ring: R, unknown_name: &'static str) -> Self {
112        Self::new_with_convolution(base_ring, unknown_name, Global, STANDARD_CONVOLUTION)
113    }
114}
115
116impl<R: RingStore, A: Allocator + Clone, C: ConvolutionAlgorithm<R::Type>> DensePolyRing<R, A, C> {
117    #[stability::unstable(feature = "enable")]
118    pub fn new_with_convolution(
119        base_ring: R,
120        unknown_name: &'static str,
121        element_allocator: A,
122        convolution_algorithm: C,
123    ) -> Self {
124        let zero = base_ring.zero();
125        RingValue::from(DensePolyRingBase {
126            base_ring,
127            unknown_name,
128            zero,
129            element_allocator,
130            convolution_algorithm,
131        })
132    }
133}
134
135impl<R: RingStore, A: Allocator + Clone, C: ConvolutionAlgorithm<R::Type>> DensePolyRingBase<R, A, C> {
136    #[stability::unstable(feature = "enable")]
137    pub fn into_base_ring(self) -> R { self.base_ring }
138}
139
140/// An element of [`DensePolyRing`].
141pub struct DensePolyRingEl<R: RingStore, A: Allocator + Clone = Global> {
142    data: Vec<El<R>, A>,
143}
144
145impl<R, A> Debug for DensePolyRingEl<R, A>
146where
147    R: RingStore,
148    A: Allocator + Clone,
149    El<R>: Debug,
150{
151    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.data.fmt(f) }
152}
153
154impl<R: RingStore, A: Allocator + Clone, C: ConvolutionAlgorithm<R::Type>> RingBase for DensePolyRingBase<R, A, C> {
155    type Element = DensePolyRingEl<R, A>;
156
157    fn clone_el(&self, val: &Self::Element) -> Self::Element {
158        let mut data = Vec::with_capacity_in(val.data.len(), self.element_allocator.clone());
159        data.extend((0..val.data.len()).map(|i| self.base_ring.clone_el(val.data.at(i))));
160        DensePolyRingEl { data }
161    }
162
163    fn add_assign_ref(&self, lhs: &mut Self::Element, rhs: &Self::Element) {
164        for i in 0..min(lhs.data.len(), rhs.data.len()) {
165            self.base_ring.add_assign_ref(&mut lhs.data[i], &rhs.data[i]);
166        }
167        for i in min(lhs.data.len(), rhs.data.len())..rhs.data.len() {
168            lhs.data.push(self.base_ring().clone_el(&rhs.data[i]));
169        }
170    }
171
172    fn add_assign(&self, lhs: &mut Self::Element, rhs: Self::Element) { self.add_assign_ref(lhs, &rhs); }
173
174    fn sub_assign_ref(&self, lhs: &mut Self::Element, rhs: &Self::Element) {
175        for i in 0..min(lhs.data.len(), rhs.data.len()) {
176            self.base_ring.sub_assign_ref(&mut lhs.data[i], &rhs.data[i]);
177        }
178        for i in min(lhs.data.len(), rhs.data.len())..rhs.data.len() {
179            lhs.data
180                .push(self.base_ring().negate(self.base_ring().clone_el(&rhs.data[i])));
181        }
182    }
183
184    fn negate_inplace(&self, lhs: &mut Self::Element) {
185        for i in 0..lhs.data.len() {
186            self.base_ring.negate_inplace(&mut lhs.data[i]);
187        }
188    }
189
190    fn mul_assign(&self, lhs: &mut Self::Element, rhs: Self::Element) { self.mul_assign_ref(lhs, &rhs); }
191
192    fn mul_assign_ref(&self, lhs: &mut Self::Element, rhs: &Self::Element) { *lhs = self.mul_ref(lhs, rhs); }
193
194    fn zero(&self) -> Self::Element {
195        DensePolyRingEl {
196            data: Vec::new_in(self.element_allocator.clone()),
197        }
198    }
199
200    fn from_int(&self, value: i32) -> Self::Element {
201        let mut result = self.zero();
202        result.data.push(self.base_ring().get_ring().from_int(value));
203        return result;
204    }
205
206    fn eq_el(&self, lhs: &Self::Element, rhs: &Self::Element) -> bool {
207        for i in 0..min(lhs.data.len(), rhs.data.len()) {
208            if !self.base_ring.eq_el(&lhs.data[i], &rhs.data[i]) {
209                return false;
210            }
211        }
212        let longer = if lhs.data.len() > rhs.data.len() { lhs } else { rhs };
213        for i in min(lhs.data.len(), rhs.data.len())..longer.data.len() {
214            if !self.base_ring.is_zero(&longer.data[i]) {
215                return false;
216            }
217        }
218        return true;
219    }
220
221    fn is_commutative(&self) -> bool { self.base_ring.is_commutative() }
222
223    fn is_noetherian(&self) -> bool {
224        // by Hilbert's basis theorem
225        self.base_ring.is_noetherian()
226    }
227
228    fn dbg_within<'a>(
229        &self,
230        value: &Self::Element,
231        out: &mut std::fmt::Formatter<'a>,
232        env: EnvBindingStrength,
233    ) -> std::fmt::Result {
234        super::generic_impls::dbg_poly(self, value, out, self.unknown_name, env)
235    }
236
237    fn dbg<'a>(&self, value: &Self::Element, out: &mut std::fmt::Formatter<'a>) -> std::fmt::Result {
238        self.dbg_within(value, out, EnvBindingStrength::Weakest)
239    }
240
241    fn square(&self, value: &mut Self::Element) { *value = self.mul_ref(value, value); }
242
243    fn mul_ref(&self, lhs: &Self::Element, rhs: &Self::Element) -> Self::Element {
244        let lhs_len = if self.base_ring().get_ring().is_approximate() {
245            lhs.data.len()
246        } else {
247            self.degree(lhs).map(|i| i + 1).unwrap_or(0)
248        };
249        let rhs_len = if self.base_ring().get_ring().is_approximate() {
250            rhs.data.len()
251        } else {
252            self.degree(rhs).map(|i| i + 1).unwrap_or(0)
253        };
254        let mut result = Vec::with_capacity_in(lhs_len + rhs_len, self.element_allocator.clone());
255        result.extend((0..(lhs_len + rhs_len)).map(|_| self.base_ring().zero()));
256        self.convolution_algorithm.compute_convolution(
257            &lhs.data[0..lhs_len],
258            &rhs.data[0..rhs_len],
259            &mut result[..],
260            self.base_ring(),
261        );
262        return DensePolyRingEl { data: result };
263    }
264
265    fn mul_assign_int(&self, lhs: &mut Self::Element, rhs: i32) {
266        for i in 0..lhs.data.len() {
267            self.base_ring().int_hom().mul_assign_map(lhs.data.at_mut(i), rhs);
268        }
269    }
270
271    fn characteristic<I: IntegerRingStore + Copy>(&self, ZZ: I) -> Option<El<I>>
272    where
273        I::Type: IntegerRing,
274    {
275        self.base_ring().characteristic(ZZ)
276    }
277
278    fn prod<I>(&self, els: I) -> Self::Element
279    where
280        I: IntoIterator<Item = Self::Element>,
281    {
282        let mut elements = els.into_iter().collect::<Vec<_>>();
283        if elements.is_empty() {
284            return self.one();
285        }
286        elements.sort_unstable_by_key(|f| self.degree(f).unwrap_or(0));
287        // this can make it much faster; in particular, in the (not too uncommon) special case that
288        // we compute the product of degree 1 polynomials, this means we actually make use
289        // of karatsuba multiplication
290        for i in 0..StaticRing::<i64>::RING
291            .abs_log2_ceil(&elements.len().try_into().unwrap())
292            .unwrap()
293        {
294            let step = 1 << i;
295            for j in (0..(elements.len() - step)).step_by(2 * step) {
296                let (a, b) = elements[j..(j + step + 1)].split_at_mut(step);
297                self.mul_assign_ref(&mut a[0], &b[0]);
298            }
299        }
300        return elements.into_iter().next().unwrap();
301    }
302
303    fn is_approximate(&self) -> bool { self.base_ring().get_ring().is_approximate() }
304}
305
306impl<R, A, C> PartialEq for DensePolyRingBase<R, A, C>
307where
308    R: RingStore,
309    A: Allocator + Clone,
310    C: ConvolutionAlgorithm<R::Type>,
311{
312    fn eq(&self, other: &Self) -> bool { self.base_ring.get_ring() == other.base_ring.get_ring() }
313}
314
315/// Marker trait to signal that for this polynomial ring `P`, we want to use the
316/// default implementation of the potential isomorphism `P <-> DensePolyRing` when
317/// applicable.
318///
319/// This is currently necessary, since we want to provide a specialized implementation
320/// of `DensePolyRingBase<R1, A1>: CanHomFrom<DensePolyRingBase<R2, A2>>`, but we cannot
321/// currently specialize on types that still have generic parameters.
322pub trait ImplGenericCanIsoFromToMarker: PolyRing {}
323
324impl<R> ImplGenericCanIsoFromToMarker for sparse_poly::SparsePolyRingBase<R> where R: RingStore {}
325
326impl<R, P, A, C> CanHomFrom<P> for DensePolyRingBase<R, A, C>
327where
328    R: RingStore,
329    R::Type: CanHomFrom<<P::BaseRing as RingStore>::Type>,
330    P: ImplGenericCanIsoFromToMarker,
331    A: Allocator + Clone,
332    C: ConvolutionAlgorithm<R::Type>,
333{
334    type Homomorphism = super::generic_impls::Homomorphism<P, DensePolyRingBase<R, A, C>>;
335
336    fn has_canonical_hom(&self, from: &P) -> Option<Self::Homomorphism> {
337        super::generic_impls::has_canonical_hom(from, self)
338    }
339
340    fn map_in(&self, from: &P, el: P::Element, hom: &Self::Homomorphism) -> Self::Element {
341        super::generic_impls::map_in(from, self, el, hom)
342    }
343}
344
345impl<R1, A1, R2, A2, C1, C2> CanHomFrom<DensePolyRingBase<R1, A1, C1>> for DensePolyRingBase<R2, A2, C2>
346where
347    R1: RingStore,
348    A1: Allocator + Clone,
349    C1: ConvolutionAlgorithm<R1::Type>,
350    R2: RingStore,
351    A2: Allocator + Clone,
352    C2: ConvolutionAlgorithm<R2::Type>,
353    R2::Type: CanHomFrom<R1::Type>,
354{
355    type Homomorphism = <R2::Type as CanHomFrom<R1::Type>>::Homomorphism;
356
357    fn has_canonical_hom(&self, from: &DensePolyRingBase<R1, A1, C1>) -> Option<Self::Homomorphism> {
358        self.base_ring()
359            .get_ring()
360            .has_canonical_hom(from.base_ring().get_ring())
361    }
362
363    fn map_in_ref(
364        &self,
365        from: &DensePolyRingBase<R1, A1, C1>,
366        el: &DensePolyRingEl<R1, A1>,
367        hom: &Self::Homomorphism,
368    ) -> Self::Element {
369        RingRef::new(self).from_terms((0..el.data.len()).map(|i| {
370            (
371                self.base_ring()
372                    .get_ring()
373                    .map_in_ref(from.base_ring().get_ring(), &el.data[i], hom),
374                i,
375            )
376        }))
377    }
378
379    fn map_in(
380        &self,
381        from: &DensePolyRingBase<R1, A1, C1>,
382        el: DensePolyRingEl<R1, A1>,
383        hom: &Self::Homomorphism,
384    ) -> Self::Element {
385        self.map_in_ref(from, &el, hom)
386    }
387}
388
389impl<R1, A1, R2, A2, C1, C2> CanIsoFromTo<DensePolyRingBase<R1, A1, C1>> for DensePolyRingBase<R2, A2, C2>
390where
391    R1: RingStore,
392    A1: Allocator + Clone,
393    C1: ConvolutionAlgorithm<R1::Type>,
394    R2: RingStore,
395    A2: Allocator + Clone,
396    C2: ConvolutionAlgorithm<R2::Type>,
397    R2::Type: CanIsoFromTo<R1::Type>,
398{
399    type Isomorphism = <R2::Type as CanIsoFromTo<R1::Type>>::Isomorphism;
400
401    fn has_canonical_iso(&self, from: &DensePolyRingBase<R1, A1, C1>) -> Option<Self::Isomorphism> {
402        self.base_ring()
403            .get_ring()
404            .has_canonical_iso(from.base_ring().get_ring())
405    }
406
407    fn map_out(
408        &self,
409        from: &DensePolyRingBase<R1, A1, C1>,
410        el: DensePolyRingEl<R2, A2>,
411        hom: &Self::Isomorphism,
412    ) -> DensePolyRingEl<R1, A1> {
413        RingRef::new(from).from_terms((0..el.data.len()).map(|i| {
414            (
415                self.base_ring().get_ring().map_out(
416                    from.base_ring().get_ring(),
417                    self.base_ring().clone_el(&el.data[i]),
418                    hom,
419                ),
420                i,
421            )
422        }))
423    }
424}
425
426impl<R, P, A, C> CanIsoFromTo<P> for DensePolyRingBase<R, A, C>
427where
428    R: RingStore,
429    R::Type: CanIsoFromTo<<P::BaseRing as RingStore>::Type>,
430    P: ImplGenericCanIsoFromToMarker,
431    A: Allocator + Clone,
432    C: ConvolutionAlgorithm<R::Type>,
433{
434    type Isomorphism = super::generic_impls::Isomorphism<P, Self>;
435
436    fn has_canonical_iso(&self, from: &P) -> Option<Self::Isomorphism> {
437        self.base_ring()
438            .get_ring()
439            .has_canonical_iso(from.base_ring().get_ring())
440    }
441
442    fn map_out(&self, from: &P, el: Self::Element, iso: &Self::Isomorphism) -> P::Element {
443        super::generic_impls::map_out(from, self, el, iso)
444    }
445}
446
447impl<R: RingStore, A: Allocator + Clone, C: ConvolutionAlgorithm<R::Type>> RingExtension
448    for DensePolyRingBase<R, A, C>
449{
450    type BaseRing = R;
451
452    fn base_ring(&self) -> &Self::BaseRing { &self.base_ring }
453
454    fn from(&self, x: El<Self::BaseRing>) -> Self::Element {
455        let mut result = self.zero();
456        result.data.push(x);
457        return result;
458    }
459
460    fn fma_base(&self, lhs: &Self::Element, rhs: &El<Self::BaseRing>, summand: Self::Element) -> Self::Element {
461        let lhs_len = self.degree(lhs).map(|d| d + 1).unwrap_or(0);
462        let summand_len = self.degree(&summand).map(|d| d + 1).unwrap_or(0);
463        let mut result = Vec::with_capacity_in(max(lhs_len, summand_len), self.element_allocator.clone());
464        let mut summand_it = summand.data.into_iter();
465        result.extend(
466            summand_it
467                .by_ref()
468                .take(min(summand_len, lhs_len))
469                .enumerate()
470                .map(|(i, x)| self.base_ring().fma(&lhs.data[i], rhs, x)),
471        );
472        result.extend(summand_it.take(summand_len - min(summand_len, lhs_len)));
473        result.extend((min(summand_len, lhs_len)..lhs_len).map(|i| self.base_ring().mul_ref(&lhs.data[i], rhs)));
474        return DensePolyRingEl { data: result };
475    }
476
477    fn mul_assign_base(&self, lhs: &mut Self::Element, rhs: &El<Self::BaseRing>) {
478        for c in &mut lhs.data {
479            self.base_ring().mul_assign_ref(c, rhs)
480        }
481    }
482}
483
484/// Iterator over all terms of an element of [`DensePolyRing`].
485pub struct TermIterator<'a, R>
486where
487    R: RingStore,
488{
489    iter: std::iter::Rev<std::iter::Enumerate<std::slice::Iter<'a, El<R>>>>,
490    ring: &'a R,
491}
492
493impl<'a, R> Clone for TermIterator<'a, R>
494where
495    R: RingStore,
496{
497    fn clone(&self) -> Self {
498        TermIterator {
499            iter: self.iter.clone(),
500            ring: self.ring,
501        }
502    }
503}
504
505impl<'a, R> Iterator for TermIterator<'a, R>
506where
507    R: RingStore,
508{
509    type Item = (&'a El<R>, usize);
510
511    fn next(&mut self) -> Option<Self::Item> {
512        for (i, c) in self.iter.by_ref() {
513            if self.ring.get_ring().is_approximate() || !self.ring.is_zero(c) {
514                return Some((c, i));
515            }
516        }
517        return None;
518    }
519}
520
521impl<R, A: Allocator + Clone, C: ConvolutionAlgorithm<R::Type>> HashableElRing for DensePolyRingBase<R, A, C>
522where
523    R: RingStore,
524    R::Type: HashableElRing,
525{
526    fn hash<H: std::hash::Hasher>(&self, el: &Self::Element, h: &mut H) {
527        let len = self.degree(el).map(|d| d + 1).unwrap_or(0);
528        h.write_length_prefix(len);
529        for i in 0..len {
530            self.base_ring().get_ring().hash(self.coefficient_at(el, i), h);
531        }
532    }
533}
534
535impl<R, A: Allocator + Clone, C: ConvolutionAlgorithm<R::Type>> SerializableElementRing for DensePolyRingBase<R, A, C>
536where
537    R: RingStore,
538    R::Type: SerializableElementRing,
539{
540    fn deserialize<'de, D>(&self, deserializer: D) -> Result<Self::Element, D::Error>
541    where
542        D: Deserializer<'de>,
543    {
544        DeserializeSeedNewtypeStruct::new(
545            "DensePoly",
546            DeserializeSeedSeq::new(
547                std::iter::repeat(DeserializeWithRing::new(self.base_ring())),
548                Vec::new_in(self.element_allocator.clone()),
549                |mut current, next| {
550                    current.push(next);
551                    current
552                },
553            ),
554        )
555        .deserialize(deserializer)
556        .map(|data| DensePolyRingEl { data })
557    }
558
559    fn serialize<S>(&self, el: &Self::Element, serializer: S) -> Result<S::Ok, S::Error>
560    where
561        S: Serializer,
562    {
563        let len = self.degree(el).map(|d| d + 1).unwrap_or(0);
564        SerializableNewtypeStruct::new(
565            "DensePoly",
566            SerializableSeq::new_with_len(
567                (0..len).map(|i| SerializeWithRing::new(self.coefficient_at(el, i), self.base_ring())),
568                len,
569            ),
570        )
571        .serialize(serializer)
572    }
573}
574
575impl<R, A: Allocator + Clone, C: ConvolutionAlgorithm<R::Type>> PolyRing for DensePolyRingBase<R, A, C>
576where
577    R: RingStore,
578{
579    type TermsIterator<'a>
580        = TermIterator<'a, R>
581    where
582        Self: 'a;
583
584    fn indeterminate(&self) -> Self::Element {
585        let mut result = self.zero();
586        result.data.extend([self.base_ring().zero(), self.base_ring().one()]);
587        return result;
588    }
589
590    fn terms<'a>(&'a self, f: &'a Self::Element) -> TermIterator<'a, R> {
591        TermIterator {
592            iter: f.data.iter().enumerate().rev(),
593            ring: self.base_ring(),
594        }
595    }
596
597    fn add_assign_from_terms<I>(&self, lhs: &mut Self::Element, rhs: I)
598    where
599        I: IntoIterator<Item = (El<Self::BaseRing>, usize)>,
600    {
601        for (c, i) in rhs {
602            if lhs.data.len() <= i {
603                lhs.data.resize_with(i + 1, || self.base_ring().zero());
604            }
605            self.base_ring().add_assign(&mut lhs.data[i], c);
606        }
607    }
608
609    fn truncate_monomials(&self, lhs: &mut Self::Element, truncated_at_degree: usize) {
610        lhs.data.truncate(truncated_at_degree)
611    }
612
613    fn coefficient_at<'a>(&'a self, f: &'a Self::Element, i: usize) -> &'a El<Self::BaseRing> {
614        if i < f.data.len() {
615            return &f.data[i];
616        } else {
617            return &self.zero;
618        }
619    }
620
621    fn mul_assign_monomial(&self, lhs: &mut Self::Element, rhs_power: usize) {
622        _ = lhs.data.splice(0..0, (0..rhs_power).map(|_| self.base_ring().zero()));
623    }
624
625    fn degree(&self, f: &Self::Element) -> Option<usize> {
626        (0..f.data.len()).rev().find(|&i| !self.base_ring().is_zero(&f.data[i]))
627    }
628
629    fn div_rem_monic(&self, lhs: Self::Element, rhs: &Self::Element) -> (Self::Element, Self::Element) {
630        assert!(
631            self.base_ring()
632                .is_one(self.coefficient_at(rhs, self.degree(rhs).unwrap()))
633        );
634        let (quo, rem) = fast_poly_div_rem(
635            RingRef::new(self),
636            lhs,
637            rhs,
638            |x| Ok(self.base_ring().clone_el(x)),
639            DontObserve,
640        )
641        .unwrap_or_else(no_error);
642        return (quo, rem);
643    }
644
645    fn evaluate<S, H>(&self, f: &Self::Element, value: &S::Element, hom: H) -> S::Element
646    where
647        S: ?Sized + RingBase,
648        H: Homomorphism<R::Type, S>,
649    {
650        let d = if self.base_ring().get_ring().is_approximate() {
651            f.data.len().saturating_sub(1)
652        } else {
653            self.degree(f).unwrap_or(0)
654        };
655        let mut current = hom.map_ref(self.coefficient_at(f, d));
656        for i in (0..d).rev() {
657            hom.codomain().mul_assign_ref(&mut current, value);
658            hom.codomain()
659                .add_assign(&mut current, hom.map_ref(self.coefficient_at(f, i)));
660        }
661        return current;
662    }
663}
664impl<R: RingStore, A: Allocator + Clone, C: ConvolutionAlgorithm<R::Type>> FiniteRingSpecializable
665    for DensePolyRingBase<R, A, C>
666{
667    fn specialize<O: FiniteRingOperation<Self>>(op: O) -> O::Output { op.fallback() }
668}
669
670impl<R, A: Allocator + Clone, C: ConvolutionAlgorithm<R::Type>> EvalPolyLocallyRing for DensePolyRingBase<R, A, C>
671where
672    R: RingStore,
673    R::Type: InterpolationBaseRing,
674{
675    type LocalRing<'ring>
676        = <R::Type as InterpolationBaseRing>::ExtendedRing<'ring>
677    where
678        Self: 'ring;
679
680    type LocalRingBase<'ring>
681        = <R::Type as InterpolationBaseRing>::ExtendedRingBase<'ring>
682    where
683        Self: 'ring;
684
685    type LocalComputationData<'ring>
686        = (
687        ToExtRingMap<'ring, R::Type>,
688        Vec<El<<R::Type as InterpolationBaseRing>::ExtendedRing<'ring>>>,
689    )
690    where
691        Self: 'ring;
692
693    fn ln_pseudo_norm(&self, el: &Self::Element) -> f64 {
694        if let Some(d) = self.degree(el) {
695            return d as f64;
696        } else {
697            return 0.0;
698        }
699    }
700
701    fn local_computation<'ring>(&'ring self, ln_pseudo_norm: f64) -> Self::LocalComputationData<'ring> {
702        let required_points = ln_pseudo_norm.ceil() as usize + 1;
703        ToExtRingMap::for_interpolation(self.base_ring().get_ring(), required_points)
704    }
705
706    fn local_ring_count<'ring>(&self, data: &Self::LocalComputationData<'ring>) -> usize
707    where
708        Self: 'ring,
709    {
710        data.1.len()
711    }
712
713    fn local_ring_at<'ring>(&self, data: &Self::LocalComputationData<'ring>, i: usize) -> Self::LocalRing<'ring>
714    where
715        Self: 'ring,
716    {
717        assert!(i < self.local_ring_count(data));
718        data.0.codomain().clone()
719    }
720
721    fn reduce<'ring>(
722        &self,
723        data: &Self::LocalComputationData<'ring>,
724        el: &Self::Element,
725    ) -> Vec<<Self::LocalRingBase<'ring> as RingBase>::Element>
726    where
727        Self: 'ring,
728    {
729        return data.1.iter().map(|x| self.evaluate(el, x, &data.0)).collect::<Vec<_>>();
730    }
731
732    fn lift_combine<'ring>(
733        &self,
734        data: &Self::LocalComputationData<'ring>,
735        els: &[<Self::LocalRingBase<'ring> as RingBase>::Element],
736    ) -> Self::Element
737    where
738        Self: 'ring,
739    {
740        let base_ring = RingRef::new(data.0.codomain().get_ring());
741        let new_ring = DensePolyRing::new(base_ring, self.unknown_name);
742        let result_in_extension = interpolate(
743            &new_ring,
744            (&data.1).into_clone_ring_els(data.0.codomain()),
745            els.into_clone_ring_els(data.0.codomain()),
746            &self.element_allocator,
747        )
748        .unwrap();
749        return RingRef::new(self).from_terms(
750            new_ring
751                .terms(&result_in_extension)
752                .map(|(c, i)| (data.0.as_base_ring_el(base_ring.clone_el(c)), i)),
753        );
754    }
755}
756
757impl<R, A: Allocator + Clone, C: ConvolutionAlgorithm<R::Type>> Domain for DensePolyRingBase<R, A, C>
758where
759    R: RingStore,
760    R::Type: Domain,
761{
762}
763
764impl<R, A: Allocator + Clone, C> DivisibilityRing for DensePolyRingBase<R, A, C>
765where
766    R: RingStore,
767    R::Type: DivisibilityRing + Domain,
768    C: ConvolutionAlgorithm<R::Type>,
769{
770    fn checked_left_div(&self, lhs: &Self::Element, rhs: &Self::Element) -> Option<Self::Element> {
771        if let Some(d) = self.degree(rhs) {
772            if d == 0 {
773                let rhs = self.coefficient_at(rhs, 0);
774                return RingRef::new(self)
775                    .try_from_terms(
776                        self.terms(lhs)
777                            .map(|(c, i)| self.base_ring().checked_left_div(c, rhs).map(|c| (c, i)).ok_or(())),
778                    )
779                    .ok();
780            } else {
781                let lc = &rhs.data[d];
782                let (quo, rem) = fast_poly_div_rem(
783                    RingRef::new(self),
784                    self.clone_el(lhs),
785                    rhs,
786                    |x| self.base_ring().checked_left_div(x, lc).ok_or(()),
787                    DontObserve,
788                )
789                .ok()?;
790                if self.is_zero(&rem) { Some(quo) } else { None }
791            }
792        } else if self.is_zero(lhs) {
793            Some(self.zero())
794        } else {
795            None
796        }
797    }
798
799    fn divides_left(&self, lhs: &Self::Element, rhs: &Self::Element) -> bool {
800        if let Some(d) = self.degree(rhs) {
801            if d == 0 {
802                true
803            } else {
804                let lc = &rhs.data[d];
805                if let Ok((_, rem)) = fast_poly_div_rem(
806                    RingRef::new(self),
807                    self.clone_el(lhs),
808                    rhs,
809                    |x| self.base_ring().checked_left_div(x, lc).ok_or(()),
810                    DontObserve,
811                ) {
812                    self.is_zero(&rem)
813                } else {
814                    false
815                }
816            }
817        } else {
818            self.is_zero(lhs)
819        }
820    }
821
822    fn balance_factor<'a, I>(&self, elements: I) -> Option<Self::Element>
823    where
824        I: Iterator<Item = &'a Self::Element>,
825        Self: 'a,
826    {
827        self.base_ring()
828            .get_ring()
829            .balance_factor(elements.flat_map(|f| self.terms(f).map(|(c, _)| c)))
830            .map(|c| RingRef::new(self).inclusion().map(c))
831    }
832}
833
834impl<R, A, C> PrincipalIdealRing for DensePolyRingBase<R, A, C>
835where
836    A: Allocator + Clone,
837    R: RingStore,
838    R::Type: Field + PolyTFracGCDRing,
839    C: ConvolutionAlgorithm<R::Type>,
840{
841    fn checked_div_min(&self, lhs: &Self::Element, rhs: &Self::Element) -> Option<Self::Element> {
842        // base ring is a field, so everything is fine
843        if self.is_zero(rhs) && self.is_zero(lhs) {
844            return Some(self.one());
845        } else if self.is_zero(rhs) {
846            return None;
847        }
848        let (quo, rem) = self.euclidean_div_rem(self.clone_el(lhs), rhs);
849        if self.is_zero(&rem) {
850            return Some(quo);
851        } else {
852            return None;
853        }
854    }
855
856    fn extended_ideal_gen(
857        &self,
858        lhs: &Self::Element,
859        rhs: &Self::Element,
860    ) -> (Self::Element, Self::Element, Self::Element) {
861        algorithms::eea::eea(self.clone_el(lhs), self.clone_el(rhs), &RingRef::new(self))
862    }
863
864    fn ideal_gen(&self, lhs: &Self::Element, rhs: &Self::Element) -> Self::Element {
865        <_ as PolyTFracGCDRing>::gcd(RingRef::new(self), lhs, rhs)
866    }
867
868    fn ideal_gen_with_controller<Controller>(
869        &self,
870        lhs: &Self::Element,
871        rhs: &Self::Element,
872        controller: Controller,
873    ) -> Self::Element
874    where
875        Controller: ComputationController,
876    {
877        <_ as PolyTFracGCDRing>::gcd_with_controller(RingRef::new(self), lhs, rhs, controller)
878    }
879}
880
881impl<R, A, C> EuclideanRing for DensePolyRingBase<R, A, C>
882where
883    A: Allocator + Clone,
884    R: RingStore,
885    R::Type: Field + PolyTFracGCDRing,
886    C: ConvolutionAlgorithm<R::Type>,
887{
888    fn euclidean_div_rem(&self, lhs: Self::Element, rhs: &Self::Element) -> (Self::Element, Self::Element) {
889        let lc_inv = self.base_ring.invert(&rhs.data[self.degree(rhs).unwrap()]).unwrap();
890        let (quo, rem) = fast_poly_div_rem(
891            RingRef::new(self),
892            lhs,
893            rhs,
894            |x| Ok(self.base_ring().mul_ref(x, &lc_inv)),
895            DontObserve,
896        )
897        .unwrap_or_else(no_error);
898        return (quo, rem);
899    }
900
901    fn euclidean_deg(&self, val: &Self::Element) -> Option<usize> {
902        return Some(self.degree(val).map(|x| x + 1).unwrap_or(0));
903    }
904}
905
906#[cfg(test)]
907use std::time::Instant;
908
909#[cfg(test)]
910use test::Bencher;
911
912#[cfg(test)]
913use super::sparse_poly::SparsePolyRing;
914#[cfg(test)]
915use crate::algorithms::cyclotomic::cyclotomic_polynomial;
916#[cfg(test)]
917use crate::iters::multiset_combinations;
918#[cfg(test)]
919use crate::ordered::OrderedRingStore;
920#[cfg(test)]
921use crate::rings::approx_real::float::Real64;
922#[cfg(test)]
923use crate::rings::extension::FreeAlgebraStore;
924#[cfg(test)]
925use crate::rings::extension::galois_field::GaloisField;
926#[cfg(test)]
927use crate::rings::finite::FiniteRingStore;
928#[cfg(test)]
929use crate::rings::zn::zn_static::{Fp, Zn};
930#[cfg(test)]
931use crate::rings::zn::*;
932
933#[cfg(test)]
934fn edge_case_elements<P: PolyRingStore>(poly_ring: P) -> impl Iterator<Item = El<P>>
935where
936    P::Type: PolyRing,
937{
938    let base_ring = poly_ring.base_ring();
939    vec![
940        poly_ring.from_terms([].into_iter()),
941        poly_ring.from_terms([(base_ring.int_hom().map(1), 0)].into_iter()),
942        poly_ring.from_terms([(base_ring.int_hom().map(1), 1)].into_iter()),
943        poly_ring.from_terms([(base_ring.int_hom().map(1), 0), (base_ring.int_hom().map(1), 1)].into_iter()),
944        poly_ring.from_terms([(base_ring.int_hom().map(-1), 0)].into_iter()),
945        poly_ring.from_terms([(base_ring.int_hom().map(-1), 1)].into_iter()),
946        poly_ring.from_terms([(base_ring.int_hom().map(-1), 0), (base_ring.int_hom().map(1), 1)].into_iter()),
947        poly_ring.from_terms([(base_ring.int_hom().map(1), 0), (base_ring.int_hom().map(-1), 1)].into_iter()),
948    ]
949    .into_iter()
950}
951
952#[test]
953fn test_prod() {
954    let poly_ring = DensePolyRing::new(StaticRing::<i64>::RING, "X");
955    assert_el_eq!(&poly_ring, poly_ring.one(), poly_ring.prod([].into_iter()));
956
957    let product =
958        poly_ring.prod((0..10).map(|n| poly_ring.add(poly_ring.indeterminate(), poly_ring.inclusion().map(n))));
959    assert_eq!(Some(10), poly_ring.degree(&product));
960    for i in 0..10 {
961        let expected = multiset_combinations(&[1; 10], 10 - i, |indices| {
962            indices
963                .iter()
964                .enumerate()
965                .filter(|(_, count)| **count > 0)
966                .map(|(n, _)| n)
967                .product::<usize>()
968        })
969        .sum::<usize>();
970        assert_eq!(expected as i64, *poly_ring.coefficient_at(&product, i));
971    }
972}
973
974#[test]
975fn test_fma_base() {
976    let poly_ring = DensePolyRing::new(Zn::<7>::RING, "X");
977    let [f, g, h] = poly_ring.with_wrapped_indeterminate(|X| [X + 3, X.pow_ref(2) - 1, X.pow_ref(2) + 2 * X + 5]);
978    assert_el_eq!(&poly_ring, h, poly_ring.get_ring().fma_base(&f, &2, g));
979
980    let poly_ring = DensePolyRing::new(Zn::<7>::RING, "X");
981    let [f, g, h] = poly_ring.with_wrapped_indeterminate(|X| {
982        [
983            X.pow_ref(3) + X,
984            X.pow_ref(2) - 1,
985            2 * X.pow_ref(3) + X.pow_ref(2) + 2 * X - 1,
986        ]
987    });
988    assert_el_eq!(&poly_ring, h, poly_ring.get_ring().fma_base(&f, &2, g));
989
990    let poly_ring = DensePolyRing::new(zn_64::Zn::new(7), "X");
991    let [f, g] = poly_ring.with_wrapped_indeterminate(|X| [3 * X.pow_ref(2) + 5, 5 * X.pow_ref(2) + 6]);
992    assert_el_eq!(
993        &poly_ring,
994        g,
995        poly_ring
996            .get_ring()
997            .fma_base(&f, &poly_ring.base_ring().int_hom().map(4), poly_ring.zero())
998    );
999}
1000
1001#[test]
1002fn test_ring_axioms() {
1003    let poly_ring = DensePolyRing::new(Zn::<7>::RING, "X");
1004    crate::ring::generic_tests::test_ring_axioms(&poly_ring, edge_case_elements(&poly_ring));
1005}
1006
1007#[test]
1008fn test_hash_axioms() {
1009    let poly_ring = DensePolyRing::new(Zn::<7>::RING, "X");
1010    crate::ring::generic_tests::test_hash_axioms(&poly_ring, edge_case_elements(&poly_ring));
1011}
1012
1013#[test]
1014fn test_poly_ring_axioms() {
1015    let poly_ring = DensePolyRing::new(Zn::<7>::RING, "X");
1016    super::generic_tests::test_poly_ring_axioms(poly_ring, Zn::<7>::RING.elements());
1017}
1018
1019#[test]
1020fn test_divisibility_ring_axioms() {
1021    let poly_ring = DensePolyRing::new(Fp::<7>::RING, "X");
1022    crate::divisibility::generic_tests::test_divisibility_axioms(&poly_ring, edge_case_elements(&poly_ring));
1023}
1024
1025#[test]
1026fn test_euclidean_ring_axioms() {
1027    let poly_ring = DensePolyRing::new(Fp::<7>::RING, "X");
1028    crate::pid::generic_tests::test_euclidean_ring_axioms(&poly_ring, edge_case_elements(&poly_ring));
1029}
1030
1031#[test]
1032fn test_principal_ideal_ring_axioms() {
1033    let poly_ring = DensePolyRing::new(Fp::<7>::RING, "X");
1034    crate::pid::generic_tests::test_principal_ideal_ring_axioms(&poly_ring, edge_case_elements(&poly_ring));
1035}
1036
1037#[test]
1038fn test_canonical_iso_axioms_different_base_ring() {
1039    let poly_ring1 = DensePolyRing::new(zn_big::Zn::new(StaticRing::<i128>::RING, 7), "X");
1040    let poly_ring2 = DensePolyRing::new(zn_64::Zn::new(7), "X");
1041    crate::ring::generic_tests::test_hom_axioms(&poly_ring1, &poly_ring2, edge_case_elements(&poly_ring1));
1042    crate::ring::generic_tests::test_iso_axioms(&poly_ring1, &poly_ring2, edge_case_elements(&poly_ring1));
1043}
1044
1045#[test]
1046fn test_canonical_iso_sparse_poly_ring() {
1047    let poly_ring1 = SparsePolyRing::new(zn_64::Zn::new(7), "X");
1048    let poly_ring2 = DensePolyRing::new(zn_64::Zn::new(7), "X");
1049    crate::ring::generic_tests::test_hom_axioms(&poly_ring1, &poly_ring2, edge_case_elements(&poly_ring1));
1050    crate::ring::generic_tests::test_iso_axioms(&poly_ring1, &poly_ring2, edge_case_elements(&poly_ring1));
1051}
1052
1053#[test]
1054fn test_print() {
1055    let base_poly_ring = DensePolyRing::new(StaticRing::<i64>::RING, "X");
1056    let poly_ring = DensePolyRing::new(&base_poly_ring, "Y");
1057
1058    let poly = poly_ring.from_terms(
1059        [
1060            (base_poly_ring.from_terms([(1, 0), (2, 2)].into_iter()), 0),
1061            (base_poly_ring.from_terms([(3, 0), (4, 2)].into_iter()), 2),
1062        ]
1063        .into_iter(),
1064    );
1065    assert_eq!("(4X^2 + 3)Y^2 + 2X^2 + 1", format!("{}", poly_ring.format(&poly)));
1066
1067    let poly = poly_ring.from_terms(
1068        [
1069            (base_poly_ring.zero(), 0),
1070            (base_poly_ring.from_terms([(4, 2)].into_iter()), 2),
1071        ]
1072        .into_iter(),
1073    );
1074    assert_eq!("4X^2Y^2", format!("{}", poly_ring.format(&poly)));
1075}
1076
1077#[test]
1078#[ignore]
1079fn test_expensive_prod() {
1080    let ring = GaloisField::new(17, 2048);
1081    let poly_ring = DensePolyRing::new(&ring, "X");
1082    let mut rng = oorandom::Rand64::new(1);
1083    let a = ring.random_element(|| rng.rand_u64());
1084
1085    let start = Instant::now();
1086    let product = poly_ring.prod((0..2048).scan(ring.clone_el(&a), |current, _| {
1087        let result = poly_ring.sub(poly_ring.indeterminate(), poly_ring.inclusion().map_ref(&current));
1088        *current = ring.pow(std::mem::replace(current, ring.zero()), 17);
1089        return Some(result);
1090    }));
1091    let end = Instant::now();
1092
1093    println!("Computed product in {} ms", (end - start).as_millis());
1094    for i in 0..2048 {
1095        let coeff_wrt_basis = ring.wrt_canonical_basis(poly_ring.coefficient_at(&product, i));
1096        assert!((1..2028).all(|j| ring.base_ring().is_zero(&coeff_wrt_basis.at(j))));
1097    }
1098}
1099
1100#[test]
1101fn test_serialize() {
1102    let poly_ring = DensePolyRing::new(Zn::<7>::RING, "X");
1103    crate::serialization::generic_tests::test_serialization(&poly_ring, edge_case_elements(&poly_ring));
1104}
1105
1106#[test]
1107fn test_evaluate_approximate_ring() {
1108    let ring = DensePolyRing::new(Real64::RING, "X");
1109    let [f] = ring.with_wrapped_indeterminate(|X| [X * X * X - X + 1]);
1110    let x = 0.47312;
1111    assert!(Real64::RING.abs((x * x * x - x + 1.0) - ring.evaluate(&f, &x, &Real64::RING.identity())) <= 0.000000001);
1112}
1113
1114#[bench]
1115fn bench_div_rem_monic(bencher: &mut Bencher) {
1116    let ZZ = BigIntRing::RING;
1117    let ring = DensePolyRing::new(ZZ, "X");
1118    let phi_n = 30 * 40;
1119    let n = 31 * 41;
1120    let cyclotomic_poly = cyclotomic_polynomial(&ring, n);
1121    assert!(ring.degree(&cyclotomic_poly).unwrap() == phi_n);
1122    bencher.iter(|| {
1123        let mut current = ring.pow(ring.indeterminate(), phi_n - 1);
1124        for _ in phi_n..=n {
1125            ring.mul_assign_monomial(&mut current, 1);
1126            current = ring.div_rem_monic(current, &cyclotomic_poly).1;
1127        }
1128        assert_el_eq!(&ring, &ring.one(), &current);
1129    });
1130}