feanor_math/reduce_lift/poly_eval.rs
1use std::fmt::Debug;
2
3use crate::algorithms::linsolve::LinSolveRing;
4use crate::algorithms::resultant::ComputeResultantRing;
5use crate::divisibility::{DivisibilityRing, Domain};
6use crate::field::*;
7use crate::homomorphism::*;
8use crate::pid::PrincipalIdealRing;
9use crate::ring::*;
10use crate::rings::finite::FiniteRing;
11use crate::specialization::FiniteRingSpecializable;
12
13/// Trait for rings that can be temporarily replaced by an extension when we need more points,
14/// e.g. for interpolation.
15///
16/// Note that a trivial implementation is possible for every ring of characteristic 0, since
17/// these already have infinitely many points whose pairwise differences are non-zero-divisors.
18/// Such an implementation can be added to new types using the macro
19/// [`impl_interpolation_base_ring_char_zero!`].
20#[stability::unstable(feature = "enable")]
21pub trait InterpolationBaseRing: DivisibilityRing {
22 /// The type of the extension ring we can switch to to get more points.
23 ///
24 /// For the reason why there are so many quite specific trait bounds here:
25 /// See the doc of [`EvalPolyLocallyRing::LocalRingBase`].
26 type ExtendedRingBase<'a>: ?Sized + PrincipalIdealRing + Domain + LinSolveRing + ComputeResultantRing + Debug
27 where
28 Self: 'a;
29
30 type ExtendedRing<'a>: RingStore<Type = Self::ExtendedRingBase<'a>> + Clone
31 where
32 Self: 'a;
33
34 fn in_base<'a, S>(&self, ext_ring: S, el: El<S>) -> Option<Self::Element>
35 where
36 Self: 'a,
37 S: RingStore<Type = Self::ExtendedRingBase<'a>>;
38
39 fn in_extension<'a, S>(&self, ext_ring: S, el: Self::Element) -> El<S>
40 where
41 Self: 'a,
42 S: RingStore<Type = Self::ExtendedRingBase<'a>>;
43
44 /// Returns `count` points such that the difference between any two of them
45 /// is a non-zero-divisor.
46 ///
47 /// Any two calls must give elements in the same order.
48 fn interpolation_points<'a>(&'a self, count: usize) -> (Self::ExtendedRing<'a>, Vec<El<Self::ExtendedRing<'a>>>);
49}
50
51/// [`RingStore`] for [`InterpolationBaseRing`].
52#[stability::unstable(feature = "enable")]
53pub trait InterpolationBaseRingStore: RingStore
54where
55 Self::Type: InterpolationBaseRing,
56{
57}
58
59impl<R> InterpolationBaseRingStore for R
60where
61 R: RingStore,
62 R::Type: InterpolationBaseRing,
63{
64}
65
66/// The inclusion map `R -> S` for a ring `R` and one of its extensions `S`
67/// as given by [`InterpolationBaseRing`].
68#[stability::unstable(feature = "enable")]
69pub struct ToExtRingMap<'a, R>
70where
71 R: ?Sized + InterpolationBaseRing,
72{
73 ring: RingRef<'a, R>,
74 ext_ring: R::ExtendedRing<'a>,
75}
76
77impl<'a, R> ToExtRingMap<'a, R>
78where
79 R: ?Sized + InterpolationBaseRing,
80{
81 #[stability::unstable(feature = "enable")]
82 pub fn for_interpolation(ring: &'a R, point_count: usize) -> (Self, Vec<El<R::ExtendedRing<'a>>>) {
83 let (ext_ring, points) = ring.interpolation_points(point_count);
84 return (
85 Self {
86 ring: RingRef::new(ring),
87 ext_ring,
88 },
89 points,
90 );
91 }
92
93 #[stability::unstable(feature = "enable")]
94 pub fn as_base_ring_el(&self, el: El<R::ExtendedRing<'a>>) -> R::Element {
95 self.ring.get_ring().in_base(&self.ext_ring, el).unwrap()
96 }
97}
98
99impl<'a, R> Homomorphism<R, R::ExtendedRingBase<'a>> for ToExtRingMap<'a, R>
100where
101 R: ?Sized + InterpolationBaseRing,
102{
103 type CodomainStore = R::ExtendedRing<'a>;
104 type DomainStore = RingRef<'a, R>;
105
106 fn codomain(&self) -> &Self::CodomainStore { &self.ext_ring }
107
108 fn domain(&self) -> &Self::DomainStore { &self.ring }
109
110 fn map(&self, x: <R as RingBase>::Element) -> <R::ExtendedRingBase<'a> as RingBase>::Element {
111 self.ring.get_ring().in_extension(&self.ext_ring, x)
112 }
113}
114
115/// Trait for rings that support performing computations locally.
116///
117/// Note that here (and in `feanor-math` generally), the term "local" is used to refer to algorithms
118/// that work modulo prime ideals (or their powers), which is different from the mathematical
119/// concept of localization.
120///
121/// More concretely, a ring `R` implementing this trait should be endowed with a "pseudo norm"
122/// ```text
123/// |.|: R -> [0, ∞)
124/// ```
125/// i.e. a symmetric, sub-additive, sub-multiplicative map.
126/// Furthermore, for any bound `b`, the ring should be able to provide prime ideals
127/// `p1, ..., pk` together with the rings `Ri = R / pi`, such that the restricted
128/// reduction map
129/// ```text
130/// { x in R | |x| <= b } -> R1 x ... x Rk
131/// ```
132/// is injective.
133/// This means that a computation can be performed in the simpler ring `R1 x ... x Rk`,
134/// and - assuming the result is of pseudo-norm `<= b`, mapped back to `R`.
135///
136/// The standard use case is the evaluation of a multivariate polynomial `f(X1, ..., Xm)`
137/// over this ring. The trait is designed to enable the following approach:
138/// - Given ring elements `a1, ..., am`, compute an upper bound `B` on `|f(a1, ..., am)|`. The
139/// values `|ai|` are given by [`EvalPolyLocallyRing::pseudo_norm()`].
140/// - Get a sufficient number of prime ideals, using [`EvalPolyLocallyRing::local_computation()`]
141/// - Compute `f(a1 mod pi, ..., am mod pi) mod pi` for each prime `pi` within the ring given by
142/// [`EvalPolyLocallyRing::local_ring_at()`]. The reductions `ai mod pj` are given by
143/// [`EvalPolyLocallyRing::reduce()`].
144/// - Recombine the results to an element of `R` by using [`EvalPolyLocallyRing::lift_combine()`].
145///
146/// # Relationship with [`crate::reduce_lift::poly_factor_gcd::PolyGCDLocallyDomain`]
147///
148/// There are generally two ways of computing something via a reduce-modulo-primes-then-lift
149/// approach. Either one can take many different prime ideals, or one can take a large power
150/// of a single/a small amount of prime ideals.
151///
152/// This trait is for the former approach, which is especially suitable when the computation to
153/// perform can be written as a polynomial evaluation. In particular, this applicable to
154/// determinants, resultant, and (with some caveats) solving linear systems.
155///
156/// On the other hand, when factoring polynomials or computing their gcds, it is common to instead
157/// rely on Hensel lifting to compute the result modulo a large power of a single prime, or very
158/// few primes. This approach is formalized by
159/// [`crate::reduce_lift::poly_factor_gcd::PolyGCDLocallyDomain`].
160///
161/// # Type-level recursion in feanor-math
162///
163/// [`EvalPolyLocallyRing`] and [`PolyGCDLocallyDomain`] are the two traits in feanor-math which
164/// use type-level recursion with blanket implementations. The idea is simple: If our ring is an
165/// [`EvalPolyLocallyRing`], whose quotients are again [`EvalPolyLocallyRing`], whose quotients are
166/// again [`EvalPolyLocallyRing`] and so on, ending with a finite field. Then, for all kinds of
167/// operations which are actually just polynomial evaluations (resultants, determinants,
168/// unique-solution linear systems, ...) we already have all the information for an efficient
169/// algorithm. However, providing this through blanket implementations is not that simple. We now
170/// explain how it is done in feanor-math.
171///
172/// The proper solution:
173/// ```compile_fail,E0391
174/// #![feature(min_specialization)]
175/// // Some special property, in practice this might be LinSolveRing, ComputeResultantRing, FactorPolyRing, ...
176/// pub trait SpecialProperty {
177///
178/// fn foo<'a>(&'a self);
179/// }
180///
181/// impl<T> SpecialProperty for T
182/// where T: Recursive,
183/// for<'a> T::PrevCase<'a>: SpecialProperty
184/// {
185/// default fn foo<'a>(&'a self) {
186/// println!("recursion");
187/// <T::PrevCase<'a> as SpecialProperty>::foo(&self.map_down());
188/// }
189/// }
190///
191/// // Newtype to allow `&'a T: SpecialProperty` without conflicting with above blanket impl
192/// pub struct RefWrapper<'a, T>(&'a T);
193///
194/// impl<'a, T: Recursive> SpecialProperty for RefWrapper<'a, T> {
195///
196/// fn foo<'b>(&'b self) {
197/// self.0.foo()
198/// }
199/// }
200///
201/// // The main recursion type
202/// pub trait Recursive {
203///
204/// type PrevCase<'a> where Self: 'a;
205///
206/// fn map_down<'a>(&'a self) -> Self::PrevCase<'a>;
207/// }
208///
209/// #[derive(Clone, Copy)]
210/// pub struct BaseCase;
211///
212/// impl Recursive for BaseCase {
213///
214/// type PrevCase<'a> = Self where Self: 'a;
215///
216/// fn map_down<'a>(&'a self) -> Self::PrevCase<'a> { *self }
217/// }
218///
219/// impl SpecialProperty for BaseCase {
220///
221/// fn foo<'a>(&'a self) {
222/// println!("BaseCaseSpecial")
223/// }
224/// }
225///
226/// pub struct RecursiveCase<T: Recursive>(T);
227///
228/// impl<T: Recursive> Recursive for RecursiveCase<T> {
229///
230/// type PrevCase<'a> = RefWrapper<'a, T> where Self: 'a;
231///
232/// fn map_down<'a>(&'a self) -> Self::PrevCase<'a> {
233/// RefWrapper(&self.0)
234/// }
235/// }
236///
237/// fn run_type_recursion<'b, T>(t: &'b T)
238/// where T: 'b + Recursive,
239/// for<'a> T::PrevCase<'a>: SpecialProperty
240/// {
241/// t.foo()
242/// }
243///
244/// run_type_recursion(&BaseCase);
245/// run_type_recursion(&RecursiveCase(BaseCase));
246/// ```
247/// Unfortunately, this currently fails with an error
248/// ```text
249/// error[E0391]: cycle detected when computing whether impls specialize one another
250/// --> src/main.rs:9:1
251/// |
252/// 9 | / impl<T> SpecialProperty for T
253/// 10 | | where T: Recursive,
254/// 11 | | for<'a> T::PrevCase<'a>: SpecialProperty
255/// | |________________________________________________^
256/// |
257/// = note: ...which requires evaluating trait selection obligation `BaseCase: SpecialProperty`...
258/// = note: ...which again requires computing whether impls specialize one another, completing the cycle
259/// ```
260/// The only solution I found is to put the constraint by `SpecialProperty` directly into
261/// the declaration `type PrevCase<'a>: SpecialProperty where Self: 'a`. This works, but
262/// unfortunately makes things much less generic than we would want to. In particular,
263/// If we have a `NonSpecialBaseCase` in addition to `BaseCase`, and we want it to not
264/// implement `SpecialProperty`, then we cannot use `RecursiveCase` as well. On the other hand,
265/// if the compiler would accept the first variant, this would work out fine - the only effect
266/// of `NonSpecialBaseCase: !SpecialProperty` would then be that also
267/// `RecursiveCase<NonSpecialBaseCase>: !SpecialProperty`, which of course makes sense.
268///
269/// However, for now we stay with this workaround, which then looks as follows:
270/// ```
271/// #![feature(min_specialization)]
272/// // Some special property, in practice this might be LinSolveRing, ComputeResultantRing, FactorPolyRing, ...
273/// pub trait SpecialProperty {
274///
275/// fn foo<'a>(&'a self);
276/// }
277///
278/// impl<T> SpecialProperty for T
279/// where T: Recursive
280/// {
281/// default fn foo<'a>(&'a self) {
282/// println!("recursion");
283/// <T::PrevCase<'a> as SpecialProperty>::foo(&self.map_down());
284/// }
285/// }
286///
287/// // Newtype to allow `&'a T: SpecialProperty` without conflicting with above blanket impl
288/// pub struct RefWrapper<'a, T: SpecialProperty>(&'a T);
289///
290/// impl<'a, T: Recursive> SpecialProperty for RefWrapper<'a, T> {
291///
292/// fn foo<'b>(&'b self) {
293/// self.0.foo()
294/// }
295/// }
296///
297/// // The main recursion type
298/// pub trait Recursive {
299///
300/// type PrevCase<'a>: SpecialProperty where Self: 'a;
301///
302/// fn map_down<'a>(&'a self) -> Self::PrevCase<'a>;
303/// }
304///
305/// #[derive(Clone, Copy)]
306/// pub struct BaseCase;
307///
308/// impl Recursive for BaseCase {
309///
310/// type PrevCase<'a> = Self where Self: 'a;
311///
312/// fn map_down<'a>(&'a self) -> Self::PrevCase<'a> { *self }
313/// }
314///
315/// impl SpecialProperty for BaseCase {
316///
317/// fn foo<'a>(&'a self) {
318/// println!("BaseCaseSpecial")
319/// }
320/// }
321///
322/// pub struct RecursiveCase<T: Recursive + SpecialProperty>(T);
323///
324/// impl<T: Recursive> Recursive for RecursiveCase<T> {
325///
326/// type PrevCase<'a> = RefWrapper<'a, T> where Self: 'a;
327///
328/// fn map_down<'a>(&'a self) -> Self::PrevCase<'a> {
329/// RefWrapper(&self.0)
330/// }
331/// }
332///
333/// fn run_type_recursion<'b, T>(t: &'b T)
334/// where T: 'b + Recursive
335/// {
336/// t.foo()
337/// }
338///
339/// run_type_recursion(&BaseCase);
340/// run_type_recursion(&RecursiveCase(BaseCase));
341/// ```
342///
343/// Another advantage is that every function
344#[stability::unstable(feature = "enable")]
345pub trait EvalPolyLocallyRing: RingBase + FiniteRingSpecializable {
346 /// The type of the ring we get once quotienting by a prime ideal.
347 ///
348 /// The proper solution would be to just require `LocalRingBase<'ring>: ?Sized + RingBase`.
349 /// Algorithms which require this type to provide additional functionality (like being
350 /// a [`ComputeResultantRing`]) should then add a constraint
351 /// ```ignore
352 /// for<'ring> R::LocalRingBase<'ring>: ComputeResultantRing
353 /// ```
354 /// However, this causes problems, more concretely an error
355 /// "cycle detected when computing whether impls specialize one another".
356 /// More on that in the trait-level doc.
357 ///
358 /// Hence, we have to already bound `LocalRingBase` by all the traits that are reasonably
359 /// required by some algorithms. This clearly makes it a lot less generic than it should be,
360 /// but so far it worked out without too much trouble.
361 type LocalRingBase<'ring>: ?Sized + PrincipalIdealRing + Domain + LinSolveRing + ComputeResultantRing + Debug
362 where
363 Self: 'ring;
364
365 type LocalRing<'ring>: RingStore<Type = Self::LocalRingBase<'ring>>
366 where
367 Self: 'ring;
368
369 /// A collection of prime ideals of the ring, and additionally any data required to reconstruct
370 /// a small ring element from its projections onto each prime ideal.
371 type LocalComputationData<'ring>
372 where
373 Self: 'ring;
374
375 /// Computes (an upper bound of) the natural logarithm of the pseudo norm of a ring element.
376 ///
377 /// The pseudo norm should be
378 /// - symmetric, i.e. `|-x| = |x|`,
379 /// - sub-additive, i.e. `|x + y| <= |x| + |y|`
380 /// - sub-multiplicative, i.e. `|xy| <= |x| |y|`
381 ///
382 /// and this function should give `ln|x|`
383 fn ln_pseudo_norm(&self, el: &Self::Element) -> f64;
384
385 /// Sets up the context for a new polynomial evaluation, whose output
386 /// should have pseudo norm less than the given bound.
387 fn local_computation<'ring>(&'ring self, ln_pseudo_norm_bound: f64) -> Self::LocalComputationData<'ring>;
388
389 /// Returns the number `k` of local rings that are required
390 /// to get the correct result of the given computation.
391 fn local_ring_count<'ring>(&self, computation: &Self::LocalComputationData<'ring>) -> usize
392 where
393 Self: 'ring;
394
395 /// Returns the `i`-th local ring belonging to the given computation.
396 fn local_ring_at<'ring>(&self, computation: &Self::LocalComputationData<'ring>, i: usize) -> Self::LocalRing<'ring>
397 where
398 Self: 'ring;
399
400 /// Computes the map `R -> R1 x ... x Rk`, i.e. maps the given element into each of
401 /// the local rings.
402 fn reduce<'ring>(
403 &self,
404 computation: &Self::LocalComputationData<'ring>,
405 el: &Self::Element,
406 ) -> Vec<<Self::LocalRingBase<'ring> as RingBase>::Element>
407 where
408 Self: 'ring;
409
410 /// Computes a preimage under the map `R -> R1 x ... x Rk`, i.e. a ring element `x` that reduces
411 /// to each of the given local rings under the map [`EvalPolyLocallyRing::reduce()`].
412 ///
413 /// The result should have pseudo-norm bounded by the bound given when the computation
414 /// was initialized, via [`EvalPolyLocallyRing::local_computation()`].
415 fn lift_combine<'ring>(
416 &self,
417 computation: &Self::LocalComputationData<'ring>,
418 el: &[<Self::LocalRingBase<'ring> as RingBase>::Element],
419 ) -> Self::Element
420 where
421 Self: 'ring;
422}
423
424impl<R> EvalPolyLocallyRing for R
425where
426 R: ?Sized + FiniteRing + Field + Debug + SelfIso,
427{
428 type LocalComputationData<'ring>
429 = RingRef<'ring, Self>
430 where
431 Self: 'ring;
432
433 type LocalRing<'ring>
434 = RingRef<'ring, Self>
435 where
436 Self: 'ring;
437
438 type LocalRingBase<'ring>
439 = Self
440 where
441 Self: 'ring;
442
443 fn ln_pseudo_norm(&self, _el: &Self::Element) -> f64 { 0.0 }
444
445 fn local_computation<'ring>(&'ring self, _ln_pseudo_norm_bound: f64) -> Self::LocalComputationData<'ring> {
446 RingRef::new(self)
447 }
448
449 fn local_ring_at<'ring>(&self, computation: &Self::LocalComputationData<'ring>, _i: usize) -> Self::LocalRing<'ring>
450 where
451 Self: 'ring,
452 {
453 *computation
454 }
455
456 fn local_ring_count<'ring>(&self, _computation: &Self::LocalComputationData<'ring>) -> usize
457 where
458 Self: 'ring,
459 {
460 1
461 }
462
463 fn reduce<'ring>(
464 &self,
465 _computation: &Self::LocalComputationData<'ring>,
466 el: &Self::Element,
467 ) -> Vec<<Self::LocalRingBase<'ring> as RingBase>::Element>
468 where
469 Self: 'ring,
470 {
471 vec![self.clone_el(el)]
472 }
473
474 fn lift_combine<'ring>(
475 &self,
476 _computation: &Self::LocalComputationData<'ring>,
477 el: &[<Self::LocalRingBase<'ring> as RingBase>::Element],
478 ) -> Self::Element
479 where
480 Self: 'ring,
481 {
482 assert_eq!(1, el.len());
483 return self.clone_el(&el[0]);
484 }
485}
486
487/// The map `R -> R/p` for a ring `R` and one of its local quotients `R/p` as
488/// given by [`EvalPolyLocallyRing`].
489#[stability::unstable(feature = "enable")]
490pub struct EvaluatePolyLocallyReductionMap<'ring, 'data, R>
491where
492 R: 'ring + ?Sized + EvalPolyLocallyRing,
493 'ring: 'data,
494{
495 ring: RingRef<'data, R>,
496 data: &'data R::LocalComputationData<'ring>,
497 local_ring: R::LocalRing<'ring>,
498 index: usize,
499}
500
501impl<'ring, 'data, R> EvaluatePolyLocallyReductionMap<'ring, 'data, R>
502where
503 R: 'ring + ?Sized + EvalPolyLocallyRing,
504 'ring: 'data,
505{
506 #[stability::unstable(feature = "enable")]
507 pub fn new(ring: &'data R, data: &'data R::LocalComputationData<'ring>, index: usize) -> Self {
508 Self {
509 ring: RingRef::new(ring),
510 data,
511 local_ring: ring.local_ring_at(data, index),
512 index,
513 }
514 }
515}
516
517impl<'ring, 'data, R> Homomorphism<R, R::LocalRingBase<'ring>> for EvaluatePolyLocallyReductionMap<'ring, 'data, R>
518where
519 R: 'ring + ?Sized + EvalPolyLocallyRing,
520 'ring: 'data,
521{
522 type CodomainStore = R::LocalRing<'ring>;
523 type DomainStore = RingRef<'data, R>;
524
525 fn codomain(&self) -> &Self::CodomainStore { &self.local_ring }
526
527 fn domain(&self) -> &Self::DomainStore { &self.ring }
528
529 fn map(&self, x: <R as RingBase>::Element) -> <R::LocalRingBase<'ring> as RingBase>::Element {
530 let ring_ref: &'data R = self.ring.into();
531 let mut reductions: Vec<<R::LocalRingBase<'ring> as RingBase>::Element> = ring_ref.reduce(self.data, &x);
532 return reductions.swap_remove(self.index);
533 }
534}
535
536/// Generates an implementation of [`crate::reduce_lift::poly_eval::InterpolationBaseRing`]
537/// for a ring of characteristic zero. Not that in this case, the only sensible implementation
538/// is trivial, since the ring itself has enough elements for any interpolation task.
539///
540/// # Example
541/// ```rust
542/// # use std::fmt::Debug;
543/// # use feanor_math::ring::*;
544/// # use feanor_math::delegate::*;
545/// # use feanor_math::reduce_lift::poly_eval::*;
546/// # use feanor_math::primitive_int::*;
547/// # use feanor_math::algorithms::resultant::*;
548/// # use feanor_math::divisibility::*;
549/// # use feanor_math::homomorphism::Homomorphism;
550/// # use feanor_math::rings::poly::*;
551/// # use feanor_math::rings::poly::dense_poly::DensePolyRing;
552/// # use feanor_math::pid::*;
553/// # use feanor_math::impl_interpolation_base_ring_char_zero;
554/// // we wrap a `RingBase` here for simplicity, but in practice a wrapper should
555/// // always store a `RingStore` instead
556/// #[derive(PartialEq, Debug)]
557/// struct MyRingWrapper<R: RingBase>(R);
558/// impl<R: RingBase> DelegateRing for MyRingWrapper<R> {
559/// type Element = R::Element;
560/// type Base = R;
561/// fn get_delegate(&self) -> &Self::Base { &self.0 }
562/// fn delegate(&self, x: R::Element) -> R::Element { x }
563/// fn rev_delegate(&self, x: R::Element) -> R::Element { x }
564/// fn delegate_ref<'a>(&self, x: &'a R::Element) -> &'a R::Element { x }
565/// fn delegate_mut<'a>(&self, x: &'a mut R::Element) -> &'a mut R::Element { x }
566/// }
567/// impl<R: RingBase> DelegateRingImplEuclideanRing for MyRingWrapper<R> {}
568/// impl<R: RingBase> DelegateRingImplFiniteRing for MyRingWrapper<R> {}
569/// impl<R: Domain> Domain for MyRingWrapper<R> {}
570/// impl_interpolation_base_ring_char_zero!{ <{ R }> InterpolationBaseRing for MyRingWrapper<R> where R: PrincipalIdealRing + Domain + ComputeResultantRing + Debug }
571///
572/// // now we can use `InterpolationBaseRing`-functionality
573/// let ring = MyRingWrapper(StaticRing::<i64>::RING.into());
574/// let (embedding, points) = ToExtRingMap::for_interpolation(&ring, 3);
575/// assert_eq!(0, points[0]);
576/// assert_eq!(1, points[1]);
577/// assert_eq!(2, points[2]);
578///
579/// // There is a problem here, described in EvalPolyLocallyRing::LocalRingBase.
580/// // Short version: we need to manually impl ComputeResultantRing
581/// impl<R: ComputeResultantRing> ComputeResultantRing for MyRingWrapper<R> {
582/// fn resultant<P>(poly_ring: P, f: El<P>, g: El<P>) -> Self::Element
583/// where P: RingStore + Copy,
584/// P::Type: PolyRing,
585/// <P::Type as RingExtension>::BaseRing: RingStore<Type = Self>
586/// {
587/// let new_poly_ring = DensePolyRing::new(RingRef::new(poly_ring.base_ring().get_ring().get_delegate()), "X");
588/// let hom = new_poly_ring.lifted_hom(&poly_ring, UnwrapHom::new(poly_ring.base_ring(), new_poly_ring.base_ring()));
589/// poly_ring.base_ring().get_ring().rev_delegate(R::resultant(&new_poly_ring, hom.map(f), hom.map(g)))
590/// }
591/// }
592/// ```
593#[macro_export]
594macro_rules! impl_interpolation_base_ring_char_zero {
595 (<{$($gen_args:tt)*}> InterpolationBaseRing for $self_type:ty where $($constraints:tt)*) => {
596 impl<$($gen_args)*> $crate::reduce_lift::poly_eval::InterpolationBaseRing for $self_type where $($constraints)* {
597
598 type ExtendedRing<'a> = RingRef<'a, Self>
599 where Self: 'a;
600
601 type ExtendedRingBase<'a> = Self
602 where Self: 'a;
603
604 fn in_base<'a, S>(&self, _ext_ring: S, el: El<S>) -> Option<Self::Element>
605 where Self: 'a, S: RingStore<Type = Self::ExtendedRingBase<'a>>
606 {
607 Some(el)
608 }
609
610 fn in_extension<'a, S>(&self, _ext_ring: S, el: Self::Element) -> El<S>
611 where Self: 'a, S: RingStore<Type = Self::ExtendedRingBase<'a>>
612 {
613 el
614 }
615
616 fn interpolation_points<'a>(&'a self, count: usize) -> (Self::ExtendedRing<'a>, Vec<El<Self::ExtendedRing<'a>>>) {
617 let ZZbig = $crate::integer::BigIntRing::RING;
618 assert!(ZZbig.is_zero(&self.characteristic(&ZZbig).unwrap()));
619 let ring = $crate::ring::RingRef::new(self);
620 (ring, (0..count).map(|n| <_ as $crate::homomorphism::Homomorphism<_, _>>::map(&ring.int_hom(), n.try_into().unwrap())).collect())
621 }
622 }
623 };
624 (InterpolationBaseRing for $self_type:ty) => {
625 impl_interpolation_base_ring_char_zero!{ <{}> InterpolationBaseRing for $self_type where }
626 }
627}
628
629/// Implements [`EvalPolyLocallyRing`] for an integer ring.
630///
631/// This uses a default implementation, where the prime ideals are given by the largest prime
632/// numbers such that the corresponding residue field can be implemented using
633/// [`crate::rings::zn::zn_64::Zn`]. This should be suitable in almost all scenarios.
634///
635/// The syntax is the same as for other impl-macros, see e.g.
636/// [`crate::impl_interpolation_base_ring_char_zero!`].
637#[macro_export]
638macro_rules! impl_eval_poly_locally_for_ZZ {
639 (EvalPolyLocallyRing for $int_ring_type:ty) => {
640 impl_eval_poly_locally_for_ZZ!{ <{}> EvalPolyLocallyRing for $int_ring_type where }
641 };
642 (<{$($gen_args:tt)*}> EvalPolyLocallyRing for $int_ring_type:ty where $($constraints:tt)*) => {
643
644 impl<$($gen_args)*> $crate::reduce_lift::poly_eval::EvalPolyLocallyRing for $int_ring_type
645 where $($constraints)*
646 {
647 type LocalComputationData<'ring> = $crate::rings::zn::zn_rns::Zn<$crate::rings::field::AsField<$crate::rings::zn::zn_64::Zn>, RingRef<'ring, Self>>
648 where Self: 'ring;
649
650 type LocalRing<'ring> = $crate::rings::field::AsField<$crate::rings::zn::zn_64::Zn>
651 where Self: 'ring;
652
653 type LocalRingBase<'ring> = $crate::rings::field::AsFieldBase<$crate::rings::zn::zn_64::Zn>
654 where Self: 'ring;
655
656 fn ln_pseudo_norm(&self, el: &Self::Element) -> f64 {
657 RingRef::new(self).abs_log2_ceil(el).unwrap_or(0) as f64 * 2f64.ln()
658 }
659
660 fn local_computation<'ring>(&'ring self, ln_pseudo_norm_bound: f64) -> Self::LocalComputationData<'ring> {
661 let mut primes = Vec::new();
662 let mut ln_current = 0.;
663 let mut prime_it = //$crate::reduce_lift::primelist::LARGE_PRIMES.iter().copied().chain
664 ((0..).scan((1 << 62) / 9, |current, _| {
665 *current = $crate::algorithms::miller_rabin::prev_prime(StaticRing::<i64>::RING, *current).unwrap();
666 if *current < (1 << 32) {
667 panic!("not enough primes");
668 }
669 return Some($crate::rings::zn::zn_64::Zn::new(*current as u64));
670 }));
671 while ln_current < ln_pseudo_norm_bound + 1. {
672 let Fp = prime_it.next().unwrap();
673 ln_current += (*$crate::rings::zn::ZnRingStore::modulus(&Fp) as f64).ln();
674 primes.push(Fp);
675 }
676 return $crate::rings::zn::zn_rns::Zn::new(
677 primes.into_iter().map(|Fp| $crate::rings::field::AsField::from($crate::rings::field::AsFieldBase::promise_is_perfect_field(Fp))).collect(),
678 RingRef::new(self)
679 );
680 }
681
682 fn local_ring_at<'ring>(&self, computation: &Self::LocalComputationData<'ring>, i: usize) -> Self::LocalRing<'ring>
683 where Self: 'ring
684 {
685 <_ as $crate::seq::VectorView<_>>::at(computation, i).clone()
686 }
687
688 fn local_ring_count<'ring>(&self, computation: &Self::LocalComputationData<'ring>) -> usize
689 where Self: 'ring
690 {
691 <_ as $crate::seq::VectorView<_>>::len(computation)
692 }
693
694 fn reduce<'ring>(&self, computation: &Self::LocalComputationData<'ring>, el: &Self::Element) -> Vec<<Self::LocalRingBase<'ring> as RingBase>::Element>
695 where Self: 'ring
696 {
697 <_ as $crate::seq::VectorView<_>>::as_iter(&computation.get_congruence(&computation.coerce(RingValue::from_ref(self), self.clone_el(el)))).map(|x| *x).collect()
698 }
699
700 fn lift_combine<'ring>(&self, computation: &Self::LocalComputationData<'ring>, el: &[<Self::LocalRingBase<'ring> as RingBase>::Element]) -> Self::Element
701 where Self: 'ring
702 {
703 <_ as $crate::rings::zn::ZnRingStore>::smallest_lift(computation, computation.from_congruence(el.iter().copied()))
704 }
705 }
706 };
707}