feanor_math/rings/zn/
zn_rns.rs

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