1use crate::divisibility::DivisibilityRingStore;
2use crate::pid::EuclideanRingStore;
3use crate::pid::PrincipalIdealRing;
4use crate::primitive_int::StaticRing;
5use crate::ring::*;
6use crate::divisibility::DivisibilityRing;
7use crate::algorithms;
8use crate::integer::*;
9use crate::homomorphism::*;
10use crate::ordered::*;
11use super::field::AsFieldBase;
12use super::finite::FiniteRing;
13use crate::rings::finite::FiniteRingStore;
14use crate::pid::*;
15
16pub mod zn_big;
22pub mod zn_64;
27pub mod zn_static;
32pub mod zn_rns;
37
38pub trait ZnRing: PrincipalIdealRing + FiniteRing + CanHomFrom<Self::IntegerRingBase> {
42
43 type IntegerRingBase: IntegerRing + ?Sized;
48 type IntegerRing: RingStore<Type = Self::IntegerRingBase>;
49
50 fn integer_ring(&self) -> &Self::IntegerRing;
51 fn modulus(&self) -> &El<Self::IntegerRing>;
52
53 fn smallest_positive_lift(&self, el: Self::Element) -> El<Self::IntegerRing>;
61
62 fn any_lift(&self, el: Self::Element) -> El<Self::IntegerRing> {
69 self.smallest_positive_lift(el)
70 }
71
72 fn from_int_promise_reduced(&self, x: El<Self::IntegerRing>) -> Self::Element;
87
88 fn smallest_lift(&self, el: Self::Element) -> El<Self::IntegerRing> {
96 let result = self.smallest_positive_lift(el);
97 let mut mod_half = self.integer_ring().clone_el(self.modulus());
98 self.integer_ring().euclidean_div_pow_2(&mut mod_half, 1);
99 if self.integer_ring().is_gt(&result, &mod_half) {
100 return self.integer_ring().sub_ref_snd(result, self.modulus());
101 } else {
102 return result;
103 }
104 }
105
106 fn is_field(&self) -> bool {
110 algorithms::miller_rabin::is_prime_base(RingRef::new(self), 10)
111 }
112}
113
114#[stability::unstable(feature = "enable")]
122pub trait FromModulusCreateableZnRing: Sized + ZnRing {
123
124 fn create<F, E>(create_modulus: F) -> Result<Self, E>
125 where F: FnOnce(&Self::IntegerRingBase) -> Result<El<Self::IntegerRing>, E>;
126}
127
128pub mod generic_impls {
129 use std::alloc::Global;
130 use std::marker::PhantomData;
131
132 use crate::algorithms::convolution::STANDARD_CONVOLUTION;
133 use crate::algorithms::int_bisect;
134 use crate::ordered::*;
135 use crate::primitive_int::{StaticRing, StaticRingBase};
136 use crate::field::*;
137 use crate::rings::zn::*;
138 use crate::divisibility::DivisibilityRingStore;
139 use crate::integer::{IntegerRing, IntegerRingStore};
140 use crate::rings::extension::galois_field::{GaloisField, GaloisFieldOver};
141
142 pub struct BigIntToZnHom<I: ?Sized + IntegerRing, J: ?Sized + IntegerRing, R: ?Sized + ZnRing>
147 where I: CanIsoFromTo<R::IntegerRingBase> + CanIsoFromTo<J>
148 {
149 highbit_mod: usize,
150 highbit_bound: usize,
151 int_ring: PhantomData<I>,
152 to_large_int_ring: PhantomData<J>,
153 hom: <I as CanHomFrom<R::IntegerRingBase>>::Homomorphism,
154 iso: <I as CanIsoFromTo<R::IntegerRingBase>>::Isomorphism,
155 iso2: <I as CanIsoFromTo<J>>::Isomorphism
156 }
157
158 #[stability::unstable(feature = "enable")]
164 pub fn has_canonical_hom_from_bigint<I: ?Sized + IntegerRing, J: ?Sized + IntegerRing, R: ?Sized + ZnRing>(from: &I, to: &R, to_large_int_ring: &J, bounded_reduce_bound: Option<&J::Element>) -> Option<BigIntToZnHom<I, J, R>>
165 where I: CanIsoFromTo<R::IntegerRingBase> + CanIsoFromTo<J>
166 {
167 if let Some(bound) = bounded_reduce_bound {
168 Some(BigIntToZnHom {
169 highbit_mod: to.integer_ring().abs_highest_set_bit(to.modulus()).unwrap(),
170 highbit_bound: to_large_int_ring.abs_highest_set_bit(bound).unwrap(),
171 int_ring: PhantomData,
172 to_large_int_ring: PhantomData,
173 hom: from.has_canonical_hom(to.integer_ring().get_ring())?,
174 iso: from.has_canonical_iso(to.integer_ring().get_ring())?,
175 iso2: from.has_canonical_iso(to_large_int_ring)?
176 })
177 } else {
178 Some(BigIntToZnHom {
179 highbit_mod: to.integer_ring().abs_highest_set_bit(to.modulus()).unwrap(),
180 highbit_bound: usize::MAX,
181 int_ring: PhantomData,
182 to_large_int_ring: PhantomData,
183 hom: from.has_canonical_hom(to.integer_ring().get_ring())?,
184 iso: from.has_canonical_iso(to.integer_ring().get_ring())?,
185 iso2: from.has_canonical_iso(to_large_int_ring)?
186 })
187 }
188 }
189
190 #[stability::unstable(feature = "enable")]
215 pub fn map_in_from_bigint<I: ?Sized + IntegerRing, J: ?Sized + IntegerRing, R: ?Sized + ZnRing, F, G>(from: &I, to: &R, to_large_int_ring: &J, el: I::Element, hom: &BigIntToZnHom<I, J, R>, from_positive_representative_exact: F, from_positive_representative_bounded: G) -> R::Element
216 where I: CanIsoFromTo<R::IntegerRingBase> + CanIsoFromTo<J>,
217 F: FnOnce(El<R::IntegerRing>) -> R::Element,
218 G: FnOnce(J::Element) -> R::Element
219 {
220 let (neg, n) = if from.is_neg(&el) {
221 (true, from.negate(el))
222 } else {
223 (false, el)
224 };
225 let ZZ = to.integer_ring().get_ring();
226 let highbit_el = from.abs_highest_set_bit(&n).unwrap_or(0);
227
228 let reduced = if highbit_el < hom.highbit_mod {
229 from_positive_representative_exact(from.map_out(ZZ, n, &hom.iso))
230 } else if highbit_el < hom.highbit_bound {
231 from_positive_representative_bounded(from.map_out(to_large_int_ring, n, &hom.iso2))
232 } else {
233 from_positive_representative_exact(from.map_out(ZZ, from.euclidean_rem(n, &from.map_in_ref(ZZ, to.modulus(), &hom.hom)), &hom.iso))
234 };
235 if neg {
236 to.negate(reduced)
237 } else {
238 reduced
239 }
240 }
241
242 #[stability::unstable(feature = "enable")]
247 pub fn random_element<R: ZnRing, G: FnMut() -> u64>(ring: &R, rng: G) -> R::Element {
248 ring.map_in(
249 ring.integer_ring().get_ring(),
250 ring.integer_ring().get_uniformly_random(ring.modulus(), rng),
251 &ring.has_canonical_hom(ring.integer_ring().get_ring()).unwrap()
252 )
253 }
254
255 #[stability::unstable(feature = "enable")]
260 pub fn checked_left_div<R: ZnRingStore>(ring: R, lhs: &El<R>, rhs: &El<R>) -> Option<El<R>>
261 where R::Type: ZnRing
262 {
263 if ring.is_zero(lhs) {
264 return Some(ring.zero());
265 }
266 let int_ring = ring.integer_ring();
267 let lhs_lift = ring.smallest_positive_lift(ring.clone_el(lhs));
268 let rhs_lift = ring.smallest_positive_lift(ring.clone_el(rhs));
269 let (s, _, d) = int_ring.extended_ideal_gen(&rhs_lift, ring.modulus());
270 if let Some(quotient) = int_ring.checked_div(&lhs_lift, &d) {
271 Some(ring.mul(ring.coerce(int_ring, quotient), ring.coerce(int_ring, s)))
272 } else {
273 None
274 }
275 }
276
277 #[stability::unstable(feature = "enable")]
278 pub fn checked_div_min<R: ZnRingStore>(ring: R, lhs: &El<R>, rhs: &El<R>) -> Option<El<R>>
279 where R::Type: ZnRing
280 {
281 if ring.is_zero(lhs) && ring.is_zero(rhs) {
282 return Some(ring.one());
283 }
284 assert!(ring.is_noetherian());
285 let int_ring = ring.integer_ring();
286 let rhs_ann = int_ring.checked_div(ring.modulus(), &int_ring.ideal_gen(ring.modulus(), &ring.smallest_positive_lift(ring.clone_el(rhs)))).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 R: Clone,
299 R::Type: ZnRing + Field + SelfIso + CanHomFrom<StaticRingBase<i64>>
300 {
301 let ZZbig = BigIntRing::RING;
302 let modulus = int_cast(ring.integer_ring().clone_el(ring.modulus()), ZZbig, ring.integer_ring());
303 let count = int_cast(count as i64, ZZbig, StaticRing::<i64>::RING);
304 let degree = int_bisect::find_root_floor(StaticRing::<i64>::RING, 1, |d| if *d > 0 && ZZbig.is_gt(&ZZbig.pow(ZZbig.clone_el(&modulus), *d as usize), &count) {
305 1
306 } else {
307 -1
308 }) + 1;
309 assert!(degree >= 1);
310 return GaloisField::new_with(ring, degree as usize, Global, STANDARD_CONVOLUTION);
311 }
312}
313
314pub trait ZnRingStore: FiniteRingStore
318 where Self::Type: ZnRing
319{
320 delegate!{ ZnRing, fn integer_ring(&self) -> &<Self::Type as ZnRing>::IntegerRing }
321 delegate!{ ZnRing, fn modulus(&self) -> &El<<Self::Type as ZnRing>::IntegerRing> }
322 delegate!{ ZnRing, fn smallest_positive_lift(&self, el: El<Self>) -> El<<Self::Type as ZnRing>::IntegerRing> }
323 delegate!{ ZnRing, fn smallest_lift(&self, el: El<Self>) -> El<<Self::Type as ZnRing>::IntegerRing> }
324 delegate!{ ZnRing, fn any_lift(&self, el: El<Self>) -> El<<Self::Type as ZnRing>::IntegerRing> }
325 delegate!{ ZnRing, fn is_field(&self) -> bool }
326
327 fn as_field(self) -> Result<RingValue<AsFieldBase<Self>>, Self>
328 where Self: Sized
329 {
330 if self.is_field() {
331 Ok(RingValue::from(AsFieldBase::promise_is_perfect_field(self)))
332 } else {
333 Err(self)
334 }
335 }
336}
337
338impl<R: RingStore> ZnRingStore for R
339 where R::Type: ZnRing
340{}
341
342pub trait ZnOperation {
349
350 type Output<'a>
351 where Self: 'a;
352
353 fn call<'a, R>(self, ring: R) -> Self::Output<'a>
354 where 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>
397 where I: 'a + RingStore,
398 I::Type: IntegerRing,
399 F: ZnOperation
400{
401 if ZZ.abs_highest_set_bit(&n).unwrap_or(0) < 57 {
402 f.call(zn_64::Zn::new(StaticRing::<i64>::RING.coerce(&ZZ, n) as u64))
403 } else {
404 f.call(zn_big::Zn::new(BigIntRing::RING, int_cast(n, &BigIntRing::RING, &ZZ)))
405 }
406}
407
408#[test]
409fn test_choose_zn_impl() {
410 let int_value = 4;
411 struct DoStuff { int_value: i64 }
413 impl ZnOperation for DoStuff {
414
415 type Output<'a> = ()
416 where Self: 'a;
417
418 fn call<'a, R>(self, Zn: R)
419 where R: 'a + RingStore, R::Type: ZnRing
420 {
421 let value = Zn.coerce(Zn.integer_ring(), int_cast(self.int_value, Zn.integer_ring(), &StaticRing::<i64>::RING));
422 assert_el_eq!(Zn, Zn.int_hom().map(-1), Zn.mul_ref(&value, &value));
423 }
424 }
425 choose_zn_impl(StaticRing::<i64>::RING, 17, DoStuff { int_value });
426}
427
428#[derive(Debug, Clone, Copy, PartialEq, Eq)]
429enum ReductionMapRequirements {
430 SmallestLift,
431 ExplicitReduce
432}
433
434pub struct ZnReductionMap<R, S>
448 where R: RingStore,
449 R::Type: ZnRing,
450 S: RingStore,
451 S::Type: ZnRing
452{
453 from: R,
454 to: S,
455 fraction_of_quotients: El<R>,
456 to_modulus: El<<R::Type as ZnRing>::IntegerRing>,
457 to_from_int: <S::Type as CanHomFrom<<S::Type as ZnRing>::IntegerRingBase>>::Homomorphism,
458 from_from_int: <R::Type as CanHomFrom<<R::Type as ZnRing>::IntegerRingBase>>::Homomorphism,
459 map_forward_requirement: ReductionMapRequirements
460}
461
462impl<R, S> ZnReductionMap<R, S>
463 where R: RingStore,
464 R::Type: ZnRing,
465 S: RingStore,
466 S::Type: ZnRing
467{
468 pub fn new(from: R, to: S) -> Option<Self> {
469 let from_char = from.characteristic(&BigIntRing::RING).unwrap();
470 let to_char = to.characteristic(&BigIntRing::RING).unwrap();
471 if let Some(frac) = BigIntRing::RING.checked_div(&from_char, &to_char) {
472 let map_forward_requirement: ReductionMapRequirements = if to.integer_ring().get_ring().representable_bits().is_none() || BigIntRing::RING.is_lt(&from_char, &BigIntRing::RING.power_of_two(to.integer_ring().get_ring().representable_bits().unwrap())) {
473 ReductionMapRequirements::SmallestLift
474 } else {
475 ReductionMapRequirements::ExplicitReduce
476 };
477 Some(Self {
478 map_forward_requirement: map_forward_requirement,
479 to_modulus: int_cast(to.integer_ring().clone_el(to.modulus()), from.integer_ring(), to.integer_ring()),
480 to_from_int: to.get_ring().has_canonical_hom(to.integer_ring().get_ring()).unwrap(),
481 from_from_int: from.get_ring().has_canonical_hom(from.integer_ring().get_ring()).unwrap(),
482 fraction_of_quotients: from.can_hom(from.integer_ring()).unwrap().map(int_cast(frac, from.integer_ring(), BigIntRing::RING)),
483 from: from,
484 to: to,
485 })
486 } else {
487 None
488 }
489 }
490
491 pub fn mul_quotient_fraction(&self, x: El<S>) -> El<R> {
508 self.from.mul_ref_snd(self.any_preimage(x), &self.fraction_of_quotients)
509 }
510
511 pub fn smallest_lift(&self, x: El<S>) -> El<R> {
529 self.from.get_ring().map_in(self.from.integer_ring().get_ring(), int_cast(self.to.smallest_lift(x), self.from.integer_ring(), self.to.integer_ring()), &self.from_from_int)
530 }
531
532 pub fn any_preimage(&self, x: El<S>) -> El<R> {
533 self.smallest_lift(x)
537 }
538
539 pub fn smallest_lift_ref(&self, x: &El<S>) -> El<R> {
540 self.smallest_lift(self.codomain().clone_el(x))
541 }
542}
543
544impl<R, S> Homomorphism<R::Type, S::Type> for ZnReductionMap<R, S>
545 where R: RingStore,
546 R::Type: ZnRing,
547 S: RingStore,
548 S::Type: ZnRing
549{
550 type CodomainStore = S;
551 type DomainStore = R;
552
553 fn map(&self, x: El<R>) -> El<S> {
554 let value = match self.map_forward_requirement {
555 ReductionMapRequirements::SmallestLift => self.from.smallest_lift(x),
556 ReductionMapRequirements::ExplicitReduce => self.from.integer_ring().euclidean_rem(self.from.any_lift(x), &self.to_modulus)
557 };
558 self.to.get_ring().map_in(self.to.integer_ring().get_ring(), int_cast(value, self.to.integer_ring(), self.from.integer_ring()), &self.to_from_int)
559 }
560
561 fn codomain<'a>(&'a self) -> &'a Self::CodomainStore {
562 &self.to
563 }
564
565 fn domain<'a>(&'a self) -> &'a Self::DomainStore {
566 &self.from
567 }
568}
569
570#[cfg(any(test, feature = "generic_tests"))]
571pub mod generic_tests {
572
573 use super::*;
574 use crate::primitive_int::{StaticRingBase, StaticRing};
575
576 pub fn test_zn_axioms<R: RingStore>(R: R)
577 where R::Type: ZnRing,
578 <R::Type as ZnRing>::IntegerRingBase: CanIsoFromTo<StaticRingBase<i128>> + CanIsoFromTo<StaticRingBase<i32>>
579 {
580 let ZZ = R.integer_ring();
581 let n = R.modulus();
582
583 assert!(R.is_zero(&R.coerce(ZZ, ZZ.clone_el(n))));
584 assert!(R.is_field() == algorithms::miller_rabin::is_prime(ZZ, n, 10));
585
586 let mut k = ZZ.one();
587 while ZZ.is_lt(&k, &n) {
588 assert!(!R.is_zero(&R.coerce(ZZ, ZZ.clone_el(&k))));
589 ZZ.add_assign(&mut k, ZZ.one());
590 }
591
592 let all_elements = R.elements().collect::<Vec<_>>();
593 assert_eq!(int_cast(ZZ.clone_el(n), &StaticRing::<i128>::RING, &ZZ) as usize, all_elements.len());
594 for (i, x) in all_elements.iter().enumerate() {
595 for (j, y) in all_elements.iter().enumerate() {
596 assert!(i == j || !R.eq_el(x, y));
597 }
598 }
599 }
600
601 pub fn test_map_in_large_int<R: RingStore>(R: R)
602 where <R as RingStore>::Type: ZnRing + CanHomFrom<BigIntRingBase>
603 {
604 let ZZ_big = BigIntRing::RING;
605 let n = ZZ_big.power_of_two(1000);
606 let x = R.coerce(&ZZ_big, n);
607 assert!(R.eq_el(&R.pow(R.int_hom().map(2), 1000), &x));
608 }
609}
610
611#[test]
612fn test_reduction_map_large_value() {
613 let ring1 = zn_64::Zn::new(1 << 42);
614 let ring2 = zn_big::Zn::new(BigIntRing::RING, BigIntRing::RING.power_of_two(666));
615 let reduce = ZnReductionMap::new(&ring2, ring1).unwrap();
616 assert_el_eq!(ring1, ring1.zero(), reduce.map(ring2.pow(ring2.int_hom().map(2), 665)));
617}
618
619#[test]
620fn test_reduction_map() {
621 let ring1 = zn_64::Zn::new(257);
622 let ring2 = zn_big::Zn::new(StaticRing::<i128>::RING, 257 * 7);
623
624 crate::homomorphism::generic_tests::test_homomorphism_axioms(ZnReductionMap::new(&ring2, &ring1).unwrap(), ring2.elements().step_by(8));
625
626 let ring1 = zn_big::Zn::new(StaticRing::<i16>::RING, 3);
627 let ring2 = zn_big::Zn::new(BigIntRing::RING, BigIntRing::RING.int_hom().map(65537 * 3));
628
629 crate::homomorphism::generic_tests::test_homomorphism_axioms(ZnReductionMap::new(&ring2, &ring1).unwrap(), ring2.elements().step_by(1024));
630}
631
632#[test]
633fn test_generic_impl_checked_div_min() {
634 let ring = zn_64::Zn::new(5 * 7 * 11 * 13);
635 let actual = ring.annihilator(&ring.int_hom().map(1001));
636 let expected = ring.int_hom().map(5);
637 assert!(ring.checked_div(&expected, &actual).is_some());
638 assert!(ring.checked_div(&actual, &expected).is_some());
639
640 let actual = ring.annihilator(&ring.zero());
641 let expected = ring.one();
642 assert!(ring.checked_div(&expected, &actual).is_some());
643 assert!(ring.checked_div(&actual, &expected).is_some());
644}