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}