Skip to main content

feanor_math/rings/zn/
zn_64.rs

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
36/// Assuming that `x <= 2 * bound`, computes `x' <= bound` that is congruent
37/// to `x` modulo `bound`
38fn 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/// Represents the ring `Z/nZ`.
44/// A variant of Barett reduction is used to perform fast modular
45/// arithmetic for `n` slightly smaller than 64 bit.
46///
47/// More concretely, the currently maximal supported modulus is `floor(2^62 / 9)`.
48/// Note that the exact value might change in the future.
49///
50/// # Examples
51/// ```rust
52/// # use feanor_math::assert_el_eq;
53/// # use feanor_math::ring::*;
54/// # use feanor_math::homomorphism::*;
55/// # use feanor_math::rings::zn::*;
56/// # use feanor_math::rings::zn::zn_64::*;
57/// let zn = Zn::new(7);
58/// assert_el_eq!(
59///     zn,
60///     zn.one(),
61///     zn.mul(zn.int_hom().map(3), zn.int_hom().map(5))
62/// );
63/// ```
64/// Too large moduli will give an error.
65/// ```should_panic
66/// # use feanor_math::assert_el_eq;
67/// # use feanor_math::ring::*;
68/// # use feanor_math::rings::zn::*;
69/// # use feanor_math::rings::zn::zn_64::*;
70/// Zn::new((1 << 62) / 9 + 1);
71/// ```
72#[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        // this should imply the statement we need later
96        assert!(modulus <= ((1 << 62) / 9));
97        // we want representatives to grow up to 6 * modulus
98        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        // we need the product `inv_modulus * (6 * modulus)^2` to fit into 192 bit, should be
102        // implied by `modulus < ((1 << 62) / 9)` debug_assert!(ZZbig.is_lt(&ZZbig.
103        // mul_ref_fst(&int_cast(inv_modulus as i128, ZZbig, StaticRing::<i128>::RING),
104        // ZZbig.pow(ZZbig.int_hom().mul_map(ZZbig.coerce(&ZZ, modulus_i64), 6), 2)),
105        // &ZZbig.power_of_two(192)));
106        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    /// Positive integers bounded by `self.repr_bound()` (inclusive) are considered
118    /// valid representatives of ring elements.
119    fn repr_bound(&self) -> u64 { self.modulus_times_six }
120
121    /// Reduces from `[0, self.repr_bound() * self.repr_bound()]` to `[0, 3 * self.modulus()[`.
122    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        // we ignore the lowest part of the sum, causing an error of at most 1;
128        // we also assume that `repr_bound * repr_bound * inv_modulus` fits into 192 bit
129        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    /// Reduces from `[0, FACTOR * self.repr_bound() * self.repr_bound()]` to `[0, 3 *
138    /// self.modulus()]`.
139    ///
140    /// As opposed to the faster [`ZnBase::bounded_reduce()`], this should work for all inputs in
141    /// `u128` (assuming configured with the right value of `FACTOR`). Currently only used by
142    /// the specialization of [`ComputeInnerProduct::inner_product()`].
143    #[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        // `approx_quotient` can be just slightly larger than 64 bits, since we optimized for
151        // `bounded_reduce()`
152        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    /// Creates a `ZnEl` from an integer that is already sufficiently reduced,
158    /// i.e. within `[0, self.repr_bound()]`.
159    ///
160    /// The reducedness of the input is only checked when debug assertions are
161    /// enabled.
162    fn from_u64_promise_reduced(&self, value: u64) -> ZnEl {
163        debug_assert!(value <= self.repr_bound());
164        ZnEl(value)
165    }
166
167    /// Reduces from `[0, self.repr_bound()]` to `[0, self.modulus()[`
168    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
210/// Represents the ring `Z/nZ`, using a variant of Barett reduction
211/// for moduli somewhat smaller than 64 bits. For details, see [`ZnBase`].
212pub 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    // a representative within `[0, ring.repr_bound()]`
221    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/// Data associated to an element of [`ZnBase`] that allows for faster division.
531/// For details, see [`DivisibilityRing::prepare_divisor()`].
532#[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        // value is in [0, 6 * self.modulus]
731        if value_u64 >= 3 * self.modulus_u64() {
732            value_u64 -= 3 * self.modulus_u64();
733        }
734        // value is in [0, 3 * self.modulus]
735        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        // value is in ]-self.modulus_half, self.modulus + self.modulus_half[ if modulus is odd
740        // value is in [-self.modulus_half, self.modulus + self.modulus_half[ if modulus is even
741        if value_i64 >= self.modulus_half {
742            value_i64 -= self.modulus;
743        }
744        // value is in ]-self.modulus_half, self.modulus_half[ if modulus is odd
745        // value is in [-self.modulus_half, self.modulus_half[ if modulus is even
746        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    /// If the given integer is within `{ 0, ..., 6 * n }`, returns the corresponding
757    /// element in `Z/nZ`. Any other input is considered a logic error.
758    ///
759    /// This function follows [`ZnRing::from_int_promise_reduced()`], but is guaranteed
760    /// to work on elements `{ 0, ..., 6 * n }` instead of only `{ 0, ..., n - 1 }`.
761    ///
762    /// # Examples
763    /// ```rust
764    /// # use feanor_math::rings::zn::*;
765    /// # use feanor_math::assert_el_eq;
766    /// # use feanor_math::ring::*;
767    /// # use feanor_math::rings::zn::zn_64::*;
768    /// let ring = Zn::new(7);
769    /// assert_el_eq!(
770    ///     ring,
771    ///     ring.zero(),
772    ///     ring.get_ring().from_int_promise_reduced(42)
773    /// );
774    /// ```
775    /// Larger values lead to a panic in debug mode, and to a logic error in release mode.
776    /// ```should_panic
777    /// # use feanor_math::rings::zn::*;
778    /// # use feanor_math::assert_el_eq;
779    /// # use feanor_math::ring::*;
780    /// # use feanor_math::rings::zn::zn_64::*;
781    /// let ring = Zn::new(7);
782    /// ring.get_ring().from_int_promise_reduced(43);
783    /// ```
784    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/// Wraps [`ZnBase`] to represent an instance of the ring `Z/nZ`.
799/// As opposed to [`ZnBase`], elements are stored with additional information
800/// to speed up multiplication `ZnBase x ZnFastmulBase -> ZnBase`, by
801/// using [`CanHomFrom::mul_assign_map_in()`].
802/// Note that normal arithmetic in this ring is much slower than [`ZnBase`].
803///
804/// # Example
805/// The following use of the FFT is usually faster than the standard use, as
806/// the FFT requires a high amount of multiplications with the internally stored
807/// roots of unity.
808/// ```rust
809/// # #![feature(const_type_name)]
810/// # use feanor_math::homomorphism::*;
811/// # use feanor_math::ring::*;
812/// # use feanor_math::rings::zn::zn_64::*;
813/// # use feanor_math::algorithms::fft::*;
814/// # use feanor_math::algorithms::fft::cooley_tuckey::*;
815/// let ring = Zn::new(1073872897);
816/// let fastmul_ring = ZnFastmul::new(ring).unwrap();
817/// // The values stored by the FFT table are elements of `ZnFastmulBase`
818/// let fft = CooleyTuckeyFFT::for_zn_with_hom(ring.can_hom(&fastmul_ring).unwrap(), 15).unwrap();
819/// // Note that data uses `ZnBase`
820/// let mut data = (0..(1 << 15))
821///     .map(|i| ring.int_hom().map(i))
822///     .collect::<Vec<_>>();
823/// fft.unordered_fft(&mut data[..], &ring);
824/// ```
825#[derive(Clone, Copy, Debug)]
826pub struct ZnFastmulBase {
827    base: RingValue<ZnBase>,
828}
829
830/// An implementation of `Z/nZ` for `n` that is optimized to provide fast multiplication with
831/// elements of [`Zn`]. For details, see [`ZnFastmulBase`].
832pub 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        // we assume that a, b are already at most `self.modulus_times_three`
999
1000        (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        // we assume that a, b, c are already at most `self.modulus_times_three`
1056
1057        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        // we assume that a, b, c are already at most `self.modulus_times_three`
1157
1158        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    // the case that the modulus is large
1479    let Zn = Zn::new(36028797018963971 /* = 2^55 + 3 */);
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    // the case that the modulus is large
1493    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    // this benchmark is inspired by the use in https://eprint.iacr.org/2023/1510.pdf
1503    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}