feanor_math/rings/poly/
dense_poly.rs

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