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