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
19pub 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
102pub 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 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 #[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 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 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 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 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 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 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}