Skip to main content

feanor_math/rings/zn/
zn_rns.rs

1use std::alloc::{Allocator, Global};
2use std::fmt::Debug;
3
4use feanor_serde::newtype_struct::*;
5use feanor_serde::seq::*;
6use serde::Serialize;
7use serde::de::DeserializeSeed;
8
9use crate::algorithms::matmul::ComputeInnerProduct;
10use crate::divisibility::DivisibilityRingStore;
11use crate::integer::*;
12use crate::iters::{MultiProduct, multi_cartesian_product};
13use crate::primitive_int::*;
14use crate::rings::zn::*;
15use crate::seq::VectorView;
16use crate::serialization::{DeserializeWithRing, SerializableElementRing, SerializeWithRing};
17use crate::specialization::*;
18
19/// A ring representing `Z/nZ` for composite n by storing the
20/// values modulo `m1, ..., mr` for `n = m1 * ... * mr`.
21/// Generally, the advantage is improved performance in cases
22/// where `m1`, ..., `mr` are sufficiently small, and can e.g.
23/// by implemented without large integers.
24///
25/// Note that the component rings `Z/miZ` of this ring can be
26/// accessed via the [`crate::seq::VectorView`]-functions.
27///
28/// # Example
29/// ```rust
30/// # use feanor_math::ring::*;
31/// # use feanor_math::homomorphism::*;
32/// # use feanor_math::rings::zn::*;
33/// # use feanor_math::rings::zn::zn_rns::*;
34/// # use feanor_math::primitive_int::*;
35/// # use feanor_math::integer::*;
36/// # use feanor_math::seq::*;
37///
38/// let R = Zn::create_from_primes(vec![17, 19], StaticRing::<i64>::RING);
39/// let x = R.get_ring().from_congruence(
40///     [
41///         R.get_ring().at(0).int_hom().map(1),
42///         R.get_ring().at(1).int_hom().map(16),
43///     ]
44///     .into_iter(),
45/// );
46/// assert_eq!(35, R.smallest_lift(R.clone_el(&x)));
47/// let y = R.mul_ref(&x, &x);
48/// let z = R.get_ring().from_congruence(
49///     [
50///         R.get_ring().at(0).int_hom().map(1 * 1),
51///         R.get_ring().at(1).int_hom().map(16 * 16),
52///     ]
53///     .into_iter(),
54/// );
55/// assert!(R.eq_el(&z, &y));
56/// ```
57///
58/// # Canonical mappings
59/// This ring has a canonical isomorphism to Barett-reduction based Zn
60/// ```rust
61/// # use feanor_math::ring::*;
62/// # use feanor_math::homomorphism::*;
63/// # use feanor_math::rings::zn::*;
64/// # use feanor_math::rings::zn::zn_rns::*;
65/// # use feanor_math::integer::*;
66/// # use feanor_math::primitive_int::*;
67/// let R = Zn::create_from_primes(vec![17, 19], BigIntRing::RING);
68/// let S = zn_big::Zn::new(StaticRing::<i64>::RING, 17 * 19);
69/// assert!(R.eq_el(&R.int_hom().map(12), &R.coerce(&S, S.int_hom().map(12))));
70/// assert!(S.eq_el(
71///     &S.int_hom().map(12),
72///     &R.can_iso(&S).unwrap().map(R.int_hom().map(12))
73/// ));
74/// ```
75/// and a canonical homomorphism from any integer ring
76/// ```rust
77/// # use feanor_math::ring::*;
78/// # use feanor_math::homomorphism::*;
79/// # use feanor_math::rings::zn::*;
80/// # use feanor_math::rings::zn::zn_rns::*;
81/// # use feanor_math::integer::*;
82/// # use feanor_math::primitive_int::*;
83/// let R = Zn::create_from_primes(vec![3, 5, 7], BigIntRing::RING);
84/// let S = BigIntRing::RING;
85/// assert!(R.eq_el(
86///     &R.int_hom().map(120493),
87///     &R.coerce(&S, S.int_hom().map(120493))
88/// ));
89/// ```
90pub struct ZnBase<C: RingStore, J: RingStore, A: Allocator + Clone = Global>
91where
92    C::Type: ZnRing + CanHomFrom<J::Type>,
93    J::Type: IntegerRing,
94    <C::Type as ZnRing>::IntegerRingBase: IntegerRing + CanIsoFromTo<J::Type>,
95{
96    components: Vec<C>,
97    total_ring: zn_big::Zn<J>,
98    unit_vectors: Vec<El<zn_big::Zn<J>>>,
99    element_allocator: A,
100}
101
102/// The ring `Z/nZ` for composite `n` implemented using the residue number system (RNS),
103/// i.e. storing values by storing their value modulo every factor of `n`.
104/// For details, see [`ZnBase`].
105pub type Zn<C, J, A = Global> = RingValue<ZnBase<C, J, A>>;
106
107impl<C: RingStore, J: RingStore> Zn<C, J, Global>
108where
109    C::Type: ZnRing + CanHomFrom<J::Type>,
110    J::Type: IntegerRing,
111    <C::Type as ZnRing>::IntegerRingBase: IntegerRing + CanIsoFromTo<J::Type>,
112{
113    /// Creates a new ring for `Z/nZ` with `n = m1 ... mr` where the `mi` are the moduli
114    /// of the given component rings. Furthermore, the corresponding large integer ring must be
115    /// provided, which has to be able to store values of size at least `n^3`.
116    pub fn new(summands: Vec<C>, large_integers: J) -> Self { Self::new_with_alloc(summands, large_integers, Global) }
117}
118
119impl<J: RingStore> Zn<zn_64::Zn, J, Global>
120where
121    zn_64::ZnBase: CanHomFrom<J::Type>,
122    J::Type: IntegerRing,
123{
124    pub fn create_from_primes(primes: Vec<i64>, large_integers: J) -> Self {
125        Self::new_with_alloc(
126            primes.into_iter().map(|p| zn_64::Zn::new(p as u64)).collect(),
127            large_integers,
128            Global,
129        )
130    }
131}
132
133impl<C: RingStore, J: RingStore, A: Allocator + Clone> Zn<C, J, A>
134where
135    C::Type: ZnRing + CanHomFrom<J::Type>,
136    J::Type: IntegerRing,
137    <C::Type as ZnRing>::IntegerRingBase: IntegerRing + CanIsoFromTo<J::Type>,
138{
139    /// Creates a new ring for `Z/nZ` with `n = m1 ... mr` where the `mi` are the moduli
140    /// of the given component rings. Furthermore, the corresponding large integer ring must be
141    /// provided, which has to be able to store values of size at least `n^3`.
142    #[stability::unstable(feature = "enable")]
143    pub fn new_with_alloc(summands: Vec<C>, large_integers: J, element_allocator: A) -> Self {
144        assert!(!summands.is_empty());
145        let total_modulus = large_integers.prod(
146            summands
147                .iter()
148                .map(|R| R.integer_ring().can_iso(&large_integers).unwrap().map_ref(R.modulus())),
149        );
150        let total_ring = zn_big::Zn::new(large_integers, total_modulus);
151        let ZZ = total_ring.integer_ring();
152        for R in &summands {
153            let R_modulus = R.integer_ring().can_iso(ZZ).unwrap().map_ref(R.modulus());
154            assert!(
155                ZZ.is_one(&algorithms::eea::signed_gcd(
156                    ZZ.checked_div(total_ring.modulus(), &R_modulus).unwrap(),
157                    R_modulus,
158                    ZZ
159                )),
160                "all moduli must be coprime"
161            );
162            // makes things much easier, e.g. during CanIsoFromTo implementation
163            assert!(R.integer_ring().get_ring() == summands[0].integer_ring().get_ring());
164        }
165        let unit_vectors = summands
166            .iter()
167            .map(|R: &C| {
168                (
169                    R,
170                    ZZ.checked_div(
171                        total_ring.modulus(),
172                        &R.integer_ring().can_iso(ZZ).unwrap().map_ref(R.modulus()),
173                    )
174                    .unwrap(),
175                )
176            })
177            .map(|(R, n)| {
178                (
179                    int_cast(
180                        R.any_lift(R.invert(&R.coerce(&ZZ, ZZ.clone_el(&n))).unwrap()),
181                        ZZ,
182                        R.integer_ring(),
183                    ),
184                    n,
185                )
186            })
187            .map(|(n_mod_inv, n)| total_ring.mul(total_ring.coerce(&ZZ, n_mod_inv), total_ring.coerce(&ZZ, n)))
188            .collect();
189        RingValue::from(ZnBase {
190            components: summands,
191            total_ring,
192            unit_vectors,
193            element_allocator,
194        })
195    }
196}
197
198impl<C: RingStore, J: RingStore, A: Allocator + Clone> Zn<C, J, A>
199where
200    C::Type: ZnRing + CanHomFrom<J::Type>,
201    J::Type: IntegerRing,
202    <C::Type as ZnRing>::IntegerRingBase: IntegerRing + CanIsoFromTo<J::Type>,
203{
204    /// Given values `ai` for each component ring `Z/miZ`, computes the unique element in this
205    /// ring `Z/nZ` that is congruent to `ai` modulo `mi`. The "opposite" function is
206    /// [`Zn::get_congruence()`].
207    pub fn from_congruence<I>(&self, el: I) -> ZnEl<C, A>
208    where
209        I: IntoIterator<Item = El<C>>,
210    {
211        self.get_ring().from_congruence(el)
212    }
213
214    /// Given `a` in `Z/nZ`, returns the vector whose `i`-th entry is `a mod mi`, where the `mi` are
215    /// the moduli of the component rings of this ring.
216    pub fn get_congruence<'a>(&self, el: &'a ZnEl<C, A>) -> impl 'a + VectorView<El<C>> {
217        self.get_ring().get_congruence(el)
218    }
219}
220
221impl<C: RingStore, J: RingStore, A: Allocator + Clone> ZnBase<C, J, A>
222where
223    C::Type: ZnRing + CanHomFrom<J::Type>,
224    J::Type: IntegerRing,
225    <C::Type as ZnRing>::IntegerRingBase: IntegerRing + CanIsoFromTo<J::Type>,
226{
227    /// Given values `ai` for each component ring `Z/miZ`, computes the unique element in this
228    /// ring `Z/nZ` that is congruent to `ai` modulo `mi`. The "opposite" function is
229    /// [`ZnBase::get_congruence()`].
230    pub fn from_congruence<I>(&self, el: I) -> ZnEl<C, A>
231    where
232        I: IntoIterator<Item = El<C>>,
233    {
234        let mut data = Vec::with_capacity_in(self.len(), self.element_allocator.clone());
235        data.extend(el);
236        assert_eq!(self.len(), data.len());
237        ZnEl { data }
238    }
239
240    /// Given `a` in `Z/nZ`, returns the vector whose `i`-th entry is `a mod mi`, where the `mi` are
241    /// the moduli of the component rings of this ring.
242    pub fn get_congruence<'a>(&self, el: &'a ZnEl<C, A>) -> impl 'a + VectorView<El<C>> { &el.data as &[El<C>] }
243}
244
245impl<C: RingStore, J: RingStore, A: Allocator + Clone> Debug for ZnBase<C, J, A>
246where
247    C::Type: ZnRing + CanHomFrom<J::Type>,
248    J::Type: IntegerRing,
249    <C::Type as ZnRing>::IntegerRingBase: IntegerRing + CanIsoFromTo<J::Type>,
250{
251    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
252        write!(f, "Z/{}Z", self.integer_ring().format(self.modulus()))
253    }
254}
255
256impl<C: RingStore, J: RingStore, A: Allocator + Clone> VectorView<C> for Zn<C, J, A>
257where
258    C::Type: ZnRing + CanHomFrom<J::Type>,
259    J::Type: IntegerRing,
260    <C::Type as ZnRing>::IntegerRingBase: IntegerRing + CanIsoFromTo<J::Type>,
261{
262    fn len(&self) -> usize { self.get_ring().len() }
263
264    fn at(&self, index: usize) -> &C { self.get_ring().at(index) }
265}
266
267impl<C: RingStore, J: RingStore, A: Allocator + Clone> VectorView<C> for ZnBase<C, J, A>
268where
269    C::Type: ZnRing + CanHomFrom<J::Type>,
270    J::Type: IntegerRing,
271    <C::Type as ZnRing>::IntegerRingBase: IntegerRing + CanIsoFromTo<J::Type>,
272{
273    fn len(&self) -> usize { self.components.len() }
274
275    fn at(&self, index: usize) -> &C { &self.components[index] }
276}
277
278pub struct ZnEl<C: RingStore, A: Allocator + Clone>
279where
280    C::Type: ZnRing,
281{
282    data: Vec<El<C>, A>,
283}
284
285impl<C, A> Debug for ZnEl<C, A>
286where
287    C: RingStore,
288    C::Type: ZnRing,
289    A: Allocator + Clone,
290    El<C>: Debug,
291{
292    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
293        f.debug_struct("ZnEl").field("congruences", &self.data).finish()
294    }
295}
296
297impl<C: RingStore, J: RingStore, A: Allocator + Clone> RingBase for ZnBase<C, J, A>
298where
299    C::Type: ZnRing + CanHomFrom<J::Type>,
300    J::Type: IntegerRing,
301    <C::Type as ZnRing>::IntegerRingBase: IntegerRing + CanIsoFromTo<J::Type>,
302{
303    type Element = ZnEl<C, A>;
304
305    fn clone_el(&self, val: &Self::Element) -> Self::Element {
306        let mut data = Vec::with_capacity_in(self.len(), self.element_allocator.clone());
307        data.extend((0..self.len()).map(|i| self.at(i).clone_el(val.data.at(i))));
308        ZnEl { data }
309    }
310
311    fn add_assign_ref(&self, lhs: &mut Self::Element, rhs: &Self::Element) {
312        for i in 0..self.components.len() {
313            self.components[i].add_assign_ref(&mut lhs.data[i], &rhs.data[i])
314        }
315    }
316
317    fn add_assign(&self, lhs: &mut Self::Element, rhs: Self::Element) {
318        for (i, el) in (0..self.components.len()).zip(rhs.data) {
319            self.components[i].add_assign_ref(&mut lhs.data[i], &el)
320        }
321    }
322
323    fn sub_assign_ref(&self, lhs: &mut Self::Element, rhs: &Self::Element) {
324        for i in 0..self.components.len() {
325            self.components[i].sub_assign_ref(&mut lhs.data[i], &rhs.data[i])
326        }
327    }
328
329    fn negate_inplace(&self, lhs: &mut Self::Element) {
330        for i in 0..self.components.len() {
331            self.components[i].negate_inplace(&mut lhs.data[i])
332        }
333    }
334
335    fn mul_assign(&self, lhs: &mut Self::Element, rhs: Self::Element) {
336        for (i, el) in (0..self.components.len()).zip(rhs.data) {
337            self.components[i].mul_assign_ref(&mut lhs.data[i], &el)
338        }
339    }
340
341    fn mul_assign_ref(&self, lhs: &mut Self::Element, rhs: &Self::Element) {
342        for i in 0..self.components.len() {
343            self.components[i].mul_assign_ref(&mut lhs.data[i], &rhs.data[i])
344        }
345    }
346
347    fn from_int(&self, value: i32) -> Self::Element {
348        self.from_congruence((0..self.len()).map(|i| self.components[i].get_ring().from_int(value)))
349    }
350
351    fn mul_assign_int(&self, lhs: &mut Self::Element, rhs: i32) {
352        for i in 0..self.components.len() {
353            self.components[i].int_hom().mul_assign_map(&mut lhs.data[i], rhs)
354        }
355    }
356
357    fn eq_el(&self, lhs: &Self::Element, rhs: &Self::Element) -> bool {
358        (0..self.components.len())
359            .zip(lhs.data.iter())
360            .zip(rhs.data.iter())
361            .all(|((i, l), r)| self.components[i].eq_el(l, r))
362    }
363
364    fn is_zero(&self, value: &Self::Element) -> bool {
365        (0..self.components.len())
366            .zip(value.data.iter())
367            .all(|(i, x)| self.components[i].is_zero(x))
368    }
369
370    fn is_one(&self, value: &Self::Element) -> bool {
371        (0..self.components.len())
372            .zip(value.data.iter())
373            .all(|(i, x)| self.components[i].is_one(x))
374    }
375
376    fn is_neg_one(&self, value: &Self::Element) -> bool {
377        (0..self.components.len())
378            .zip(value.data.iter())
379            .all(|(i, x)| self.components[i].is_neg_one(x))
380    }
381
382    fn is_commutative(&self) -> bool { true }
383    fn is_noetherian(&self) -> bool { true }
384
385    fn dbg_within<'a>(
386        &self,
387        value: &Self::Element,
388        out: &mut std::fmt::Formatter<'a>,
389        _: EnvBindingStrength,
390    ) -> std::fmt::Result {
391        self.total_ring.get_ring().dbg(
392            &RingRef::new(self).can_iso(&self.total_ring).unwrap().map_ref(value),
393            out,
394        )
395    }
396
397    fn characteristic<I: RingStore + Copy>(&self, ZZ: I) -> Option<El<I>>
398    where
399        I::Type: IntegerRing,
400    {
401        self.size(ZZ)
402    }
403
404    fn is_approximate(&self) -> bool { false }
405}
406
407impl<C: RingStore, J: RingStore, A: Allocator + Clone> Clone for ZnBase<C, J, A>
408where
409    C::Type: ZnRing + CanHomFrom<J::Type>,
410    J::Type: IntegerRing,
411    <C::Type as ZnRing>::IntegerRingBase: IntegerRing + CanIsoFromTo<J::Type>,
412    C: Clone,
413    J: Clone,
414{
415    fn clone(&self) -> Self {
416        ZnBase {
417            components: self.components.clone(),
418            total_ring: self.total_ring.clone(),
419            unit_vectors: self.unit_vectors.iter().map(|e| self.total_ring.clone_el(e)).collect(),
420            element_allocator: self.element_allocator.clone(),
421        }
422    }
423}
424
425impl<C1: RingStore, J1: RingStore, C2: RingStore, J2: RingStore, A1: Allocator + Clone, A2: Allocator + Clone>
426    CanHomFrom<ZnBase<C2, J2, A2>> for ZnBase<C1, J1, A1>
427where
428    C1::Type: ZnRing + CanHomFrom<C2::Type> + CanHomFrom<J1::Type>,
429    <C1::Type as ZnRing>::IntegerRingBase: IntegerRing + CanIsoFromTo<J1::Type>,
430    C2::Type: ZnRing + CanHomFrom<J2::Type>,
431    <C2::Type as ZnRing>::IntegerRingBase: IntegerRing + CanIsoFromTo<J2::Type>,
432    J1::Type: IntegerRing,
433    J2::Type: IntegerRing,
434{
435    type Homomorphism = Vec<<C1::Type as CanHomFrom<C2::Type>>::Homomorphism>;
436
437    fn has_canonical_hom(&self, from: &ZnBase<C2, J2, A2>) -> Option<Self::Homomorphism> {
438        if self.components.len() == from.components.len() {
439            self.components
440                .iter()
441                .zip(from.components.iter())
442                .map(|(s, f): (&C1, &C2)| s.get_ring().has_canonical_hom(f.get_ring()).ok_or(()))
443                .collect::<Result<Self::Homomorphism, ()>>()
444                .ok()
445        } else {
446            None
447        }
448    }
449
450    fn map_in_ref(&self, from: &ZnBase<C2, J2, A2>, el: &ZnEl<C2, A2>, hom: &Self::Homomorphism) -> Self::Element {
451        assert_eq!(from.len(), el.data.len());
452        self.from_congruence((0..self.len()).map(|i| {
453            self.at(i)
454                .get_ring()
455                .map_in_ref(from.at(i).get_ring(), el.data.at(i), &hom[i])
456        }))
457    }
458
459    fn map_in(&self, from: &ZnBase<C2, J2, A2>, el: ZnEl<C2, A2>, hom: &Self::Homomorphism) -> Self::Element {
460        self.map_in_ref(from, &el, hom)
461    }
462}
463
464impl<C: RingStore, J: RingStore, A: Allocator + Clone> PartialEq for ZnBase<C, J, A>
465where
466    C::Type: ZnRing + CanHomFrom<J::Type>,
467    <C::Type as ZnRing>::IntegerRingBase: IntegerRing + CanIsoFromTo<J::Type>,
468    J::Type: IntegerRing,
469{
470    fn eq(&self, other: &Self) -> bool {
471        self.components.len() == other.components.len()
472            && self
473                .components
474                .iter()
475                .zip(other.components.iter())
476                .all(|(R1, R2)| R1.get_ring() == R2.get_ring())
477    }
478}
479
480impl<C1: RingStore, J1: RingStore, C2: RingStore, J2: RingStore, A1: Allocator + Clone, A2: Allocator + Clone>
481    CanIsoFromTo<ZnBase<C2, J2, A2>> for ZnBase<C1, J1, A1>
482where
483    C1::Type: ZnRing + CanIsoFromTo<C2::Type> + CanHomFrom<J1::Type>,
484    <C1::Type as ZnRing>::IntegerRingBase: IntegerRing + CanIsoFromTo<J1::Type>,
485    C2::Type: ZnRing + CanHomFrom<J2::Type>,
486    <C2::Type as ZnRing>::IntegerRingBase: IntegerRing + CanIsoFromTo<J2::Type>,
487    J1::Type: IntegerRing,
488    J2::Type: IntegerRing,
489{
490    type Isomorphism = Vec<<C1::Type as CanIsoFromTo<C2::Type>>::Isomorphism>;
491
492    fn has_canonical_iso(&self, from: &ZnBase<C2, J2, A2>) -> Option<Self::Isomorphism> {
493        if self.components.len() == from.components.len() {
494            self.components
495                .iter()
496                .zip(from.components.iter())
497                .map(|(s, f): (&C1, &C2)| s.get_ring().has_canonical_iso(f.get_ring()).ok_or(()))
498                .collect::<Result<Self::Isomorphism, ()>>()
499                .ok()
500        } else {
501            None
502        }
503    }
504
505    fn map_out(&self, from: &ZnBase<C2, J2, A2>, el: ZnEl<C1, A1>, iso: &Self::Isomorphism) -> ZnEl<C2, A2> {
506        assert_eq!(self.len(), el.data.len());
507        from.from_congruence((0..from.len()).map(|i| {
508            self.at(i)
509                .get_ring()
510                .map_out(from.at(i).get_ring(), self.at(i).clone_el(el.data.at(i)), &iso[i])
511        }))
512    }
513}
514
515impl<C: RingStore, J: RingStore, K: RingStore, A: Allocator + Clone> CanHomFrom<zn_big::ZnBase<K>> for ZnBase<C, J, A>
516where
517    C::Type: ZnRing + CanHomFrom<J::Type>,
518    J::Type: IntegerRing + CanIsoFromTo<K::Type>,
519    <C::Type as ZnRing>::IntegerRingBase: IntegerRing + CanIsoFromTo<J::Type>,
520    K::Type: IntegerRing,
521{
522    type Homomorphism = (
523        <J::Type as CanHomFrom<K::Type>>::Homomorphism,
524        Vec<<C::Type as CanHomFrom<J::Type>>::Homomorphism>,
525    );
526
527    fn has_canonical_hom(&self, from: &zn_big::ZnBase<K>) -> Option<Self::Homomorphism> {
528        if self.total_ring.get_ring().has_canonical_hom(from).is_some() {
529            Some((
530                self.total_ring.get_ring().has_canonical_hom(from)?,
531                self.components
532                    .iter()
533                    .map(|s| s.get_ring())
534                    .map(|s| s.has_canonical_hom(self.integer_ring().get_ring()).ok_or(()))
535                    .collect::<Result<Vec<_>, ()>>()
536                    .ok()?,
537            ))
538        } else {
539            None
540        }
541    }
542
543    fn map_in(&self, from: &zn_big::ZnBase<K>, el: zn_big::ZnEl<K>, hom: &Self::Homomorphism) -> ZnEl<C, A> {
544        let lift = from.smallest_positive_lift(el);
545        let mapped_lift = <J::Type as CanHomFrom<K::Type>>::map_in(
546            self.integer_ring().get_ring(),
547            from.integer_ring().get_ring(),
548            lift,
549            &hom.0,
550        );
551        self.from_congruence((0..self.len()).map(|i| {
552            self.at(i)
553                .get_ring()
554                .map_in_ref(self.integer_ring().get_ring(), &mapped_lift, &hom.1[i])
555        }))
556    }
557}
558
559impl<C: RingStore, J: RingStore, K: RingStore, A: Allocator + Clone> CanIsoFromTo<zn_big::ZnBase<K>> for ZnBase<C, J, A>
560where
561    C::Type: ZnRing + CanHomFrom<J::Type>,
562    J::Type: IntegerRing + CanIsoFromTo<K::Type>,
563    <C::Type as ZnRing>::IntegerRingBase: IntegerRing + CanIsoFromTo<J::Type>,
564    K::Type: IntegerRing,
565{
566    // we first map each `lift(x[i]) into `self.total_ring.integer_ring(): J`, then reduce it to
567    // `self.total_ring: Zn<J>`, then compute the value `sum_i lift(x[i]) * unit_vectors[i]`
568    // in `self.total_ring: Zn<J>` and then map this to `from: Zn<K>`.
569    type Isomorphism = (
570        <zn_big::ZnBase<J> as CanIsoFromTo<zn_big::ZnBase<K>>>::Isomorphism,
571        <zn_big::ZnBase<J> as CanHomFrom<J::Type>>::Homomorphism,
572    );
573
574    fn has_canonical_iso(&self, from: &zn_big::ZnBase<K>) -> Option<Self::Isomorphism> {
575        Some((
576            <zn_big::ZnBase<J> as CanIsoFromTo<zn_big::ZnBase<K>>>::has_canonical_iso(
577                self.total_ring.get_ring(),
578                from,
579            )?,
580            self.total_ring
581                .get_ring()
582                .has_canonical_hom(self.total_ring.integer_ring().get_ring())?,
583        ))
584    }
585
586    fn map_out(
587        &self,
588        from: &zn_big::ZnBase<K>,
589        el: Self::Element,
590        (final_iso, red): &Self::Isomorphism,
591    ) -> zn_big::ZnEl<K> {
592        assert_eq!(self.len(), el.data.len());
593        let small_integer_ring = self.at(0).integer_ring();
594        let result = <_ as ComputeInnerProduct>::inner_product_ref_fst(
595            self.total_ring.get_ring(),
596            self.components
597                .iter()
598                .zip(el.data)
599                .map(|(R, x): (&C, El<C>)| R.smallest_positive_lift(x))
600                .zip(self.unit_vectors.iter())
601                .map(|(x, u)| {
602                    (
603                        u,
604                        self.total_ring.get_ring().map_in(
605                            self.total_ring.integer_ring().get_ring(),
606                            int_cast(x, self.total_ring.integer_ring(), small_integer_ring),
607                            red,
608                        ),
609                    )
610                }),
611        );
612        return <zn_big::ZnBase<J> as CanIsoFromTo<zn_big::ZnBase<K>>>::map_out(
613            self.total_ring.get_ring(),
614            from,
615            result,
616            final_iso,
617        );
618    }
619}
620
621impl<C: RingStore, J: RingStore, A: Allocator + Clone> CanHomFrom<zn_64::ZnBase> for ZnBase<C, J, A>
622where
623    C::Type: ZnRing + CanHomFrom<J::Type>,
624    J::Type: IntegerRing + CanIsoFromTo<StaticRingBase<i64>>,
625    <C::Type as ZnRing>::IntegerRingBase: IntegerRing + CanIsoFromTo<J::Type>,
626{
627    type Homomorphism = (
628        <Self as CanHomFrom<zn_big::ZnBase<J>>>::Homomorphism,
629        <zn_big::ZnBase<J> as CanHomFrom<zn_64::ZnBase>>::Homomorphism,
630    );
631
632    fn has_canonical_hom(&self, from: &zn_64::ZnBase) -> Option<Self::Homomorphism> {
633        Some((
634            self.has_canonical_hom(self.total_ring.get_ring())?,
635            self.total_ring.get_ring().has_canonical_hom(from)?,
636        ))
637    }
638
639    fn map_in(&self, from: &zn_64::ZnBase, el: zn_64::ZnEl, hom: &Self::Homomorphism) -> ZnEl<C, A> {
640        self.map_in(
641            self.total_ring.get_ring(),
642            self.total_ring.get_ring().map_in(from, el, &hom.1),
643            &hom.0,
644        )
645    }
646}
647
648impl<C: RingStore, J: RingStore, K: IntegerRing, A: Allocator + Clone> CanHomFrom<K> for ZnBase<C, J, A>
649where
650    C::Type: ZnRing + CanHomFrom<J::Type> + CanHomFrom<K>,
651    J::Type: IntegerRing,
652    <C::Type as ZnRing>::IntegerRingBase: IntegerRing + CanIsoFromTo<J::Type>,
653    K: ?Sized,
654{
655    type Homomorphism = Vec<<C::Type as CanHomFrom<K>>::Homomorphism>;
656
657    fn has_canonical_hom(&self, from: &K) -> Option<Self::Homomorphism> {
658        self.components
659            .iter()
660            .map(|R| <C::Type as CanHomFrom<K>>::has_canonical_hom(R.get_ring(), from).ok_or(()))
661            .collect::<Result<Vec<<C::Type as CanHomFrom<K>>::Homomorphism>, ()>>()
662            .ok()
663    }
664
665    fn map_in(&self, from: &K, el: K::Element, hom: &Self::Homomorphism) -> Self::Element {
666        self.from_congruence(
667            (0..self.len()).map(|i| <C::Type as CanHomFrom<K>>::map_in_ref(self.at(i).get_ring(), from, &el, &hom[i])),
668        )
669    }
670}
671
672impl<C: RingStore, J: RingStore, A: Allocator + Clone> DivisibilityRing for ZnBase<C, J, A>
673where
674    C::Type: ZnRing + CanHomFrom<J::Type>,
675    J::Type: IntegerRing,
676    <C::Type as ZnRing>::IntegerRingBase: IntegerRing + CanIsoFromTo<J::Type>,
677{
678    fn checked_left_div(&self, lhs: &Self::Element, rhs: &Self::Element) -> Option<Self::Element> {
679        let mut data = Vec::with_capacity_in(self.len(), self.element_allocator.clone());
680        for i in 0..self.len() {
681            data.push(self.at(i).checked_div(lhs.data.at(i), rhs.data.at(i))?);
682        }
683        return Some(ZnEl { data });
684    }
685}
686
687pub struct FromCongruenceElementCreator<'a, C: RingStore, J: RingStore, A: Allocator + Clone>
688where
689    C::Type: ZnRing + CanHomFrom<J::Type>,
690    J::Type: IntegerRing,
691    <C::Type as ZnRing>::IntegerRingBase: IntegerRing + CanIsoFromTo<J::Type>,
692{
693    ring: &'a ZnBase<C, J, A>,
694}
695
696impl<'a, 'b, C: RingStore, J: RingStore, A: Allocator + Clone> Clone for FromCongruenceElementCreator<'a, C, J, A>
697where
698    C::Type: ZnRing + CanHomFrom<J::Type>,
699    J::Type: IntegerRing,
700    <C::Type as ZnRing>::IntegerRingBase: IntegerRing + CanIsoFromTo<J::Type>,
701{
702    fn clone(&self) -> Self { *self }
703}
704
705impl<'a, 'b, C: RingStore, J: RingStore, A: Allocator + Clone> Copy for FromCongruenceElementCreator<'a, C, J, A>
706where
707    C::Type: ZnRing + CanHomFrom<J::Type>,
708    J::Type: IntegerRing,
709    <C::Type as ZnRing>::IntegerRingBase: IntegerRing + CanIsoFromTo<J::Type>,
710{
711}
712
713impl<'a, 'b, C: RingStore, J: RingStore, A: Allocator + Clone> FnOnce<(&'b [El<C>],)>
714    for FromCongruenceElementCreator<'a, C, J, A>
715where
716    C::Type: ZnRing + CanHomFrom<J::Type>,
717    J::Type: IntegerRing,
718    <C::Type as ZnRing>::IntegerRingBase: IntegerRing + CanIsoFromTo<J::Type>,
719{
720    type Output = <ZnBase<C, J, A> as RingBase>::Element;
721
722    extern "rust-call" fn call_once(mut self, args: (&'b [El<C>],)) -> Self::Output { self.call_mut(args) }
723}
724
725impl<'a, 'b, C: RingStore, J: RingStore, A: Allocator + Clone> FnMut<(&'b [El<C>],)>
726    for FromCongruenceElementCreator<'a, C, J, A>
727where
728    C::Type: ZnRing + CanHomFrom<J::Type>,
729    J::Type: IntegerRing,
730    <C::Type as ZnRing>::IntegerRingBase: IntegerRing + CanIsoFromTo<J::Type>,
731{
732    extern "rust-call" fn call_mut(&mut self, args: (&'b [El<C>],)) -> Self::Output {
733        self.ring
734            .from_congruence(args.0.iter().enumerate().map(|(i, x)| self.ring.at(i).clone_el(x)))
735    }
736}
737
738pub struct CloneComponentElement<'a, C: RingStore, J: RingStore, A: Allocator + Clone>
739where
740    C::Type: ZnRing + CanHomFrom<J::Type>,
741    J::Type: IntegerRing,
742    <C::Type as ZnRing>::IntegerRingBase: IntegerRing + CanIsoFromTo<J::Type>,
743{
744    ring: &'a ZnBase<C, J, A>,
745}
746
747impl<'a, 'b, C: RingStore, J: RingStore, A: Allocator + Clone> Clone for CloneComponentElement<'a, C, J, A>
748where
749    C::Type: ZnRing + CanHomFrom<J::Type>,
750    J::Type: IntegerRing,
751    <C::Type as ZnRing>::IntegerRingBase: IntegerRing + CanIsoFromTo<J::Type>,
752{
753    fn clone(&self) -> Self { *self }
754}
755
756impl<'a, 'b, C: RingStore, J: RingStore, A: Allocator + Clone> Copy for CloneComponentElement<'a, C, J, A>
757where
758    C::Type: ZnRing + CanHomFrom<J::Type>,
759    J::Type: IntegerRing,
760    <C::Type as ZnRing>::IntegerRingBase: IntegerRing + CanIsoFromTo<J::Type>,
761{
762}
763
764impl<'a, 'b, C: RingStore, J: RingStore, A: Allocator + Clone> FnOnce<(usize, &'b El<C>)>
765    for CloneComponentElement<'a, C, J, A>
766where
767    C::Type: ZnRing + CanHomFrom<J::Type>,
768    J::Type: IntegerRing,
769    <C::Type as ZnRing>::IntegerRingBase: IntegerRing + CanIsoFromTo<J::Type>,
770{
771    type Output = El<C>;
772
773    extern "rust-call" fn call_once(mut self, args: (usize, &'b El<C>)) -> Self::Output { self.call_mut(args) }
774}
775
776impl<'a, 'b, C: RingStore, J: RingStore, A: Allocator + Clone> FnMut<(usize, &'b El<C>)>
777    for CloneComponentElement<'a, C, J, A>
778where
779    C::Type: ZnRing + CanHomFrom<J::Type>,
780    J::Type: IntegerRing,
781    <C::Type as ZnRing>::IntegerRingBase: IntegerRing + CanIsoFromTo<J::Type>,
782{
783    extern "rust-call" fn call_mut(&mut self, args: (usize, &'b El<C>)) -> Self::Output { self.call(args) }
784}
785
786impl<'a, 'b, C: RingStore, J: RingStore, A: Allocator + Clone> Fn<(usize, &'b El<C>)>
787    for CloneComponentElement<'a, C, J, A>
788where
789    C::Type: ZnRing + CanHomFrom<J::Type>,
790    J::Type: IntegerRing,
791    <C::Type as ZnRing>::IntegerRingBase: IntegerRing + CanIsoFromTo<J::Type>,
792{
793    extern "rust-call" fn call(&self, args: (usize, &'b El<C>)) -> Self::Output {
794        self.ring.at(args.0).clone_el(args.1)
795    }
796}
797
798impl<C: RingStore, J: RingStore, A: Allocator + Clone> HashableElRing for ZnBase<C, J, A>
799where
800    C::Type: ZnRing + CanHomFrom<J::Type> + HashableElRing,
801    J::Type: IntegerRing,
802    <C::Type as ZnRing>::IntegerRingBase: IntegerRing + CanIsoFromTo<J::Type>,
803{
804    fn hash<H: std::hash::Hasher>(&self, el: &Self::Element, h: &mut H) {
805        for (i, el) in (0..self.components.len()).zip(el.data.iter()) {
806            self.components[i].hash(el, h);
807        }
808    }
809}
810
811impl<C: RingStore, J: RingStore, A: Allocator + Clone> FiniteRingSpecializable for ZnBase<C, J, A>
812where
813    C::Type: ZnRing + CanHomFrom<J::Type>,
814    J::Type: IntegerRing,
815    <C::Type as ZnRing>::IntegerRingBase: IntegerRing + CanIsoFromTo<J::Type>,
816{
817    fn specialize<O: FiniteRingOperation<Self>>(op: O) -> O::Output { op.execute() }
818}
819
820impl<C: RingStore, J: RingStore, A: Allocator + Clone> FiniteRing for ZnBase<C, J, A>
821where
822    C::Type: ZnRing + CanHomFrom<J::Type>,
823    J::Type: IntegerRing,
824    <C::Type as ZnRing>::IntegerRingBase: IntegerRing + CanIsoFromTo<J::Type>,
825{
826    type ElementsIter<'a>
827        = MultiProduct<
828        <C::Type as FiniteRing>::ElementsIter<'a>,
829        FromCongruenceElementCreator<'a, C, J, A>,
830        CloneComponentElement<'a, C, J, A>,
831        Self::Element,
832    >
833    where
834        Self: 'a;
835
836    fn elements<'a>(&'a self) -> Self::ElementsIter<'a> {
837        multi_cartesian_product(
838            (0..self.len()).map(|i| self.at(i).elements()),
839            FromCongruenceElementCreator { ring: self },
840            CloneComponentElement { ring: self },
841        )
842    }
843
844    fn random_element<G: FnMut() -> u64>(&self, mut rng: G) -> ZnEl<C, A> {
845        self.from_congruence((0..self.len()).map(|i| self.at(i).random_element(&mut rng)))
846    }
847
848    fn size<I: RingStore + Copy>(&self, ZZ: I) -> Option<El<I>>
849    where
850        I::Type: IntegerRing,
851    {
852        if ZZ.get_ring().representable_bits().is_none()
853            || self.integer_ring().abs_log2_ceil(self.modulus()) < ZZ.get_ring().representable_bits()
854        {
855            Some(int_cast(
856                self.integer_ring().clone_el(self.modulus()),
857                ZZ,
858                self.integer_ring(),
859            ))
860        } else {
861            None
862        }
863    }
864}
865
866impl<C: RingStore, J: RingStore, A: Allocator + Clone> PrincipalIdealRing for ZnBase<C, J, A>
867where
868    C::Type: ZnRing + CanHomFrom<J::Type>,
869    J::Type: IntegerRing,
870    <C::Type as ZnRing>::IntegerRingBase: IntegerRing + CanIsoFromTo<J::Type>,
871{
872    fn checked_div_min(&self, lhs: &Self::Element, rhs: &Self::Element) -> Option<Self::Element> {
873        let mut data = Vec::with_capacity_in(self.len(), self.element_allocator.clone());
874        for i in 0..self.len() {
875            data.push(self.at(i).checked_div_min(lhs.data.at(i), rhs.data.at(i))?);
876        }
877        return Some(ZnEl { data });
878    }
879
880    fn extended_ideal_gen(
881        &self,
882        lhs: &Self::Element,
883        rhs: &Self::Element,
884    ) -> (Self::Element, Self::Element, Self::Element) {
885        let mut result = (self.zero(), self.zero(), self.zero());
886        for (i, Zn) in self.as_iter().enumerate() {
887            (result.0.data[i], result.1.data[i], result.2.data[i]) = Zn.extended_ideal_gen(&lhs.data[i], &rhs.data[i]);
888        }
889        return result;
890    }
891}
892
893impl<C: RingStore, J: RingStore, A: Allocator + Clone> ZnRing for ZnBase<C, J, A>
894where
895    C::Type: ZnRing + CanHomFrom<J::Type>,
896    J::Type: IntegerRing,
897    <C::Type as ZnRing>::IntegerRingBase: IntegerRing + CanIsoFromTo<J::Type>,
898{
899    type IntegerRingBase = J::Type;
900    type IntegerRing = J;
901
902    fn integer_ring(&self) -> &Self::IntegerRing { self.total_ring.integer_ring() }
903
904    fn modulus(&self) -> &El<Self::IntegerRing> { self.total_ring.modulus() }
905
906    fn smallest_positive_lift(&self, el: Self::Element) -> El<Self::IntegerRing> {
907        self.total_ring
908            .smallest_positive_lift(<Self as CanIsoFromTo<zn_big::ZnBase<J>>>::map_out(
909                self,
910                self.total_ring.get_ring(),
911                el,
912                &<Self as CanIsoFromTo<zn_big::ZnBase<J>>>::has_canonical_iso(self, self.total_ring.get_ring())
913                    .unwrap(),
914            ))
915    }
916
917    fn smallest_lift(&self, el: Self::Element) -> El<Self::IntegerRing> {
918        self.total_ring
919            .smallest_lift(<Self as CanIsoFromTo<zn_big::ZnBase<J>>>::map_out(
920                self,
921                self.total_ring.get_ring(),
922                el,
923                &<Self as CanIsoFromTo<zn_big::ZnBase<J>>>::has_canonical_iso(self, self.total_ring.get_ring())
924                    .unwrap(),
925            ))
926    }
927
928    fn is_field(&self) -> bool { self.components.len() == 1 && self.components[0].is_field() }
929
930    fn from_int_promise_reduced(&self, x: El<Self::IntegerRing>) -> Self::Element {
931        debug_assert!(!self.integer_ring().is_neg(&x));
932        debug_assert!(self.integer_ring().is_lt(&x, self.modulus()));
933        RingRef::new(self).can_hom(self.integer_ring()).unwrap().map(x)
934    }
935}
936
937impl<C: RingStore, J: RingStore, A: Allocator + Clone> SerializableElementRing for ZnBase<C, J, A>
938where
939    C::Type: ZnRing + CanHomFrom<J::Type> + SerializableElementRing,
940    J::Type: IntegerRing + SerializableElementRing,
941    <C::Type as ZnRing>::IntegerRingBase: IntegerRing + CanIsoFromTo<J::Type>,
942{
943    fn serialize<S>(&self, el: &Self::Element, serializer: S) -> Result<S::Ok, S::Error>
944    where
945        S: serde::Serializer,
946    {
947        if serializer.is_human_readable() {
948            self.total_ring.get_ring().serialize(
949                &RingRef::new(self).can_iso(&self.total_ring).unwrap().map_ref(el),
950                serializer,
951            )
952        } else {
953            let el_congruence = self.get_congruence(el);
954            SerializableNewtypeStruct::new(
955                "RNSZnEl",
956                SerializableSeq::new_with_len(
957                    (0..self.len()).map(|i| SerializeWithRing::new(el_congruence.at(i), self.at(i))),
958                    self.len(),
959                ),
960            )
961            .serialize(serializer)
962        }
963    }
964
965    fn deserialize<'de, D>(&self, deserializer: D) -> Result<Self::Element, D::Error>
966    where
967        D: serde::Deserializer<'de>,
968    {
969        if deserializer.is_human_readable() {
970            Ok(RingRef::new(self)
971                .can_hom(&self.total_ring)
972                .unwrap()
973                .map(self.total_ring.get_ring().deserialize(deserializer)?))
974        } else {
975            let dummy_ring = self.at(0);
976            DeserializeSeedNewtypeStruct::new(
977                "RNSZnEl",
978                DeserializeSeedSeq::new(
979                    self.as_iter()
980                        .map(DeserializeWithRing::new)
981                        .chain([DeserializeWithRing::new(dummy_ring)]),
982                    Vec::with_capacity_in(self.len(), self.element_allocator.clone()),
983                    |mut current, next| {
984                        current.push(next);
985                        current
986                    },
987                ),
988            )
989            .deserialize(deserializer)
990            .map(|result| ZnEl { data: result })
991        }
992    }
993}
994
995#[cfg(test)]
996use crate::primitive_int::StaticRing;
997
998#[cfg(test)]
999const EDGE_CASE_ELEMENTS: [i32; 9] = [0, 1, 7, 9, 62, 8, 10, 11, 12];
1000
1001#[test]
1002fn test_ring_axioms() {
1003    let ring = Zn::create_from_primes(vec![7, 11], StaticRing::<i64>::RING);
1004    crate::ring::generic_tests::test_ring_axioms(
1005        &ring,
1006        EDGE_CASE_ELEMENTS.iter().cloned().map(|x| ring.int_hom().map(x)),
1007    )
1008}
1009
1010#[test]
1011fn test_hash_axioms() {
1012    let ring = Zn::create_from_primes(vec![7, 11], StaticRing::<i64>::RING);
1013    crate::ring::generic_tests::test_hash_axioms(
1014        &ring,
1015        EDGE_CASE_ELEMENTS.iter().cloned().map(|x| ring.int_hom().map(x)),
1016    )
1017}
1018
1019#[test]
1020fn test_map_in_map_out() {
1021    let ring1 = Zn::create_from_primes(vec![7, 11, 17], StaticRing::<i64>::RING);
1022    let ring2 = zn_big::Zn::new(StaticRing::<i64>::RING, 7 * 11 * 17);
1023    for x in [0, 1, 7, 8, 9, 10, 11, 17, 7 * 17, 11 * 8, 11 * 17, 7 * 11 * 17 - 1] {
1024        let value = ring2.int_hom().map(x);
1025        assert!(ring2.eq_el(
1026            &value,
1027            &ring1.can_iso(&ring2).unwrap().map(ring1.coerce(&ring2, value.clone()))
1028        ));
1029    }
1030}
1031
1032#[test]
1033fn test_canonical_iso_axioms_zn_big() {
1034    let from = zn_big::Zn::new(StaticRing::<i128>::RING, 7 * 11);
1035    let to = Zn::create_from_primes(vec![7, 11], StaticRing::<i64>::RING);
1036    crate::ring::generic_tests::test_hom_axioms(
1037        &from,
1038        &to,
1039        EDGE_CASE_ELEMENTS.iter().cloned().map(|x| from.int_hom().map(x)),
1040    );
1041    crate::ring::generic_tests::test_iso_axioms(
1042        &from,
1043        &to,
1044        EDGE_CASE_ELEMENTS.iter().cloned().map(|x| from.int_hom().map(x)),
1045    );
1046
1047    let from = zn_big::Zn::new(StaticRing::<i128>::RING, 7 * 11 * 65537);
1048    let to = Zn::create_from_primes(vec![7, 11, 65537], StaticRing::<i128>::RING);
1049    crate::ring::generic_tests::test_hom_axioms(&from, &to, from.elements().step_by(65536));
1050    crate::ring::generic_tests::test_iso_axioms(&from, &to, from.elements().step_by(65536));
1051}
1052
1053#[test]
1054fn test_canonical_hom_axioms_static_int() {
1055    let from = StaticRing::<i32>::RING;
1056    let to = Zn::create_from_primes(vec![7, 11], StaticRing::<i64>::RING);
1057    crate::ring::generic_tests::test_hom_axioms(
1058        &from,
1059        to,
1060        EDGE_CASE_ELEMENTS.iter().cloned().map(|x| from.int_hom().map(x)),
1061    );
1062}
1063
1064#[test]
1065fn test_zn_ring_axioms() {
1066    let ring = Zn::create_from_primes(vec![7, 11], StaticRing::<i64>::RING);
1067    super::generic_tests::test_zn_axioms(ring);
1068}
1069
1070#[test]
1071fn test_zn_map_in_large_int() {
1072    let ring = Zn::create_from_primes(vec![7, 11], BigIntRing::RING);
1073    super::generic_tests::test_map_in_large_int(ring);
1074
1075    let R = Zn::create_from_primes(vec![3, 5, 7], BigIntRing::RING);
1076    let S = BigIntRing::RING;
1077    assert!(R.eq_el(&R.int_hom().map(120493), &R.coerce(&S, S.int_hom().map(120493))));
1078}
1079
1080#[test]
1081fn test_principal_ideal_ring_axioms() {
1082    let R = Zn::create_from_primes(vec![5], BigIntRing::RING);
1083    crate::pid::generic_tests::test_principal_ideal_ring_axioms(&R, R.elements());
1084
1085    let R = Zn::create_from_primes(vec![3, 5], BigIntRing::RING);
1086    crate::pid::generic_tests::test_principal_ideal_ring_axioms(&R, R.elements());
1087
1088    let R = Zn::create_from_primes(vec![2, 3, 5], BigIntRing::RING);
1089    crate::pid::generic_tests::test_principal_ideal_ring_axioms(&R, R.elements());
1090
1091    let R = Zn::create_from_primes(vec![3, 5, 2], BigIntRing::RING);
1092    let modulo = R.int_hom();
1093    crate::pid::generic_tests::test_principal_ideal_ring_axioms(
1094        &R,
1095        [-1, 0, 1, 3, 2, 4, 5, 9, 18, 15, 30].into_iter().map(|x| modulo.map(x)),
1096    );
1097}
1098
1099#[test]
1100fn test_finite_ring_axioms() {
1101    crate::rings::finite::generic_tests::test_finite_ring_axioms(&Zn::create_from_primes(
1102        vec![3, 5, 7, 11],
1103        StaticRing::<i64>::RING,
1104    ));
1105    crate::rings::finite::generic_tests::test_finite_ring_axioms(&Zn::create_from_primes(
1106        vec![3, 5],
1107        StaticRing::<i64>::RING,
1108    ));
1109    crate::rings::finite::generic_tests::test_finite_ring_axioms(&Zn::create_from_primes(
1110        vec![3],
1111        StaticRing::<i64>::RING,
1112    ));
1113    crate::rings::finite::generic_tests::test_finite_ring_axioms(&Zn::create_from_primes(
1114        vec![2],
1115        StaticRing::<i64>::RING,
1116    ));
1117}
1118
1119#[test]
1120fn test_not_prime() {
1121    let ring = Zn::new(vec![zn_64::Zn::new(15), zn_64::Zn::new(7)], StaticRing::<i64>::RING);
1122    let equivalent_ring = zn_big::Zn::new(StaticRing::<i64>::RING, 15 * 7);
1123    crate::ring::generic_tests::test_ring_axioms(&ring, ring.elements());
1124    crate::divisibility::generic_tests::test_divisibility_axioms(&ring, ring.elements());
1125    crate::homomorphism::generic_tests::test_homomorphism_axioms(
1126        ring.can_hom(&equivalent_ring).unwrap(),
1127        equivalent_ring.elements(),
1128    );
1129    crate::homomorphism::generic_tests::test_homomorphism_axioms(
1130        ring.can_iso(&equivalent_ring).unwrap(),
1131        ring.elements(),
1132    );
1133}
1134
1135#[test]
1136fn test_serialization() {
1137    let ring = Zn::create_from_primes(vec![3, 5, 7], StaticRing::<i64>::RING);
1138    crate::serialization::generic_tests::test_serialization(&ring, ring.elements());
1139}
1140
1141#[test]
1142#[should_panic]
1143fn test_not_coprime() { _ = Zn::new(vec![zn_64::Zn::new(15), zn_64::Zn::new(35)], StaticRing::<i64>::RING); }
1144
1145#[test]
1146fn test_format() {
1147    let ring = Zn::new(
1148        [
1149            72057594035352641,
1150            72057594035418113,
1151            72057594036334721,
1152            72057594036945793,
1153        ]
1154        .iter()
1155        .map(|p| zn_64::Zn::new(*p))
1156        .collect(),
1157        BigIntRing::RING,
1158    );
1159    assert_eq!("1", format!("{}", ring.format(&ring.int_hom().map(1))));
1160}