feanor_math/
homomorphism.rs

1use std::fmt::Debug;
2use std::marker::PhantomData;
3
4use crate::ring::*;
5use crate::primitive_int::{StaticRingBase, StaticRing};
6
7///
8/// The user-facing trait for ring homomorphisms, i.e. maps `R -> S`
9/// between rings that respect the ring structure. Since all considered
10/// rings are unital, ring homomorphisms also must be unital.
11/// 
12/// Objects are expected to know their domain and codomain rings and
13/// can thus make sense without an implicit ambient ring (unlike e.g.
14/// ring elements).
15/// 
16/// Ring homomorphisms are usually obtained by a corresponding method
17/// on [`RingStore`], and their functionality is provided by underlying
18/// functions of [`RingBase`]. Main examples include
19///  - Every ring `R` has a homomorphism `Z -> R`. The corresponding
20///    [`Homomorphism`]-object is obtained with [`RingStore::int_hom()`],
21///    and the functionality provided by [`RingBase::from_int()`].
22///  - [`RingExtension`]s have give a (injective) homomorphism `R -> S`
23///    which can be obtained by [`RingExtensionStore::inclusion()`].
24///    The functionality is provided by functions on [`RingExtension`],
25///    like [`RingExtension::from()`].
26///  - Other "natural" homomorphisms can be obtained via [`RingStore::can_hom()`].
27///    This requires the underlying [`RingBase`]s to implement [`CanHomFrom`],
28///    and the functions of that trait define the homomorphism.
29///  
30pub trait Homomorphism<Domain: ?Sized, Codomain: ?Sized> 
31    where Domain: RingBase, Codomain: RingBase
32{
33    type DomainStore: RingStore<Type = Domain>;
34    type CodomainStore: RingStore<Type = Codomain>;
35
36    fn domain<'a>(&'a self) -> &'a Self::DomainStore;
37    fn codomain<'a>(&'a self) -> &'a Self::CodomainStore;
38
39    fn map(&self, x: Domain::Element) -> Codomain::Element;
40
41    fn map_ref(&self, x: &Domain::Element) -> Codomain::Element {
42        self.map(self.domain().clone_el(x))
43    }
44
45    fn mul_assign_map(&self, lhs: &mut Codomain::Element, rhs: Domain::Element) {
46        self.codomain().mul_assign(lhs, self.map(rhs))
47    }
48
49    fn mul_assign_ref_map(&self, lhs: &mut Codomain::Element, rhs: &Domain::Element) {
50        self.codomain().mul_assign(lhs, self.map_ref(rhs))
51    }
52
53    fn mul_map(&self, mut lhs: Codomain::Element, rhs: Domain::Element) -> Codomain::Element {
54        self.mul_assign_map(&mut lhs, rhs);
55        lhs
56    }
57
58    fn mul_ref_fst_map(&self, lhs: &Codomain::Element, rhs: Domain::Element) -> Codomain::Element {
59        self.mul_map(self.codomain().clone_el(lhs), rhs)
60    }
61
62    fn mul_ref_snd_map(&self, mut lhs: Codomain::Element, rhs: &Domain::Element) -> Codomain::Element {
63        self.mul_assign_ref_map(&mut lhs, rhs);
64        lhs
65    }
66
67    fn mul_ref_map(&self, lhs: &Codomain::Element, rhs: &Domain::Element) -> Codomain::Element {
68        self.mul_ref_snd_map(self.codomain().clone_el(lhs), rhs)
69    }
70
71    fn compose<F, PrevDomain: ?Sized + RingBase>(self, prev: F) -> ComposedHom<PrevDomain, Domain, Codomain, F, Self>
72        where Self: Sized, F: Homomorphism<PrevDomain, Domain>
73    {
74        assert!(prev.codomain().get_ring() == self.domain().get_ring());
75        ComposedHom { f: prev, g: self, domain: PhantomData, intermediate: PhantomData, codomain: PhantomData }
76    }
77}
78
79///
80/// Trait for rings R that have a canonical homomorphism `S -> R`.
81/// A ring homomorphism is expected to be unital. 
82/// 
83/// This trait is considered implementor-facing, so it is designed to easily 
84/// implement natural maps between rings. When using homomorphisms, consider
85/// using instead [`CanHom`], as it does not require weird syntax like
86/// `R.get_ring().map_in(S.get_ring(), x, &hom)`.
87/// 
88/// **Warning** Because of type-system limitations (see below), this trait
89/// is not implemented in all cases where it makes sense, in particular when
90/// type parameters are involved. Thus, you should always consider this trait
91/// to be for convenience only. A truly generic algorithm should, if possible,
92/// not constrain input ring types using `CanHomFrom`, but instead take an 
93/// additional object of generic type bounded by [`Homomorphism`] that provides
94/// the required homomorphism.
95/// 
96/// # Exact requirements
97/// 
98/// Which homomorphisms are considered canonical is up to implementors,
99/// as long as any diagram of canonical homomorphisms commutes. In
100/// other words, if there are rings `R, S` and "intermediate rings"
101/// `R1, ..., Rn` resp. `R1', ..., Rm'` such that there are canonical
102/// homomorphisms
103/// ```text
104///   S -> R1 -> R2 -> ... -> Rn -> R
105/// ```
106/// and
107/// ```text
108///   S -> R1' -> R2' -> ... -> Rm' -> R
109/// ```
110/// then both homomorphism chains should yield same results on same
111/// inputs.
112/// 
113/// If the canonical homomorphism might be an isomorphism, consider also
114/// implementing [`CanIsoFromTo`].
115/// 
116/// # Example
117/// 
118/// Most integer rings support canonical homomorphisms between them.
119/// ```
120/// # use feanor_math::ring::*;
121/// # use feanor_math::homomorphism::*;
122/// # use feanor_math::primitive_int::*;
123/// # use feanor_math::integer::*;
124/// let R = StaticRing::<i64>::RING;
125/// let S = BigIntRing::RING;
126/// let eight = S.int_hom().map(8);
127/// // on RingBase level
128/// let hom = R.get_ring().has_canonical_hom(S.get_ring()).unwrap();
129/// assert_eq!(8, R.get_ring().map_in(S.get_ring(), S.clone_el(&eight), &hom));
130/// // on RingStore level
131/// assert_eq!(8, R.coerce(&S, S.clone_el(&eight)));
132/// // or
133/// let hom = R.can_hom(&S).unwrap();
134/// assert_eq!(8, hom.map_ref(&eight));
135/// ```
136/// 
137/// # Limitations
138/// 
139/// The rust constraints regarding conflicting impl make it, in some cases,
140/// impossible to implement all the canonical homomorphisms that we would like.
141/// This is true in particular, if the rings are highly generic, and build
142/// on base rings. In this case, it should always be preferred to implement
143/// `CanIsoFromTo` for rings that are "the same", and on the other hand not
144/// to implement classical homomorphisms, like `ZZ -> R` which exists for any
145/// ring R. In applicable cases, consider also implementing [`RingExtension`].
146/// 
147/// Because of this reason, implementing [`RingExtension`] also does not require
148/// an implementation of `CanHomFrom<Self::BaseRing>`. Hence, if you as a user
149/// miss a certain implementation of `CanHomFrom`, check whether there maybe
150/// is a corresponding implementation of [`RingExtension`], or a member function.
151/// 
152/// # More examples
153/// 
154/// ## Integer rings
155/// 
156/// All given integer rings have canonical isomorphisms between each other.
157/// ```
158/// # use feanor_math::ring::*;
159/// # use feanor_math::integer::*;
160/// # use feanor_math::primitive_int::*;
161/// # use feanor_math::integer::*;
162/// let Z_i8 = StaticRing::<i8>::RING;
163/// let Z_i32 = StaticRing::<i32>::RING;
164/// let Z_i128 = StaticRing::<i128>::RING;
165/// let Z_big = BigIntRing::RING;
166/// 
167/// assert!(Z_i8.can_iso(&Z_i8).is_some());
168/// assert!(Z_i8.can_iso(&Z_i32).is_some());
169/// assert!(Z_i8.can_iso(&Z_i128).is_some());
170/// assert!(Z_i8.can_iso(&Z_big).is_some());
171/// 
172/// assert!(Z_i32.can_iso(&Z_i8).is_some());
173/// assert!(Z_i32.can_iso(&Z_i32).is_some());
174/// assert!(Z_i32.can_iso(&Z_i128).is_some());
175/// assert!(Z_i32.can_iso(&Z_big).is_some());
176/// 
177/// assert!(Z_i128.can_iso(&Z_i8).is_some());
178/// assert!(Z_i128.can_iso(&Z_i32).is_some());
179/// assert!(Z_i128.can_iso(&Z_i128).is_some());
180/// assert!(Z_i128.can_iso(&Z_big).is_some());
181/// 
182/// assert!(Z_big.can_iso(&Z_i8).is_some());
183/// assert!(Z_big.can_iso(&Z_i32).is_some());
184/// assert!(Z_big.can_iso(&Z_i128).is_some());
185/// assert!(Z_big.can_iso(&Z_big).is_some());
186/// ```
187/// 
188/// ## Integer quotient rings `Z/nZ`
189/// 
190/// Due to conflicting implementations, only the most useful conversions
191/// are implemented for `Z/nZ`.
192/// ```
193/// # use feanor_math::ring::*;
194/// # use feanor_math::primitive_int::*;
195/// # use feanor_math::homomorphism::*;
196/// # use feanor_math::integer::*;
197/// # use feanor_math::rings::zn::*;
198/// # use feanor_math::rings::zn::zn_big;
199/// # use feanor_math::rings::zn::zn_rns;
200/// let ZZ = StaticRing::<i128>::RING;
201/// let ZZ_big = BigIntRing::RING;
202/// 
203/// let zn_big_i128 = zn_big::Zn::new(ZZ, 17 * 257);
204/// let zn_big_big = zn_big::Zn::new(ZZ_big, ZZ_big.int_hom().map(17 * 257));
205/// let Zn_std = zn_64::Zn::new(17 * 257);
206/// let Zn_rns = zn_rns::Zn::create_from_primes(vec![17, 257], ZZ_big);
207/// 
208/// assert!(zn_big_i128.can_iso(&zn_big_i128).is_some());
209/// assert!(zn_big_i128.can_iso(&zn_big_big).is_some());
210/// 
211/// assert!(zn_big_big.can_iso(&zn_big_i128).is_some());
212/// assert!(zn_big_big.can_iso(&zn_big_big).is_some());
213/// 
214/// assert!(Zn_std.can_iso(&zn_big_i128).is_some());
215/// assert!(Zn_std.can_iso(&Zn_std).is_some());
216/// 
217/// assert!(Zn_rns.can_iso(&zn_big_i128).is_some());
218/// assert!(Zn_rns.can_iso(&zn_big_big).is_some());
219/// assert!(Zn_rns.can_iso(&Zn_rns).is_some());
220/// ```
221/// Most notably, reduction homomorphisms are currently not available.
222/// You can use [`crate::rings::zn::ZnReductionMap`] instead.
223/// ```
224/// # use feanor_math::ring::*;
225/// # use feanor_math::primitive_int::*;
226/// # use feanor_math::homomorphism::*;
227/// # use feanor_math::integer::*;
228/// # use feanor_math::rings::zn::*;
229/// # use feanor_math::assert_el_eq;
230/// let Z9 = zn_64::Zn::new(9);
231/// let Z3 = zn_64::Zn::new(3);
232/// assert!(Z3.can_hom(&Z9).is_none());
233/// let mod_3 = ZnReductionMap::new(&Z9, &Z3).unwrap();
234/// assert_el_eq!(Z3, Z3.one(), mod_3.map(Z9.int_hom().map(4)));
235/// ```
236/// Additionally, there are the projections `Z -> Z/nZ`.
237/// They are all implemented, even though [`crate::rings::zn::ZnRing`] currently
238/// only requires the projection from the "associated" integer ring.
239/// ```
240/// # use feanor_math::ring::*;
241/// # use feanor_math::homomorphism::*;
242/// # use feanor_math::primitive_int::*;
243/// # use feanor_math::integer::*;
244/// # use feanor_math::rings::zn::*;
245/// let ZZ = StaticRing::<i128>::RING;
246/// let ZZ_big = BigIntRing::RING;
247/// 
248/// let zn_big_i128 = zn_big::Zn::new(ZZ, 17 * 257);
249/// let zn_big_big = zn_big::Zn::new(ZZ_big, ZZ_big.int_hom().map(17 * 257));
250/// let Zn_std = zn_64::Zn::new(17 * 257);
251/// let Zn_rns = zn_rns::Zn::create_from_primes(vec![17, 257], ZZ_big);
252/// 
253/// assert!(zn_big_i128.can_hom(&ZZ).is_some());
254/// assert!(zn_big_i128.can_hom(&ZZ_big).is_some());
255/// 
256/// assert!(zn_big_big.can_hom(&ZZ).is_some());
257/// assert!(zn_big_big.can_hom(&ZZ_big).is_some());
258/// 
259/// assert!(Zn_std.can_hom(&ZZ).is_some());
260/// assert!(Zn_std.can_hom(&ZZ_big).is_some());
261/// 
262/// assert!(Zn_rns.can_hom(&ZZ).is_some());
263/// assert!(Zn_rns.can_hom(&ZZ_big).is_some());
264/// ```
265/// 
266/// ## Polynomial Rings
267/// 
268/// For the two provided univariate polynomial ring implementations, we have the isomorphisms
269/// ```
270/// # use feanor_math::ring::*;
271/// # use feanor_math::primitive_int::*;
272/// # use feanor_math::integer::*;
273/// # use feanor_math::rings::poly::*;
274/// 
275/// let ZZ = StaticRing::<i128>::RING;
276/// let P_dense = dense_poly::DensePolyRing::new(ZZ, "X");
277/// let P_sparse = sparse_poly::SparsePolyRing::new(ZZ, "X");
278/// 
279/// assert!(P_dense.can_iso(&P_dense).is_some());
280/// assert!(P_dense.can_iso(&P_sparse).is_some());
281/// assert!(P_sparse.can_iso(&P_dense).is_some());
282/// assert!(P_sparse.can_iso(&P_sparse).is_some());
283/// ```
284/// Unfortunately, the inclusions `R -> R[X]` are not implemented as canonical homomorphisms,
285/// however provided by the functions of [`RingExtension`].
286/// 
287pub trait CanHomFrom<S>: RingBase
288    where S: RingBase + ?Sized
289{
290    type Homomorphism;
291
292    fn has_canonical_hom(&self, from: &S) -> Option<Self::Homomorphism>;
293    fn map_in(&self, from: &S, el: S::Element, hom: &Self::Homomorphism) -> Self::Element;
294
295    fn map_in_ref(&self, from: &S, el: &S::Element, hom: &Self::Homomorphism) -> Self::Element {
296        self.map_in(from, from.clone_el(el), hom)
297    }
298
299    fn mul_assign_map_in(&self, from: &S, lhs: &mut Self::Element, rhs: S::Element, hom: &Self::Homomorphism) {
300        self.mul_assign(lhs, self.map_in(from, rhs, hom));
301    }
302
303    fn mul_assign_map_in_ref(&self, from: &S, lhs: &mut Self::Element, rhs: &S::Element, hom: &Self::Homomorphism) {
304        self.mul_assign(lhs, self.map_in_ref(from, rhs, hom));
305    }
306}
307
308///
309/// Trait for rings R that have a canonical isomorphism `S -> R`.
310/// A ring homomorphism is expected to be unital.
311/// 
312/// **Warning** Because of type-system limitations (see [`CanHomFrom`]), this trait
313/// is not implemented in all cases where it makes sense, in particular when
314/// type parameters are involved. Thus, you should always consider this trait
315/// to be for convenience only. A truly generic algorithm should, if possible,
316/// not constrain input ring types using `CanHomFrom`, but instead take an 
317/// additional object of generic type bounded by [`Homomorphism`] that provides
318/// the required homomorphism.
319/// 
320/// # Exact requirements
321/// 
322/// Same as for [`CanHomFrom`], it is up to implementors to decide which
323/// isomorphisms are canonical, as long as each diagram that contains
324/// only canonical homomorphisms, canonical isomorphisms and their inverses
325/// commutes.
326/// In other words, if there are rings `R, S` and "intermediate rings"
327/// `R1, ..., Rn` resp. `R1', ..., Rm'` such that there are canonical
328/// homomorphisms `->` or isomorphisms `<~>` connecting them - e.g. like
329/// ```text
330///   S -> R1 -> R2 <~> R3 <~> R4 -> ... -> Rn -> R
331/// ```
332/// and
333/// ```text
334///   S <~> R1' -> R2' -> ... -> Rm' -> R
335/// ```
336/// then both chains should yield same results on same inputs.
337/// 
338/// Hence, it would be natural if the trait were symmetrical, i.e.
339/// for any implementation `R: CanIsoFromTo<S>` there is also an
340/// implementation `S: CanIsoFromTo<R>`. However, because of the trait
341/// impl constraints of Rust, this is unpracticable and so we only
342/// require the implementation `R: CanHomFrom<S>`.
343/// 
344pub trait CanIsoFromTo<S>: CanHomFrom<S>
345    where S: RingBase + ?Sized
346{
347    type Isomorphism;
348
349    fn has_canonical_iso(&self, from: &S) -> Option<Self::Isomorphism>;
350    fn map_out(&self, from: &S, el: Self::Element, iso: &Self::Isomorphism) -> S::Element;
351}
352
353///
354/// Basically an alias for `CanIsoFromTo<Self>`, but implemented as new
355/// trait since trait aliases are not available.
356/// 
357pub trait SelfIso: CanIsoFromTo<Self> {}
358
359impl<R: ?Sized + CanIsoFromTo<R>> SelfIso for R {}
360
361///
362/// A high-level wrapper of [`CanHomFrom::Homomorphism`] that references the
363/// domain and codomain rings, and is much easier to use.
364/// 
365/// # Example
366/// ```
367/// # use feanor_math::ring::*;
368/// # use feanor_math::homomorphism::*;
369/// # use feanor_math::homomorphism::*;
370/// # use feanor_math::primitive_int::*;
371/// let from = StaticRing::<i32>::RING;
372/// let to = StaticRing::<i64>::RING;
373/// let hom = to.can_hom(&from).unwrap();
374/// assert_eq!(7, hom.map(7));
375/// // instead of
376/// let hom = to.get_ring().has_canonical_hom(from.get_ring()).unwrap();
377/// assert_eq!(7, to.get_ring().map_in(from.get_ring(), 7, &hom));
378/// ```
379/// 
380/// # See also
381/// The "bi-directional" variant [`CanHom`], the basic interfaces [`CanHomFrom`] and
382/// [`CanIsoFromTo`] and the very simplified function [`RingStore::coerce`].
383/// 
384pub struct CanHom<R, S>
385    where R: RingStore, S: RingStore, S::Type: CanHomFrom<R::Type>
386{
387    from: R,
388    to: S,
389    data: <S::Type as CanHomFrom<R::Type>>::Homomorphism
390}
391
392impl<R, S> Debug for CanHom<R, S>
393    where R: RingStore + Debug, S: RingStore + Debug, S::Type: CanHomFrom<R::Type>
394{
395    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
396        write!(f, "CanHom({:?}, {:?})", self.from, self.to)
397    }
398}
399
400impl<R, S> CanHom<R, S>
401    where R: RingStore, S: RingStore, S::Type: CanHomFrom<R::Type>
402{
403    pub fn new(from: R, to: S) -> Result<Self, (R, S)> {
404        match to.get_ring().has_canonical_hom(from.get_ring()) {
405            Some(data) => Ok(Self::from_raw_parts(from, to, data)),
406            _ => Err((from, to))
407        }
408    }
409
410    pub fn raw_hom(&self) -> &<S::Type as CanHomFrom<R::Type>>::Homomorphism {
411        &self.data
412    }
413
414    #[stability::unstable(feature = "enable")]
415    pub fn from_raw_parts(from: R, to: S, data: <S::Type as CanHomFrom<R::Type>>::Homomorphism) -> Self {
416        Self { from, to, data }
417    }
418}
419
420impl<R, S> Clone for CanHom<R, S>
421    where R: RingStore + Clone, S: RingStore + Clone, S::Type: CanHomFrom<R::Type>
422{
423    fn clone(&self) -> Self {
424        Self::new(self.from.clone(), self.to.clone()).ok().unwrap()
425    }
426}
427
428impl<R, S> Homomorphism<R::Type, S::Type> for CanHom<R, S>
429    where R: RingStore, S: RingStore, S::Type: CanHomFrom<R::Type>
430{
431    type CodomainStore = S;
432    type DomainStore = R;
433
434    fn map(&self, el: El<R>) -> El<S> {
435        self.to.get_ring().map_in(self.from.get_ring(), el, &self.data)
436    }
437
438    fn map_ref(&self, el: &El<R>) -> El<S> {
439        self.to.get_ring().map_in_ref(self.from.get_ring(), el, &self.data)
440    }
441    
442    fn domain(&self) -> &R {
443        &self.from
444    }
445
446    fn codomain(&self) -> &S {
447        &self.to
448    }
449}
450
451///
452/// A wrapper of [`CanHomFrom::Homomorphism`] that does not own the data associated
453/// with the homomorphism. Use cases are rare, prefer to use [`CanHom`] whenever possible.
454/// 
455/// More concretely, this should only be used when you only have a reference to `<R as CanHomFrom<S>>::Homomorphism`,
456/// but cannot refactor code to wrap that object in a [`CanHom`] instead. The main situation
457/// where this occurs is when implementing [`CanHomFrom`], since a the lifetime of [`CanHom`] is
458/// bound by the lifetime of the domain and codomain rings, but `CanHomFrom::Type` does not allow
459/// this.
460/// 
461#[stability::unstable(feature = "enable")]
462pub struct CanHomRef<'a, R, S>
463    where R: RingStore, S: RingStore, S::Type: CanHomFrom<R::Type>
464{
465    from: R,
466    to: S,
467    data: &'a <S::Type as CanHomFrom<R::Type>>::Homomorphism
468}
469
470impl<'a, R, S> CanHomRef<'a, R, S>
471    where R: RingStore, S: RingStore, S::Type: CanHomFrom<R::Type>
472{
473    #[stability::unstable(feature = "enable")]
474    pub fn raw_hom(&self) -> &<S::Type as CanHomFrom<R::Type>>::Homomorphism {
475        &self.data
476    }
477
478    #[stability::unstable(feature = "enable")]
479    pub fn from_raw_parts(from: R, to: S, data: &'a <S::Type as CanHomFrom<R::Type>>::Homomorphism) -> Self {
480        Self { from, to, data }
481    }
482}
483
484impl<'a, R, S> Clone for CanHomRef<'a, R, S>
485    where R: RingStore + Clone, S: RingStore + Clone, S::Type: CanHomFrom<R::Type>
486{
487    fn clone(&self) -> Self {
488        Self::from_raw_parts(self.from.clone(), self.to.clone(), self.data)
489    }
490}
491
492impl<'a, R, S> Copy for CanHomRef<'a, R, S>
493    where R: RingStore + Copy, 
494        S: RingStore + Copy, 
495        S::Type: CanHomFrom<R::Type>,
496        El<R>: Copy,
497        El<S>: Copy
498{}
499
500impl<'a, R, S> Homomorphism<R::Type, S::Type> for CanHomRef<'a, R, S>
501    where R: RingStore, S: RingStore, S::Type: CanHomFrom<R::Type>
502{
503    type CodomainStore = S;
504    type DomainStore = R;
505
506    fn map(&self, el: El<R>) -> El<S> {
507        self.to.get_ring().map_in(self.from.get_ring(), el, &self.data)
508    }
509
510    fn map_ref(&self, el: &El<R>) -> El<S> {
511        self.to.get_ring().map_in_ref(self.from.get_ring(), el, &self.data)
512    }
513    
514    fn domain(&self) -> &R {
515        &self.from
516    }
517
518    fn codomain(&self) -> &S {
519        &self.to
520    }
521}
522
523///
524/// A wrapper around [`CanIsoFromTo::Isomorphism`] that references the domain and
525/// codomain rings, making it much easier to use. This also contains the homomorphism
526/// in the other direction, i.e. allows mapping in both directions.
527/// 
528/// # Example
529/// ```
530/// # use feanor_math::ring::*;
531/// # use feanor_math::homomorphism::*;
532/// # use feanor_math::primitive_int::*;
533/// 
534/// let from = StaticRing::<i32>::RING;
535/// let to = StaticRing::<i64>::RING;
536/// let hom = to.can_iso(&from).unwrap();
537/// assert_eq!(7, hom.map(7));
538/// // instead of
539/// let hom = to.get_ring().has_canonical_iso(from.get_ring()).unwrap();
540/// assert_eq!(7, from.get_ring().map_out(to.get_ring(), 7, &hom));
541/// ```
542/// 
543/// # See also
544/// The "one-directional" variant [`CanHom`], the basic interfaces [`CanHomFrom`] and
545/// [`CanIsoFromTo`] and the very simplified function [`RingStore::coerce()`].
546/// 
547pub struct CanIso<R, S>
548    where R: RingStore, S: RingStore, S::Type: CanIsoFromTo<R::Type>
549{
550    from: R,
551    to: S,
552    data: <S::Type as CanIsoFromTo<R::Type>>::Isomorphism
553}
554
555impl<R, S> Debug for CanIso<R, S>
556    where R: RingStore + Debug, S: RingStore + Debug, S::Type: CanIsoFromTo<R::Type>
557{
558    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
559        write!(f, "CanIso({:?}, {:?})", self.from, self.to)
560    }
561}
562
563impl<R, S> Clone for CanIso<R, S>
564    where R: RingStore + Clone, S: RingStore + Clone, S::Type: CanIsoFromTo<R::Type>
565{
566    fn clone(&self) -> Self {
567        Self::new(self.from.clone(), self.to.clone()).ok().unwrap()
568    }
569}
570
571impl<R, S> CanIso<R, S>
572    where R: RingStore, S: RingStore, S::Type: CanIsoFromTo<R::Type>
573{
574    pub fn new(from: R, to: S) -> Result<Self, (R, S)> {
575        match to.get_ring().has_canonical_iso(from.get_ring()) {
576            Some(data) => {
577                assert!(to.get_ring().has_canonical_hom(from.get_ring()).is_some());
578                Ok(Self { from, to, data })
579            },
580            _ => Err((from, to))
581        }
582    }
583
584    pub fn into_inv(self) -> CanHom<R, S> {
585        CanHom::new(self.from, self.to).unwrap_or_else(|_| unreachable!())
586    }
587
588    pub fn inv<'a>(&'a self) -> CanHom<&'a R, &'a S> {
589        CanHom::new(&self.from, &self.to).unwrap_or_else(|_| unreachable!())
590    }
591
592    pub fn raw_iso(&self) -> &<S::Type as CanIsoFromTo<R::Type>>::Isomorphism {
593        &self.data
594    }
595}
596
597impl<R, S> Homomorphism<S::Type,R::Type> for CanIso<R, S>
598    where R: RingStore, S: RingStore, S::Type: CanIsoFromTo<R::Type>
599{
600    type DomainStore = S;
601    type CodomainStore = R;
602    
603    fn map(&self, x: El<S>) -> El<R> {
604        self.to.get_ring().map_out(self.from.get_ring(), x, &self.data)
605    }
606
607    fn domain(&self) -> &S {
608        &self.to
609    }
610
611    fn codomain(&self) -> &R {
612        &self.from
613    }
614}
615
616///
617/// The ring homomorphism induced by a [`RingExtension`].
618/// 
619/// # Example
620/// ```
621/// # use feanor_math::assert_el_eq;
622/// # use feanor_math::ring::*;
623/// # use feanor_math::primitive_int::*;
624/// # use feanor_math::homomorphism::*;
625/// # use feanor_math::rings::poly::*;
626/// let base = StaticRing::<i32>::RING;
627/// let extension = dense_poly::DensePolyRing::new(base, "X");
628/// let hom = extension.inclusion();
629/// let f = extension.add(hom.map(8), extension.indeterminate());
630/// assert_el_eq!(extension, extension.from_terms([(8, 0), (1, 1)].into_iter()), &f);
631/// ```
632/// 
633#[derive(Clone, Debug)]
634pub struct Inclusion<R>
635    where R: RingStore, R::Type: RingExtension
636{
637    ring: R
638}
639
640impl<R: RingStore> Copy for Inclusion<R>
641    where R: Copy, El<R>: Copy, R::Type: RingExtension
642{}
643
644impl<R> Inclusion<R>
645    where R: RingStore, R::Type: RingExtension
646{
647    pub fn new(ring: R) -> Self {
648        Inclusion { ring }
649    }
650}
651    
652impl<R> Homomorphism<<<R::Type as RingExtension>::BaseRing as RingStore>::Type, R::Type> for Inclusion<R>
653    where R: RingStore, R::Type: RingExtension
654{
655    type CodomainStore = R;
656    type DomainStore = <R::Type as RingExtension>::BaseRing;
657
658    fn domain<'a>(&'a self) -> &'a Self::DomainStore {
659        self.ring.base_ring()
660    }
661
662    fn codomain<'a>(&'a self) -> &'a Self::CodomainStore {
663        &self.ring
664    }
665
666    fn map(&self, x: <<<R::Type as RingExtension>::BaseRing as RingStore>::Type as RingBase>::Element) -> <R::Type as RingBase>::Element {
667        self.ring.get_ring().from(x)
668    }
669
670    fn map_ref(&self, x: &<<<R::Type as RingExtension>::BaseRing as RingStore>::Type as RingBase>::Element) -> <R::Type as RingBase>::Element {
671        self.ring.get_ring().from_ref(x)
672    }
673
674    fn mul_assign_ref_map(&self, lhs: &mut <R::Type as RingBase>::Element, rhs: &<<<R::Type as RingExtension>::BaseRing as RingStore>::Type as RingBase>::Element) {
675        self.ring.get_ring().mul_assign_base(lhs, rhs)
676    }
677
678    fn mul_assign_map(&self, lhs: &mut <R::Type as RingBase>::Element, rhs: <<<R::Type as RingExtension>::BaseRing as RingStore>::Type as RingBase>::Element) {
679        self.mul_assign_ref_map(lhs, &rhs)
680    }
681}
682
683///
684/// The ring homomorphism `Z -> R` that exists for any ring `R`.
685/// 
686/// # Example
687/// ```
688/// # use feanor_math::assert_el_eq;
689/// # use feanor_math::ring::*;
690/// # use feanor_math::primitive_int::*;
691/// # use feanor_math::homomorphism::*;
692/// # use feanor_math::rings::zn::*;
693/// let ring = zn_static::F17;
694/// let hom = ring.int_hom();
695/// assert_el_eq!(ring, hom.map(1), hom.map(18));
696/// ```
697/// 
698#[derive(Clone)]
699pub struct IntHom<R>
700    where R: RingStore
701{
702    ring: R
703}
704
705impl<R: RingStore> Copy for IntHom<R>
706    where R: Copy, El<R>: Copy
707{}
708
709impl<R> Homomorphism<StaticRingBase<i32>, R::Type> for IntHom<R>
710    where R: RingStore
711{
712    type CodomainStore = R;
713    type DomainStore = StaticRing<i32>;
714
715    fn domain<'a>(&'a self) -> &'a Self::DomainStore {
716        &StaticRing::<i32>::RING
717    }
718
719    fn codomain<'a>(&'a self) -> &'a Self::CodomainStore {
720        &self.ring
721    }
722
723    fn map(&self, x: i32) -> <R::Type as RingBase>::Element {
724        self.ring.get_ring().from_int(x)
725    }
726
727    fn mul_assign_map(&self, lhs: &mut <R::Type as RingBase>::Element, rhs: i32) {
728        self.ring.get_ring().mul_assign_int(lhs, rhs)
729    }
730
731    fn mul_assign_ref_map(&self, lhs: &mut <R::Type as RingBase>::Element, rhs: &<StaticRingBase<i32> as RingBase>::Element) {
732        self.mul_assign_map(lhs, *rhs)
733    }
734}
735
736impl<R> IntHom<R>
737    where R: RingStore
738{
739    pub fn new(ring: R) -> Self {
740        Self { ring }
741    }
742}
743
744#[derive(Clone)]
745pub struct Identity<R: RingStore> {
746    ring: R
747}
748
749impl<R: RingStore> Copy for Identity<R>
750    where R: Copy, El<R>: Copy
751{}
752
753impl<R: RingStore> Identity<R> {
754
755    pub fn new(ring: R) -> Self {
756        Identity { ring }
757    }
758}
759
760impl<R: RingStore> Homomorphism<R::Type, R::Type> for Identity<R> {
761
762    type CodomainStore = R;
763    type DomainStore = R;
764
765    fn codomain<'a>(&'a self) -> &'a Self::CodomainStore {
766        &self.ring
767    }
768
769    fn domain<'a>(&'a self) -> &'a Self::DomainStore {
770        &self.ring
771    }
772
773    fn map(&self, x: <R::Type as RingBase>::Element) -> <R::Type as RingBase>::Element {
774        x
775    }
776}
777
778impl<'a, S, R, H> Homomorphism<S, R> for &'a H 
779    where S: ?Sized + RingBase, R: ?Sized + RingBase, H: Homomorphism<S, R>
780{
781    type CodomainStore = H::CodomainStore;
782    type DomainStore = H::DomainStore;
783
784    fn codomain<'b>(&'b self) -> &'b Self::CodomainStore {
785        (*self).codomain()
786    }
787
788    fn domain<'b>(&'b self) -> &'b Self::DomainStore {
789        (*self).domain()
790    }
791
792    fn map(&self, x: <S as RingBase>::Element) -> <R as RingBase>::Element {
793        (*self).map(x)
794    }
795
796    fn map_ref(&self, x: &<S as RingBase>::Element) -> <R as RingBase>::Element {
797        (*self).map_ref(x)
798    }
799
800    fn mul_assign_map(&self, lhs: &mut <R as RingBase>::Element, rhs: <S as RingBase>::Element) {
801        (*self).mul_assign_map(lhs, rhs)
802    }
803
804    fn mul_assign_ref_map(&self, lhs: &mut <R as RingBase>::Element, rhs: &<S as RingBase>::Element) {
805        (*self).mul_assign_ref_map(lhs, rhs)
806    }
807}
808
809#[derive(Clone)]
810pub struct LambdaHom<R: RingStore, S: RingStore, F>
811    where F: Fn(&R, &S, &El<R>) -> El<S>
812{
813    from: R,
814    to: S,
815    f: F
816}
817
818impl<R: RingStore, S: RingStore, F> Copy for LambdaHom<R, S, F>
819    where F: Copy + Fn(&R, &S, &El<R>) -> El<S>,
820        R: Copy, El<R>: Copy,
821        S: Copy, El<S>: Copy
822{}
823
824impl<R: RingStore, S: RingStore, F> LambdaHom<R, S, F>
825    where F: Fn(&R, &S, &El<R>) -> El<S>
826{
827    pub fn new(from: R, to: S, f: F) -> Self {
828        Self { from, to, f }
829    }
830
831    #[stability::unstable(feature = "enable")]
832    pub fn into_domain_codomain(self) -> (R, S) {
833        (self.from, self.to)
834    }
835}
836
837impl<R: RingStore, S: RingStore, F> Homomorphism<R::Type, S::Type> for LambdaHom<R, S, F>
838    where F: Fn(&R, &S, &El<R>) -> El<S>
839{
840    type CodomainStore = S;
841    type DomainStore = R;
842
843    fn codomain<'a>(&'a self) -> &'a Self::CodomainStore {
844        &self.to
845    }
846
847    fn domain<'a>(&'a self) -> &'a Self::DomainStore {
848        &self.from
849    }
850
851    fn map(&self, x: <R::Type as RingBase>::Element) -> <S::Type as RingBase>::Element {
852        (self.f)(self.domain(), self.codomain(), &x)
853    }
854
855    fn map_ref(&self, x: &<R::Type as RingBase>::Element) -> <S::Type as RingBase>::Element {
856        (self.f)(self.domain(), self.codomain(), x)
857    }
858}
859
860pub struct ComposedHom<R, S, T, F, G>
861    where F: Homomorphism<R, S>,
862        G: Homomorphism<S, T>,
863        R: ?Sized + RingBase,
864        S: ?Sized + RingBase,
865        T: ?Sized + RingBase
866{
867    f: F,
868    g: G,
869    domain: PhantomData<R>,
870    intermediate: PhantomData<S>,
871    codomain: PhantomData<T>
872}
873
874impl<R, S, T, F, G> Clone for ComposedHom<R, S, T, F, G>
875    where F: Clone + Homomorphism<R, S>,
876        G: Clone + Homomorphism<S, T>,
877        R: ?Sized + RingBase,
878        S: ?Sized + RingBase,
879        T: ?Sized + RingBase
880{
881    fn clone(&self) -> Self {
882        Self {
883            f: self.f.clone(),
884            g: self.g.clone(),
885            domain: PhantomData,
886            codomain: PhantomData,
887            intermediate: PhantomData
888        }
889    }
890}
891
892impl<R, S, T, F, G> Copy for ComposedHom<R, S, T, F, G>
893    where F: Copy + Homomorphism<R, S>,
894        G: Copy + Homomorphism<S, T>,
895        R: ?Sized + RingBase,
896        S: ?Sized + RingBase,
897        T: ?Sized + RingBase
898{}
899
900impl<R, S, T, F, G> Homomorphism<R, T> for ComposedHom<R, S, T, F, G>
901    where F: Homomorphism<R, S>,
902        G: Homomorphism<S, T>,
903        R: ?Sized + RingBase,
904        S: ?Sized + RingBase,
905        T: ?Sized + RingBase
906{
907    type DomainStore = <F as Homomorphism<R, S>>::DomainStore;
908    type CodomainStore = <G as Homomorphism<S, T>>::CodomainStore;
909
910    fn domain<'a>(&'a self) -> &'a Self::DomainStore {
911        self.f.domain()
912    }
913
914    fn codomain<'a>(&'a self) -> &'a Self::CodomainStore {
915        self.g.codomain()
916    }
917
918    fn map(&self, x: <R as RingBase>::Element) -> <T as RingBase>::Element {
919        self.g.map(self.f.map(x))
920    }
921
922    fn map_ref(&self, x: &<R as RingBase>::Element) -> <T as RingBase>::Element {
923        self.g.map(self.f.map_ref(x))
924    }
925
926    fn mul_assign_map(&self, lhs: &mut <T as RingBase>::Element, rhs: <R as RingBase>::Element) {
927        self.g.mul_assign_map(lhs, self.f.map(rhs))
928    }
929
930    fn mul_assign_ref_map(&self, lhs: &mut <T as RingBase>::Element, rhs: &<R as RingBase>::Element) {
931        self.g.mul_assign_map(lhs, self.f.map_ref(rhs))
932    }
933}
934
935///
936/// Implements the trivial canonical isomorphism `Self: CanIsoFromTo<Self>` for the
937/// given type. 
938/// 
939/// Note that this does not support generic types, as for those, it is
940/// usually better to implement
941/// ```rust
942/// # use feanor_math::ring::*;
943/// # use feanor_math::homomorphism::*;
944/// # use feanor_math::delegate::*;
945/// // define `RingConstructor<R: RingStore>`
946/// # struct RingConstructor<R: RingStore>(R);
947/// # impl<R: RingStore> DelegateRing for RingConstructor<R> {
948/// #     type Element = El<R>;
949/// #     type Base = R::Type;
950/// #     fn get_delegate(&self) -> &Self::Base { self.0.get_ring() }
951/// #     fn delegate_ref<'a>(&self, el: &'a Self::Element) -> &'a <Self::Base as RingBase>::Element { el }
952/// #     fn delegate_mut<'a>(&self, el: &'a mut Self::Element) -> &'a mut <Self::Base as RingBase>::Element { el }
953/// #     fn delegate(&self, el: Self::Element) -> <Self::Base as RingBase>::Element { el }
954/// #     fn rev_delegate(&self, el: <Self::Base as RingBase>::Element) -> Self::Element { el }
955/// # }
956/// # impl<R: RingStore> PartialEq for RingConstructor<R> {
957/// #     fn eq(&self, other: &Self) -> bool {
958/// #         self.0.get_ring() == other.0.get_ring()
959/// #     }
960/// # }
961/// impl<R, S> CanHomFrom<RingConstructor<S>> for RingConstructor<R>
962///     where R: RingStore, S: RingStore, R::Type: CanHomFrom<S::Type>
963/// {
964///     type Homomorphism = <R::Type as CanHomFrom<S::Type>>::Homomorphism;
965/// 
966///     fn has_canonical_hom(&self, from: &RingConstructor<S>) -> Option<<R::Type as CanHomFrom<S::Type>>::Homomorphism> {
967///         // delegate to base ring of type `R::Type`
968/// #       self.get_delegate().has_canonical_hom(from.get_delegate())
969///     }
970/// 
971///     fn map_in(&self, from: &RingConstructor<S>, el: <RingConstructor<S> as RingBase>::Element, hom: &Self::Homomorphism) -> <Self as RingBase>::Element {
972///         // delegate to base ring of type `R::Type`
973/// #       self.get_delegate().map_in(from.get_delegate(), el, hom)
974///     }
975/// }
976/// 
977/// // and same for CanIsoFromTo
978/// ```
979/// or something similar.
980/// 
981/// # Example
982/// ```
983/// # use feanor_math::ring::*;
984/// # use feanor_math::homomorphism::*;
985/// # use feanor_math::primitive_int::*;
986/// # use feanor_math::delegate::*;
987/// # use feanor_math::{assert_el_eq, impl_eq_based_self_iso};
988/// 
989/// #[derive(PartialEq, Clone)]
990/// struct MyI32Ring;
991/// 
992/// impl DelegateRing for MyI32Ring {
993/// 
994///     type Base = StaticRingBase<i32>;
995///     type Element = i32;
996/// 
997///     fn get_delegate(&self) -> &Self::Base {
998///         StaticRing::<i32>::RING.get_ring()
999///     }
1000/// 
1001///     fn delegate_ref<'a>(&self, el: &'a i32) -> &'a i32 {
1002///         el
1003///     }
1004/// 
1005///     fn delegate_mut<'a>(&self, el: &'a mut i32) -> &'a mut i32 {
1006///         el
1007///     }
1008/// 
1009///     fn delegate(&self, el: i32) -> i32 {
1010///         el
1011///     }
1012/// 
1013///     fn postprocess_delegate_mut(&self, _: &mut i32) {
1014///         // sometimes it might be necessary to fix some data of `Self::Element`
1015///         // if the underlying `Self::Base::Element` was modified via `delegate_mut()`;
1016///         // this is not the case here, so leave empty
1017///     }
1018/// 
1019///     fn rev_delegate(&self, el: i32) -> i32 {
1020///         el
1021///     }
1022/// }
1023/// 
1024/// // since we provide `PartialEq`, the trait `CanIsoFromTo<Self>` is trivial
1025/// // to implement
1026/// impl_eq_based_self_iso!{ MyI32Ring }
1027/// 
1028/// let ring = RingValue::from(MyI32Ring);
1029/// assert_el_eq!(ring, ring.int_hom().map(1), ring.one());
1030/// ```
1031/// 
1032#[macro_export]
1033macro_rules! impl_eq_based_self_iso {
1034    ($type:ty) => {
1035        impl $crate::homomorphism::CanHomFrom<Self> for $type {
1036
1037            type Homomorphism = ();
1038
1039            fn has_canonical_hom(&self, from: &Self) -> Option<()> {
1040                if self == from {
1041                    Some(())
1042                } else {
1043                    None
1044                }
1045            }
1046
1047            fn map_in(&self, _from: &Self, el: <Self as $crate::ring::RingBase>::Element, _: &Self::Homomorphism) -> <Self as $crate::ring::RingBase>::Element {
1048                el
1049            }
1050        }
1051        
1052        impl $crate::homomorphism::CanIsoFromTo<Self> for $type {
1053
1054            type Isomorphism = ();
1055
1056            fn has_canonical_iso(&self, from: &Self) -> Option<()> {
1057                if self == from {
1058                    Some(())
1059                } else {
1060                    None
1061                }
1062            }
1063
1064            fn map_out(&self, _from: &Self, el: <Self as $crate::ring::RingBase>::Element, _: &Self::Homomorphism) -> <Self as $crate::ring::RingBase>::Element {
1065                el
1066            }
1067        }
1068    };
1069}
1070
1071#[cfg(any(test, feature = "generic_tests"))]
1072pub mod generic_tests {
1073
1074    use super::*;
1075
1076    pub fn test_homomorphism_axioms<R: ?Sized + RingBase, S: ?Sized + RingBase, H, I: Iterator<Item = R::Element>>(hom: H, edge_case_elements: I)
1077        where H: Homomorphism<R, S>
1078    {
1079        let from = hom.domain();
1080        let to = hom.codomain();
1081        let elements = edge_case_elements.collect::<Vec<_>>();
1082
1083        assert!(to.is_zero(&hom.map(from.zero())));
1084        assert!(to.is_one(&hom.map(from.one())));
1085
1086        for a in &elements {
1087            for b in &elements {
1088                {
1089                    let map_a = hom.map_ref(a);
1090                    let map_b = hom.map_ref(b);
1091                    let map_sum = to.add_ref(&map_a, &map_b);
1092                    let sum_map = hom.map(from.add_ref(a, b));
1093                    assert!(to.eq_el(&map_sum, &sum_map), "Additive homomorphic property failed: hom({} + {}) = {} != {} = {} + {}", from.format(a), from.format(b), to.format(&sum_map), to.format(&map_sum), to.format(&map_a), to.format(&map_b));
1094                }
1095                {
1096                    let map_a = hom.map_ref(a);
1097                    let map_b = hom.map_ref(b);
1098                    let map_prod = to.mul_ref(&map_a, &map_b);
1099                    let prod_map = hom.map(from.mul_ref(a, b));
1100                    assert!(to.eq_el(&map_prod, &prod_map), "Multiplicative homomorphic property failed: hom({} * {}) = {} != {} = {} * {}", from.format(a), from.format(b), to.format(&prod_map), to.format(&map_prod), to.format(&map_a), to.format(&map_b));
1101                }
1102                {
1103                    let map_a = hom.map_ref(a);
1104                    let prod_map = hom.map(from.mul_ref(a, b));
1105                    let mut mul_assign = to.clone_el(&map_a);
1106                    hom.mul_assign_ref_map( &mut mul_assign, b);
1107                    assert!(to.eq_el(&mul_assign, &prod_map), "mul_assign_ref_map() failed: hom({} * {}) = {} != {} = mul_map_in(hom({}), {})", from.format(a), from.format(b), to.format(&prod_map), to.format(&mul_assign), to.format(&map_a), from.format(b));
1108                }
1109            }
1110        }
1111    }
1112}