1use std::fmt::Debug;
2use std::marker::PhantomData;
3
4use feanor_serde::newtype_struct::*;
5use serde::de::{DeserializeSeed, Error};
6use serde::{Deserialize, Deserializer, Serialize, Serializer};
7
8use super::{zn_big, *};
9use crate::algorithms::convolution::KaratsubaHint;
10use crate::algorithms::fft::cooley_tuckey::CooleyTuckeyButterfly;
11use crate::algorithms::fft::radix3::CooleyTukeyRadix3Butterfly;
12use crate::algorithms::matmul::{ComputeInnerProduct, StrassenHint};
13use crate::delegate::{DelegateRing, DelegateRingImplFiniteRing};
14use crate::divisibility::*;
15use crate::ordered::OrderedRingStore;
16use crate::primitive_int::*;
17use crate::reduce_lift::poly_eval::InterpolationBaseRing;
18use crate::rings::extension::FreeAlgebraStore;
19use crate::rings::extension::galois_field::*;
20use crate::seq::*;
21use crate::serialization::*;
22use crate::specialization::*;
23use crate::{
24 impl_eq_based_self_iso, impl_field_wrap_unwrap_homs, impl_field_wrap_unwrap_isos, impl_localpir_wrap_unwrap_homs,
25 impl_localpir_wrap_unwrap_isos,
26};
27
28fn high(x: u128) -> u64 { (x >> 64) as u64 }
29
30fn low(x: u128) -> u64 { (x & ((1 << 64) - 1)) as u64 }
31
32fn mulhi(lhs: u64, rhs: u64) -> u64 { high(lhs as u128 * rhs as u128) }
33
34fn mullo(lhs: u64, rhs: u64) -> u64 { lhs.wrapping_mul(rhs) }
35
36fn reduce_to_half(x: u64, bound: u64) -> u64 {
39 let (result, overflow) = x.overflowing_sub(bound);
40 if overflow { x } else { result }
41}
42
43#[derive(Clone, Copy)]
73pub struct ZnBase {
74 modulus: i64,
75 modulus_half: i64,
76 modulus_times_three: u64,
77 modulus_times_six: u64,
78 inv_modulus: u128,
79}
80
81const fn two_pow_128_div(x: u64) -> u128 {
82 let q_high = (1u128 << 64) / (x as u128);
83 let r_high = (1u128 << 64) % (x as u128);
84 let q_low = (r_high << 64) / (x as u128);
85 let result = (q_high << 64) | q_low;
86 debug_assert!(
87 result.wrapping_mul(x as u128) == 0 || result.wrapping_mul(x as u128) >= 0u128.wrapping_sub(x as u128)
88 );
89 return result;
90}
91
92impl ZnBase {
93 pub const fn new(modulus: u64) -> Self {
94 assert!(modulus > 1);
95 assert!(modulus <= ((1 << 62) / 9));
97 assert!(modulus as u128 * 6 <= u64::MAX as u128);
99 let modulus_i64: i64 = modulus as i64;
100 let inv_modulus = two_pow_128_div(modulus);
101 Self {
107 modulus: modulus_i64,
108 inv_modulus,
109 modulus_half: (modulus_i64 - 1) / 2 + 1,
110 modulus_times_three: modulus * 3,
111 modulus_times_six: modulus * 6,
112 }
113 }
114
115 fn modulus_u64(&self) -> u64 { self.modulus as u64 }
116
117 fn repr_bound(&self) -> u64 { self.modulus_times_six }
120
121 fn bounded_reduce(&self, value: u128) -> u64 {
123 debug_assert!(value <= self.repr_bound() as u128 * self.repr_bound() as u128);
124
125 let (in_low, in_high) = (low(value), high(value));
126 let (invmod_low, invmod_high) = (low(self.inv_modulus), high(self.inv_modulus));
127 let approx_quotient = mulhi(in_low, invmod_high) + mulhi(in_high, invmod_low) + mullo(in_high, invmod_high);
130 let result = low(value).wrapping_sub(mullo(approx_quotient, self.modulus_u64()));
131
132 debug_assert!(result < self.modulus_times_three);
133 debug_assert!((value - result as u128).is_multiple_of(self.modulus_u64() as u128));
134 return result;
135 }
136
137 #[inline(never)]
144 fn bounded_reduce_larger<const FACTOR: usize>(&self, value: u128) -> u64 {
145 assert!(FACTOR == 32);
146 debug_assert!(value <= FACTOR as u128 * self.repr_bound() as u128 * self.repr_bound() as u128);
147
148 let (in_low, in_high) = (low(value), high(value));
149 let invmod_high = high(self.inv_modulus);
150 let approx_quotient = in_high as u128 * invmod_high as u128 + mulhi(in_low, invmod_high) as u128;
153
154 return self.bounded_reduce(value - (approx_quotient * self.modulus as u128));
155 }
156
157 fn from_u64_promise_reduced(&self, value: u64) -> ZnEl {
163 debug_assert!(value <= self.repr_bound());
164 ZnEl(value)
165 }
166
167 fn complete_reduce(&self, mut value: u64) -> u64 {
169 debug_assert!(value <= self.repr_bound());
170 if value >= 4 * self.modulus_u64() {
171 value -= 4 * self.modulus_u64();
172 }
173 if value >= 2 * self.modulus_u64() {
174 value -= 2 * self.modulus_u64();
175 }
176 if value >= self.modulus_u64() {
177 value -= self.modulus_u64();
178 }
179 debug_assert!(value < self.modulus_u64());
180 return value;
181 }
182
183 #[stability::unstable(feature = "enable")]
184 pub fn from_primitive_int<T: PrimitiveInt>(&self, el: T) -> ZnEl {
185 let is_neg = StaticRing::<T>::RING.is_neg(&el);
186 let el_abs = <T as Into<i128>>::into(el).unsigned_abs();
187 if el_abs <= self.modulus_u64() as u128 {
188 if is_neg {
189 self.negate(self.from_u64_promise_reduced(el_abs as u64))
190 } else {
191 self.from_u64_promise_reduced(el_abs as u64)
192 }
193 } else if el_abs <= self.repr_bound() as u128 {
194 if is_neg {
195 self.negate(self.from_u64_promise_reduced(self.bounded_reduce(el_abs)))
196 } else {
197 self.from_u64_promise_reduced(self.bounded_reduce(el_abs))
198 }
199 } else {
200 let el_i128 = <T as Into<i128>>::into(el);
201 if is_neg {
202 self.from_u64_promise_reduced(((el_i128 % self.modulus as i128) as i64 + self.modulus) as u64)
203 } else {
204 self.from_u64_promise_reduced((el_i128 % self.modulus as i128) as u64)
205 }
206 }
207 }
208}
209
210pub type Zn = RingValue<ZnBase>;
213
214impl Zn {
215 pub const fn new(modulus: u64) -> Self { RingValue::from(ZnBase::new(modulus)) }
216}
217
218#[derive(Clone, Copy, Debug)]
219pub struct ZnEl(
220 u64,
222);
223
224impl PartialEq for ZnBase {
225 fn eq(&self, other: &Self) -> bool { self.modulus == other.modulus }
226}
227
228impl Debug for ZnBase {
229 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "Z/{}Z", self.modulus) }
230}
231
232impl RingBase for ZnBase {
233 type Element = ZnEl;
234
235 fn clone_el(&self, val: &Self::Element) -> Self::Element { *val }
236
237 fn add_assign(&self, lhs: &mut Self::Element, rhs: Self::Element) {
238 debug_assert!(lhs.0 <= self.repr_bound());
239 debug_assert!(rhs.0 <= self.repr_bound());
240 lhs.0 += rhs.0;
241 lhs.0 = reduce_to_half(lhs.0, self.repr_bound());
242 debug_assert!(lhs.0 <= self.repr_bound());
243 }
244
245 fn sub_assign(&self, lhs: &mut Self::Element, rhs: Self::Element) {
246 debug_assert!(lhs.0 <= self.repr_bound());
247 debug_assert!(rhs.0 <= self.repr_bound());
248 let (result, overflow) = lhs.0.overflowing_sub(rhs.0);
249 if overflow {
250 lhs.0 = result.wrapping_add(self.repr_bound());
251 } else {
252 lhs.0 = result;
253 }
254 debug_assert!(lhs.0 <= self.repr_bound());
255 }
256
257 fn negate_inplace(&self, lhs: &mut Self::Element) {
258 debug_assert!(lhs.0 <= self.repr_bound());
259 lhs.0 = self.repr_bound() - lhs.0;
260 }
261
262 fn mul_assign(&self, lhs: &mut Self::Element, rhs: Self::Element) {
263 debug_assert!(lhs.0 <= self.repr_bound());
264 debug_assert!(rhs.0 <= self.repr_bound());
265 lhs.0 = self.bounded_reduce(lhs.0 as u128 * rhs.0 as u128);
266 }
267
268 fn from_int(&self, value: i32) -> Self::Element { self.from_primitive_int(value) }
269
270 fn zero(&self) -> Self::Element { ZnEl(0) }
271
272 fn eq_el(&self, lhs: &Self::Element, rhs: &Self::Element) -> bool {
273 debug_assert!(lhs.0 <= self.repr_bound());
274 debug_assert!(rhs.0 <= self.repr_bound());
275 if lhs.0 > rhs.0 {
276 self.is_zero(&self.from_u64_promise_reduced(lhs.0 - rhs.0))
277 } else {
278 self.is_zero(&self.from_u64_promise_reduced(rhs.0 - lhs.0))
279 }
280 }
281
282 fn is_zero(&self, val: &Self::Element) -> bool {
283 debug_assert!(val.0 <= self.repr_bound());
284 self.complete_reduce(val.0) == 0
285 }
286
287 fn is_commutative(&self) -> bool { true }
288 fn is_noetherian(&self) -> bool { true }
289
290 fn dbg_within<'a>(
291 &self,
292 value: &Self::Element,
293 out: &mut std::fmt::Formatter<'a>,
294 _: EnvBindingStrength,
295 ) -> std::fmt::Result {
296 write!(out, "{}", self.complete_reduce(value.0))
297 }
298
299 fn characteristic<I: RingStore + Copy>(&self, other_ZZ: I) -> Option<El<I>>
300 where
301 I::Type: IntegerRing,
302 {
303 self.size(other_ZZ)
304 }
305
306 fn sum<I>(&self, els: I) -> Self::Element
307 where
308 I: IntoIterator<Item = Self::Element>,
309 {
310 let mut result = self.zero();
311 let mut els_it = els.into_iter();
312 while let Some(ZnEl(start)) = els_it.next() {
313 let mut current = start as u128;
314 for ZnEl(c) in els_it.by_ref().take(self.repr_bound() as usize / 2 - 1) {
315 current += c as u128;
316 }
317 self.add_assign(&mut result, self.from_u64_promise_reduced(self.bounded_reduce(current)));
318 }
319 debug_assert!(result.0 <= self.repr_bound());
320 return result;
321 }
322
323 fn pow_gen<R: RingStore>(&self, x: Self::Element, power: &El<R>, integers: R) -> Self::Element
324 where
325 R::Type: IntegerRing,
326 {
327 assert!(!integers.is_neg(power));
328 algorithms::sqr_mul::generic_abs_square_and_multiply(
329 x,
330 power,
331 &integers,
332 |mut a| {
333 self.square(&mut a);
334 a
335 },
336 |a, b| self.mul_ref_fst(a, b),
337 self.one(),
338 )
339 }
340
341 fn is_approximate(&self) -> bool { false }
342}
343
344impl SerializableElementRing for ZnBase {
345 fn deserialize<'de, D>(&self, deserializer: D) -> Result<Self::Element, D::Error>
346 where
347 D: Deserializer<'de>,
348 {
349 <i64 as Deserialize>::deserialize(deserializer)
350 .and_then(|x| {
351 if x < 0 || x >= *self.modulus() {
352 Err(Error::custom("ring element value out of bounds for ring Z/nZ"))
353 } else {
354 Ok(x)
355 }
356 })
357 .map(|x| self.from_int_promise_reduced(x))
358 }
359
360 fn serialize<S>(&self, el: &Self::Element, serializer: S) -> Result<S::Ok, S::Error>
361 where
362 S: Serializer,
363 {
364 <i64 as Serialize>::serialize(&self.smallest_positive_lift(*el), serializer)
365 }
366}
367
368impl FromModulusCreateableZnRing for ZnBase {
369 fn from_modulus<F, E>(create_modulus: F) -> Result<Self, E>
370 where
371 F: FnOnce(&Self::IntegerRingBase) -> Result<El<Self::IntegerRing>, E>,
372 {
373 create_modulus(StaticRing::<i64>::RING.get_ring()).map(|n| Self::new(n as u64))
374 }
375}
376
377impl InterpolationBaseRing for AsFieldBase<Zn> {
378 type ExtendedRingBase<'a>
379 = GaloisFieldBaseOver<RingRef<'a, Self>>
380 where
381 Self: 'a;
382
383 type ExtendedRing<'a>
384 = GaloisFieldOver<RingRef<'a, Self>>
385 where
386 Self: 'a;
387
388 fn in_base<'a, S>(&self, ext_ring: S, el: El<S>) -> Option<Self::Element>
389 where
390 Self: 'a,
391 S: RingStore<Type = Self::ExtendedRingBase<'a>>,
392 {
393 let wrt_basis = ext_ring.wrt_canonical_basis(&el);
394 if wrt_basis.iter().skip(1).all(|x| self.is_zero(&x)) {
395 return Some(wrt_basis.at(0));
396 } else {
397 return None;
398 }
399 }
400
401 fn in_extension<'a, S>(&self, ext_ring: S, el: Self::Element) -> El<S>
402 where
403 Self: 'a,
404 S: RingStore<Type = Self::ExtendedRingBase<'a>>,
405 {
406 ext_ring.inclusion().map(el)
407 }
408
409 fn interpolation_points<'a>(&'a self, count: usize) -> (Self::ExtendedRing<'a>, Vec<El<Self::ExtendedRing<'a>>>) {
410 let ring = super::generic_impls::interpolation_ring(RingRef::new(self), count);
411 let points = ring.elements().take(count).collect();
412 return (ring, points);
413 }
414}
415
416impl ComputeInnerProduct for ZnBase {
417 fn inner_product<I: Iterator<Item = (Self::Element, Self::Element)>>(&self, els: I) -> Self::Element {
418 debug_assert!(u128::MAX / (self.repr_bound() as u128 * self.repr_bound() as u128) >= 36);
419 const REDUCE_AFTER_STEPS: usize = 32;
420 let mut array_chunks = els.array_chunks::<REDUCE_AFTER_STEPS>();
421 let mut result = self.zero();
422 for chunk in array_chunks.by_ref() {
423 let mut sum: u128 = 0;
424 for (l, r) in chunk {
425 debug_assert!(l.0 <= self.repr_bound());
426 debug_assert!(r.0 <= self.repr_bound());
427 sum += l.0 as u128 * r.0 as u128;
428 }
429 self.add_assign(&mut result, ZnEl(self.bounded_reduce_larger::<REDUCE_AFTER_STEPS>(sum)));
430 }
431 let mut sum: u128 = 0;
432 for (l, r) in array_chunks.into_remainder() {
433 debug_assert!(l.0 <= self.repr_bound());
434 debug_assert!(r.0 <= self.repr_bound());
435 sum += l.0 as u128 * r.0 as u128;
436 }
437 self.add_assign(&mut result, ZnEl(self.bounded_reduce_larger::<REDUCE_AFTER_STEPS>(sum)));
438 return result;
439 }
440
441 fn inner_product_ref_fst<'a, I: Iterator<Item = (&'a Self::Element, Self::Element)>>(&self, els: I) -> Self::Element
442 where
443 Self::Element: 'a,
444 {
445 self.inner_product(els.map(|(l, r)| (self.clone_el(l), r)))
446 }
447
448 fn inner_product_ref<'a, I: Iterator<Item = (&'a Self::Element, &'a Self::Element)>>(&self, els: I) -> Self::Element
449 where
450 Self::Element: 'a,
451 {
452 self.inner_product_ref_fst(els.map(|(l, r)| (l, self.clone_el(r))))
453 }
454}
455
456impl_eq_based_self_iso! { ZnBase }
457
458impl<I: RingStore> CanHomFrom<zn_big::ZnBase<I>> for ZnBase
459where
460 I::Type: IntegerRing,
461{
462 type Homomorphism = ();
463
464 fn has_canonical_hom(&self, from: &zn_big::ZnBase<I>) -> Option<Self::Homomorphism> {
465 if from.integer_ring().get_ring().representable_bits().is_none()
466 || from.integer_ring().get_ring().representable_bits().unwrap()
467 >= self.integer_ring().abs_log2_ceil(self.modulus()).unwrap()
468 {
469 if from.integer_ring().eq_el(
470 from.modulus(),
471 &int_cast(*self.modulus(), from.integer_ring(), self.integer_ring()),
472 ) {
473 Some(())
474 } else {
475 None
476 }
477 } else {
478 None
479 }
480 }
481
482 fn map_in(
483 &self,
484 from: &zn_big::ZnBase<I>,
485 el: <zn_big::ZnBase<I> as RingBase>::Element,
486 _: &Self::Homomorphism,
487 ) -> Self::Element {
488 self.from_u64_promise_reduced(int_cast(
489 from.smallest_positive_lift(el),
490 self.integer_ring(),
491 from.integer_ring(),
492 ) as u64)
493 }
494}
495
496impl<I: RingStore> CanIsoFromTo<zn_big::ZnBase<I>> for ZnBase
497where
498 I::Type: IntegerRing,
499{
500 type Isomorphism = <zn_big::ZnBase<I> as CanHomFrom<StaticRingBase<i64>>>::Homomorphism;
501
502 fn has_canonical_iso(&self, from: &zn_big::ZnBase<I>) -> Option<Self::Isomorphism> {
503 if from.integer_ring().get_ring().representable_bits().is_none()
504 || from.integer_ring().get_ring().representable_bits().unwrap()
505 >= self.integer_ring().abs_log2_ceil(self.modulus()).unwrap()
506 {
507 if from.integer_ring().eq_el(
508 from.modulus(),
509 &int_cast(*self.modulus(), from.integer_ring(), self.integer_ring()),
510 ) {
511 from.has_canonical_hom(self.integer_ring().get_ring())
512 } else {
513 None
514 }
515 } else {
516 None
517 }
518 }
519
520 fn map_out(
521 &self,
522 from: &zn_big::ZnBase<I>,
523 el: <Self as RingBase>::Element,
524 iso: &Self::Isomorphism,
525 ) -> <zn_big::ZnBase<I> as RingBase>::Element {
526 from.map_in(self.integer_ring().get_ring(), el.0.try_into().unwrap(), iso)
527 }
528}
529
530#[derive(Copy, Clone, Debug)]
533pub struct ZnPreparedDivisorData {
534 unit_part: El<Zn>,
535 is_unit: bool,
536 smallest_positive_zero_divisor_part: PreparedDivisor<StaticRingBase<i64>>,
537}
538
539impl DivisibilityRing for ZnBase {
540 type PreparedDivisorData = ZnPreparedDivisorData;
541
542 fn checked_left_div(&self, lhs: &Self::Element, rhs: &Self::Element) -> Option<Self::Element> {
543 super::generic_impls::checked_left_div(RingRef::new(self), lhs, rhs)
544 }
545
546 fn prepare_divisor(&self, x: &Self::Element) -> Self::PreparedDivisorData {
547 let (s, _t, d) =
548 algorithms::eea::signed_eea(self.smallest_positive_lift(*x), *self.modulus(), self.integer_ring());
549 debug_assert!(d > 0);
550 debug_assert!(d <= *self.modulus());
551 return ZnPreparedDivisorData {
552 is_unit: d == 1,
553 unit_part: if s < 0 {
554 self.negate(self.from_u64_promise_reduced(-s as u64))
555 } else {
556 self.from_u64_promise_reduced(s as u64)
557 },
558 smallest_positive_zero_divisor_part: PreparedDivisor::new(StaticRing::<i64>::RING.get_ring(), d),
559 };
560 }
561
562 fn checked_left_div_prepared(
563 &self,
564 lhs: &Self::Element,
565 _rhs: &Self::Element,
566 rhs_prep: &Self::PreparedDivisorData,
567 ) -> Option<Self::Element> {
568 if rhs_prep.is_unit {
569 Some(self.mul_ref(lhs, &rhs_prep.unit_part))
570 } else {
571 StaticRing::<i64>::RING
572 .get_ring()
573 .checked_left_div_prepared(
574 &self.smallest_positive_lift(*lhs),
575 &rhs_prep.smallest_positive_zero_divisor_part.element,
576 &rhs_prep.smallest_positive_zero_divisor_part.data,
577 )
578 .map(|x| self.mul(self.from_u64_promise_reduced(x as u64), rhs_prep.unit_part))
579 }
580 }
581}
582
583impl<I: ?Sized + IntegerRing> CanHomFrom<I> for ZnBase {
584 type Homomorphism = super::generic_impls::BigIntToZnHom<I, StaticRingBase<i128>, Self>;
585
586 default fn has_canonical_hom(&self, from: &I) -> Option<Self::Homomorphism> {
587 super::generic_impls::has_canonical_hom_from_bigint(
588 from,
589 self,
590 StaticRing::<i128>::RING.get_ring(),
591 Some(&(self.repr_bound() as i128 * self.repr_bound() as i128)),
592 )
593 }
594
595 default fn map_in(&self, from: &I, el: I::Element, hom: &Self::Homomorphism) -> Self::Element {
596 super::generic_impls::map_in_from_bigint(
597 from,
598 self,
599 StaticRing::<i128>::RING.get_ring(),
600 el,
601 hom,
602 |n| {
603 debug_assert!((n as u64) < self.modulus_u64());
604 self.from_u64_promise_reduced(n as u64)
605 },
606 |n| {
607 debug_assert!(n <= (self.repr_bound() as i128 * self.repr_bound() as i128));
608 self.from_u64_promise_reduced(self.bounded_reduce(n as u128))
609 },
610 )
611 }
612}
613
614impl Serialize for ZnBase {
615 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
616 where
617 S: Serializer,
618 {
619 SerializableNewtypeStruct::new("Zn", *self.modulus()).serialize(serializer)
620 }
621}
622
623impl<'de> Deserialize<'de> for ZnBase {
624 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
625 where
626 D: Deserializer<'de>,
627 {
628 DeserializeSeedNewtypeStruct::new("Zn", PhantomData::<i64>)
629 .deserialize(deserializer)
630 .map(|n| ZnBase::new(n as u64))
631 }
632}
633
634macro_rules! impl_static_int_to_zn {
635 ($($int:ident),*) => {
636 $(
637 impl CanHomFrom<StaticRingBase<$int>> for ZnBase {
638 fn map_in(&self, _from: &StaticRingBase<$int>, el: $int, _hom: &Self::Homomorphism) -> Self::Element {
639 self.from_primitive_int(el)
640 }
641 }
642 )*
643 };
644}
645
646impl_static_int_to_zn! { i8, i16, i32, i64, i128 }
647
648#[derive(Clone, Copy)]
649pub struct ZnBaseElementsIter<'a> {
650 ring: &'a ZnBase,
651 current: u64,
652}
653
654impl<'a> Iterator for ZnBaseElementsIter<'a> {
655 type Item = ZnEl;
656
657 fn next(&mut self) -> Option<Self::Item> {
658 if self.current < self.ring.modulus_u64() {
659 let result = self.current;
660 self.current += 1;
661 return Some(self.ring.from_u64_promise_reduced(result));
662 } else {
663 return None;
664 }
665 }
666}
667
668impl FiniteRingSpecializable for ZnBase {
669 fn specialize<O: FiniteRingOperation<Self>>(op: O) -> O::Output { op.execute() }
670}
671
672impl FiniteRing for ZnBase {
673 type ElementsIter<'a> = ZnBaseElementsIter<'a>;
674
675 fn elements<'a>(&'a self) -> Self::ElementsIter<'a> { ZnBaseElementsIter { ring: self, current: 0 } }
676
677 fn random_element<G: FnMut() -> u64>(&self, rng: G) -> <Self as RingBase>::Element {
678 super::generic_impls::random_element(self, rng)
679 }
680
681 fn size<I: RingStore + Copy>(&self, other_ZZ: I) -> Option<El<I>>
682 where
683 I::Type: IntegerRing,
684 {
685 if other_ZZ.get_ring().representable_bits().is_none()
686 || self.integer_ring().abs_log2_ceil(&(self.modulus() + 1)) <= other_ZZ.get_ring().representable_bits()
687 {
688 Some(int_cast(*self.modulus(), other_ZZ, self.integer_ring()))
689 } else {
690 None
691 }
692 }
693}
694
695impl PrincipalIdealRing for ZnBase {
696 fn checked_div_min(&self, lhs: &Self::Element, rhs: &Self::Element) -> Option<Self::Element> {
697 super::generic_impls::checked_div_min(RingRef::new(self), lhs, rhs)
698 }
699
700 fn extended_ideal_gen(
701 &self,
702 lhs: &Self::Element,
703 rhs: &Self::Element,
704 ) -> (Self::Element, Self::Element, Self::Element) {
705 let (s, t, d) =
706 StaticRing::<i64>::RING.extended_ideal_gen(&lhs.0.try_into().unwrap(), &rhs.0.try_into().unwrap());
707 let quo = RingRef::new(self).into_can_hom(StaticRing::<i64>::RING).ok().unwrap();
708 (quo.map(s), quo.map(t), quo.map(d))
709 }
710}
711
712impl StrassenHint for ZnBase {
713 default fn strassen_threshold(&self) -> usize { 6 }
714}
715
716impl KaratsubaHint for ZnBase {
717 default fn karatsuba_threshold(&self) -> usize { 6 }
718}
719
720impl ZnRing for ZnBase {
721 type IntegerRingBase = StaticRingBase<i64>;
722 type IntegerRing = StaticRing<i64>;
723
724 fn integer_ring(&self) -> &Self::IntegerRing { &StaticRing::<i64>::RING }
725
726 fn smallest_positive_lift(&self, el: Self::Element) -> El<Self::IntegerRing> { self.complete_reduce(el.0) as i64 }
727
728 fn smallest_lift(&self, ZnEl(mut value_u64): Self::Element) -> El<Self::IntegerRing> {
729 debug_assert!(value_u64 <= self.repr_bound());
730 if value_u64 >= 3 * self.modulus_u64() {
732 value_u64 -= 3 * self.modulus_u64();
733 }
734 let mut value_i64 = value_u64 as i64;
736 if value_i64 >= self.modulus + self.modulus_half {
737 value_i64 -= 2 * self.modulus;
738 }
739 if value_i64 >= self.modulus_half {
742 value_i64 -= self.modulus;
743 }
744 debug_assert!(value_i64 < self.modulus_half);
747 debug_assert!(self.modulus() % 2 == 0 || value_i64 > -self.modulus_half);
748 debug_assert!(self.modulus() % 2 == 1 || value_i64 >= -self.modulus_half);
749 return value_i64;
750 }
751
752 fn modulus(&self) -> &El<Self::IntegerRing> { &self.modulus }
753
754 fn any_lift(&self, el: Self::Element) -> El<Self::IntegerRing> { el.0 as i64 }
755
756 fn from_int_promise_reduced(&self, x: El<Self::IntegerRing>) -> Self::Element {
785 debug_assert!(self.repr_bound() == 6 * self.modulus_u64());
786 debug_assert!(x >= 0);
787 debug_assert!(x as u64 <= self.repr_bound());
788 self.from_u64_promise_reduced(x as u64)
789 }
790}
791
792impl HashableElRing for ZnBase {
793 fn hash<H: std::hash::Hasher>(&self, el: &Self::Element, h: &mut H) {
794 self.integer_ring().hash(&self.smallest_positive_lift(*el), h)
795 }
796}
797
798#[derive(Clone, Copy, Debug)]
826pub struct ZnFastmulBase {
827 base: RingValue<ZnBase>,
828}
829
830pub type ZnFastmul = RingValue<ZnFastmulBase>;
833
834impl ZnFastmul {
835 pub fn new(base: Zn) -> Option<Self> { Some(RingValue::from(ZnFastmulBase { base })) }
836}
837
838impl PartialEq for ZnFastmulBase {
839 fn eq(&self, other: &Self) -> bool { self.base.get_ring() == other.base.get_ring() }
840}
841
842impl PrincipalIdealRing for ZnFastmulBase {
843 fn checked_div_min(&self, lhs: &Self::Element, rhs: &Self::Element) -> Option<Self::Element> {
844 let result = self
845 .get_delegate()
846 .checked_div_min(self.delegate_ref(lhs), self.delegate_ref(rhs));
847 result.map(|x| self.rev_delegate(x))
848 }
849
850 fn extended_ideal_gen(
851 &self,
852 lhs: &Self::Element,
853 rhs: &Self::Element,
854 ) -> (Self::Element, Self::Element, Self::Element) {
855 let (s, t, d) = self
856 .get_delegate()
857 .extended_ideal_gen(self.delegate_ref(lhs), self.delegate_ref(rhs));
858 (self.rev_delegate(s), self.rev_delegate(t), self.rev_delegate(d))
859 }
860}
861
862impl_eq_based_self_iso! { ZnFastmulBase }
863
864#[derive(Clone, Copy, Debug)]
865pub struct ZnFastmulEl {
866 el: ZnEl,
867 value_invmod_shifted: u64,
868}
869
870impl DelegateRing for ZnFastmulBase {
871 type Base = ZnBase;
872 type Element = ZnFastmulEl;
873
874 fn get_delegate(&self) -> &Self::Base { self.base.get_ring() }
875
876 fn delegate(&self, el: Self::Element) -> <Self::Base as RingBase>::Element { el.el }
877
878 fn delegate_mut<'a>(&self, el: &'a mut Self::Element) -> &'a mut <Self::Base as RingBase>::Element { &mut el.el }
879
880 fn delegate_ref<'a>(&self, el: &'a Self::Element) -> &'a <Self::Base as RingBase>::Element { &el.el }
881
882 fn postprocess_delegate_mut(&self, el: &mut Self::Element) {
883 assert!(el.el.0 <= self.base.get_ring().repr_bound());
884 el.el.0 = self.base.get_ring().complete_reduce(el.el.0);
885 let value = el.el.0;
886 el.value_invmod_shifted = (((value as u128) << 64) / self.base.get_ring().modulus_u64() as u128) as u64;
887 }
888
889 fn rev_delegate(&self, el: <Self::Base as RingBase>::Element) -> Self::Element {
890 let mut result = ZnFastmulEl {
891 el,
892 value_invmod_shifted: 0,
893 };
894 self.postprocess_delegate_mut(&mut result);
895 return result;
896 }
897}
898
899impl DelegateRingImplFiniteRing for ZnFastmulBase {}
900
901impl CanHomFrom<ZnBase> for ZnFastmulBase {
902 type Homomorphism = ();
903
904 fn has_canonical_hom(&self, from: &ZnBase) -> Option<Self::Homomorphism> {
905 if from == self.base.get_ring() { Some(()) } else { None }
906 }
907
908 fn map_in(&self, _from: &ZnBase, el: <ZnBase as RingBase>::Element, _hom: &Self::Homomorphism) -> Self::Element {
909 self.rev_delegate(el)
910 }
911}
912
913impl CanHomFrom<ZnFastmulBase> for ZnBase {
914 type Homomorphism = ();
915
916 fn has_canonical_hom(&self, from: &ZnFastmulBase) -> Option<Self::Homomorphism> {
917 if self == from.base.get_ring() { Some(()) } else { None }
918 }
919
920 fn map_in(
921 &self,
922 _from: &ZnFastmulBase,
923 el: <ZnFastmulBase as RingBase>::Element,
924 _hom: &Self::Homomorphism,
925 ) -> Self::Element {
926 el.el
927 }
928
929 fn mul_assign_map_in(
930 &self,
931 _from: &ZnFastmulBase,
932 lhs: &mut Self::Element,
933 rhs: <ZnFastmulBase as RingBase>::Element,
934 _hom: &Self::Homomorphism,
935 ) {
936 debug_assert!(lhs.0 <= self.repr_bound());
937 let lhs_original = lhs.0;
938 let product = mullo(lhs.0, rhs.el.0);
939 let approx_quotient = mulhi(lhs.0, rhs.value_invmod_shifted);
940 lhs.0 = product.wrapping_sub(mullo(approx_quotient, self.modulus_u64()));
941 debug_assert!(lhs.0 < self.modulus_times_three);
942 debug_assert!(
943 (lhs_original as u128 * rhs.el.0 as u128 - lhs.0 as u128).is_multiple_of(self.modulus_u64() as u128)
944 );
945 }
946
947 fn mul_assign_map_in_ref(
948 &self,
949 from: &ZnFastmulBase,
950 lhs: &mut Self::Element,
951 rhs: &<ZnFastmulBase as RingBase>::Element,
952 hom: &Self::Homomorphism,
953 ) {
954 self.mul_assign_map_in(from, lhs, *rhs, hom);
955 }
956}
957
958impl CanIsoFromTo<ZnFastmulBase> for ZnBase {
959 type Isomorphism = <ZnFastmulBase as CanHomFrom<Self>>::Homomorphism;
960
961 fn has_canonical_iso(&self, from: &ZnFastmulBase) -> Option<Self::Isomorphism> { from.has_canonical_hom(self) }
962
963 fn map_out(
964 &self,
965 from: &ZnFastmulBase,
966 el: Self::Element,
967 iso: &Self::Isomorphism,
968 ) -> <ZnFastmulBase as RingBase>::Element {
969 from.map_in(self, el, iso)
970 }
971}
972
973impl CooleyTuckeyButterfly<ZnFastmulBase> for ZnBase {
974 #[inline(always)]
975 fn butterfly_new<H: Homomorphism<ZnFastmulBase, Self>>(
976 hom: H,
977 a: &mut ZnEl,
978 b: &mut ZnEl,
979 twiddle: &<ZnFastmulBase as RingBase>::Element,
980 ) {
981 let self_ = hom.codomain().get_ring();
982
983 a.0 = reduce_to_half(a.0, self_.modulus_times_three);
984 hom.mul_assign_ref_map(b, twiddle);
985
986 (a.0, b.0) = (a.0 + b.0, a.0 + self_.modulus_times_three - b.0);
987 }
988
989 #[inline(always)]
990 fn inv_butterfly_new<H: Homomorphism<ZnFastmulBase, Self>>(
991 hom: H,
992 a: &mut ZnEl,
993 b: &mut ZnEl,
994 twiddle: &<ZnFastmulBase as RingBase>::Element,
995 ) {
996 let self_ = hom.codomain().get_ring();
997
998 (a.0, b.0) = (a.0 + b.0, a.0 + self_.modulus_times_three - b.0);
1001 a.0 = reduce_to_half(a.0, self_.modulus_times_three);
1002 hom.mul_assign_ref_map(b, twiddle);
1003 }
1004
1005 #[inline(always)]
1006 fn prepare_for_inv_fft(&self, value: &mut Self::Element) {
1007 if value.0 >= self.modulus_times_three {
1008 value.0 -= self.modulus_times_three;
1009 }
1010 }
1011}
1012
1013impl CooleyTukeyRadix3Butterfly<ZnFastmulBase> for ZnBase {
1014 fn butterfly<H: Homomorphism<ZnFastmulBase, Self>>(
1015 hom: H,
1016 a: &mut Self::Element,
1017 b: &mut Self::Element,
1018 c: &mut Self::Element,
1019 z: &ZnFastmulEl,
1020 t: &ZnFastmulEl,
1021 t_sqr_z_sqr: &ZnFastmulEl,
1022 ) {
1023 let self_ = hom.codomain().get_ring();
1024
1025 hom.mul_assign_ref_map(b, t);
1026 hom.mul_assign_ref_map(c, t_sqr_z_sqr);
1027 let b_ = hom.mul_ref_map(b, z);
1028 let c_ = hom.mul_ref_map(c, z);
1029
1030 let mut s1 = b.0 + c_.0;
1031 let mut s2 = b_.0 + c.0;
1032 a.0 = reduce_to_half(a.0, self_.modulus_times_three);
1033 s1 = reduce_to_half(s1, self_.modulus_times_three);
1034 s2 = reduce_to_half(s2, self_.modulus_times_three);
1035
1036 let mut s3 = s1 + s2;
1037 s3 = reduce_to_half(s3, self_.modulus_times_three);
1038
1039 b.0 = a.0 + s2;
1040 c.0 = a.0 + self_.modulus_times_three - s3;
1041 a.0 += s1;
1042 }
1043
1044 fn inv_butterfly<H: Homomorphism<ZnFastmulBase, Self>>(
1045 hom: H,
1046 a: &mut Self::Element,
1047 b: &mut Self::Element,
1048 c: &mut Self::Element,
1049 z: &ZnFastmulEl,
1050 t: &ZnFastmulEl,
1051 t_sqr: &ZnFastmulEl,
1052 ) {
1053 let self_ = hom.codomain().get_ring();
1054
1055 let b_ = hom.mul_ref_map(b, z);
1058 let mut s1 = b.0 + c.0;
1059 s1 = reduce_to_half(s1, self_.modulus_times_three);
1060
1061 let s2 = b_.0 + c.0;
1062 let s2_ = hom.mul_ref_snd_map(self_.from_u64_promise_reduced(s2), z);
1063
1064 let mut s3 = s1 + s2_.0;
1065 s3 = reduce_to_half(s3, self_.modulus_times_three);
1066
1067 b.0 = a.0 + s2_.0;
1068 c.0 = a.0 + self_.modulus_times_three - s3;
1069 a.0 += s1;
1070
1071 a.0 = reduce_to_half(a.0, self_.modulus_times_three);
1072 hom.mul_assign_ref_map(b, t);
1073 hom.mul_assign_ref_map(c, t_sqr);
1074 }
1075
1076 #[inline(always)]
1077 fn prepare_for_fft(&self, _value: &mut Self::Element) {}
1078
1079 #[inline(always)]
1080 fn prepare_for_inv_fft(&self, value: &mut Self::Element) {
1081 if value.0 >= self.modulus_times_three {
1082 value.0 -= self.modulus_times_three
1083 }
1084 }
1085}
1086
1087impl CooleyTuckeyButterfly<ZnBase> for ZnBase {
1088 #[inline(always)]
1089 fn butterfly_new<H: Homomorphism<Self, Self>>(hom: H, a: &mut ZnEl, b: &mut ZnEl, twiddle: &ZnEl) {
1090 let self_ = hom.codomain().get_ring();
1091
1092 a.0 = reduce_to_half(a.0, self_.modulus_times_three);
1093 hom.mul_assign_ref_map(b, twiddle);
1094
1095 (a.0, b.0) = (a.0 + b.0, a.0 + self_.modulus_times_three - b.0);
1096 }
1097
1098 #[inline(always)]
1099 fn inv_butterfly_new<H: Homomorphism<Self, Self>>(hom: H, a: &mut ZnEl, b: &mut ZnEl, twiddle: &ZnEl) {
1100 let self_ = hom.codomain().get_ring();
1101
1102 (a.0, b.0) = (a.0 + b.0, a.0 + self_.modulus_times_three - b.0);
1103 a.0 = reduce_to_half(a.0, self_.modulus_times_three);
1104 hom.mul_assign_ref_map(b, twiddle);
1105 }
1106
1107 #[inline(always)]
1108 fn prepare_for_inv_fft(&self, value: &mut Self::Element) {
1109 if value.0 >= self.modulus_times_three {
1110 value.0 -= self.modulus_times_three;
1111 }
1112 }
1113}
1114
1115impl CooleyTukeyRadix3Butterfly<ZnBase> for ZnBase {
1116 fn butterfly<H: Homomorphism<ZnBase, Self>>(
1117 hom: H,
1118 a: &mut Self::Element,
1119 b: &mut Self::Element,
1120 c: &mut Self::Element,
1121 z: &ZnEl,
1122 t: &ZnEl,
1123 t_sqr_z_sqr: &ZnEl,
1124 ) {
1125 let self_ = hom.codomain().get_ring();
1126
1127 hom.mul_assign_ref_map(b, t);
1128 hom.mul_assign_ref_map(c, t_sqr_z_sqr);
1129 let b_ = hom.mul_ref_map(b, z);
1130 let c_ = hom.mul_ref_map(c, z);
1131
1132 let mut s1 = b.0 + c_.0;
1133 let mut s2 = b_.0 + c.0;
1134 a.0 = reduce_to_half(a.0, self_.modulus_times_three);
1135 s1 = reduce_to_half(s1, self_.modulus_times_three);
1136 s2 = reduce_to_half(s2, self_.modulus_times_three);
1137 let mut s3 = s1 + s2;
1138 s3 = reduce_to_half(s3, self_.modulus_times_three);
1139
1140 b.0 = a.0 + s2;
1141 c.0 = a.0 + self_.modulus_times_three - s3;
1142 a.0 += s1;
1143 }
1144
1145 fn inv_butterfly<H: Homomorphism<ZnBase, Self>>(
1146 hom: H,
1147 a: &mut Self::Element,
1148 b: &mut Self::Element,
1149 c: &mut Self::Element,
1150 z: &ZnEl,
1151 t: &ZnEl,
1152 t_sqr: &ZnEl,
1153 ) {
1154 let self_ = hom.codomain().get_ring();
1155
1156 let b_ = hom.mul_ref_map(b, z);
1159 let mut s1 = b.0 + c.0;
1160 s1 = reduce_to_half(s1, self_.modulus_times_three);
1161
1162 let s2 = b_.0 + c.0;
1163 let s2_ = hom.mul_ref_snd_map(self_.from_u64_promise_reduced(s2), z);
1164
1165 let mut s3 = s1 + s2_.0;
1166 s3 = reduce_to_half(s3, self_.modulus_times_three);
1167
1168 b.0 = a.0 + s2_.0;
1169 c.0 = a.0 + self_.modulus_times_three - s3;
1170 a.0 += s1;
1171 a.0 = reduce_to_half(a.0, self_.modulus_times_three);
1172
1173 hom.mul_assign_ref_map(b, t);
1174 hom.mul_assign_ref_map(c, t_sqr);
1175 }
1176
1177 #[inline(always)]
1178 fn prepare_for_fft(&self, _value: &mut Self::Element) {}
1179
1180 #[inline(always)]
1181 fn prepare_for_inv_fft(&self, value: &mut Self::Element) {
1182 if value.0 >= self.modulus_times_three {
1183 value.0 -= self.modulus_times_three
1184 }
1185 }
1186}
1187
1188impl<I: ?Sized + IntegerRing> CanHomFrom<I> for ZnFastmulBase
1189where
1190 ZnBase: CanHomFrom<I>,
1191{
1192 type Homomorphism = <ZnBase as CanHomFrom<I>>::Homomorphism;
1193
1194 fn has_canonical_hom(&self, from: &I) -> Option<Self::Homomorphism> { self.base.get_ring().has_canonical_hom(from) }
1195
1196 fn map_in(&self, from: &I, el: I::Element, hom: &Self::Homomorphism) -> Self::Element {
1197 self.rev_delegate(self.base.get_ring().map_in(from, el, hom))
1198 }
1199}
1200
1201impl_field_wrap_unwrap_homs! { ZnBase, ZnBase }
1202impl_field_wrap_unwrap_isos! { ZnBase, ZnBase }
1203impl_localpir_wrap_unwrap_homs! { ZnBase, ZnBase }
1204impl_localpir_wrap_unwrap_isos! { ZnBase, ZnBase }
1205
1206impl<I> CanHomFrom<zn_big::ZnBase<I>> for AsFieldBase<Zn>
1207where
1208 I: RingStore,
1209 I::Type: IntegerRing,
1210{
1211 type Homomorphism = <ZnBase as CanHomFrom<zn_big::ZnBase<I>>>::Homomorphism;
1212
1213 fn has_canonical_hom(&self, from: &zn_big::ZnBase<I>) -> Option<Self::Homomorphism> {
1214 self.get_delegate().has_canonical_hom(from)
1215 }
1216
1217 fn map_in(
1218 &self,
1219 from: &zn_big::ZnBase<I>,
1220 el: <zn_big::ZnBase<I> as RingBase>::Element,
1221 hom: &Self::Homomorphism,
1222 ) -> Self::Element {
1223 self.rev_delegate(self.get_delegate().map_in(from, el, hom))
1224 }
1225}
1226
1227impl<I> CanIsoFromTo<zn_big::ZnBase<I>> for AsFieldBase<Zn>
1228where
1229 I: RingStore,
1230 I::Type: IntegerRing,
1231{
1232 type Isomorphism = <ZnBase as CanIsoFromTo<zn_big::ZnBase<I>>>::Isomorphism;
1233
1234 fn has_canonical_iso(&self, from: &zn_big::ZnBase<I>) -> Option<Self::Isomorphism> {
1235 self.get_delegate().has_canonical_iso(from)
1236 }
1237
1238 fn map_out(
1239 &self,
1240 from: &zn_big::ZnBase<I>,
1241 el: Self::Element,
1242 hom: &Self::Isomorphism,
1243 ) -> <zn_big::ZnBase<I> as RingBase>::Element {
1244 self.get_delegate().map_out(from, self.delegate(el), hom)
1245 }
1246}
1247
1248#[cfg(test)]
1249use test::Bencher;
1250
1251#[cfg(test)]
1252fn elements<'a>(ring: &'a Zn) -> impl 'a + Iterator<Item = El<Zn>> {
1253 (0..63).map(|i| ring.coerce(&StaticRing::<i64>::RING, 1 << i))
1254}
1255
1256#[cfg(test)]
1257const LARGE_MODULI: [u64; 6] = [
1258 (1 << 41) - 1,
1259 (1 << 42) - 1,
1260 (1 << 58) - 1,
1261 (1 << 58) + 1,
1262 (3 << 57) - 1,
1263 (3 << 57) + 1,
1264];
1265
1266#[test]
1267fn test_complete_reduce() {
1268 let ring = Zn::new(32);
1269 assert_eq!(31, ring.get_ring().complete_reduce(4 * 32 - 1));
1270}
1271
1272#[test]
1273fn test_sum() {
1274 for n in LARGE_MODULI {
1275 let Zn = Zn::new(n);
1276 assert_el_eq!(
1277 Zn,
1278 Zn.int_hom().map(10001 * 5000),
1279 Zn.sum((0..=10000).map(|x| Zn.int_hom().map(x)))
1280 );
1281 }
1282}
1283
1284#[test]
1285fn test_ring_axioms() {
1286 for n in 2..=17 {
1287 let ring = Zn::new(n);
1288 crate::ring::generic_tests::test_ring_axioms(&ring, (0..=ring.get_ring().repr_bound()).map(|n| ZnEl(n)));
1289 }
1290 for n in LARGE_MODULI {
1291 let ring = Zn::new(n);
1292 crate::ring::generic_tests::test_ring_axioms(&ring, elements(&ring));
1293 }
1294}
1295
1296#[test]
1297fn test_hash_axioms() {
1298 for n in 2..=17 {
1299 let ring = Zn::new(n);
1300 crate::ring::generic_tests::test_hash_axioms(&ring, (0..=ring.get_ring().repr_bound()).map(|n| ZnEl(n)));
1301 }
1302 for n in LARGE_MODULI {
1303 let ring = Zn::new(n);
1304 crate::ring::generic_tests::test_hash_axioms(&ring, elements(&ring));
1305 }
1306}
1307
1308#[test]
1309fn test_divisibility_axioms() {
1310 for n in 2..=17 {
1311 let Zn = Zn::new(n);
1312 crate::divisibility::generic_tests::test_divisibility_axioms(&Zn, Zn.elements());
1313 }
1314 for n in LARGE_MODULI {
1315 let Zn = Zn::new(n);
1316 crate::divisibility::generic_tests::test_divisibility_axioms(&Zn, elements(&Zn));
1317 }
1318}
1319
1320#[test]
1321fn test_zn_axioms() {
1322 for n in 2..=17 {
1323 let Zn = Zn::new(n);
1324 super::generic_tests::test_zn_axioms(&Zn);
1325 }
1326}
1327
1328#[test]
1329fn test_principal_ideal_ring_axioms() {
1330 for n in 2..=17 {
1331 let R = Zn::new(n);
1332 crate::pid::generic_tests::test_principal_ideal_ring_axioms(R, R.elements());
1333 }
1334 let R = Zn::new(63);
1335 crate::pid::generic_tests::test_principal_ideal_ring_axioms(R, R.elements());
1336}
1337
1338#[test]
1339fn test_hom_from_fastmul() {
1340 for n in 2..=17 {
1341 let Zn = Zn::new(n);
1342 let Zn_fastmul = ZnFastmul::new(Zn).unwrap();
1343 crate::ring::generic_tests::test_hom_axioms(Zn_fastmul, Zn, Zn.elements().map(|x| Zn_fastmul.coerce(&Zn, x)));
1344 }
1345 for n in [
1346 (1 << 41) - 1,
1347 (1 << 42) - 1,
1348 (1 << 58) - 1,
1349 (1 << 58) + 1,
1350 (3 << 57) - 1,
1351 (3 << 57) + 1,
1352 ] {
1353 let Zn = Zn::new(n);
1354 let Zn_fastmul = ZnFastmul::new(Zn).unwrap();
1355 crate::ring::generic_tests::test_hom_axioms(Zn_fastmul, Zn, elements(&Zn).map(|x| Zn_fastmul.coerce(&Zn, x)));
1356 }
1357}
1358
1359#[test]
1360fn test_finite_ring_axioms() {
1361 for n in 2..=17 {
1362 crate::rings::finite::generic_tests::test_finite_ring_axioms(&Zn::new(n));
1363 }
1364 crate::rings::finite::generic_tests::test_finite_ring_axioms(&Zn::new(128));
1365 crate::rings::finite::generic_tests::test_finite_ring_axioms(&Zn::new(1 << 32));
1366}
1367
1368#[test]
1369fn test_from_int_hom() {
1370 for n in 2..=17 {
1371 let Zn = Zn::new(n);
1372 crate::ring::generic_tests::test_hom_axioms(StaticRing::<i8>::RING, Zn, -8..8);
1373 crate::ring::generic_tests::test_hom_axioms(StaticRing::<i16>::RING, Zn, -8..8);
1374 crate::ring::generic_tests::test_hom_axioms(StaticRing::<i32>::RING, Zn, -8..8);
1375 crate::ring::generic_tests::test_hom_axioms(StaticRing::<i64>::RING, Zn, -8..8);
1376 crate::ring::generic_tests::test_hom_axioms(StaticRing::<i128>::RING, Zn, -8..8);
1377 }
1378 let Zn = Zn::new(5);
1379 assert_el_eq!(
1380 Zn,
1381 Zn.int_hom().map(3),
1382 Zn.can_hom(&StaticRing::<i64>::RING).unwrap().map(-1596802)
1383 );
1384}
1385
1386#[test]
1387fn test_bounded_reduce_large() {
1388 const FACTOR: usize = 32;
1389 let n_max = (1 << 62) / 9;
1390 for n in (n_max - 10)..=n_max {
1391 let Zn = Zn::new(n);
1392 let val_max = Zn.get_ring().repr_bound() as u128 * Zn.get_ring().repr_bound() as u128 * FACTOR as u128;
1393 for k in (val_max - 100)..=val_max {
1394 assert_eq!(
1395 (k % (n as u128)) as i64,
1396 Zn.smallest_positive_lift(ZnEl(Zn.get_ring().bounded_reduce_larger::<FACTOR>(k)))
1397 );
1398 }
1399 }
1400}
1401
1402#[test]
1403fn test_smallest_lift() {
1404 for n in 2..=17 {
1405 let ring = Zn::new(n);
1406 for k in 0..=ring.get_ring().repr_bound() {
1407 let expected = if (k % n) <= n / 2 {
1408 (k % n) as i64
1409 } else {
1410 (k % n) as i64 - n as i64
1411 };
1412 if n % 2 == 0 && (k % n) == n / 2 {
1413 assert!(ring.smallest_lift(ZnEl(k)) == n as i64 / 2 || ring.smallest_lift(ZnEl(k)) == -(n as i64) / 2)
1414 } else {
1415 assert_eq!(expected, ring.smallest_lift(ZnEl(k)));
1416 }
1417 }
1418 }
1419}
1420
1421#[test]
1422fn test_smallest_positive_lift() {
1423 for n in 2..=17 {
1424 let ring = Zn::new(n);
1425 for k in 0..=ring.get_ring().repr_bound() {
1426 let expected = (k % n) as i64;
1427 assert_eq!(expected, ring.smallest_positive_lift(ZnEl(k)));
1428 }
1429 }
1430}
1431
1432#[test]
1433fn test_bounded_reduce_small() {
1434 for n in 2..=17 {
1435 let Zn = Zn::new(n);
1436 let val_max = Zn.get_ring().repr_bound() as u128 * Zn.get_ring().repr_bound() as u128;
1437 for k in (val_max - 100)..=val_max {
1438 assert_eq!(
1439 (k % (n as u128)) as i64,
1440 Zn.smallest_positive_lift(ZnEl(Zn.get_ring().bounded_reduce(k)))
1441 );
1442 }
1443 }
1444}
1445
1446#[test]
1447fn test_bounded_reduce_large_small() {
1448 const FACTOR: usize = 32;
1449 for n in 2..=17 {
1450 let Zn = Zn::new(n);
1451 let val_max = Zn.get_ring().repr_bound() as u128 * Zn.get_ring().repr_bound() as u128 * FACTOR as u128;
1452 for k in (val_max - 100)..=val_max {
1453 assert_eq!(
1454 (k % (n as u128)) as i64,
1455 Zn.smallest_positive_lift(ZnEl(Zn.get_ring().bounded_reduce_larger::<FACTOR>(k)))
1456 );
1457 }
1458 }
1459}
1460
1461#[test]
1462fn test_bounded_reduce() {
1463 let n_max = (1 << 62) / 9;
1464 for n in (n_max - 10)..=n_max {
1465 let Zn = Zn::new(n);
1466 let val_max = Zn.get_ring().repr_bound() as u128 * Zn.get_ring().repr_bound() as u128;
1467 for k in (val_max - 100)..=val_max {
1468 assert_eq!(
1469 (k % (n as u128)) as i64,
1470 Zn.smallest_positive_lift(ZnEl(Zn.get_ring().bounded_reduce(k)))
1471 );
1472 }
1473 }
1474}
1475
1476#[bench]
1477fn bench_hom_from_i64_large_modulus(bencher: &mut Bencher) {
1478 let Zn = Zn::new(36028797018963971 );
1480 bencher.iter(|| {
1481 let hom = Zn.can_hom(&StaticRing::<i64>::RING).unwrap();
1482 assert_el_eq!(
1483 Zn,
1484 Zn.int_hom().map(-1300),
1485 Zn.sum((0..100).flat_map(|_| (0..=56).map(|k| 1 << k)).map(|x| hom.map(x)))
1486 )
1487 });
1488}
1489
1490#[bench]
1491fn bench_hom_from_i64_small_modulus(bencher: &mut Bencher) {
1492 let Zn = Zn::new(17);
1494 bencher.iter(|| {
1495 let hom = Zn.can_hom(&StaticRing::<i64>::RING).unwrap();
1496 assert_el_eq!(Zn, Zn.int_hom().map(2850 * 5699), Zn.sum((0..5700).map(|x| hom.map(x))))
1497 });
1498}
1499
1500#[bench]
1501fn bench_reduction_map_use_case(bencher: &mut Bencher) {
1502 let p = 17;
1504 let Zp2 = Zn::new(p * p);
1505 let Zp = Zn::new(p);
1506 let Zp2_mod_p = ZnReductionMap::new(&Zp2, &Zp).unwrap();
1507 let Zp2_p = PreparedDivisor::new(Zp2.get_ring(), Zp2.int_hom().map(p as i32));
1508
1509 let split_quo_rem = |x: El<Zn>| {
1510 let rem = Zp2_mod_p.map_ref(&x);
1511 let Zp2_rem = Zp2_mod_p.smallest_lift(rem);
1512 let quo = Zp2_p.checked_left_div_by(&Zp2.sub(x, Zp2_rem), Zp2.get_ring()).unwrap();
1513 (rem, Zp2_mod_p.map(quo))
1514 };
1515
1516 bencher.iter(|| {
1517 for x in Zp2.elements() {
1518 for y in Zp2.elements() {
1519 let (x_low, x_high) = split_quo_rem(x);
1520 let (y_low, y_high) = split_quo_rem(y);
1521 assert_el_eq!(
1522 Zp2,
1523 Zp2.mul(x, y),
1524 &Zp2.add(
1525 Zp2.mul(Zp2_mod_p.smallest_lift(x_low), Zp2_mod_p.smallest_lift(y_low)),
1526 Zp2_mod_p.mul_quotient_fraction(Zp.add(Zp.mul(x_low, y_high), Zp.mul(x_high, y_low)))
1527 )
1528 );
1529 }
1530 }
1531 });
1532}
1533
1534#[bench]
1535fn bench_inner_product(bencher: &mut Bencher) {
1536 let Fp = Zn::new(65537);
1537 let len = 1 << 12;
1538 let lhs = (0..len).map(|i| Fp.int_hom().map(i)).collect::<Vec<_>>();
1539 let rhs = (0..len).map(|i| Fp.int_hom().map(i)).collect::<Vec<_>>();
1540 let expected = (0..len)
1541 .map(|i| Fp.int_hom().map(i * i))
1542 .fold(Fp.zero(), |l, r| Fp.add(l, r));
1543
1544 bencher.iter(|| {
1545 let actual = <_ as ComputeInnerProduct>::inner_product_ref(
1546 Fp.get_ring(),
1547 lhs.iter().zip(rhs.iter()).map(|x| std::hint::black_box(x)),
1548 );
1549 assert_el_eq!(Fp, expected, actual);
1550 })
1551}
1552
1553#[test]
1554fn test_serialize() {
1555 let ring = Zn::new(128);
1556 crate::serialization::generic_tests::test_serialization(ring, ring.elements())
1557}