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
28pub 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
106pub 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
140pub 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 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 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
315pub 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
484pub 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 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(¤t));
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(), ¤t);
1129 });
1130}