feanor_math/rings/poly/
dense_poly.rs

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