feanor_math/rings/zn/
zn_64.rs

1use crate::algorithms::fft::cooley_tuckey::CooleyTuckeyButterfly;
2use crate::compute_locally::InterpolationBaseRing;
3use crate::delegate::DelegateRing;
4use crate::delegate::DelegateRingImplFiniteRing;
5use crate::divisibility::*;
6use crate::impl_eq_based_self_iso;
7use crate::impl_localpir_wrap_unwrap_homs;
8use crate::impl_localpir_wrap_unwrap_isos;
9use crate::impl_field_wrap_unwrap_homs;
10use crate::impl_field_wrap_unwrap_isos;
11use crate::ordered::OrderedRingStore;
12use crate::primitive_int::*;
13use crate::integer::*;
14use crate::pid::*;
15use crate::rings::extension::FreeAlgebraStore;
16use crate::ring::*;
17use crate::rings::extension::galois_field::*;
18use crate::seq::*;
19use crate::serialization::SerializableElementRing;
20use crate::algorithms::convolution::KaratsubaHint;
21use crate::algorithms::matmul::ComputeInnerProduct;
22use crate::algorithms::matmul::StrassenHint;
23use serde::de;
24use serde::{Deserialize, Deserializer, Serialize, Serializer}; 
25
26use crate::homomorphism::*;
27
28use super::*;
29use super::zn_big;
30
31fn high(x: u128) -> u64 {
32    (x >> 64) as u64
33}
34
35fn low(x: u128) -> u64 {
36    (x & ((1 << 64) - 1)) as u64
37}
38
39fn mulhi(lhs: u64, rhs: u64) -> u64 {
40    high(lhs as u128 * rhs as u128)
41}
42
43fn mullo(lhs: u64, rhs: u64) -> u64 {
44    lhs.wrapping_mul(rhs)
45}
46
47///
48/// Represents the ring `Z/nZ`.
49/// A variant of Barett reduction is used to perform fast modular
50/// arithmetic for `n` slightly smaller than 64 bit.
51/// 
52/// More concretely, the currently maximal supported modulus is `floor(2^62 / 9)`.
53/// Note that the exact value might change in the future.
54/// 
55/// # Examples
56/// ```
57/// # use feanor_math::assert_el_eq;
58/// # use feanor_math::ring::*;
59/// # use feanor_math::homomorphism::*;
60/// # use feanor_math::rings::zn::*;
61/// # use feanor_math::rings::zn::zn_64::*;
62/// let zn = Zn::new(7);
63/// assert_el_eq!(zn, zn.one(), zn.mul(zn.int_hom().map(3), zn.int_hom().map(5)));
64/// ```
65/// Too large moduli will give an error.
66/// ```should_panic
67/// # use feanor_math::assert_el_eq;
68/// # use feanor_math::ring::*;
69/// # use feanor_math::rings::zn::*;
70/// # use feanor_math::rings::zn::zn_64::*;
71/// Zn::new((1 << 62) / 9 + 1);
72/// ```
73/// 
74#[derive(Clone, Copy)]
75pub struct ZnBase {
76    modulus: i64,
77    modulus_half: i64,
78    modulus_times_three: u64,
79    inv_modulus: u128
80}
81
82const ZZ: StaticRing<i64> = StaticRing::<i64>::RING;
83#[allow(non_upper_case_globals)]
84const ZZbig: BigIntRing = BigIntRing::RING;
85
86impl ZnBase {
87
88    pub fn new(modulus: u64) -> Self {
89        assert!(modulus > 1);
90        // this should imply the statement we need later
91        assert!(modulus <= ((1 << 62) / 9));
92        // we want representatives to grow up to 6 * modulus
93        assert!(modulus as u128 * 6 <= u64::MAX as u128);
94        let inv_modulus = ZZbig.euclidean_div(ZZbig.power_of_two(128), &ZZbig.coerce(&ZZ, modulus as i64));
95        // we need the product `inv_modulus * (6 * modulus)^2` to fit into 192 bit, should be implied by `modulus < ((1 << 62) / 9)`
96        debug_assert!(ZZbig.is_lt(&ZZbig.mul_ref_fst(&inv_modulus, ZZbig.pow(ZZbig.int_hom().mul_map(ZZbig.coerce(&ZZ, modulus as i64), 6), 2)), &ZZbig.power_of_two(192)));
97        let inv_modulus = if ZZbig.eq_el(&inv_modulus, &ZZbig.power_of_two(127)) {
98            1u128 << 127
99        } else {
100            int_cast(inv_modulus, &StaticRing::<i128>::RING, &ZZbig) as u128
101        };
102        Self {
103            modulus: modulus as i64,
104            inv_modulus: inv_modulus,
105            modulus_half: (modulus as i64 - 1) / 2 + 1,
106            modulus_times_three: modulus * 3
107        }
108    }
109
110    fn modulus_u64(&self) -> u64 {
111        self.modulus as u64
112    }
113
114    ///
115    /// Positive integers bounded by `self.repr_bound()` (inclusive) are considered
116    /// valid representatives of ring elements.
117    /// 
118    fn repr_bound(&self) -> u64 {
119        self.modulus_times_three * 2
120    }
121
122    ///
123    /// Reduces from `[0, self.repr_bound() * self.repr_bound()]` to `[0, 3 * self.modulus()[`.
124    /// 
125    fn bounded_reduce(&self, value: u128) -> u64 {
126        debug_assert!(value <= self.repr_bound() as u128 * self.repr_bound() as u128);
127
128        let (in_low, in_high) = (low(value), high(value));
129        let (invmod_low, invmod_high) = (low(self.inv_modulus), high(self.inv_modulus));
130        // we ignore the lowest part of the sum, causing an error of at most 1;
131        // we also assume that `repr_bound * repr_bound * inv_modulus` fits into 192 bit
132        let approx_quotient = mulhi(in_low, invmod_high) + mulhi(in_high, invmod_low) + mullo(in_high, invmod_high);
133        let result = low(value).wrapping_sub(mullo(approx_quotient, self.modulus_u64()));
134
135        debug_assert!(result < self.modulus_times_three);
136        debug_assert!((value - result as u128) % (self.modulus_u64() as u128) == 0);
137        return result;
138    }
139
140    ///
141    /// Reduces from `[0, FACTOR * self.repr_bound() * self.repr_bound()]` to `[0, 3 * self.modulus()]`.
142    /// 
143    /// As opposed to the faster [`ZnBase::bounded_reduce()`], this should work for all inputs in `u128`
144    /// (assuming configured with the right value of `FACTOR`). Currently only used by the specialization of 
145    /// [`ComputeInnerProduct::inner_product()`].
146    /// 
147    #[inline(never)]
148    fn bounded_reduce_larger<const FACTOR: usize>(&self, value: u128) -> u64 {
149        assert!(FACTOR == 32);
150        debug_assert!(value <= FACTOR as u128 * self.repr_bound() as u128 * self.repr_bound() as u128);
151
152        let (in_low, in_high) = (low(value), high(value));
153        let invmod_high = high(self.inv_modulus);
154        // `approx_quotient` can be just slightly larger than 64 bits, since we optimized for `bounded_reduce()`
155        let approx_quotient = in_high as u128 * invmod_high as u128 + mulhi(in_low, invmod_high) as u128;
156
157        return self.bounded_reduce(value - (approx_quotient * self.modulus as u128));
158    }
159
160    ///
161    /// Reduces from `[0, 2 * self.repr_bound()]` to `[0, 3 * self.modulus()]`
162    /// 
163    fn potential_reduce(&self, mut value: u64) -> u64 {
164        debug_assert!(value as u128 <= 2 * self.repr_bound() as u128);
165        if value >= self.repr_bound() {
166            value -= self.repr_bound();
167        }
168        if value >= self.modulus_times_three {
169            value -= self.modulus_times_three;
170        }
171        debug_assert!(value <= self.modulus_times_three);
172        return value;
173    }
174
175    ///
176    /// Creates a `ZnEl` from an integer that is already sufficiently reduced,
177    /// i.e. within `[0, self.repr_bound()]`.
178    /// 
179    /// The reducedness of the input is only checked when debug assertions are
180    /// enabled.
181    /// 
182    fn from_u64_promise_reduced(&self, value: u64) -> ZnEl {
183        debug_assert!(value <= self.repr_bound());
184        ZnEl(value)
185    }
186
187    ///
188    /// Reduces from `[0, self.repr_bound()]` to `[0, self.modulus()[`
189    /// 
190    fn complete_reduce(&self, mut value: u64) -> u64 {
191        debug_assert!(value <= self.repr_bound());
192        if value >= 4 * self.modulus_u64() {
193            value -= 4 * self.modulus_u64();
194        }
195        if value >= 2 * self.modulus_u64() {
196            value -= 2 * self.modulus_u64();
197        }
198        if value >= self.modulus_u64() {
199            value -= self.modulus_u64();
200        }
201        debug_assert!(value < self.modulus_u64());
202        return value;
203    }
204}
205
206/// 
207/// Represents the ring `Z/nZ`, using a variant of Barett reduction
208/// for moduli somewhat smaller than 64 bits. For details, see [`ZnBase`].
209/// 
210pub type Zn = RingValue<ZnBase>;
211
212impl Zn {
213
214    pub fn new(modulus: u64) -> Self {
215        RingValue::from(ZnBase::new(modulus))
216    }
217}
218
219#[derive(Clone, Copy, Debug)]
220pub struct ZnEl(/* a representative within `[0, ring.repr_bound()]` */ u64);
221
222impl PartialEq for ZnBase {
223    fn eq(&self, other: &Self) -> bool {
224        self.modulus == other.modulus
225    }
226}
227
228impl RingBase for ZnBase {
229
230    type Element = ZnEl;
231
232    fn clone_el(&self, val: &Self::Element) -> Self::Element {
233        *val
234    }
235
236    fn add_assign(&self, lhs: &mut Self::Element, rhs: Self::Element) {
237        debug_assert!(lhs.0 <= self.repr_bound());
238        debug_assert!(rhs.0 <= self.repr_bound());
239        lhs.0 = self.potential_reduce(lhs.0 + rhs.0);
240    }
241
242    fn negate_inplace(&self, lhs: &mut Self::Element) {
243        debug_assert!(lhs.0 <= self.repr_bound());
244        lhs.0 = self.repr_bound() - lhs.0;
245    }
246
247    fn mul_assign(&self, lhs: &mut Self::Element, rhs: Self::Element) {
248        debug_assert!(lhs.0 <= self.repr_bound());
249        debug_assert!(rhs.0 <= self.repr_bound());
250        lhs.0 = self.bounded_reduce(lhs.0 as u128 * rhs.0 as u128);
251    }
252
253    fn from_int(&self, value: i32) -> Self::Element {
254        RingRef::new(self).coerce(&StaticRing::<i32>::RING, value)
255    }
256
257    fn eq_el(&self, lhs: &Self::Element, rhs: &Self::Element) -> bool {
258        debug_assert!(lhs.0 <= self.repr_bound());
259        debug_assert!(rhs.0 <= self.repr_bound());
260        if lhs.0 > rhs.0 {
261            self.is_zero(&self.from_u64_promise_reduced(lhs.0 - rhs.0))
262        } else {
263            self.is_zero(&self.from_u64_promise_reduced(rhs.0 - lhs.0))
264        }
265    }
266
267    fn is_zero(&self, val: &Self::Element) -> bool {
268        debug_assert!(val.0 <= self.repr_bound());
269        self.complete_reduce(val.0) == 0
270    }
271
272    fn is_commutative(&self) -> bool { true }
273    fn is_noetherian(&self) -> bool { true }
274
275    fn dbg_within<'a>(&self, value: &Self::Element, out: &mut std::fmt::Formatter<'a>, _: EnvBindingStrength) -> std::fmt::Result {
276        write!(out, "{}", self.complete_reduce(value.0))
277    }
278    
279    fn characteristic<I: RingStore + Copy>(&self, other_ZZ: I) -> Option<El<I>>
280        where I::Type: IntegerRing
281    {
282        self.size(other_ZZ)
283    }
284    
285    fn sum<I>(&self, els: I) -> Self::Element 
286        where I: IntoIterator<Item = Self::Element>
287    {
288        let mut result = self.zero();
289        let mut els_it = els.into_iter();
290        while let Some(ZnEl(start)) = els_it.next() {
291            let mut current = start as u128;
292            for ZnEl(c) in els_it.by_ref().take(self.repr_bound() as usize / 2 - 1) {
293                current += c as u128;
294            }
295            self.add_assign(&mut result, self.from_u64_promise_reduced(self.bounded_reduce(current)));
296        }
297        debug_assert!(result.0 <= self.repr_bound());
298        return result;
299    }
300
301    fn pow_gen<R: RingStore>(&self, x: Self::Element, power: &El<R>, integers: R) -> Self::Element 
302        where R::Type: IntegerRing
303    {
304        assert!(!integers.is_neg(power));
305        algorithms::sqr_mul::generic_abs_square_and_multiply(
306            x, 
307            power, 
308            &integers,
309            |mut a| {
310                self.square(&mut a);
311                a
312            },
313            |a, b| self.mul_ref_fst(a, b),
314            self.one()
315        )
316    }
317
318    fn is_approximate(&self) -> bool { false }
319}
320
321impl SerializableElementRing for ZnBase {
322
323    fn deserialize<'de, D>(&self, deserializer: D) -> Result<Self::Element, D::Error>
324        where D: Deserializer<'de>
325    {
326        <i64 as Deserialize>::deserialize(deserializer)
327            .and_then(|x| if x < 0 || x >= *self.modulus() { Err(de::Error::custom("ring element value out of bounds for ring Z/nZ")) } else { Ok(x) })
328            .map(|x| self.from_int_promise_reduced(x))
329    }
330
331    fn serialize<S>(&self, el: &Self::Element, serializer: S) -> Result<S::Ok, S::Error>
332        where S: Serializer
333    {
334        <i64 as Serialize>::serialize(&self.smallest_positive_lift(*el), serializer)
335    }
336}
337
338impl FromModulusCreateableZnRing for ZnBase {
339
340    fn create<F, E>(create_modulus: F) -> Result<Self, E>
341        where F: FnOnce(&Self::IntegerRingBase) -> Result<El<Self::IntegerRing>, E>
342    {
343        create_modulus(StaticRing::<i64>::RING.get_ring()).map(|n| Self::new(n as u64))
344    }
345}
346
347impl InterpolationBaseRing for AsFieldBase<Zn> {
348
349    type ExtendedRingBase<'a> = GaloisFieldBaseOver<RingRef<'a, Self>>
350        where Self: 'a;
351
352    type ExtendedRing<'a> = GaloisFieldOver<RingRef<'a, Self>>
353        where Self: 'a;
354
355    fn in_base<'a, S>(&self, ext_ring: S, el: El<S>) -> Option<Self::Element>
356        where Self: 'a, S: RingStore<Type = Self::ExtendedRingBase<'a>>
357    {
358        let wrt_basis = ext_ring.wrt_canonical_basis(&el);
359        if wrt_basis.iter().skip(1).all(|x| self.is_zero(&x)) {
360            return Some(wrt_basis.at(0));
361        } else {
362            return None;
363        }
364    }
365
366    fn in_extension<'a, S>(&self, ext_ring: S, el: Self::Element) -> El<S>
367        where Self: 'a, S: RingStore<Type = Self::ExtendedRingBase<'a>>
368    {
369        ext_ring.inclusion().map(el)
370    }
371
372    fn interpolation_points<'a>(&'a self, count: usize) -> (Self::ExtendedRing<'a>, Vec<El<Self::ExtendedRing<'a>>>) {
373        let ring = generic_impls::interpolation_ring(RingRef::new(self), count);
374        let points = ring.elements().take(count).collect();
375        return (ring, points);
376    }
377}
378
379impl ComputeInnerProduct for ZnBase {
380
381    fn inner_product<I: Iterator<Item = (Self::Element, Self::Element)>>(&self, mut els: I) -> Self::Element
382    {
383        fn body<I: Iterator<Item = (ZnEl, ZnEl)>>(ring: &ZnBase, els: &mut I) -> Option<ZnEl> {
384            debug_assert!(u128::MAX / (ring.repr_bound() as u128 * ring.repr_bound() as u128) >= 36);
385            const REDUCE_AFTER_STEPS: usize = 32;
386            
387            let mut current = 0;
388            for i in 0..REDUCE_AFTER_STEPS {
389                let next_pair = els.next();
390                if let Some((l, r)) = next_pair {
391                    debug_assert!(l.0 <= ring.repr_bound());
392                    debug_assert!(r.0 <= ring.repr_bound());
393                    current += l.0 as u128 * r.0 as u128;
394                    debug_assert!(current <= (i + 1) as u128 * ring.repr_bound() as u128 * ring.repr_bound() as u128);
395                } else if i == 0 {
396                    return None;
397                } else {
398                    break;
399                }
400            }
401            return Some(ZnEl(ring.bounded_reduce_larger::<REDUCE_AFTER_STEPS>(current)));
402        }
403
404        self.sum((0..).map(|_| body(self, &mut els)).take_while(|x| x.is_some()).map(|x| x.unwrap()))
405    }
406
407    fn inner_product_ref_fst<'a, I: Iterator<Item = (&'a Self::Element, Self::Element)>>(&self, els: I) -> Self::Element
408        where Self::Element: 'a
409    {
410        self.inner_product(els.map(|(l, r)| (self.clone_el(l), r)))
411    }
412
413    fn inner_product_ref<'a, I: Iterator<Item = (&'a Self::Element, &'a Self::Element)>>(&self, els: I) -> Self::Element
414        where Self::Element: 'a
415    {
416        self.inner_product_ref_fst(els.map(|(l, r)| (l, self.clone_el(r))))
417    }
418}
419
420impl_eq_based_self_iso!{ ZnBase }
421
422impl<I: RingStore> CanHomFrom<zn_big::ZnBase<I>> for ZnBase
423    where I::Type: IntegerRing
424{
425    type Homomorphism = ();
426
427    fn has_canonical_hom(&self, from: &zn_big::ZnBase<I>) -> Option<Self::Homomorphism> {
428        if from.integer_ring().get_ring().representable_bits().is_none() || from.integer_ring().get_ring().representable_bits().unwrap() >= self.integer_ring().abs_log2_ceil(self.modulus()).unwrap() {
429            if from.integer_ring().eq_el(from.modulus(), &int_cast(*self.modulus(), from.integer_ring(), self.integer_ring())) {
430                Some(())
431            } else {
432                None
433            }
434        } else {
435            None
436        }
437    }
438
439    fn map_in(&self, from: &zn_big::ZnBase<I>, el: <zn_big::ZnBase<I> as RingBase>::Element, _: &Self::Homomorphism) -> Self::Element {
440        self.from_u64_promise_reduced(int_cast(from.smallest_positive_lift(el), self.integer_ring(), from.integer_ring()) as u64)
441    }
442}
443
444impl<I: RingStore> CanIsoFromTo<zn_big::ZnBase<I>> for ZnBase
445    where I::Type: IntegerRing
446{
447    type Isomorphism = <zn_big::ZnBase<I> as CanHomFrom<StaticRingBase<i64>>>::Homomorphism;
448
449    fn has_canonical_iso(&self, from: &zn_big::ZnBase<I>) -> Option<Self::Isomorphism> {
450        if from.integer_ring().get_ring().representable_bits().is_none() || from.integer_ring().get_ring().representable_bits().unwrap() >= self.integer_ring().abs_log2_ceil(self.modulus()).unwrap() {
451            if from.integer_ring().eq_el(from.modulus(), &int_cast(*self.modulus(), from.integer_ring(), self.integer_ring())) {
452                from.has_canonical_hom(self.integer_ring().get_ring())
453            } else {
454                None
455            }
456        } else {
457            None
458        }
459    }
460
461    fn map_out(&self, from: &zn_big::ZnBase<I>, el: <Self as RingBase>::Element, iso: &Self::Isomorphism) -> <zn_big::ZnBase<I> as RingBase>::Element {
462        from.map_in(self.integer_ring().get_ring(), el.0 as i64, iso)
463    }
464}
465
466///
467/// Data associated to an element of [`ZnBase`] that allows for faster division. 
468/// For details, see [`DivisibilityRing::prepare_divisor()`].
469/// 
470#[derive(Copy, Clone, Debug)]
471pub struct ZnPreparedDivisorData {
472    unit_part: El<Zn>,
473    is_unit: bool,
474    smallest_positive_zero_divisor_part: PreparedDivisor<StaticRingBase<i64>>
475}
476
477impl DivisibilityRing for ZnBase {
478
479    type PreparedDivisorData = ZnPreparedDivisorData;
480
481    fn checked_left_div(&self, lhs: &Self::Element, rhs: &Self::Element) -> Option<Self::Element> {
482        super::generic_impls::checked_left_div(RingRef::new(self), lhs, rhs)
483    }
484
485    fn prepare_divisor(&self, x: Self::Element) -> PreparedDivisor<Self> {
486        let (s, _t, d) = algorithms::eea::signed_eea(self.smallest_positive_lift(x), *self.modulus(), self.integer_ring());
487        debug_assert!(d > 0);
488        debug_assert!(d <= *self.modulus());
489        return PreparedDivisor {
490            data: ZnPreparedDivisorData {
491                is_unit: d == 1,
492                unit_part: if s < 0 { self.negate(self.from_u64_promise_reduced(-s as u64)) } else { self.from_u64_promise_reduced(s as u64) },
493                smallest_positive_zero_divisor_part: StaticRing::<i64>::RING.get_ring().prepare_divisor(d)
494            },
495            element: x
496        };
497    }
498
499    fn checked_left_div_prepared(&self, lhs: &Self::Element, rhs: &PreparedDivisor<Self>) -> Option<Self::Element> {
500        if rhs.data.is_unit {
501            Some(self.mul_ref(lhs, &rhs.data.unit_part))
502        } else {
503            StaticRing::<i64>::RING.get_ring().checked_left_div_prepared(&self.smallest_positive_lift(*lhs), &rhs.data.smallest_positive_zero_divisor_part)
504                .map(|x| self.mul(self.from_u64_promise_reduced(x as u64), rhs.data.unit_part))
505        }
506    }
507
508    fn divides_left_prepared(&self, lhs: &Self::Element, rhs: &PreparedDivisor<Self>) -> bool {
509        self.checked_left_div_prepared(lhs, rhs).is_some()
510    }
511}
512
513impl<I: ?Sized + IntegerRing> CanHomFrom<I> for ZnBase {
514
515    type Homomorphism = super::generic_impls::BigIntToZnHom<I, StaticRingBase<i128>, Self>;
516
517    default fn has_canonical_hom(&self, from: &I) -> Option<Self::Homomorphism> {
518        super::generic_impls::has_canonical_hom_from_bigint(from, self, StaticRing::<i128>::RING.get_ring(), Some(&(self.repr_bound() as i128 * self.repr_bound() as i128)))
519    }
520
521    default fn map_in(&self, from: &I, el: I::Element, hom: &Self::Homomorphism) -> Self::Element {
522        super::generic_impls::map_in_from_bigint(from, self, StaticRing::<i128>::RING.get_ring(), el, hom, |n| {
523            debug_assert!((n as u64) < self.modulus_u64());
524            self.from_u64_promise_reduced(n as u64)
525        }, |n| {
526            debug_assert!(n <= (self.repr_bound() as i128 * self.repr_bound() as i128));
527            self.from_u64_promise_reduced(self.bounded_reduce(n as u128))
528        })
529    }
530}
531
532macro_rules! impl_static_int_to_zn {
533    ($($int:ident),*) => {
534        $(
535            impl CanHomFrom<StaticRingBase<$int>> for ZnBase {
536            
537                fn map_in(&self, _from: &StaticRingBase<$int>, el: $int, _hom: &Self::Homomorphism) -> Self::Element {
538                    if el.abs() as u128 <= self.modulus_u64() as u128 {
539                        if el < 0 {
540                            self.negate(self.from_u64_promise_reduced(el.unsigned_abs() as u64))
541                        } else {
542                            self.from_u64_promise_reduced(el as u64)
543                        }
544                    } else if el.abs() as u128 <= self.repr_bound() as u128 {
545                        if el < 0 {
546                            self.negate(self.from_u64_promise_reduced(self.bounded_reduce(el.unsigned_abs() as u128)))
547                        } else {
548                            self.from_u64_promise_reduced(self.bounded_reduce(el as u128))
549                        }
550                    } else {
551                        if el < 0 {
552                            self.from_u64_promise_reduced(((el as i128 % self.modulus as i128) as i64 + self.modulus) as u64)
553                        } else {
554                            self.from_u64_promise_reduced((el as i128 % self.modulus as i128) as u64)
555                        }
556                    }
557                }
558            }
559        )*
560    };
561}
562
563impl_static_int_to_zn!{ i8, i16, i32, i64, i128 }
564
565#[derive(Clone, Copy)]
566pub struct ZnBaseElementsIter<'a> {
567    ring: &'a ZnBase,
568    current: u64
569}
570
571impl<'a> Iterator for ZnBaseElementsIter<'a> {
572
573    type Item = ZnEl;
574
575    fn next(&mut self) -> Option<Self::Item> {
576        if self.current < self.ring.modulus_u64() {
577            let result = self.current;
578            self.current += 1;
579            return Some(self.ring.from_u64_promise_reduced(result));
580        } else {
581            return None;
582        }
583    }
584}
585
586impl FiniteRing for ZnBase {
587
588    type ElementsIter<'a> = ZnBaseElementsIter<'a>;
589
590    fn elements<'a>(&'a self) -> Self::ElementsIter<'a> {
591        ZnBaseElementsIter {
592            ring: self,
593            current: 0
594        }
595    }
596
597    fn random_element<G: FnMut() -> u64>(&self, rng: G) -> <Self as RingBase>::Element {
598        super::generic_impls::random_element(self, rng)
599    }
600
601    fn size<I: RingStore + Copy>(&self, other_ZZ: I) -> Option<El<I>>
602        where I::Type: IntegerRing
603    {
604        if other_ZZ.get_ring().representable_bits().is_none() || self.integer_ring().abs_log2_ceil(&(self.modulus() + 1)) <= other_ZZ.get_ring().representable_bits() {
605            Some(int_cast(*self.modulus(), other_ZZ, self.integer_ring()))
606        } else {
607            None
608        }
609    }
610}
611
612impl PrincipalIdealRing for ZnBase {
613
614    fn checked_div_min(&self, lhs: &Self::Element, rhs: &Self::Element) -> Option<Self::Element> {
615        generic_impls::checked_div_min(RingRef::new(self), lhs, rhs)
616    }
617
618    fn extended_ideal_gen(&self, lhs: &Self::Element, rhs: &Self::Element) -> (Self::Element, Self::Element, Self::Element) {
619        let (s, t, d) = StaticRing::<i64>::RING.extended_ideal_gen(&(lhs.0 as i64), &(rhs.0 as i64));
620        let quo = RingRef::new(self).into_can_hom(StaticRing::<i64>::RING).ok().unwrap();
621        (quo.map(s), quo.map(t), quo.map(d))
622    }
623}
624
625impl StrassenHint for ZnBase {
626    default fn strassen_threshold(&self) -> usize {
627        6
628    }
629}
630
631impl KaratsubaHint for ZnBase {
632    default fn karatsuba_threshold(&self) -> usize {
633        6
634    }
635}
636
637impl ZnRing for ZnBase {
638
639    type IntegerRingBase = StaticRingBase<i64>;
640    type IntegerRing = StaticRing<i64>;
641
642    fn integer_ring(&self) -> &Self::IntegerRing {
643        &StaticRing::<i64>::RING
644    }
645
646    fn smallest_positive_lift(&self, el: Self::Element) -> El<Self::IntegerRing> {
647        self.complete_reduce(el.0) as i64
648    }
649
650    fn smallest_lift(&self, ZnEl(mut value_u64): Self::Element) -> El<Self::IntegerRing> {
651        debug_assert!(value_u64 <= self.repr_bound());
652        // value is in [0, 6 * self.modulus]
653        if value_u64 >= 3 * self.modulus_u64() {
654            value_u64 -= 3 * self.modulus_u64();
655        }
656        // value is in [0, 3 * self.modulus]
657        let mut value_i64 = value_u64 as i64;
658        if value_i64 >= self.modulus + self.modulus_half {
659            value_i64 -= 2 * self.modulus;
660        }
661        // value is in ]-self.modulus_half, self.modulus + self.modulus_half[ if modulus is odd
662        // value is in [-self.modulus_half, self.modulus + self.modulus_half[ if modulus is even
663        if value_i64 >= self.modulus_half {
664            value_i64 -= self.modulus;
665        }
666        // value is in ]-self.modulus_half, self.modulus_half[ if modulus is odd
667        // value is in [-self.modulus_half, self.modulus_half[ if modulus is even
668        debug_assert!(value_i64 < self.modulus_half);
669        debug_assert!(self.modulus() % 2 == 0 || value_i64 > -self.modulus_half);
670        debug_assert!(self.modulus() % 2 == 1 || value_i64 >= -self.modulus_half);
671        return value_i64;
672    }
673
674    fn modulus(&self) -> &El<Self::IntegerRing> {
675        &self.modulus
676    }
677
678    fn any_lift(&self, el: Self::Element) -> El<Self::IntegerRing> {
679        el.0 as i64
680    }
681
682    ///
683    /// If the given integer is within `{ 0, ..., 6 * n }`, returns the corresponding
684    /// element in `Z/nZ`. Any other input is considered a logic error.
685    /// 
686    /// This function follows [`ZnRing::from_int_promise_reduced()`], but is guaranteed
687    /// to work on elements `{ 0, ..., 6 * n }` instead of only `{ 0, ..., n - 1 }`.
688    /// 
689    /// # Examples
690    /// ```
691    /// # use feanor_math::rings::zn::*;
692    /// # use feanor_math::assert_el_eq;
693    /// # use feanor_math::ring::*;
694    /// # use feanor_math::rings::zn::zn_64::*;
695    /// let ring = Zn::new(7);
696    /// assert_el_eq!(ring, ring.zero(), ring.get_ring().from_int_promise_reduced(42));
697    /// ```
698    /// Larger values lead to a panic in debug mode, and to a logic error in release mode.
699    /// ```should_panic
700    /// # use feanor_math::rings::zn::*;
701    /// # use feanor_math::assert_el_eq;
702    /// # use feanor_math::ring::*;
703    /// # use feanor_math::rings::zn::zn_64::*;
704    /// let ring = Zn::new(7);
705    /// ring.get_ring().from_int_promise_reduced(43);
706    /// ```
707    /// 
708    fn from_int_promise_reduced(&self, x: El<Self::IntegerRing>) -> Self::Element {
709        debug_assert!(self.repr_bound() == 6 * self.modulus_u64());
710        debug_assert!(x >= 0);
711        debug_assert!(x as u64 <= self.repr_bound());
712        self.from_u64_promise_reduced(x as u64)
713    }
714}
715
716impl HashableElRing for ZnBase {
717
718    fn hash<H: std::hash::Hasher>(&self, el: &Self::Element, h: &mut H) {
719        self.integer_ring().hash(&self.smallest_positive_lift(*el), h)
720    }
721}
722
723///
724/// Wraps [`ZnBase`] to represent an instance of the ring `Z/nZ`.
725/// As opposed to [`ZnBase`], elements are stored with additional information
726/// to speed up multiplication `ZnBase x ZnFastmulBase -> ZnBase`, by
727/// using [`CanHomFrom::mul_assign_map_in()`].
728/// Note that normal arithmetic in this ring is much slower than [`ZnBase`].
729/// 
730/// # Example
731/// The following use of the FFT is usually faster than the standard use, as
732/// the FFT requires a high amount of multiplications with the internally stored
733/// roots of unity.
734/// ```
735/// # #![feature(const_type_name)]
736/// # use feanor_math::homomorphism::*;
737/// # use feanor_math::ring::*;
738/// # use feanor_math::rings::zn::zn_64::*;
739/// # use feanor_math::algorithms::fft::*;
740/// # use feanor_math::algorithms::fft::cooley_tuckey::*;
741/// let ring = Zn::new(1073872897);
742/// let fastmul_ring = ZnFastmul::new(ring).unwrap();
743/// // The values stored by the FFT table are elements of `ZnFastmulBase`
744/// let fft = CooleyTuckeyFFT::for_zn_with_hom(ring.can_hom(&fastmul_ring).unwrap(), 15).unwrap();
745/// // Note that data uses `ZnBase`
746/// let mut data = (0..(1 << 15)).map(|i| ring.int_hom().map(i)).collect::<Vec<_>>();
747/// fft.unordered_fft(&mut data[..], &ring);
748/// ```
749/// 
750#[derive(Clone, Copy)]
751pub struct ZnFastmulBase {
752    base: RingValue<ZnBase>
753}
754
755///
756/// An implementation of `Z/nZ` for `n` that is optimized to provide fast multiplication with elements
757/// of [`Zn`]. For details, see [`ZnFastmulBase`].
758/// 
759pub type ZnFastmul = RingValue<ZnFastmulBase>;
760
761impl ZnFastmul {
762
763    pub fn new(base: Zn) -> Option<Self> {
764        Some(RingValue::from(ZnFastmulBase { base }))
765    }
766}
767
768impl PartialEq for ZnFastmulBase {
769    
770    fn eq(&self, other: &Self) -> bool {
771        self.base.get_ring() == other.base.get_ring()
772    }
773}
774
775impl PrincipalIdealRing for ZnFastmulBase {
776
777    fn checked_div_min(&self, lhs: &Self::Element, rhs: &Self::Element) -> Option<Self::Element> {
778        let result = self.get_delegate().checked_div_min(self.delegate_ref(lhs), self.delegate_ref(rhs));
779        result.map(|x| self.rev_delegate(x))
780    }
781
782    fn extended_ideal_gen(&self, lhs: &Self::Element, rhs: &Self::Element) -> (Self::Element, Self::Element, Self::Element) {
783        let (s, t, d) = self.get_delegate().extended_ideal_gen(self.delegate_ref(lhs), self.delegate_ref(rhs));
784        (self.rev_delegate(s), self.rev_delegate(t), self.rev_delegate(d))
785    }
786}
787
788impl_eq_based_self_iso!{ ZnFastmulBase }
789
790#[derive(Clone, Copy)]
791pub struct ZnFastmulEl {
792    el: ZnEl,
793    value_invmod_shifted: u64
794}
795
796impl DelegateRing for ZnFastmulBase {
797
798    type Base = ZnBase;
799    type Element = ZnFastmulEl;
800
801    fn get_delegate(&self) -> &Self::Base {
802        self.base.get_ring()
803    }
804
805    fn delegate(&self, el: Self::Element) -> <Self::Base as RingBase>::Element {
806        el.el
807    }
808
809    fn delegate_mut<'a>(&self, el: &'a mut Self::Element) -> &'a mut <Self::Base as RingBase>::Element {
810        &mut el.el
811    }
812
813    fn delegate_ref<'a>(&self, el: &'a Self::Element) -> &'a <Self::Base as RingBase>::Element {
814        &el.el
815    }
816
817    fn postprocess_delegate_mut(&self, el: &mut Self::Element) {
818        assert!(el.el.0 <= self.base.get_ring().repr_bound());
819        el.el.0 = self.base.get_ring().complete_reduce(el.el.0);
820        let value = el.el.0;
821        el.value_invmod_shifted = (((value as u128) << 64) / self.base.get_ring().modulus_u64() as u128) as u64;
822    }
823
824    fn rev_delegate(&self, el: <Self::Base as RingBase>::Element) -> Self::Element {
825        let mut result = ZnFastmulEl {
826            el: el,
827            value_invmod_shifted: 0
828        };
829        self.postprocess_delegate_mut(&mut result);
830        return result;
831    }
832}
833
834impl DelegateRingImplFiniteRing for ZnFastmulBase {}
835
836impl CanHomFrom<ZnBase> for ZnFastmulBase {
837
838    type Homomorphism = ();
839
840    fn has_canonical_hom(&self, from: &ZnBase) -> Option<Self::Homomorphism> {
841        if from == self.base.get_ring() {
842            Some(())
843        } else {
844            None
845        }
846    }
847
848    fn map_in(&self, _from: &ZnBase, el: <ZnBase as RingBase>::Element, _hom: &Self::Homomorphism) -> Self::Element {
849        self.rev_delegate(el)
850    }
851}
852
853impl CanHomFrom<ZnFastmulBase> for ZnBase {
854
855    type Homomorphism = ();
856
857    fn has_canonical_hom(&self, from: &ZnFastmulBase) -> Option<Self::Homomorphism> {
858        if self == from.base.get_ring() {
859            Some(())
860        } else {
861            None
862        }
863    }
864
865    fn map_in(&self, _from: &ZnFastmulBase, el: <ZnFastmulBase as RingBase>::Element, _hom: &Self::Homomorphism) -> Self::Element {
866        el.el
867    }
868
869    fn mul_assign_map_in(&self, _from: &ZnFastmulBase, lhs: &mut Self::Element, rhs: <ZnFastmulBase as RingBase>::Element, _hom: &Self::Homomorphism) {
870        debug_assert!(lhs.0 <= self.repr_bound());
871        let lhs_original = lhs.0;
872        let product = mullo(lhs.0, rhs.el.0);
873        let approx_quotient = mulhi(lhs.0, rhs.value_invmod_shifted);
874        lhs.0 = product.wrapping_sub(mullo(approx_quotient, self.modulus_u64()));
875        debug_assert!(lhs.0 < self.modulus_times_three);
876        debug_assert!((lhs_original as u128 * rhs.el.0 as u128 - lhs.0 as u128) % (self.modulus_u64() as u128) == 0);
877    }
878
879    fn mul_assign_map_in_ref(&self, from: &ZnFastmulBase, lhs: &mut Self::Element, rhs: &<ZnFastmulBase as RingBase>::Element, hom: &Self::Homomorphism) {
880        self.mul_assign_map_in(from, lhs, *rhs, hom);
881    }
882}
883
884impl CanIsoFromTo<ZnFastmulBase> for ZnBase {
885
886    type Isomorphism = <ZnFastmulBase as CanHomFrom<Self>>::Homomorphism;
887
888    fn has_canonical_iso(&self, from: &ZnFastmulBase) -> Option<Self::Isomorphism> {
889        from.has_canonical_hom(self)
890    }
891
892    fn map_out(&self, from: &ZnFastmulBase, el: Self::Element, iso: &Self::Isomorphism) -> <ZnFastmulBase as RingBase>::Element {
893        from.map_in(self, el, iso)
894    }
895}
896
897impl CooleyTuckeyButterfly<ZnFastmulBase> for ZnBase {
898
899    #[inline(always)]
900    fn butterfly<V: crate::seq::VectorViewMut<Self::Element>, H: Homomorphism<ZnFastmulBase, Self>>(&self, hom: H, values: &mut V, twiddle: &<ZnFastmulBase as RingBase>::Element, i1: usize, i2: usize) {
901        let mut a = *values.at(i1);
902        if a.0 >= self.modulus_times_three {
903            a.0 -= self.modulus_times_three;
904        }
905        let mut b = *values.at(i2);
906        hom.mul_assign_ref_map(&mut b, twiddle);
907
908        *values.at_mut(i1) = self.from_u64_promise_reduced(a.0 + b.0);
909        *values.at_mut(i2) = self.from_u64_promise_reduced(a.0 + self.modulus_times_three - b.0);
910    }
911
912    fn inv_butterfly<V: crate::seq::VectorViewMut<Self::Element>, H: Homomorphism<ZnFastmulBase, Self>>(&self, hom: H, values: &mut V, twiddle: &<ZnFastmulBase as RingBase>::Element, i1: usize, i2: usize) {
913        let a = *values.at(i1);
914        let b = *values.at(i2);
915
916        *values.at_mut(i1) = self.add(a, b);
917        *values.at_mut(i2) = self.sub(a, b);
918        hom.mul_assign_ref_map(values.at_mut(i2), twiddle);
919    }
920}
921
922impl CooleyTuckeyButterfly<ZnBase> for ZnBase {
923
924    #[inline(always)]
925    fn butterfly<V: crate::seq::VectorViewMut<Self::Element>, H: Homomorphism<ZnBase, Self>>(&self, _hom: H, values: &mut V, twiddle: &ZnEl, i1: usize, i2: usize) {
926        let mut a = *values.at(i1);
927        if a.0 >= self.modulus_times_three {
928            a.0 -= self.modulus_times_three;
929        }
930        let mut b = *values.at(i2);
931        self.mul_assign_ref(&mut b, twiddle);
932
933        *values.at_mut(i1) = self.from_u64_promise_reduced(a.0 + b.0);
934        *values.at_mut(i2) = self.from_u64_promise_reduced(a.0 + self.modulus_times_three - b.0);
935    }
936
937    fn inv_butterfly<V: crate::seq::VectorViewMut<Self::Element>, H: Homomorphism<ZnBase, Self>>(&self, _hom: H, values: &mut V, twiddle: &ZnEl, i1: usize, i2: usize) {
938        let a = *values.at(i1);
939        let b = *values.at(i2);
940
941        *values.at_mut(i1) = self.add(a, b);
942        *values.at_mut(i2) = self.sub(a, b);
943        self.mul_assign_ref(values.at_mut(i2), twiddle);
944    }
945}
946
947impl<I: ?Sized + IntegerRing> CanHomFrom<I> for ZnFastmulBase 
948    where ZnBase: CanHomFrom<I>
949{
950    type Homomorphism = <ZnBase as CanHomFrom<I>>::Homomorphism;
951
952    fn has_canonical_hom(&self, from: &I) -> Option<Self::Homomorphism> {
953        self.base.get_ring().has_canonical_hom(from)
954    }
955
956    fn map_in(&self, from: &I, el: I::Element, hom: &Self::Homomorphism) -> Self::Element {
957        self.rev_delegate(self.base.get_ring().map_in(from, el, hom))
958    }
959}
960
961impl_field_wrap_unwrap_homs!{ ZnBase, ZnBase }
962impl_field_wrap_unwrap_isos!{ ZnBase, ZnBase }
963impl_localpir_wrap_unwrap_homs!{ ZnBase, ZnBase }
964impl_localpir_wrap_unwrap_isos!{ ZnBase, ZnBase }
965
966impl<I> CanHomFrom<zn_big::ZnBase<I>> for AsFieldBase<Zn>
967    where I: RingStore,
968        I::Type: IntegerRing
969{
970    type Homomorphism = <ZnBase as CanHomFrom<zn_big::ZnBase<I>>>::Homomorphism;
971
972    fn has_canonical_hom(&self, from: &zn_big::ZnBase<I>) -> Option<Self::Homomorphism> {
973        self.get_delegate().has_canonical_hom(from)
974    }
975
976    fn map_in(&self, from: &zn_big::ZnBase<I>, el: <zn_big::ZnBase<I> as RingBase>::Element, hom: &Self::Homomorphism) -> Self::Element {
977        self.rev_delegate(self.get_delegate().map_in(from, el, hom))
978    }
979}
980
981impl<I> CanIsoFromTo<zn_big::ZnBase<I>> for AsFieldBase<Zn>
982    where I: RingStore,
983        I::Type: IntegerRing
984{
985    type Isomorphism = <ZnBase as CanIsoFromTo<zn_big::ZnBase<I>>>::Isomorphism;
986
987    fn has_canonical_iso(&self, from: &zn_big::ZnBase<I>) -> Option<Self::Isomorphism> {
988        self.get_delegate().has_canonical_iso(from)
989    }
990
991    fn map_out(&self, from: &zn_big::ZnBase<I>, el: Self::Element, hom: &Self::Isomorphism) -> <zn_big::ZnBase<I> as RingBase>::Element {
992        self.get_delegate().map_out(from, self.delegate(el), hom)
993    }
994}
995
996#[cfg(test)]
997use test::Bencher;
998
999#[cfg(test)]
1000fn elements<'a>(ring: &'a Zn) -> impl 'a + Iterator<Item = El<Zn>> {
1001    (0..63).map(|i| ring.coerce(&ZZ, 1 << i))
1002}
1003
1004#[cfg(test)]
1005const LARGE_MODULI: [u64; 6] = [(1 << 41) - 1, (1 << 42) - 1, (1 << 58) - 1, (1 << 58) + 1, (3 << 57) - 1, (3 << 57) + 1];
1006
1007#[test]
1008fn test_complete_reduce() {
1009    let ring = Zn::new(32);
1010    assert_eq!(31, ring.get_ring().complete_reduce(4 * 32 - 1));
1011}
1012
1013#[test]
1014fn test_sum() {
1015    for n in LARGE_MODULI {
1016        let Zn = Zn::new(n);
1017        assert_el_eq!(Zn, Zn.int_hom().map(10001 * 5000), Zn.sum((0..=10000).map(|x| Zn.int_hom().map(x))));
1018    }
1019}
1020
1021#[test]
1022fn test_ring_axioms() {
1023    for n in 2..=17 {
1024        let ring = Zn::new(n);
1025        crate::ring::generic_tests::test_ring_axioms(&ring, (0..=ring.get_ring().repr_bound()).map(|n| ZnEl(n)));
1026    }
1027    for n in LARGE_MODULI {
1028        let ring = Zn::new(n);
1029        crate::ring::generic_tests::test_ring_axioms(&ring, elements(&ring));
1030    }
1031}
1032
1033#[test]
1034fn test_hash_axioms() {
1035    for n in 2..=17 {
1036        let ring = Zn::new(n);
1037        crate::ring::generic_tests::test_hash_axioms(&ring, (0..=ring.get_ring().repr_bound()).map(|n| ZnEl(n)));
1038    }
1039    for n in LARGE_MODULI {
1040        let ring = Zn::new(n);
1041        crate::ring::generic_tests::test_hash_axioms(&ring, elements(&ring));
1042    }
1043}
1044
1045#[test]
1046fn test_divisibility_axioms() {
1047    for n in 2..=17 {
1048        let Zn = Zn::new(n);
1049        crate::divisibility::generic_tests::test_divisibility_axioms(&Zn, Zn.elements());
1050    }
1051    for n in LARGE_MODULI {
1052        let Zn = Zn::new(n);
1053        crate::divisibility::generic_tests::test_divisibility_axioms(&Zn, elements(&Zn));
1054    }
1055}
1056
1057#[test]
1058fn test_zn_axioms() {
1059    for n in 2..=17 {
1060        let Zn = Zn::new(n);
1061        super::generic_tests::test_zn_axioms(&Zn);
1062    }
1063}
1064
1065#[test]
1066fn test_principal_ideal_ring_axioms() {
1067    for n in 2..=17 {
1068        let R = Zn::new(n);
1069        crate::pid::generic_tests::test_principal_ideal_ring_axioms(R, R.elements());
1070    }
1071    let R = Zn::new(63);
1072    crate::pid::generic_tests::test_principal_ideal_ring_axioms(R, R.elements());
1073}
1074
1075#[test]
1076fn test_hom_from_fastmul() {
1077    for n in 2..=17 {
1078        let Zn = Zn::new(n);
1079        let Zn_fastmul = ZnFastmul::new(Zn).unwrap();
1080        crate::ring::generic_tests::test_hom_axioms(Zn_fastmul, Zn, Zn.elements().map(|x| Zn_fastmul.coerce(&Zn, x)));
1081    }
1082    for n in [(1 << 41) - 1, (1 << 42) - 1, (1 << 58) - 1, (1 << 58) + 1, (3 << 57) - 1, (3 << 57) + 1] {
1083        let Zn = Zn::new(n);
1084        let Zn_fastmul = ZnFastmul::new(Zn).unwrap();
1085        crate::ring::generic_tests::test_hom_axioms(Zn_fastmul, Zn, elements(&Zn).map(|x| Zn_fastmul.coerce(&Zn, x)));
1086    }
1087}
1088
1089#[test]
1090fn test_finite_ring_axioms() {
1091    for n in 2..=17 {
1092        crate::rings::finite::generic_tests::test_finite_ring_axioms(&Zn::new(n));
1093    }
1094    crate::rings::finite::generic_tests::test_finite_ring_axioms(&Zn::new(128));
1095    crate::rings::finite::generic_tests::test_finite_ring_axioms(&Zn::new(1 << 32));
1096}
1097
1098#[test]
1099fn test_from_int_hom() {
1100    for n in 2..=17 {
1101        let Zn = Zn::new(n);
1102        crate::ring::generic_tests::test_hom_axioms(StaticRing::<i8>::RING, Zn, -8..8);
1103        crate::ring::generic_tests::test_hom_axioms(StaticRing::<i16>::RING, Zn, -8..8);
1104        crate::ring::generic_tests::test_hom_axioms(StaticRing::<i32>::RING, Zn, -8..8);
1105        crate::ring::generic_tests::test_hom_axioms(StaticRing::<i64>::RING, Zn, -8..8);
1106        crate::ring::generic_tests::test_hom_axioms(StaticRing::<i128>::RING, Zn, -8..8);
1107    }
1108    let Zn = Zn::new(5);
1109    assert_el_eq!(Zn, Zn.int_hom().map(3), Zn.can_hom(&StaticRing::<i64>::RING).unwrap().map(-1596802));
1110}
1111
1112#[test]
1113fn test_bounded_reduce_large() {
1114    const FACTOR: usize = 32;
1115    let n_max = (1 << 62) / 9;
1116    for n in (n_max - 10)..=n_max {
1117        let Zn = Zn::new(n);
1118        let val_max = Zn.get_ring().repr_bound() as u128 * Zn.get_ring().repr_bound() as u128 * FACTOR as u128;
1119        for k in (val_max - 100)..=val_max {
1120            assert_eq!((k % (n as u128)) as i64, Zn.smallest_positive_lift(ZnEl(Zn.get_ring().bounded_reduce_larger::<FACTOR>(k))));
1121        }
1122    }
1123}
1124
1125#[test]
1126fn test_smallest_lift() {
1127    for n in 2..=17 {
1128        let ring = Zn::new(n);
1129        for k in 0..=ring.get_ring().repr_bound() {
1130            let expected = if (k % n) <= n / 2 { (k % n) as i64 } else { (k % n) as i64 - n as i64 };
1131            if n % 2 == 0 && (k % n) == n / 2 {
1132                assert!(ring.smallest_lift(ZnEl(k)) == n as i64 / 2 || ring.smallest_lift(ZnEl(k)) == -(n as i64) / 2)
1133            } else {
1134                assert_eq!(expected, ring.smallest_lift(ZnEl(k)));
1135            }
1136        }
1137    }
1138}
1139
1140#[test]
1141fn test_smallest_positive_lift() {
1142    for n in 2..=17 {
1143        let ring = Zn::new(n);
1144        for k in 0..=ring.get_ring().repr_bound() {
1145            let expected = (k % n) as i64;
1146            assert_eq!(expected, ring.smallest_positive_lift(ZnEl(k)));
1147        }
1148    }
1149}
1150
1151#[test]
1152fn test_bounded_reduce_small() {
1153    for n in 2..=17 {
1154        let Zn = Zn::new(n);
1155        let val_max = Zn.get_ring().repr_bound() as u128 * Zn.get_ring().repr_bound() as u128;
1156        for k in (val_max - 100)..=val_max {
1157            assert_eq!((k % (n as u128)) as i64, Zn.smallest_positive_lift(ZnEl(Zn.get_ring().bounded_reduce(k))));
1158        }
1159    }
1160}
1161
1162#[test]
1163fn test_bounded_reduce_large_small() {
1164    const FACTOR: usize = 32;
1165    for n in 2..=17 {
1166        let Zn = Zn::new(n);
1167        let val_max = Zn.get_ring().repr_bound() as u128 * Zn.get_ring().repr_bound() as u128 * FACTOR as u128;
1168        for k in (val_max - 100)..=val_max {
1169            assert_eq!((k % (n as u128)) as i64, Zn.smallest_positive_lift(ZnEl(Zn.get_ring().bounded_reduce_larger::<FACTOR>(k))));
1170        }
1171    }
1172}
1173
1174#[test]
1175fn test_bounded_reduce() {
1176    let n_max = (1 << 62) / 9;
1177    for n in (n_max - 10)..=n_max {
1178        let Zn = Zn::new(n);
1179        let val_max = Zn.get_ring().repr_bound() as u128 * Zn.get_ring().repr_bound() as u128;
1180        for k in (val_max - 100)..=val_max {
1181            assert_eq!((k % (n as u128)) as i64, Zn.smallest_positive_lift(ZnEl(Zn.get_ring().bounded_reduce(k))));
1182        }
1183    }
1184}
1185
1186#[bench]
1187fn bench_hom_from_i64_large_modulus(bencher: &mut Bencher) {
1188    // the case that the modulus is large
1189    let Zn = Zn::new(36028797018963971 /* = 2^55 + 3 */);
1190    bencher.iter(|| {
1191        let hom = Zn.can_hom(&StaticRing::<i64>::RING).unwrap();
1192        assert_el_eq!(Zn, Zn.int_hom().map(-1300), Zn.sum((0..100).flat_map(|_| (0..=56).map(|k| 1 << k)).map(|x| hom.map(x))))
1193    });
1194}
1195
1196#[bench]
1197fn bench_hom_from_i64_small_modulus(bencher: &mut Bencher) {
1198    // the case that the modulus is large
1199    let Zn = Zn::new(17);
1200    bencher.iter(|| {
1201        let hom = Zn.can_hom(&StaticRing::<i64>::RING).unwrap();
1202        assert_el_eq!(Zn, Zn.int_hom().map(2850 * 5699), Zn.sum((0..5700).map(|x| hom.map(x))))
1203    });
1204}
1205
1206#[bench]
1207fn bench_reduction_map_use_case(bencher: &mut Bencher) {
1208    // this benchmark is inspired by the use in https://eprint.iacr.org/2023/1510.pdf
1209    let p = 17;
1210    let Zp2 = Zn::new(p * p);
1211    let Zp = Zn::new(p);
1212    let Zp2_mod_p = ZnReductionMap::new(&Zp2, &Zp).unwrap();
1213    let Zp2_p = Zp2.get_ring().prepare_divisor(Zp2.int_hom().map(p as i32));
1214
1215    let split_quo_rem = |x: El<Zn>| {
1216        let rem = Zp2_mod_p.map_ref(&x);
1217        let Zp2_rem = Zp2_mod_p.smallest_lift(rem);
1218        let quo = Zp2.get_ring().checked_left_div_prepared(&Zp2.sub(x, Zp2_rem), &Zp2_p).unwrap();
1219        (rem, Zp2_mod_p.map(quo))
1220    };
1221
1222    bencher.iter(|| {
1223        for x in Zp2.elements() {
1224            for y in Zp2.elements() {
1225                let (x_low, x_high) = split_quo_rem(x);
1226                let (y_low, y_high) = split_quo_rem(y);
1227                assert_el_eq!(Zp2, Zp2.mul(x, y), &Zp2.add(Zp2.mul(Zp2_mod_p.smallest_lift(x_low), Zp2_mod_p.smallest_lift(y_low)), Zp2_mod_p.mul_quotient_fraction(Zp.add(Zp.mul(x_low, y_high), Zp.mul(x_high, y_low)))));
1228            }
1229        }
1230    });
1231}
1232
1233#[bench]
1234fn bench_inner_product(bencher: &mut Bencher) {
1235    let Fp = Zn::new(65537);
1236    let len = 1 << 12;
1237    let lhs = (0..len).map(|i| Fp.int_hom().map(i)).collect::<Vec<_>>();
1238    let rhs = (0..len).map(|i| Fp.int_hom().map(i)).collect::<Vec<_>>();
1239    let expected = (0..len).map(|i| Fp.int_hom().map(i * i)).fold(Fp.zero(), |l, r| Fp.add(l, r));
1240
1241    bencher.iter(|| {
1242        let actual = <_ as ComputeInnerProduct>::inner_product_ref(Fp.get_ring(), lhs.iter().zip(rhs.iter()).map(|x| std::hint::black_box(x)));
1243        assert_el_eq!(Fp, expected, actual);
1244    })
1245}
1246
1247#[test]
1248fn test_serialize() {
1249    let ring = Zn::new(128);
1250    crate::serialization::generic_tests::test_serialization(ring, ring.elements())
1251}