Skip to main content

feanor_math/
homomorphism.rs

1use std::fmt::Debug;
2use std::marker::PhantomData;
3
4use crate::primitive_int::{StaticRing, StaticRingBase};
5use crate::ring::*;
6
7/// The user-facing trait for ring homomorphisms, i.e. maps `R -> S`
8/// between rings that respect the ring structure. Since all considered
9/// rings are unital, ring homomorphisms also must be unital.
10///
11/// Objects are expected to know their domain and codomain rings and
12/// can thus make sense without an implicit ambient ring (unlike e.g.
13/// ring elements).
14///
15/// Ring homomorphisms are usually obtained by a corresponding method
16/// on [`RingStore`], and their functionality is provided by underlying
17/// functions of [`RingBase`]. Main examples include
18///  - Every ring `R` has a homomorphism `Z -> R`. The corresponding [`Homomorphism`]-object is
19///    obtained with [`RingStore::int_hom()`], and the functionality provided by
20///    [`RingBase::from_int()`].
21///  - [`RingExtension`]s have give a (injective) homomorphism `R -> S` which can be obtained by
22///    [`RingExtensionStore::inclusion()`]. The functionality is provided by functions on
23///    [`RingExtension`], like [`RingExtension::from()`].
24///  - Other "natural" homomorphisms can be obtained via [`RingStore::can_hom()`]. This requires the
25///    underlying [`RingBase`]s to implement [`CanHomFrom`], and the functions of that trait define
26///    the homomorphism.
27///  
28pub trait Homomorphism<Domain: ?Sized, Codomain: ?Sized>
29where
30    Domain: RingBase,
31    Codomain: RingBase,
32{
33    /// The type of the [`RingStore`] used by this object to store the domain ring.
34    type DomainStore: RingStore<Type = Domain>;
35    /// The type of the [`RingStore`] used by this object to store the codomain ring.
36    type CodomainStore: RingStore<Type = Codomain>;
37
38    /// Returns a reference to the domain ring.
39    fn domain(&self) -> &Self::DomainStore;
40
41    /// Returns a reference to the codomain ring.
42    fn codomain(&self) -> &Self::CodomainStore;
43
44    /// Applies this homomorphism to the given element from the domain ring,
45    /// resulting in an element in the codomain ring.
46    fn map(&self, x: Domain::Element) -> Codomain::Element;
47
48    /// Applies this homomorphism to the given element from the domain ring,
49    /// resulting in an element in the codomain ring.
50    fn map_ref(&self, x: &Domain::Element) -> Codomain::Element { self.map(self.domain().clone_el(x)) }
51
52    /// Multiplies the given element in the codomain ring with an element obtained
53    /// by applying this homomorphism to a given element from the domain ring.
54    ///
55    /// This is equivalent to, but may be faster than, first mapping the domain
56    /// ring element via this homomorphism, and then performing ring multiplication.
57    fn mul_assign_map(&self, lhs: &mut Codomain::Element, rhs: Domain::Element) {
58        self.codomain().mul_assign(lhs, self.map(rhs))
59    }
60
61    /// Multiplies the given element in the codomain ring with an element obtained
62    /// by applying this homomorphism to a given element from the domain ring.
63    ///
64    /// This is equivalent to, but may be faster than, first mapping the domain
65    /// ring element via this homomorphism, and then performing ring multiplication.
66    fn mul_assign_ref_map(&self, lhs: &mut Codomain::Element, rhs: &Domain::Element) {
67        self.codomain().mul_assign(lhs, self.map_ref(rhs))
68    }
69
70    /// Multiplies the given element in the codomain ring with an element obtained
71    /// by applying this homomorphism to a given element from the domain ring.
72    ///
73    /// This is equivalent to, but may be faster than, first mapping the domain
74    /// ring element via this homomorphism, and then performing ring multiplication.
75    fn mul_map(&self, mut lhs: Codomain::Element, rhs: Domain::Element) -> Codomain::Element {
76        self.mul_assign_map(&mut lhs, rhs);
77        lhs
78    }
79
80    /// Fused-multiply-add. This computes `lhs * rhs + summand`, where `rhs` is mapped
81    /// into the ring via this homomorphism.
82    ///
83    /// This is equivalent to, but may be faster than, first mapping the domain
84    /// ring element via this homomorphism, and then using [`RingBase::fma()`].
85    fn fma_map(&self, lhs: &Codomain::Element, rhs: &Domain::Element, summand: Codomain::Element) -> Codomain::Element {
86        self.codomain().add(summand, self.mul_ref_map(lhs, rhs))
87    }
88
89    /// Multiplies the given element in the codomain ring with an element obtained
90    /// by applying this homomorphism to a given element from the domain ring.
91    ///
92    /// This is equivalent to, but may be faster than, first mapping the domain
93    /// ring element via this homomorphism, and then performing ring multiplication.
94    fn mul_ref_fst_map(&self, lhs: &Codomain::Element, rhs: Domain::Element) -> Codomain::Element {
95        self.mul_map(self.codomain().clone_el(lhs), rhs)
96    }
97
98    /// Multiplies the given element in the codomain ring with an element obtained
99    /// by applying this homomorphism to a given element from the domain ring.
100    ///
101    /// This is equivalent to, but may be faster than, first mapping the domain
102    /// ring element via this homomorphism, and then performing ring multiplication.
103    fn mul_ref_snd_map(&self, mut lhs: Codomain::Element, rhs: &Domain::Element) -> Codomain::Element {
104        self.mul_assign_ref_map(&mut lhs, rhs);
105        lhs
106    }
107
108    /// Multiplies the given element in the codomain ring with an element obtained
109    /// by applying this homomorphism to a given element from the domain ring.
110    ///
111    /// This is equivalent to, but may be faster than, first mapping the domain
112    /// ring element via this homomorphism, and then performing ring multiplication.
113    fn mul_ref_map(&self, lhs: &Codomain::Element, rhs: &Domain::Element) -> Codomain::Element {
114        self.mul_ref_snd_map(self.codomain().clone_el(lhs), rhs)
115    }
116
117    /// Constructs the homomorphism `x -> self.map(prev.map(x))`.
118    fn compose<F, PrevDomain: ?Sized + RingBase>(self, prev: F) -> ComposedHom<PrevDomain, Domain, Codomain, F, Self>
119    where
120        Self: Sized,
121        F: Homomorphism<PrevDomain, Domain>,
122    {
123        assert!(prev.codomain().get_ring() == self.domain().get_ring());
124        ComposedHom {
125            f: prev,
126            g: self,
127            domain: PhantomData,
128            intermediate: PhantomData,
129            codomain: PhantomData,
130        }
131    }
132
133    /// Multiplies the given element in the codomain ring with an element obtained
134    /// by applying this and another homomorphism to a given element from another ring.
135    ///
136    /// The equivalent of [`Homomorphism::mul_assign_map()`] for a chain of two
137    /// homomorphisms. While providing specialized implementations for longer and longer
138    /// chains soon becomes ridiculous, there is one important use case why we would want
139    /// at least length-2 chains:
140    ///
141    /// In particular, many [`RingExtension`]s have elements that consist of multiple
142    /// elements of the base ring, with base-ring-multiplication being scalar-vector
143    /// multiplication. Hence, if the base ring allows a fast-multiplication through
144    /// a single homomorphism, it makes sense to extend that along an [`Inclusion`].
145    /// Hence, we also have [`RingExtension::mul_assign_base_through_hom()`].
146    #[stability::unstable(feature = "enable")]
147    fn mul_assign_ref_map_through_hom<First: ?Sized + RingBase, H: Homomorphism<First, Domain>>(
148        &self,
149        lhs: &mut Codomain::Element,
150        rhs: &First::Element,
151        hom: H,
152    ) {
153        self.mul_assign_map(lhs, hom.map_ref(rhs));
154    }
155
156    /// Multiplies the given element in the codomain ring with an element obtained
157    /// by applying this and another homomorphism to a given element from another ring.
158    ///
159    /// For details, see [`Homomorphism::mul_assign_ref_map_through_hom()`].
160    #[stability::unstable(feature = "enable")]
161    fn mul_assign_map_through_hom<First: ?Sized + RingBase, H: Homomorphism<First, Domain>>(
162        &self,
163        lhs: &mut Codomain::Element,
164        rhs: First::Element,
165        hom: H,
166    ) {
167        self.mul_assign_map(lhs, hom.map(rhs));
168    }
169}
170
171/// Trait for rings R that have a canonical homomorphism `S -> R`.
172/// A ring homomorphism is expected to be unital.
173///
174/// This trait is considered implementor-facing, so it is designed to easily
175/// implement natural maps between rings. When using homomorphisms, consider
176/// using instead [`CanHom`], as it does not require weird syntax like
177/// `R.get_ring().map_in(S.get_ring(), x, &hom)`.
178///
179/// **Warning** Because of type-system limitations (see below), this trait
180/// is not implemented in all cases where it makes sense, in particular when
181/// type parameters are involved. Thus, you should always consider this trait
182/// to be for convenience only. A truly generic algorithm should, if possible,
183/// not constrain input ring types using `CanHomFrom`, but instead take an
184/// additional object of generic type bounded by [`Homomorphism`] that provides
185/// the required homomorphism.
186///
187/// # Exact requirements
188///
189/// Which homomorphisms are considered canonical is up to implementors,
190/// as long as any diagram of canonical homomorphisms commutes. In
191/// other words, if there are rings `R, S` and "intermediate rings"
192/// `R1, ..., Rn` resp. `R1', ..., Rm'` such that there are canonical
193/// homomorphisms
194/// ```text
195///   S -> R1 -> R2 -> ... -> Rn -> R
196/// ```
197/// and
198/// ```text
199///   S -> R1' -> R2' -> ... -> Rm' -> R
200/// ```
201/// then both homomorphism chains should yield same results on same
202/// inputs.
203///
204/// If the canonical homomorphism might be an isomorphism, consider also
205/// implementing [`CanIsoFromTo`].
206///
207/// # Example
208///
209/// Most integer rings support canonical homomorphisms between them.
210/// ```rust
211/// # use feanor_math::ring::*;
212/// # use feanor_math::homomorphism::*;
213/// # use feanor_math::primitive_int::*;
214/// # use feanor_math::integer::*;
215/// let R = StaticRing::<i64>::RING;
216/// let S = BigIntRing::RING;
217/// let eight = S.int_hom().map(8);
218/// // on RingBase level
219/// let hom = R.get_ring().has_canonical_hom(S.get_ring()).unwrap();
220/// assert_eq!(
221///     8,
222///     R.get_ring().map_in(S.get_ring(), S.clone_el(&eight), &hom)
223/// );
224/// // on RingStore level
225/// assert_eq!(8, R.coerce(&S, S.clone_el(&eight)));
226/// // or
227/// let hom = R.can_hom(&S).unwrap();
228/// assert_eq!(8, hom.map_ref(&eight));
229/// ```
230///
231/// # Limitations
232///
233/// The rust constraints regarding conflicting impl make it, in some cases,
234/// impossible to implement all the canonical homomorphisms that we would like.
235/// This is true in particular, if the rings are highly generic, and build
236/// on base rings. In this case, it should always be preferred to implement
237/// `CanIsoFromTo` for rings that are "the same", and on the other hand not
238/// to implement classical homomorphisms, like `ZZ -> R` which exists for any
239/// ring R. In applicable cases, consider also implementing [`RingExtension`].
240///
241/// Because of this reason, implementing [`RingExtension`] also does not require
242/// an implementation of `CanHomFrom<Self::BaseRing>`. Hence, if you as a user
243/// miss a certain implementation of `CanHomFrom`, check whether there maybe
244/// is a corresponding implementation of [`RingExtension`], or a member function.
245///
246/// # More examples
247///
248/// ## Integer rings
249///
250/// All given integer rings have canonical isomorphisms between each other.
251/// ```rust
252/// # use feanor_math::ring::*;
253/// # use feanor_math::integer::*;
254/// # use feanor_math::primitive_int::*;
255/// # use feanor_math::integer::*;
256/// let Z_i8 = StaticRing::<i8>::RING;
257/// let Z_i32 = StaticRing::<i32>::RING;
258/// let Z_i128 = StaticRing::<i128>::RING;
259/// let Z_big = BigIntRing::RING;
260///
261/// assert!(Z_i8.can_iso(&Z_i8).is_some());
262/// assert!(Z_i8.can_iso(&Z_i32).is_some());
263/// assert!(Z_i8.can_iso(&Z_i128).is_some());
264/// assert!(Z_i8.can_iso(&Z_big).is_some());
265///
266/// assert!(Z_i32.can_iso(&Z_i8).is_some());
267/// assert!(Z_i32.can_iso(&Z_i32).is_some());
268/// assert!(Z_i32.can_iso(&Z_i128).is_some());
269/// assert!(Z_i32.can_iso(&Z_big).is_some());
270///
271/// assert!(Z_i128.can_iso(&Z_i8).is_some());
272/// assert!(Z_i128.can_iso(&Z_i32).is_some());
273/// assert!(Z_i128.can_iso(&Z_i128).is_some());
274/// assert!(Z_i128.can_iso(&Z_big).is_some());
275///
276/// assert!(Z_big.can_iso(&Z_i8).is_some());
277/// assert!(Z_big.can_iso(&Z_i32).is_some());
278/// assert!(Z_big.can_iso(&Z_i128).is_some());
279/// assert!(Z_big.can_iso(&Z_big).is_some());
280/// ```
281///
282/// ## Integer quotient rings `Z/nZ`
283///
284/// Due to conflicting implementations, only the most useful conversions
285/// are implemented for `Z/nZ`.
286/// ```rust
287/// # use feanor_math::ring::*;
288/// # use feanor_math::primitive_int::*;
289/// # use feanor_math::homomorphism::*;
290/// # use feanor_math::integer::*;
291/// # use feanor_math::rings::zn::*;
292/// # use feanor_math::rings::zn::zn_big;
293/// # use feanor_math::rings::zn::zn_rns;
294/// let ZZ = StaticRing::<i128>::RING;
295/// let ZZ_big = BigIntRing::RING;
296///
297/// let zn_big_i128 = zn_big::Zn::new(ZZ, 17 * 257);
298/// let zn_big_big = zn_big::Zn::new(ZZ_big, ZZ_big.int_hom().map(17 * 257));
299/// let Zn_std = zn_64::Zn::new(17 * 257);
300/// let Zn_rns = zn_rns::Zn::create_from_primes(vec![17, 257], ZZ_big);
301///
302/// assert!(zn_big_i128.can_iso(&zn_big_i128).is_some());
303/// assert!(zn_big_i128.can_iso(&zn_big_big).is_some());
304///
305/// assert!(zn_big_big.can_iso(&zn_big_i128).is_some());
306/// assert!(zn_big_big.can_iso(&zn_big_big).is_some());
307///
308/// assert!(Zn_std.can_iso(&zn_big_i128).is_some());
309/// assert!(Zn_std.can_iso(&Zn_std).is_some());
310///
311/// assert!(Zn_rns.can_iso(&zn_big_i128).is_some());
312/// assert!(Zn_rns.can_iso(&zn_big_big).is_some());
313/// assert!(Zn_rns.can_iso(&Zn_rns).is_some());
314/// ```
315/// Most notably, reduction homomorphisms are currently not available.
316/// You can use [`crate::rings::zn::ZnReductionMap`] instead.
317/// ```rust
318/// # use feanor_math::ring::*;
319/// # use feanor_math::primitive_int::*;
320/// # use feanor_math::homomorphism::*;
321/// # use feanor_math::integer::*;
322/// # use feanor_math::rings::zn::*;
323/// # use feanor_math::assert_el_eq;
324/// let Z9 = zn_64::Zn::new(9);
325/// let Z3 = zn_64::Zn::new(3);
326/// assert!(Z3.can_hom(&Z9).is_none());
327/// let mod_3 = ZnReductionMap::new(&Z9, &Z3).unwrap();
328/// assert_el_eq!(Z3, Z3.one(), mod_3.map(Z9.int_hom().map(4)));
329/// ```
330/// Additionally, there are the projections `Z -> Z/nZ`.
331/// They are all implemented, even though [`crate::rings::zn::ZnRing`] currently
332/// only requires the projection from the "associated" integer ring.
333/// ```rust
334/// # use feanor_math::ring::*;
335/// # use feanor_math::homomorphism::*;
336/// # use feanor_math::primitive_int::*;
337/// # use feanor_math::integer::*;
338/// # use feanor_math::rings::zn::*;
339/// let ZZ = StaticRing::<i128>::RING;
340/// let ZZ_big = BigIntRing::RING;
341///
342/// let zn_big_i128 = zn_big::Zn::new(ZZ, 17 * 257);
343/// let zn_big_big = zn_big::Zn::new(ZZ_big, ZZ_big.int_hom().map(17 * 257));
344/// let Zn_std = zn_64::Zn::new(17 * 257);
345/// let Zn_rns = zn_rns::Zn::create_from_primes(vec![17, 257], ZZ_big);
346///
347/// assert!(zn_big_i128.can_hom(&ZZ).is_some());
348/// assert!(zn_big_i128.can_hom(&ZZ_big).is_some());
349///
350/// assert!(zn_big_big.can_hom(&ZZ).is_some());
351/// assert!(zn_big_big.can_hom(&ZZ_big).is_some());
352///
353/// assert!(Zn_std.can_hom(&ZZ).is_some());
354/// assert!(Zn_std.can_hom(&ZZ_big).is_some());
355///
356/// assert!(Zn_rns.can_hom(&ZZ).is_some());
357/// assert!(Zn_rns.can_hom(&ZZ_big).is_some());
358/// ```
359///
360/// ## Polynomial Rings
361///
362/// For the two provided univariate polynomial ring implementations, we have the isomorphisms
363/// ```rust
364/// # use feanor_math::ring::*;
365/// # use feanor_math::primitive_int::*;
366/// # use feanor_math::integer::*;
367/// # use feanor_math::rings::poly::*;
368///
369/// let ZZ = StaticRing::<i128>::RING;
370/// let P_dense = dense_poly::DensePolyRing::new(ZZ, "X");
371/// let P_sparse = sparse_poly::SparsePolyRing::new(ZZ, "X");
372///
373/// assert!(P_dense.can_iso(&P_dense).is_some());
374/// assert!(P_dense.can_iso(&P_sparse).is_some());
375/// assert!(P_sparse.can_iso(&P_dense).is_some());
376/// assert!(P_sparse.can_iso(&P_sparse).is_some());
377/// ```
378/// Unfortunately, the inclusions `R -> R[X]` are not implemented as canonical homomorphisms,
379/// however provided by the functions of [`RingExtension`].
380pub trait CanHomFrom<S>: RingBase
381where
382    S: RingBase + ?Sized,
383{
384    /// Data required to compute the action of the canonical homomorphism on ring elements.
385    type Homomorphism;
386
387    /// If there is a canonical homomorphism `from -> self`, returns `Some(data)`, where
388    /// `data` is additional data that can be used to compute the action of the homomorphism
389    /// on ring elements. Otherwise, `None` is returned.
390    fn has_canonical_hom(&self, from: &S) -> Option<Self::Homomorphism>;
391
392    /// Evaluates the homomorphism.
393    fn map_in(&self, from: &S, el: S::Element, hom: &Self::Homomorphism) -> Self::Element;
394
395    /// Evaluates the homomorphism, taking the element by reference.
396    fn map_in_ref(&self, from: &S, el: &S::Element, hom: &Self::Homomorphism) -> Self::Element {
397        self.map_in(from, from.clone_el(el), hom)
398    }
399
400    /// Evaluates the homomorphism on `rhs`, and multiplies the result to `lhs`.
401    fn mul_assign_map_in(&self, from: &S, lhs: &mut Self::Element, rhs: S::Element, hom: &Self::Homomorphism) {
402        self.mul_assign(lhs, self.map_in(from, rhs, hom));
403    }
404
405    /// Evaluates the homomorphism on `rhs`, taking it by reference, and multiplies the result to
406    /// `lhs`.
407    fn mul_assign_map_in_ref(&self, from: &S, lhs: &mut Self::Element, rhs: &S::Element, hom: &Self::Homomorphism) {
408        self.mul_assign(lhs, self.map_in_ref(from, rhs, hom));
409    }
410
411    /// Fused-multiply-add. Computes `summand + lhs * rhs`, where `rhs` is mapped into the ring via
412    /// the homomorphism.
413    fn fma_map_in(
414        &self,
415        from: &S,
416        lhs: &Self::Element,
417        rhs: &S::Element,
418        summand: Self::Element,
419        hom: &Self::Homomorphism,
420    ) -> Self::Element {
421        let mut lhs_copy = self.clone_el(lhs);
422        self.mul_assign_map_in_ref(from, &mut lhs_copy, rhs, hom);
423        return self.add(lhs_copy, summand);
424    }
425}
426
427/// Trait for rings R that have a canonical isomorphism `S -> R`.
428/// A ring homomorphism is expected to be unital.
429///
430/// **Warning** Because of type-system limitations (see [`CanHomFrom`]), this trait
431/// is not implemented in all cases where it makes sense, in particular when
432/// type parameters are involved. Thus, you should always consider this trait
433/// to be for convenience only. A truly generic algorithm should, if possible,
434/// not constrain input ring types using `CanHomFrom`, but instead take an
435/// additional object of generic type bounded by [`Homomorphism`] that provides
436/// the required homomorphism.
437///
438/// # Exact requirements
439///
440/// Same as for [`CanHomFrom`], it is up to implementors to decide which
441/// isomorphisms are canonical, as long as each diagram that contains
442/// only canonical homomorphisms, canonical isomorphisms and their inverses
443/// commutes.
444/// In other words, if there are rings `R, S` and "intermediate rings"
445/// `R1, ..., Rn` resp. `R1', ..., Rm'` such that there are canonical
446/// homomorphisms `->` or isomorphisms `<~>` connecting them - e.g. like
447/// ```text
448///   S -> R1 -> R2 <~> R3 <~> R4 -> ... -> Rn -> R
449/// ```
450/// and
451/// ```text
452///   S <~> R1' -> R2' -> ... -> Rm' -> R
453/// ```
454/// then both chains should yield same results on same inputs.
455///
456/// Hence, it would be natural if the trait were symmetrical, i.e.
457/// for any implementation `R: CanIsoFromTo<S>` there is also an
458/// implementation `S: CanIsoFromTo<R>`. However, because of the trait
459/// impl constraints of Rust, this is unpracticable and so we only
460/// require the implementation `R: CanHomFrom<S>`.
461pub trait CanIsoFromTo<S>: CanHomFrom<S>
462where
463    S: RingBase + ?Sized,
464{
465    /// Data required to compute a preimage under the canonical homomorphism.
466    type Isomorphism;
467
468    /// If there is a canonical homomorphism `from -> self`, and this homomorphism
469    /// is an isomorphism, returns `Some(data)`, where `data` is additional data that
470    /// can be used to compute preimages under the homomorphism. Otherwise, `None` is
471    /// returned.
472    fn has_canonical_iso(&self, from: &S) -> Option<Self::Isomorphism>;
473
474    /// Computes the preimage of `el` under the canonical homomorphism `from -> self`.
475    fn map_out(&self, from: &S, el: Self::Element, iso: &Self::Isomorphism) -> S::Element;
476}
477
478/// Basically an alias for `CanIsoFromTo<Self>`, but implemented as new
479/// trait since trait aliases are not available.
480pub trait SelfIso: CanIsoFromTo<Self> {}
481
482impl<R: ?Sized + CanIsoFromTo<R>> SelfIso for R {}
483
484/// A high-level wrapper of [`CanHomFrom::Homomorphism`] that references the
485/// domain and codomain rings, and is much easier to use.
486///
487/// # Example
488/// ```rust
489/// # use feanor_math::ring::*;
490/// # use feanor_math::homomorphism::*;
491/// # use feanor_math::homomorphism::*;
492/// # use feanor_math::primitive_int::*;
493/// let from = StaticRing::<i32>::RING;
494/// let to = StaticRing::<i64>::RING;
495/// let hom = to.can_hom(&from).unwrap();
496/// assert_eq!(7, hom.map(7));
497/// // instead of
498/// let hom = to.get_ring().has_canonical_hom(from.get_ring()).unwrap();
499/// assert_eq!(7, to.get_ring().map_in(from.get_ring(), 7, &hom));
500/// ```
501///
502/// # See also
503/// The "bi-directional" variant [`CanHom`], the basic interfaces [`CanHomFrom`] and
504/// [`CanIsoFromTo`] and the very simplified function [`RingStore::coerce`].
505pub struct CanHom<R, S>
506where
507    R: RingStore,
508    S: RingStore,
509    S::Type: CanHomFrom<R::Type>,
510{
511    from: R,
512    to: S,
513    data: <S::Type as CanHomFrom<R::Type>>::Homomorphism,
514}
515
516impl<R, S> Debug for CanHom<R, S>
517where
518    R: RingStore + Debug,
519    S: RingStore + Debug,
520    S::Type: CanHomFrom<R::Type>,
521{
522    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
523        f.debug_struct("CanHom")
524            .field("from", &self.from)
525            .field("to", &self.to)
526            .finish()
527    }
528}
529
530impl<R, S> CanHom<R, S>
531where
532    R: RingStore,
533    S: RingStore,
534    S::Type: CanHomFrom<R::Type>,
535{
536    /// Creates a new [`CanHom`] from `from` to `to`, if the
537    /// corresonding rings support it.
538    pub fn new(from: R, to: S) -> Result<Self, (R, S)> {
539        match to.get_ring().has_canonical_hom(from.get_ring()) {
540            Some(data) => Ok(Self::from_raw_parts(from, to, data)),
541            _ => Err((from, to)),
542        }
543    }
544
545    /// Returns a reference to the underlying [`CanHomFrom::Homomorphism`].
546    pub fn raw_hom(&self) -> &<S::Type as CanHomFrom<R::Type>>::Homomorphism { &self.data }
547
548    /// Returns the underlying [`CanHomFrom::Homomorphism`], consuming this object.
549    pub fn into_raw_hom(self) -> <S::Type as CanHomFrom<R::Type>>::Homomorphism { self.data }
550
551    #[stability::unstable(feature = "enable")]
552    pub fn from_raw_parts(from: R, to: S, data: <S::Type as CanHomFrom<R::Type>>::Homomorphism) -> Self {
553        Self { from, to, data }
554    }
555}
556
557impl<R, S> Clone for CanHom<R, S>
558where
559    R: RingStore + Clone,
560    S: RingStore + Clone,
561    S::Type: CanHomFrom<R::Type>,
562{
563    fn clone(&self) -> Self { Self::new(self.from.clone(), self.to.clone()).ok().unwrap() }
564}
565
566impl<R, S> Homomorphism<R::Type, S::Type> for CanHom<R, S>
567where
568    R: RingStore,
569    S: RingStore,
570    S::Type: CanHomFrom<R::Type>,
571{
572    type CodomainStore = S;
573    type DomainStore = R;
574
575    fn map(&self, el: El<R>) -> El<S> { self.to.get_ring().map_in(self.from.get_ring(), el, &self.data) }
576
577    fn map_ref(&self, el: &El<R>) -> El<S> { self.to.get_ring().map_in_ref(self.from.get_ring(), el, &self.data) }
578
579    fn domain(&self) -> &R { &self.from }
580
581    fn codomain(&self) -> &S { &self.to }
582
583    fn mul_assign_map(&self, lhs: &mut El<S>, rhs: El<R>) {
584        self.to
585            .get_ring()
586            .mul_assign_map_in(self.from.get_ring(), lhs, rhs, &self.data);
587    }
588
589    fn mul_assign_ref_map(&self, lhs: &mut El<S>, rhs: &El<R>) {
590        self.to
591            .get_ring()
592            .mul_assign_map_in_ref(self.from.get_ring(), lhs, rhs, &self.data);
593    }
594
595    fn fma_map(&self, lhs: &El<S>, rhs: &El<R>, summand: El<S>) -> El<S> {
596        self.to
597            .get_ring()
598            .fma_map_in(self.from.get_ring(), lhs, rhs, summand, &self.data)
599    }
600}
601
602/// A wrapper of [`CanHomFrom::Homomorphism`] that does not own the data associated
603/// with the homomorphism. Use cases are rare, prefer to use [`CanHom`] whenever possible.
604///
605/// More concretely, this should only be used when you only have a reference to `<R as
606/// CanHomFrom<S>>::Homomorphism`, but cannot refactor code to wrap that object in a [`CanHom`]
607/// instead. The main situation where this occurs is when implementing [`CanHomFrom`], since a the
608/// lifetime of [`CanHom`] is bound by the lifetime of the domain and codomain rings, but
609/// `CanHomFrom::Type` does not allow this.
610#[stability::unstable(feature = "enable")]
611pub struct CanHomRef<'a, R, S>
612where
613    R: RingStore,
614    S: RingStore,
615    S::Type: CanHomFrom<R::Type>,
616{
617    from: R,
618    to: S,
619    data: &'a <S::Type as CanHomFrom<R::Type>>::Homomorphism,
620}
621
622impl<'a, R, S> CanHomRef<'a, R, S>
623where
624    R: RingStore,
625    S: RingStore,
626    S::Type: CanHomFrom<R::Type>,
627{
628    #[stability::unstable(feature = "enable")]
629    pub fn raw_hom(&self) -> &<S::Type as CanHomFrom<R::Type>>::Homomorphism { &self.data }
630
631    #[stability::unstable(feature = "enable")]
632    pub fn from_raw_parts(from: R, to: S, data: &'a <S::Type as CanHomFrom<R::Type>>::Homomorphism) -> Self {
633        Self { from, to, data }
634    }
635}
636
637impl<'a, R, S> Clone for CanHomRef<'a, R, S>
638where
639    R: RingStore + Clone,
640    S: RingStore + Clone,
641    S::Type: CanHomFrom<R::Type>,
642{
643    fn clone(&self) -> Self { Self::from_raw_parts(self.from.clone(), self.to.clone(), self.data) }
644}
645
646impl<'a, R, S> Copy for CanHomRef<'a, R, S>
647where
648    R: RingStore + Copy,
649    S: RingStore + Copy,
650    S::Type: CanHomFrom<R::Type>,
651    El<R>: Copy,
652    El<S>: Copy,
653{
654}
655
656impl<'a, R, S> Homomorphism<R::Type, S::Type> for CanHomRef<'a, R, S>
657where
658    R: RingStore,
659    S: RingStore,
660    S::Type: CanHomFrom<R::Type>,
661{
662    type CodomainStore = S;
663    type DomainStore = R;
664
665    fn map(&self, el: El<R>) -> El<S> { self.to.get_ring().map_in(self.from.get_ring(), el, self.data) }
666
667    fn map_ref(&self, el: &El<R>) -> El<S> { self.to.get_ring().map_in_ref(self.from.get_ring(), el, self.data) }
668
669    fn domain(&self) -> &R { &self.from }
670
671    fn codomain(&self) -> &S { &self.to }
672
673    fn mul_assign_map(&self, lhs: &mut El<S>, rhs: El<R>) {
674        self.to
675            .get_ring()
676            .mul_assign_map_in(self.from.get_ring(), lhs, rhs, self.data);
677    }
678
679    fn mul_assign_ref_map(&self, lhs: &mut El<S>, rhs: &El<R>) {
680        self.to
681            .get_ring()
682            .mul_assign_map_in_ref(self.from.get_ring(), lhs, rhs, self.data);
683    }
684
685    fn fma_map(&self, lhs: &El<S>, rhs: &El<R>, summand: El<S>) -> El<S> {
686        self.to
687            .get_ring()
688            .fma_map_in(self.from.get_ring(), lhs, rhs, summand, self.data)
689    }
690}
691
692/// A wrapper around [`CanIsoFromTo::Isomorphism`] that references the domain and
693/// codomain rings, making it much easier to use. This also contains the homomorphism
694/// in the other direction, i.e. allows mapping in both directions.
695///
696/// # Example
697/// ```rust
698/// # use feanor_math::ring::*;
699/// # use feanor_math::homomorphism::*;
700/// # use feanor_math::primitive_int::*;
701///
702/// let from = StaticRing::<i32>::RING;
703/// let to = StaticRing::<i64>::RING;
704/// let hom = to.can_iso(&from).unwrap();
705/// assert_eq!(7, hom.map(7));
706/// // instead of
707/// let hom = to.get_ring().has_canonical_iso(from.get_ring()).unwrap();
708/// assert_eq!(7, from.get_ring().map_out(to.get_ring(), 7, &hom));
709/// ```
710///
711/// # See also
712///
713/// The "one-directional" variant [`CanHom`], the basic interfaces [`CanHomFrom`] and
714/// [`CanIsoFromTo`] and the very simplified function [`RingStore::coerce()`].
715pub struct CanIso<R, S>
716where
717    R: RingStore,
718    S: RingStore,
719    S::Type: CanIsoFromTo<R::Type>,
720{
721    from: R,
722    to: S,
723    data: <S::Type as CanIsoFromTo<R::Type>>::Isomorphism,
724}
725
726impl<R, S> Debug for CanIso<R, S>
727where
728    R: RingStore + Debug,
729    S: RingStore + Debug,
730    S::Type: CanIsoFromTo<R::Type>,
731{
732    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
733        f.debug_struct("CanIso")
734            .field("from", &self.from)
735            .field("to", &self.to)
736            .finish()
737    }
738}
739
740impl<R, S> Clone for CanIso<R, S>
741where
742    R: RingStore + Clone,
743    S: RingStore + Clone,
744    S::Type: CanIsoFromTo<R::Type>,
745{
746    fn clone(&self) -> Self { Self::new(self.from.clone(), self.to.clone()).ok().unwrap() }
747}
748
749impl<R, S> CanIso<R, S>
750where
751    R: RingStore,
752    S: RingStore,
753    S::Type: CanIsoFromTo<R::Type>,
754{
755    /// Creates a new [`CanIso`] between `from` and `to`, if the
756    /// corresonding rings support it.
757    ///
758    /// Somewhat counter-intuitively, the returned object actually
759    /// implements the homomorphism in the direction `to -> from`.
760    /// However, for an isomorphism, the direction is not that relevant,
761    /// and the map in the direction `from -> to` can be obtained by
762    /// [`CanIso::inv()`] or [`CanIso::into_inv()`].
763    pub fn new(from: R, to: S) -> Result<Self, (R, S)> {
764        match to.get_ring().has_canonical_iso(from.get_ring()) {
765            Some(data) => {
766                assert!(to.get_ring().has_canonical_hom(from.get_ring()).is_some());
767                Ok(Self { from, to, data })
768            }
769            _ => Err((from, to)),
770        }
771    }
772
773    /// Returns the inverse homomorphism, consuming this object.
774    pub fn into_inv(self) -> CanHom<R, S> { CanHom::new(self.from, self.to).unwrap_or_else(|_| unreachable!()) }
775
776    /// Returns the inverse homomorphism.
777    pub fn inv(&self) -> CanHom<&R, &S> { CanHom::new(&self.from, &self.to).unwrap_or_else(|_| unreachable!()) }
778
779    /// Returns a reference to the underlying [`CanIsoFromTo::Isomorphism`].
780    pub fn raw_iso(&self) -> &<S::Type as CanIsoFromTo<R::Type>>::Isomorphism { &self.data }
781
782    /// Returns the underlying [`CanIsoFromTo::Isomorphism`], consuming this object.
783    pub fn into_raw_iso(self) -> <S::Type as CanIsoFromTo<R::Type>>::Isomorphism { self.data }
784}
785
786impl<R, S> Homomorphism<S::Type, R::Type> for CanIso<R, S>
787where
788    R: RingStore,
789    S: RingStore,
790    S::Type: CanIsoFromTo<R::Type>,
791{
792    type DomainStore = S;
793    type CodomainStore = R;
794
795    fn map(&self, x: El<S>) -> El<R> { self.to.get_ring().map_out(self.from.get_ring(), x, &self.data) }
796
797    fn domain(&self) -> &S { &self.to }
798
799    fn codomain(&self) -> &R { &self.from }
800}
801
802/// The ring homomorphism induced by a [`RingExtension`].
803///
804/// # Example
805/// ```rust
806/// # use feanor_math::assert_el_eq;
807/// # use feanor_math::ring::*;
808/// # use feanor_math::primitive_int::*;
809/// # use feanor_math::homomorphism::*;
810/// # use feanor_math::rings::poly::*;
811/// let base = StaticRing::<i32>::RING;
812/// let extension = dense_poly::DensePolyRing::new(base, "X");
813/// let hom = extension.inclusion();
814/// let f = extension.add(hom.map(8), extension.indeterminate());
815/// assert_el_eq!(
816///     extension,
817///     extension.from_terms([(8, 0), (1, 1)].into_iter()),
818///     &f
819/// );
820/// ```
821#[derive(Clone, Debug)]
822pub struct Inclusion<R>
823where
824    R: RingStore,
825    R::Type: RingExtension,
826{
827    ring: R,
828}
829
830impl<R: RingStore> Copy for Inclusion<R>
831where
832    R: Copy,
833    El<R>: Copy,
834    R::Type: RingExtension,
835{
836}
837
838impl<R> Inclusion<R>
839where
840    R: RingStore,
841    R::Type: RingExtension,
842{
843    /// Returns the [`Inclusion`] from the base ring of the given ring to itself.
844    pub fn new(ring: R) -> Self { Inclusion { ring } }
845}
846
847impl<R> Homomorphism<<<R::Type as RingExtension>::BaseRing as RingStore>::Type, R::Type> for Inclusion<R>
848where
849    R: RingStore,
850    R::Type: RingExtension,
851{
852    type CodomainStore = R;
853    type DomainStore = <R::Type as RingExtension>::BaseRing;
854
855    fn domain(&self) -> &Self::DomainStore { self.ring.base_ring() }
856
857    fn codomain(&self) -> &Self::CodomainStore { &self.ring }
858
859    fn map(&self, x: El<<R::Type as RingExtension>::BaseRing>) -> El<R> { self.ring.get_ring().from(x) }
860
861    fn map_ref(&self, x: &El<<R::Type as RingExtension>::BaseRing>) -> El<R> { self.ring.get_ring().from_ref(x) }
862
863    fn mul_assign_ref_map(&self, lhs: &mut El<R>, rhs: &El<<R::Type as RingExtension>::BaseRing>) {
864        self.ring.get_ring().mul_assign_base(lhs, rhs)
865    }
866
867    fn mul_assign_map(&self, lhs: &mut El<R>, rhs: El<<R::Type as RingExtension>::BaseRing>) {
868        self.mul_assign_ref_map(lhs, &rhs)
869    }
870
871    fn mul_assign_map_through_hom<
872        First: ?Sized + RingBase,
873        H: Homomorphism<First, <<R::Type as RingExtension>::BaseRing as RingStore>::Type>,
874    >(
875        &self,
876        lhs: &mut El<R>,
877        rhs: First::Element,
878        hom: H,
879    ) {
880        self.ring.get_ring().mul_assign_base_through_hom(lhs, &rhs, hom)
881    }
882
883    fn mul_assign_ref_map_through_hom<
884        First: ?Sized + RingBase,
885        H: Homomorphism<First, <<R::Type as RingExtension>::BaseRing as RingStore>::Type>,
886    >(
887        &self,
888        lhs: &mut El<R>,
889        rhs: &First::Element,
890        hom: H,
891    ) {
892        self.ring.get_ring().mul_assign_base_through_hom(lhs, rhs, hom)
893    }
894
895    fn fma_map(&self, lhs: &El<R>, rhs: &El<<R::Type as RingExtension>::BaseRing>, summand: El<R>) -> El<R> {
896        self.ring.get_ring().fma_base(lhs, rhs, summand)
897    }
898}
899
900/// The ring homomorphism `Z -> R` that exists for any ring `R`.
901///
902/// # Example
903/// ```rust
904/// # use feanor_math::assert_el_eq;
905/// # use feanor_math::ring::*;
906/// # use feanor_math::primitive_int::*;
907/// # use feanor_math::homomorphism::*;
908/// # use feanor_math::rings::zn::*;
909/// let ring = zn_static::F17;
910/// let hom = ring.int_hom();
911/// assert_el_eq!(ring, hom.map(1), hom.map(18));
912/// ```
913#[derive(Clone)]
914pub struct IntHom<R>
915where
916    R: RingStore,
917{
918    ring: R,
919}
920
921impl<R: RingStore> Copy for IntHom<R>
922where
923    R: Copy,
924    El<R>: Copy,
925{
926}
927
928impl<R> Homomorphism<StaticRingBase<i32>, R::Type> for IntHom<R>
929where
930    R: RingStore,
931{
932    type CodomainStore = R;
933    type DomainStore = StaticRing<i32>;
934
935    fn domain(&self) -> &Self::DomainStore { &StaticRing::<i32>::RING }
936
937    fn codomain(&self) -> &Self::CodomainStore { &self.ring }
938
939    fn map(&self, x: i32) -> El<R> { self.ring.get_ring().from_int(x) }
940
941    fn mul_assign_map(&self, lhs: &mut El<R>, rhs: i32) { self.ring.get_ring().mul_assign_int(lhs, rhs) }
942
943    fn mul_assign_ref_map(&self, lhs: &mut El<R>, rhs: &<StaticRingBase<i32> as RingBase>::Element) {
944        self.mul_assign_map(lhs, *rhs)
945    }
946
947    fn fma_map(&self, lhs: &El<R>, rhs: &i32, summand: El<R>) -> El<R> {
948        self.ring.get_ring().fma_int(lhs, *rhs, summand)
949    }
950}
951
952impl<R> IntHom<R>
953where
954    R: RingStore,
955{
956    /// Creates the [`IntHom`] homomorphism
957    /// ```text
958    ///   Z -> R, n -> 1 + ... + 1 [n times]
959    /// ```
960    /// for the given ring `R`.
961    pub fn new(ring: R) -> Self { Self { ring } }
962}
963
964/// The identity homomorphism `R -> R, x -> x` on the given ring `R`.
965#[derive(Clone)]
966pub struct Identity<R: RingStore> {
967    ring: R,
968}
969
970impl<R: RingStore> Copy for Identity<R>
971where
972    R: Copy,
973    El<R>: Copy,
974{
975}
976
977impl<R: RingStore> Identity<R> {
978    /// Creates the [`Identity`] homomorphism `R -> R, x -> x` on the given ring `R`.
979    pub fn new(ring: R) -> Self { Identity { ring } }
980}
981
982impl<R: RingStore> Homomorphism<R::Type, R::Type> for Identity<R> {
983    type CodomainStore = R;
984    type DomainStore = R;
985
986    fn codomain(&self) -> &Self::CodomainStore { &self.ring }
987
988    fn domain(&self) -> &Self::DomainStore { &self.ring }
989
990    fn map(&self, x: El<R>) -> El<R> { x }
991
992    fn mul_assign_map_through_hom<First: ?Sized + RingBase, H: Homomorphism<First, R::Type>>(
993        &self,
994        lhs: &mut El<R>,
995        rhs: First::Element,
996        hom: H,
997    ) {
998        hom.mul_assign_map(lhs, rhs);
999    }
1000
1001    fn mul_assign_ref_map_through_hom<First: ?Sized + RingBase, H: Homomorphism<First, R::Type>>(
1002        &self,
1003        lhs: &mut El<R>,
1004        rhs: &First::Element,
1005        hom: H,
1006    ) {
1007        hom.mul_assign_ref_map(lhs, rhs);
1008    }
1009
1010    fn fma_map(&self, lhs: &El<R>, rhs: &El<R>, summand: El<R>) -> El<R> { self.ring.get_ring().fma(lhs, rhs, summand) }
1011}
1012
1013impl<S, R, H> Homomorphism<S, R> for &H
1014where
1015    S: ?Sized + RingBase,
1016    R: ?Sized + RingBase,
1017    H: Homomorphism<S, R>,
1018{
1019    type CodomainStore = H::CodomainStore;
1020    type DomainStore = H::DomainStore;
1021
1022    fn codomain(&self) -> &Self::CodomainStore { (*self).codomain() }
1023
1024    fn domain(&self) -> &Self::DomainStore { (*self).domain() }
1025
1026    fn map(&self, x: <S as RingBase>::Element) -> <R as RingBase>::Element { (*self).map(x) }
1027
1028    fn map_ref(&self, x: &<S as RingBase>::Element) -> <R as RingBase>::Element { (*self).map_ref(x) }
1029
1030    fn mul_assign_map(&self, lhs: &mut <R as RingBase>::Element, rhs: <S as RingBase>::Element) {
1031        (*self).mul_assign_map(lhs, rhs)
1032    }
1033
1034    fn mul_assign_ref_map(&self, lhs: &mut <R as RingBase>::Element, rhs: &<S as RingBase>::Element) {
1035        (*self).mul_assign_ref_map(lhs, rhs)
1036    }
1037
1038    fn fma_map(
1039        &self,
1040        lhs: &<R as RingBase>::Element,
1041        rhs: &<S as RingBase>::Element,
1042        summand: <R as RingBase>::Element,
1043    ) -> <R as RingBase>::Element {
1044        (*self).fma_map(lhs, rhs, summand)
1045    }
1046}
1047
1048/// A homomorphism between rings `R -> S`, with its action on elements
1049/// defined by a user-provided closure.
1050///
1051/// It is up to the user to ensure that the given closure indeed
1052/// satisfies the ring homomorphism axioms:
1053///  - For `x, y in R`, it should satisfy `f(x) + f(y) = f(x + y)`
1054///  - For `x, y in R`, it should satisfy `f(x) * f(y) = f(x * y)`
1055///  - It should map `f(0) = 0` and `f(1) = 1`
1056///
1057/// Hence, a [`LambdaHom`] should only be used if none of the builtin
1058/// homomorphisms can achieve the same result, since a function that does
1059/// not follow the above axioms will make algorithms misbehave, and can
1060/// lead to hard-to-debug errors.
1061#[derive(Clone)]
1062pub struct LambdaHom<R: RingStore, S: RingStore, F>
1063where
1064    F: Fn(&R, &S, &El<R>) -> El<S>,
1065{
1066    from: R,
1067    to: S,
1068    f: F,
1069}
1070
1071impl<R: RingStore, S: RingStore, F> Copy for LambdaHom<R, S, F>
1072where
1073    F: Copy + Fn(&R, &S, &El<R>) -> El<S>,
1074    R: Copy,
1075    El<R>: Copy,
1076    S: Copy,
1077    El<S>: Copy,
1078{
1079}
1080
1081impl<R: RingStore, S: RingStore, F> LambdaHom<R, S, F>
1082where
1083    F: Fn(&R, &S, &El<R>) -> El<S>,
1084{
1085    /// Creates a new [`LambdaHom`] from `from` to `to`, mapping elements as
1086    /// specified by the given function.
1087    ///
1088    /// It is up to the user to ensure that the given closure indeed
1089    /// satisfies the ring homomorphism axioms:
1090    ///  - For `x, y in R`, it should satisfy `f(x) + f(y) = f(x + y)`
1091    ///  - For `x, y in R`, it should satisfy `f(x) * f(y) = f(x * y)`
1092    ///  - It should map `f(0) = 0` and `f(1) = 1`
1093    ///
1094    /// Hence, a [`LambdaHom`] should only be used if none of the builtin
1095    /// homomorphisms can achieve the same result, since a function that does
1096    /// not follow the above axioms will make algorithms misbehave, and can
1097    /// lead to hard-to-debug errors.
1098    pub fn new(from: R, to: S, f: F) -> Self { Self { from, to, f } }
1099
1100    /// Returns the stored domain and codomain rings, consuming this object.
1101    #[stability::unstable(feature = "enable")]
1102    pub fn into_domain_codomain(self) -> (R, S) { (self.from, self.to) }
1103}
1104
1105impl<R: RingStore, S: RingStore, F> Homomorphism<R::Type, S::Type> for LambdaHom<R, S, F>
1106where
1107    F: Fn(&R, &S, &El<R>) -> El<S>,
1108{
1109    type CodomainStore = S;
1110    type DomainStore = R;
1111
1112    fn codomain(&self) -> &Self::CodomainStore { &self.to }
1113
1114    fn domain(&self) -> &Self::DomainStore { &self.from }
1115
1116    fn map(&self, x: El<R>) -> <S::Type as RingBase>::Element { (self.f)(self.domain(), self.codomain(), &x) }
1117
1118    fn map_ref(&self, x: &El<R>) -> <S::Type as RingBase>::Element { (self.f)(self.domain(), self.codomain(), x) }
1119}
1120
1121/// The function composition of two homomorphisms `f: R -> S` and `g: S -> T`.
1122///
1123/// More concretely, this is the homomorphism `R -> T` that maps `x` to `g(f(x))`.
1124/// The best way to create a [`ComposedHom`] is through [`Homomorphism::compose()`].
1125pub struct ComposedHom<R, S, T, F, G>
1126where
1127    F: Homomorphism<R, S>,
1128    G: Homomorphism<S, T>,
1129    R: ?Sized + RingBase,
1130    S: ?Sized + RingBase,
1131    T: ?Sized + RingBase,
1132{
1133    f: F,
1134    g: G,
1135    domain: PhantomData<R>,
1136    intermediate: PhantomData<S>,
1137    codomain: PhantomData<T>,
1138}
1139
1140impl<R, S, T, F, G> ComposedHom<R, S, T, F, G>
1141where
1142    F: Clone + Homomorphism<R, S>,
1143    G: Clone + Homomorphism<S, T>,
1144    R: ?Sized + RingBase,
1145    S: ?Sized + RingBase,
1146    T: ?Sized + RingBase,
1147{
1148    /// Returns a reference to `f`, the homomorphism that is applied first
1149    /// to input elements `x`.
1150    #[stability::unstable(feature = "enable")]
1151    pub fn first(&self) -> &F { &self.f }
1152
1153    /// Returns a reference to `g`, the homomorphism that is applied second,
1154    /// so to `f(x)` when mapping an input element `x`.
1155    #[stability::unstable(feature = "enable")]
1156    pub fn second(&self) -> &G { &self.g }
1157}
1158
1159impl<R, S, T, F, G> Clone for ComposedHom<R, S, T, F, G>
1160where
1161    F: Clone + Homomorphism<R, S>,
1162    G: Clone + Homomorphism<S, T>,
1163    R: ?Sized + RingBase,
1164    S: ?Sized + RingBase,
1165    T: ?Sized + RingBase,
1166{
1167    fn clone(&self) -> Self {
1168        Self {
1169            f: self.f.clone(),
1170            g: self.g.clone(),
1171            domain: PhantomData,
1172            codomain: PhantomData,
1173            intermediate: PhantomData,
1174        }
1175    }
1176}
1177
1178impl<R, S, T, F, G> Copy for ComposedHom<R, S, T, F, G>
1179where
1180    F: Copy + Homomorphism<R, S>,
1181    G: Copy + Homomorphism<S, T>,
1182    R: ?Sized + RingBase,
1183    S: ?Sized + RingBase,
1184    T: ?Sized + RingBase,
1185{
1186}
1187
1188impl<R, S, T, F, G> Homomorphism<R, T> for ComposedHom<R, S, T, F, G>
1189where
1190    F: Homomorphism<R, S>,
1191    G: Homomorphism<S, T>,
1192    R: ?Sized + RingBase,
1193    S: ?Sized + RingBase,
1194    T: ?Sized + RingBase,
1195{
1196    type DomainStore = <F as Homomorphism<R, S>>::DomainStore;
1197    type CodomainStore = <G as Homomorphism<S, T>>::CodomainStore;
1198
1199    fn domain(&self) -> &Self::DomainStore { self.f.domain() }
1200
1201    fn codomain(&self) -> &Self::CodomainStore { self.g.codomain() }
1202
1203    fn map(&self, x: <R as RingBase>::Element) -> <T as RingBase>::Element { self.g.map(self.f.map(x)) }
1204
1205    fn map_ref(&self, x: &<R as RingBase>::Element) -> <T as RingBase>::Element { self.g.map(self.f.map_ref(x)) }
1206
1207    fn mul_assign_map(&self, lhs: &mut <T as RingBase>::Element, rhs: <R as RingBase>::Element) {
1208        self.g.mul_assign_map_through_hom(lhs, rhs, &self.f)
1209    }
1210
1211    fn mul_assign_ref_map(&self, lhs: &mut <T as RingBase>::Element, rhs: &<R as RingBase>::Element) {
1212        self.g.mul_assign_ref_map_through_hom(lhs, rhs, &self.f)
1213    }
1214
1215    fn fma_map(
1216        &self,
1217        lhs: &<T as RingBase>::Element,
1218        rhs: &<R as RingBase>::Element,
1219        summand: <T as RingBase>::Element,
1220    ) -> <T as RingBase>::Element {
1221        self.g.fma_map(lhs, &self.f.map_ref(rhs), summand)
1222    }
1223}
1224
1225/// Implements the trivial canonical isomorphism `Self: CanIsoFromTo<Self>` for the
1226/// given type.
1227///
1228/// Note that this does not support generic types, as for those, it is
1229/// usually better to implement
1230/// ```rust
1231/// # use feanor_math::ring::*;
1232/// # use feanor_math::homomorphism::*;
1233/// # use feanor_math::delegate::*;
1234/// // define `RingConstructor<R: RingStore>`
1235/// # struct RingConstructor<R: RingStore>(R);
1236/// # impl<R: RingStore> DelegateRing for RingConstructor<R> {
1237/// #     type Element = El<R>;
1238/// #     type Base = R::Type;
1239/// #     fn get_delegate(&self) -> &Self::Base { self.0.get_ring() }
1240/// #     fn delegate_ref<'a>(&self, el: &'a Self::Element) -> &'a <Self::Base as RingBase>::Element { el }
1241/// #     fn delegate_mut<'a>(&self, el: &'a mut Self::Element) -> &'a mut <Self::Base as RingBase>::Element { el }
1242/// #     fn delegate(&self, el: Self::Element) -> <Self::Base as RingBase>::Element { el }
1243/// #     fn rev_delegate(&self, el: <Self::Base as RingBase>::Element) -> Self::Element { el }
1244/// # }
1245/// # impl<R: RingStore> PartialEq for RingConstructor<R> {
1246/// #     fn eq(&self, other: &Self) -> bool {
1247/// #         self.0.get_ring() == other.0.get_ring()
1248/// #     }
1249/// # }
1250/// impl<R, S> CanHomFrom<RingConstructor<S>> for RingConstructor<R>
1251///     where R: RingStore, S: RingStore, R::Type: CanHomFrom<S::Type>
1252/// {
1253///     type Homomorphism = <R::Type as CanHomFrom<S::Type>>::Homomorphism;
1254///
1255///     fn has_canonical_hom(&self, from: &RingConstructor<S>) -> Option<<R::Type as CanHomFrom<S::Type>>::Homomorphism> {
1256///         // delegate to base ring of type `R::Type`
1257/// #       self.get_delegate().has_canonical_hom(from.get_delegate())
1258///     }
1259///
1260///     fn map_in(&self, from: &RingConstructor<S>, el: <RingConstructor<S> as RingBase>::Element, hom: &Self::Homomorphism) -> <Self as RingBase>::Element {
1261///         // delegate to base ring of type `R::Type`
1262/// #       self.get_delegate().map_in(from.get_delegate(), el, hom)
1263///     }
1264/// }
1265///
1266/// // and same for CanIsoFromTo
1267/// ```
1268/// or something similar.
1269///
1270/// # Example
1271/// ```rust
1272/// # use feanor_math::ring::*;
1273/// # use feanor_math::homomorphism::*;
1274/// # use feanor_math::primitive_int::*;
1275/// # use feanor_math::delegate::*;
1276/// # use feanor_math::{assert_el_eq, impl_eq_based_self_iso};
1277///
1278/// #[derive(PartialEq, Clone)]
1279/// struct MyI32Ring;
1280///
1281/// impl DelegateRing for MyI32Ring {
1282///     type Base = StaticRingBase<i32>;
1283///     type Element = i32;
1284///
1285///     fn get_delegate(&self) -> &Self::Base { StaticRing::<i32>::RING.get_ring() }
1286///
1287///     fn delegate_ref<'a>(&self, el: &'a i32) -> &'a i32 { el }
1288///
1289///     fn delegate_mut<'a>(&self, el: &'a mut i32) -> &'a mut i32 { el }
1290///
1291///     fn delegate(&self, el: i32) -> i32 { el }
1292///
1293///     fn postprocess_delegate_mut(&self, _: &mut i32) {
1294///         // sometimes it might be necessary to fix some data of `Self::Element`
1295///         // if the underlying `Self::Base::Element` was modified via `delegate_mut()`;
1296///         // this is not the case here, so leave empty
1297///     }
1298///
1299///     fn rev_delegate(&self, el: i32) -> i32 { el }
1300/// }
1301///
1302/// // since we provide `PartialEq`, the trait `CanIsoFromTo<Self>` is trivial
1303/// // to implement
1304/// impl_eq_based_self_iso! { MyI32Ring }
1305///
1306/// let ring = RingValue::from(MyI32Ring);
1307/// assert_el_eq!(ring, ring.int_hom().map(1), ring.one());
1308/// ```
1309#[macro_export]
1310macro_rules! impl_eq_based_self_iso {
1311    ($type:ty) => {
1312        impl $crate::homomorphism::CanHomFrom<Self> for $type {
1313            type Homomorphism = ();
1314
1315            fn has_canonical_hom(&self, from: &Self) -> Option<()> { if self == from { Some(()) } else { None } }
1316
1317            fn map_in(
1318                &self,
1319                _from: &Self,
1320                el: <Self as $crate::ring::RingBase>::Element,
1321                _: &Self::Homomorphism,
1322            ) -> <Self as $crate::ring::RingBase>::Element {
1323                el
1324            }
1325        }
1326
1327        impl $crate::homomorphism::CanIsoFromTo<Self> for $type {
1328            type Isomorphism = ();
1329
1330            fn has_canonical_iso(&self, from: &Self) -> Option<()> { if self == from { Some(()) } else { None } }
1331
1332            fn map_out(
1333                &self,
1334                _from: &Self,
1335                el: <Self as $crate::ring::RingBase>::Element,
1336                _: &Self::Homomorphism,
1337            ) -> <Self as $crate::ring::RingBase>::Element {
1338                el
1339            }
1340        }
1341    };
1342}
1343
1344#[cfg(any(test, feature = "generic_tests"))]
1345pub mod generic_tests {
1346
1347    use super::*;
1348
1349    pub fn test_homomorphism_axioms<R: ?Sized + RingBase, S: ?Sized + RingBase, H, I: Iterator<Item = R::Element>>(
1350        hom: H,
1351        edge_case_elements: I,
1352    ) where
1353        H: Homomorphism<R, S>,
1354    {
1355        let from = hom.domain();
1356        let to = hom.codomain();
1357        let elements = edge_case_elements.collect::<Vec<_>>();
1358
1359        assert!(to.is_zero(&hom.map(from.zero())));
1360        assert!(to.is_one(&hom.map(from.one())));
1361
1362        for a in &elements {
1363            for b in &elements {
1364                {
1365                    let map_a = hom.map_ref(a);
1366                    let map_b = hom.map_ref(b);
1367                    let map_sum = to.add_ref(&map_a, &map_b);
1368                    let sum_map = hom.map(from.add_ref(a, b));
1369                    assert!(
1370                        to.eq_el(&map_sum, &sum_map),
1371                        "Additive homomorphic property failed: hom({} + {}) = {} != {} = {} + {}",
1372                        from.format(a),
1373                        from.format(b),
1374                        to.format(&sum_map),
1375                        to.format(&map_sum),
1376                        to.format(&map_a),
1377                        to.format(&map_b)
1378                    );
1379                }
1380                {
1381                    let map_a = hom.map_ref(a);
1382                    let map_b = hom.map_ref(b);
1383                    let map_prod = to.mul_ref(&map_a, &map_b);
1384                    let prod_map = hom.map(from.mul_ref(a, b));
1385                    assert!(
1386                        to.eq_el(&map_prod, &prod_map),
1387                        "Multiplicative homomorphic property failed: hom({} * {}) = {} != {} = {} * {}",
1388                        from.format(a),
1389                        from.format(b),
1390                        to.format(&prod_map),
1391                        to.format(&map_prod),
1392                        to.format(&map_a),
1393                        to.format(&map_b)
1394                    );
1395                }
1396                {
1397                    let map_a = hom.map_ref(a);
1398                    let prod_map = hom.map(from.mul_ref(a, b));
1399                    let mut mul_assign = to.clone_el(&map_a);
1400                    hom.mul_assign_ref_map(&mut mul_assign, b);
1401                    assert!(
1402                        to.eq_el(&mul_assign, &prod_map),
1403                        "mul_assign_ref_map() failed: hom({} * {}) = {} != {} = mul_map_in(hom({}), {})",
1404                        from.format(a),
1405                        from.format(b),
1406                        to.format(&prod_map),
1407                        to.format(&mul_assign),
1408                        to.format(&map_a),
1409                        from.format(b)
1410                    );
1411                }
1412            }
1413        }
1414    }
1415}