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