1use super::field::AsFieldBase;
2use super::finite::FiniteRing;
3use crate::algorithms;
4use crate::divisibility::{DivisibilityRing, DivisibilityRingStore};
5use crate::homomorphism::*;
6use crate::integer::*;
7use crate::ordered::*;
8use crate::pid::{EuclideanRingStore, PrincipalIdealRing, *};
9use crate::primitive_int::StaticRing;
10use crate::ring::*;
11use crate::rings::finite::FiniteRingStore;
12
13pub mod zn_64;
16pub mod zn_big;
20pub mod zn_rns;
23pub mod zn_static;
26
27pub trait ZnRing: PrincipalIdealRing + FiniteRing + CanHomFrom<Self::IntegerRingBase> {
29 type IntegerRingBase: IntegerRing + ?Sized;
32 type IntegerRing: RingStore<Type = Self::IntegerRingBase>;
33
34 fn integer_ring(&self) -> &Self::IntegerRing;
35 fn modulus(&self) -> &El<Self::IntegerRing>;
36
37 fn smallest_positive_lift(&self, el: Self::Element) -> El<Self::IntegerRing>;
43
44 fn any_lift(&self, el: Self::Element) -> El<Self::IntegerRing> { self.smallest_positive_lift(el) }
50
51 fn from_int_promise_reduced(&self, x: El<Self::IntegerRing>) -> Self::Element;
64
65 fn smallest_lift(&self, el: Self::Element) -> El<Self::IntegerRing> {
71 let result = self.smallest_positive_lift(el);
72 let mut mod_half = self.integer_ring().clone_el(self.modulus());
73 self.integer_ring().euclidean_div_pow_2(&mut mod_half, 1);
74 if self.integer_ring().is_gt(&result, &mod_half) {
75 return self.integer_ring().sub_ref_snd(result, self.modulus());
76 } else {
77 return result;
78 }
79 }
80
81 fn is_field(&self) -> bool { algorithms::miller_rabin::is_prime_base(RingRef::new(self), 10) }
83}
84
85#[stability::unstable(feature = "enable")]
91pub trait FromModulusCreateableZnRing: Sized + ZnRing {
92 fn from_modulus<F, E>(create_modulus: F) -> Result<Self, E>
93 where
94 F: FnOnce(&Self::IntegerRingBase) -> Result<El<Self::IntegerRing>, E>;
95}
96
97pub mod generic_impls {
98 use std::alloc::Global;
99 use std::marker::PhantomData;
100
101 use crate::algorithms::convolution::STANDARD_CONVOLUTION;
102 use crate::algorithms::int_bisect;
103 use crate::divisibility::DivisibilityRingStore;
104 use crate::field::*;
105 use crate::integer::{IntegerRing, IntegerRingStore};
106 use crate::ordered::*;
107 use crate::primitive_int::{StaticRing, StaticRingBase};
108 use crate::rings::extension::galois_field::{GaloisField, GaloisFieldOver};
109 use crate::rings::zn::*;
110
111 pub struct BigIntToZnHom<I: ?Sized + IntegerRing, J: ?Sized + IntegerRing, R: ?Sized + ZnRing>
115 where
116 I: CanIsoFromTo<R::IntegerRingBase> + CanIsoFromTo<J>,
117 {
118 highbit_mod: usize,
119 highbit_bound: usize,
120 int_ring: PhantomData<I>,
121 to_large_int_ring: PhantomData<J>,
122 hom: <I as CanHomFrom<R::IntegerRingBase>>::Homomorphism,
123 iso: <I as CanIsoFromTo<R::IntegerRingBase>>::Isomorphism,
124 iso2: <I as CanIsoFromTo<J>>::Isomorphism,
125 }
126
127 #[stability::unstable(feature = "enable")]
132 pub fn has_canonical_hom_from_bigint<I: ?Sized + IntegerRing, J: ?Sized + IntegerRing, R: ?Sized + ZnRing>(
133 from: &I,
134 to: &R,
135 to_large_int_ring: &J,
136 bounded_reduce_bound: Option<&J::Element>,
137 ) -> Option<BigIntToZnHom<I, J, R>>
138 where
139 I: CanIsoFromTo<R::IntegerRingBase> + CanIsoFromTo<J>,
140 {
141 if let Some(bound) = bounded_reduce_bound {
142 Some(BigIntToZnHom {
143 highbit_mod: to.integer_ring().abs_highest_set_bit(to.modulus()).unwrap(),
144 highbit_bound: to_large_int_ring.abs_highest_set_bit(bound).unwrap(),
145 int_ring: PhantomData,
146 to_large_int_ring: PhantomData,
147 hom: from.has_canonical_hom(to.integer_ring().get_ring())?,
148 iso: from.has_canonical_iso(to.integer_ring().get_ring())?,
149 iso2: from.has_canonical_iso(to_large_int_ring)?,
150 })
151 } else {
152 Some(BigIntToZnHom {
153 highbit_mod: to.integer_ring().abs_highest_set_bit(to.modulus()).unwrap(),
154 highbit_bound: usize::MAX,
155 int_ring: PhantomData,
156 to_large_int_ring: PhantomData,
157 hom: from.has_canonical_hom(to.integer_ring().get_ring())?,
158 iso: from.has_canonical_iso(to.integer_ring().get_ring())?,
159 iso2: from.has_canonical_iso(to_large_int_ring)?,
160 })
161 }
162 }
163
164 #[stability::unstable(feature = "enable")]
196 pub fn map_in_from_bigint<I: ?Sized + IntegerRing, J: ?Sized + IntegerRing, R: ?Sized + ZnRing, F, G>(
197 from: &I,
198 to: &R,
199 to_large_int_ring: &J,
200 el: I::Element,
201 hom: &BigIntToZnHom<I, J, R>,
202 from_positive_representative_exact: F,
203 from_positive_representative_bounded: G,
204 ) -> R::Element
205 where
206 I: CanIsoFromTo<R::IntegerRingBase> + CanIsoFromTo<J>,
207 F: FnOnce(El<R::IntegerRing>) -> R::Element,
208 G: FnOnce(J::Element) -> R::Element,
209 {
210 let (neg, n) = if from.is_neg(&el) {
211 (true, from.negate(el))
212 } else {
213 (false, el)
214 };
215 let ZZ = to.integer_ring().get_ring();
216 let highbit_el = from.abs_highest_set_bit(&n).unwrap_or(0);
217
218 let reduced = if highbit_el < hom.highbit_mod {
219 from_positive_representative_exact(from.map_out(ZZ, n, &hom.iso))
220 } else if highbit_el < hom.highbit_bound {
221 from_positive_representative_bounded(from.map_out(to_large_int_ring, n, &hom.iso2))
222 } else {
223 from_positive_representative_exact(from.map_out(
224 ZZ,
225 from.euclidean_rem(n, &from.map_in_ref(ZZ, to.modulus(), &hom.hom)),
226 &hom.iso,
227 ))
228 };
229 if neg { to.negate(reduced) } else { reduced }
230 }
231
232 #[stability::unstable(feature = "enable")]
236 pub fn random_element<R: ZnRing, G: FnMut() -> u64>(ring: &R, rng: G) -> R::Element {
237 ring.map_in(
238 ring.integer_ring().get_ring(),
239 ring.integer_ring().get_uniformly_random(ring.modulus(), rng),
240 &ring.has_canonical_hom(ring.integer_ring().get_ring()).unwrap(),
241 )
242 }
243
244 #[stability::unstable(feature = "enable")]
247 pub fn checked_left_div<R: ZnRingStore>(ring: R, lhs: &El<R>, rhs: &El<R>) -> Option<El<R>>
248 where
249 R::Type: ZnRing,
250 {
251 if ring.is_zero(lhs) {
252 return Some(ring.zero());
253 }
254 let int_ring = ring.integer_ring();
255 let lhs_lift = ring.smallest_positive_lift(ring.clone_el(lhs));
256 let rhs_lift = ring.smallest_positive_lift(ring.clone_el(rhs));
257 let (s, _, d) = int_ring.extended_ideal_gen(&rhs_lift, ring.modulus());
258 if let Some(quotient) = int_ring.checked_div(&lhs_lift, &d) {
259 Some(ring.mul(ring.coerce(int_ring, quotient), ring.coerce(int_ring, s)))
260 } else {
261 None
262 }
263 }
264
265 #[stability::unstable(feature = "enable")]
266 pub fn checked_div_min<R: ZnRingStore>(ring: R, lhs: &El<R>, rhs: &El<R>) -> Option<El<R>>
267 where
268 R::Type: ZnRing,
269 {
270 if ring.is_zero(lhs) && ring.is_zero(rhs) {
271 return Some(ring.one());
272 }
273 assert!(ring.is_noetherian());
274 let int_ring = ring.integer_ring();
275 let rhs_ann = int_ring
276 .checked_div(
277 ring.modulus(),
278 &int_ring.ideal_gen(ring.modulus(), &ring.smallest_positive_lift(ring.clone_el(rhs))),
279 )
280 .unwrap();
281 let some_sol = ring.smallest_positive_lift(ring.checked_div(lhs, rhs)?);
282 let minimal_solution = int_ring.euclidean_rem(some_sol, &rhs_ann);
283 if int_ring.is_zero(&minimal_solution) {
284 return Some(ring.coerce(&int_ring, rhs_ann));
285 } else {
286 return Some(ring.coerce(&int_ring, minimal_solution));
287 }
288 }
289
290 #[stability::unstable(feature = "enable")]
291 pub fn interpolation_ring<R: ZnRingStore>(ring: R, count: usize) -> GaloisFieldOver<R>
292 where
293 R: Clone,
294 R::Type: ZnRing + Field + SelfIso + CanHomFrom<StaticRingBase<i64>>,
295 {
296 let ZZbig = BigIntRing::RING;
297 let modulus = int_cast(ring.integer_ring().clone_el(ring.modulus()), ZZbig, ring.integer_ring());
298 let count = int_cast(count.try_into().unwrap(), ZZbig, StaticRing::<i64>::RING);
299 let degree = int_bisect::find_root_floor(StaticRing::<i64>::RING, 1, |d| {
300 if *d > 0 && ZZbig.is_gt(&ZZbig.pow(ZZbig.clone_el(&modulus), *d as usize), &count) {
301 1
302 } else {
303 -1
304 }
305 }) + 1;
306 assert!(degree >= 1);
307 return GaloisField::new_with_convolution(ring, degree as usize, Global, STANDARD_CONVOLUTION);
308 }
309}
310
311pub trait ZnRingStore: FiniteRingStore
313where
314 Self::Type: ZnRing,
315{
316 delegate! { ZnRing, fn integer_ring(&self) -> &<Self::Type as ZnRing>::IntegerRing }
317 delegate! { ZnRing, fn modulus(&self) -> &El<<Self::Type as ZnRing>::IntegerRing> }
318 delegate! { ZnRing, fn smallest_positive_lift(&self, el: El<Self>) -> El<<Self::Type as ZnRing>::IntegerRing> }
319 delegate! { ZnRing, fn smallest_lift(&self, el: El<Self>) -> El<<Self::Type as ZnRing>::IntegerRing> }
320 delegate! { ZnRing, fn any_lift(&self, el: El<Self>) -> El<<Self::Type as ZnRing>::IntegerRing> }
321 delegate! { ZnRing, fn is_field(&self) -> bool }
322
323 fn as_field(self) -> Result<RingValue<AsFieldBase<Self>>, Self>
324 where
325 Self: Sized,
326 {
327 if self.is_field() {
328 Ok(RingValue::from(AsFieldBase::promise_is_perfect_field(self)))
329 } else {
330 Err(self)
331 }
332 }
333}
334
335impl<R: RingStore> ZnRingStore for R where R::Type: ZnRing {}
336
337pub trait ZnOperation {
342 type Output<'a>
343 where
344 Self: 'a;
345
346 fn call<'a, R>(self, ring: R) -> Self::Output<'a>
347 where
348 Self: 'a,
349 R: 'a + RingStore + Send + Sync,
350 R::Type: ZnRing,
351 El<R>: Send;
352}
353
354pub fn choose_zn_impl<'a, I, F>(ZZ: I, n: El<I>, f: F) -> F::Output<'a>
397where
398 I: 'a + RingStore,
399 I::Type: IntegerRing,
400 F: ZnOperation,
401{
402 if ZZ.abs_highest_set_bit(&n).unwrap_or(0) < 57 {
403 f.call(zn_64::Zn::new(StaticRing::<i64>::RING.coerce(&ZZ, n) as u64))
404 } else {
405 f.call(zn_big::Zn::new(BigIntRing::RING, int_cast(n, &BigIntRing::RING, &ZZ)))
406 }
407}
408
409#[test]
410fn test_choose_zn_impl() {
411 let int_value = 4;
412 struct DoStuff {
414 int_value: i64,
415 }
416 impl ZnOperation for DoStuff {
417 type Output<'a>
418 = ()
419 where
420 Self: 'a;
421
422 fn call<'a, R>(self, Zn: R)
423 where
424 R: 'a + RingStore,
425 R::Type: ZnRing,
426 {
427 let value = Zn.coerce(
428 Zn.integer_ring(),
429 int_cast(self.int_value, Zn.integer_ring(), &StaticRing::<i64>::RING),
430 );
431 assert_el_eq!(Zn, Zn.int_hom().map(-1), Zn.mul_ref(&value, &value));
432 }
433 }
434 choose_zn_impl(StaticRing::<i64>::RING, 17, DoStuff { int_value });
435}
436
437#[derive(Debug, Clone, Copy, PartialEq, Eq)]
438enum ReductionMapRequirements {
439 SmallestLift,
440 ExplicitReduce,
441}
442
443pub struct ZnReductionMap<R, S>
455where
456 R: RingStore,
457 R::Type: ZnRing,
458 S: RingStore,
459 S::Type: ZnRing,
460{
461 from: R,
462 to: S,
463 fraction_of_quotients: El<R>,
464 to_modulus: El<<R::Type as ZnRing>::IntegerRing>,
465 to_from_int: <S::Type as CanHomFrom<<S::Type as ZnRing>::IntegerRingBase>>::Homomorphism,
466 from_from_int: <R::Type as CanHomFrom<<R::Type as ZnRing>::IntegerRingBase>>::Homomorphism,
467 map_forward_requirement: ReductionMapRequirements,
468}
469
470impl<R, S> ZnReductionMap<R, S>
471where
472 R: RingStore,
473 R::Type: ZnRing,
474 S: RingStore,
475 S::Type: ZnRing,
476{
477 pub fn new(from: R, to: S) -> Option<Self> {
478 let from_char = from.characteristic(&BigIntRing::RING).unwrap();
479 let to_char = to.characteristic(&BigIntRing::RING).unwrap();
480 if let Some(frac) = BigIntRing::RING.checked_div(&from_char, &to_char) {
481 let map_forward_requirement: ReductionMapRequirements =
482 if to.integer_ring().get_ring().representable_bits().is_none()
483 || BigIntRing::RING.is_lt(
484 &from_char,
485 &BigIntRing::RING.power_of_two(to.integer_ring().get_ring().representable_bits().unwrap()),
486 )
487 {
488 ReductionMapRequirements::SmallestLift
489 } else {
490 ReductionMapRequirements::ExplicitReduce
491 };
492 Some(Self {
493 map_forward_requirement,
494 to_modulus: int_cast(
495 to.integer_ring().clone_el(to.modulus()),
496 from.integer_ring(),
497 to.integer_ring(),
498 ),
499 to_from_int: to.get_ring().has_canonical_hom(to.integer_ring().get_ring()).unwrap(),
500 from_from_int: from
501 .get_ring()
502 .has_canonical_hom(from.integer_ring().get_ring())
503 .unwrap(),
504 fraction_of_quotients: from.can_hom(from.integer_ring()).unwrap().map(int_cast(
505 frac,
506 from.integer_ring(),
507 BigIntRing::RING,
508 )),
509 from,
510 to,
511 })
512 } else {
513 None
514 }
515 }
516
517 pub fn mul_quotient_fraction(&self, x: El<S>) -> El<R> {
536 self.from.mul_ref_snd(self.any_preimage(x), &self.fraction_of_quotients)
537 }
538
539 pub fn smallest_lift(&self, x: El<S>) -> El<R> {
559 self.from.get_ring().map_in(
560 self.from.integer_ring().get_ring(),
561 int_cast(
562 self.to.smallest_lift(x),
563 self.from.integer_ring(),
564 self.to.integer_ring(),
565 ),
566 &self.from_from_int,
567 )
568 }
569
570 pub fn any_preimage(&self, x: El<S>) -> El<R> {
571 self.smallest_lift(x)
576 }
577
578 pub fn smallest_lift_ref(&self, x: &El<S>) -> El<R> { self.smallest_lift(self.codomain().clone_el(x)) }
579}
580
581impl<R, S> Homomorphism<R::Type, S::Type> for ZnReductionMap<R, S>
582where
583 R: RingStore,
584 R::Type: ZnRing,
585 S: RingStore,
586 S::Type: ZnRing,
587{
588 type CodomainStore = S;
589 type DomainStore = R;
590
591 fn map(&self, x: El<R>) -> El<S> {
592 let value = match self.map_forward_requirement {
593 ReductionMapRequirements::SmallestLift => self.from.smallest_lift(x),
594 ReductionMapRequirements::ExplicitReduce => self
595 .from
596 .integer_ring()
597 .euclidean_rem(self.from.any_lift(x), &self.to_modulus),
598 };
599 self.to.get_ring().map_in(
600 self.to.integer_ring().get_ring(),
601 int_cast(value, self.to.integer_ring(), self.from.integer_ring()),
602 &self.to_from_int,
603 )
604 }
605
606 fn codomain(&self) -> &Self::CodomainStore { &self.to }
607
608 fn domain(&self) -> &Self::DomainStore { &self.from }
609}
610
611#[cfg(any(test, feature = "generic_tests"))]
612pub mod generic_tests {
613
614 use super::*;
615 use crate::primitive_int::{StaticRing, StaticRingBase};
616
617 pub fn test_zn_axioms<R: RingStore>(R: R)
618 where
619 R::Type: ZnRing,
620 <R::Type as ZnRing>::IntegerRingBase: CanIsoFromTo<StaticRingBase<i128>> + CanIsoFromTo<StaticRingBase<i32>>,
621 {
622 let ZZ = R.integer_ring();
623 let n = R.modulus();
624
625 assert!(R.is_zero(&R.coerce(ZZ, ZZ.clone_el(n))));
626 assert!(R.is_field() == algorithms::miller_rabin::is_prime(ZZ, n, 10));
627
628 let mut k = ZZ.one();
629 while ZZ.is_lt(&k, &n) {
630 assert!(!R.is_zero(&R.coerce(ZZ, ZZ.clone_el(&k))));
631 ZZ.add_assign(&mut k, ZZ.one());
632 }
633
634 let all_elements = R.elements().collect::<Vec<_>>();
635 assert_eq!(
636 int_cast(ZZ.clone_el(n), &StaticRing::<i128>::RING, &ZZ) as usize,
637 all_elements.len()
638 );
639 for (i, x) in all_elements.iter().enumerate() {
640 for (j, y) in all_elements.iter().enumerate() {
641 assert!(i == j || !R.eq_el(x, y));
642 }
643 }
644 }
645
646 pub fn test_map_in_large_int<R: RingStore>(R: R)
647 where
648 <R as RingStore>::Type: ZnRing + CanHomFrom<BigIntRingBase>,
649 {
650 let ZZ_big = BigIntRing::RING;
651 let n = ZZ_big.power_of_two(1000);
652 let x = R.coerce(&ZZ_big, n);
653 assert!(R.eq_el(&R.pow(R.int_hom().map(2), 1000), &x));
654 }
655}
656
657#[test]
658fn test_reduction_map_large_value() {
659 let ring1 = zn_64::Zn::new(1 << 42);
660 let ring2 = zn_big::Zn::new(BigIntRing::RING, BigIntRing::RING.power_of_two(666));
661 let reduce = ZnReductionMap::new(&ring2, ring1).unwrap();
662 assert_el_eq!(ring1, ring1.zero(), reduce.map(ring2.pow(ring2.int_hom().map(2), 665)));
663}
664
665#[test]
666fn test_reduction_map() {
667 let ring1 = zn_64::Zn::new(257);
668 let ring2 = zn_big::Zn::new(StaticRing::<i128>::RING, 257 * 7);
669
670 crate::homomorphism::generic_tests::test_homomorphism_axioms(
671 ZnReductionMap::new(&ring2, &ring1).unwrap(),
672 ring2.elements().step_by(8),
673 );
674
675 let ring1 = zn_big::Zn::new(StaticRing::<i16>::RING, 3);
676 let ring2 = zn_big::Zn::new(BigIntRing::RING, BigIntRing::RING.int_hom().map(65537 * 3));
677
678 crate::homomorphism::generic_tests::test_homomorphism_axioms(
679 ZnReductionMap::new(&ring2, &ring1).unwrap(),
680 ring2.elements().step_by(1024),
681 );
682}
683
684#[test]
685fn test_generic_impl_checked_div_min() {
686 let ring = zn_64::Zn::new(5 * 7 * 11 * 13);
687 let actual = ring.annihilator(&ring.int_hom().map(1001));
688 let expected = ring.int_hom().map(5);
689 assert!(ring.checked_div(&expected, &actual).is_some());
690 assert!(ring.checked_div(&actual, &expected).is_some());
691
692 let actual = ring.annihilator(&ring.zero());
693 let expected = ring.one();
694 assert!(ring.checked_div(&expected, &actual).is_some());
695 assert!(ring.checked_div(&actual, &expected).is_some());
696}