malachite_base/num/arithmetic/
mod_pow.rs

1// Copyright © 2025 Mikhail Hogrefe
2//
3// Uses code adopted from the FLINT Library.
4//
5//      Copyright © 2009, 2010, 2012, 2016 William Hart
6//
7// This file is part of Malachite.
8//
9// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
10// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
11// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
12
13use crate::num::arithmetic::mod_mul::{limbs_invert_limb_u32, limbs_invert_limb_u64};
14use crate::num::arithmetic::traits::{
15    ModPow, ModPowAssign, ModPowPrecomputed, ModPowPrecomputedAssign,
16};
17use crate::num::basic::integers::USIZE_IS_U32;
18use crate::num::basic::unsigneds::PrimitiveUnsigned;
19use crate::num::conversion::traits::WrappingFrom;
20use crate::num::conversion::traits::{HasHalf, JoinHalves, SplitInHalf};
21use crate::num::logic::traits::{BitIterable, LeadingZeros};
22
23pub_test! {simple_binary_mod_pow<T: PrimitiveUnsigned>(x: T, exp: u64, m: T) -> T {
24    assert!(x < m, "x must be reduced mod m, but {x} >= {m}");
25    if m == T::ONE {
26        return T::ZERO;
27    }
28    let data = T::precompute_mod_mul_data(&m);
29    let mut out = T::ONE;
30    for bit in exp.bits().rev() {
31        out.mod_mul_precomputed_assign(out, m, &data);
32        if bit {
33            out.mod_mul_precomputed_assign(x, m, &data);
34        }
35    }
36    out
37}}
38
39// m.get_highest_bit(), x < m, y < m
40//
41// This is equivalent to `n_mulmod_preinv` from `ulong_extras/mulmod_preinv.c`, FLINT 2.7.1.
42pub(crate) fn mul_mod_helper<
43    T: PrimitiveUnsigned,
44    DT: From<T> + HasHalf<Half = T> + JoinHalves + PrimitiveUnsigned + SplitInHalf,
45>(
46    mut x: T,
47    y: T,
48    m: T,
49    inverse: T,
50    shift: u64,
51) -> T {
52    x >>= shift;
53    let p = DT::from(x) * DT::from(y);
54    let (p_hi, p_lo) = p.split_in_half();
55    let (q_1, q_0) = (DT::from(p_hi) * DT::from(inverse))
56        .wrapping_add(p)
57        .split_in_half();
58    let mut r = p_lo.wrapping_sub(q_1.wrapping_add(T::ONE).wrapping_mul(m));
59    if r > q_0 {
60        r.wrapping_add_assign(m);
61    }
62    if r < m {
63        r
64    } else {
65        r.wrapping_sub(m)
66    }
67}
68
69// m.get_highest_bit(), x < m
70//
71// This is equivalent to `n_powmod_ui_preinv` from ulong_extras/powmod_ui_preinv.c, FLINT 2.7.1.
72pub(crate) fn fast_mod_pow<
73    T: PrimitiveUnsigned,
74    DT: From<T> + HasHalf<Half = T> + JoinHalves + PrimitiveUnsigned + SplitInHalf,
75>(
76    mut x: T,
77    exp: u64,
78    m: T,
79    inverse: T,
80    shift: u64,
81) -> T {
82    assert!(x < m, "x must be reduced mod m, but {x} >= {m}");
83    if exp == 0 {
84        let x = T::power_of_2(shift);
85        if x == m {
86            T::ZERO
87        } else {
88            x
89        }
90    } else if x == T::ZERO {
91        T::ZERO
92    } else {
93        let mut bits = exp.bits();
94        let mut out = if bits.next().unwrap() {
95            x
96        } else {
97            T::power_of_2(shift)
98        };
99        for bit in bits {
100            x = mul_mod_helper::<T, DT>(x, x, m, inverse, shift);
101            if bit {
102                out = mul_mod_helper::<T, DT>(out, x, m, inverse, shift);
103            }
104        }
105        out
106    }
107}
108
109macro_rules! impl_mod_pow_precomputed_fast {
110    ($t:ident, $dt:ident, $invert_limb:ident) => {
111        impl ModPowPrecomputed<u64, $t> for $t {
112            type Output = $t;
113            type Data = ($t, u64);
114
115            /// Precomputes data for modular exponentiation.
116            ///
117            /// See `mod_pow_precomputed` and
118            /// [`mod_pow_precomputed_assign`](super::traits::ModPowPrecomputedAssign).
119            ///
120            /// # Worst-case complexity
121            /// Constant time and additional memory.
122            fn precompute_mod_pow_data(&m: &$t) -> ($t, u64) {
123                let leading_zeros = LeadingZeros::leading_zeros(m);
124                ($invert_limb(m << leading_zeros), leading_zeros)
125            }
126
127            /// Raises a number to a power modulo another number $m$. The base must be already
128            /// reduced modulo $m$.
129            ///
130            /// Some precomputed data is provided; this speeds up computations involving several
131            /// modular exponentiations with the same modulus. The precomputed data should be
132            /// obtained using
133            /// [`precompute_mod_pow_data`](ModPowPrecomputed::precompute_mod_pow_data).
134            ///
135            /// # Worst-case complexity
136            /// $T(n) = O(n)$
137            ///
138            /// $M(n) = O(1)$
139            ///
140            /// where $T$ is time, $M$ is additional memory, and $n$ is `exp.significant_bits()`.
141            ///
142            /// # Panics
143            /// Panics if `self` is greater than or equal to `m`.
144            ///
145            /// # Examples
146            /// See [here](super::mod_pow#mod_pow_precomputed).
147            fn mod_pow_precomputed(self, exp: u64, m: $t, data: &($t, u64)) -> $t {
148                let (inverse, shift) = *data;
149                fast_mod_pow::<$t, $dt>(self << shift, exp, m << shift, inverse, shift) >> shift
150            }
151        }
152    };
153}
154impl_mod_pow_precomputed_fast!(u32, u64, limbs_invert_limb_u32);
155impl_mod_pow_precomputed_fast!(u64, u128, limbs_invert_limb_u64);
156
157macro_rules! impl_mod_pow_precomputed_promoted {
158    ($t:ident) => {
159        impl ModPowPrecomputed<u64, $t> for $t {
160            type Output = $t;
161            type Data = (u32, u64);
162
163            /// Precomputes data for modular exponentiation.
164            ///
165            /// See `mod_pow_precomputed` and
166            /// [`mod_pow_precomputed_assign`](super::traits::ModPowPrecomputedAssign).
167            ///
168            /// # Worst-case complexity
169            /// Constant time and additional memory.
170            fn precompute_mod_pow_data(&m: &$t) -> (u32, u64) {
171                u32::precompute_mod_pow_data(&u32::from(m))
172            }
173
174            /// Raises a number to a power modulo another number $m$. The base must be already
175            /// reduced modulo $m$.
176            ///
177            /// Some precomputed data is provided; this speeds up computations involving several
178            /// modular exponentiations with the same modulus. The precomputed data should be
179            /// obtained using
180            /// [`precompute_mod_pow_data`](ModPowPrecomputed::precompute_mod_pow_data).
181            ///
182            /// # Worst-case complexity
183            /// $T(n) = O(n)$
184            ///
185            /// $M(n) = O(1)$
186            ///
187            /// where $T$ is time, $M$ is additional memory, and $n$ is `exp.significant_bits()`.
188            ///
189            /// # Panics
190            /// Panics if `self` is greater than or equal to `m`.
191            ///
192            /// # Examples
193            /// See [here](super::mod_pow#mod_pow_precomputed).
194            fn mod_pow_precomputed(self, exp: u64, m: $t, data: &(u32, u64)) -> $t {
195                $t::wrapping_from(u32::from(self).mod_pow_precomputed(exp, u32::from(m), data))
196            }
197        }
198    };
199}
200impl_mod_pow_precomputed_promoted!(u8);
201impl_mod_pow_precomputed_promoted!(u16);
202
203impl ModPowPrecomputed<u64, u128> for u128 {
204    type Output = u128;
205    type Data = ();
206
207    /// Precomputes data for modular exponentiation.
208    ///
209    /// See `mod_pow_precomputed` and
210    /// [`mod_pow_precomputed_assign`](super::traits::ModPowPrecomputedAssign).
211    ///
212    /// # Worst-case complexity
213    /// Constant time and additional memory.
214    fn precompute_mod_pow_data(_m: &u128) {}
215
216    /// Raises a number to a power modulo another number $m$. The base must be already reduced
217    /// modulo $m$.
218    ///
219    /// Some precomputed data is provided; this speeds up computations involving several modular
220    /// exponentiations with the same modulus. The precomputed data should be obtained using
221    /// [`precompute_mod_pow_data`](ModPowPrecomputed::precompute_mod_pow_data).
222    ///
223    /// # Worst-case complexity
224    /// $T(n) = O(n)$
225    ///
226    /// $M(n) = O(1)$
227    ///
228    /// where $T$ is time, $M$ is additional memory, and $n$ is `exp.significant_bits()`.
229    ///
230    /// # Panics
231    /// Panics if `self` is greater than or equal to `m`.
232    ///
233    /// # Examples
234    /// See [here](super::mod_pow#mod_pow_precomputed).
235    #[inline]
236    fn mod_pow_precomputed(self, exp: u64, m: u128, _data: &()) -> u128 {
237        simple_binary_mod_pow(self, exp, m)
238    }
239}
240
241impl ModPowPrecomputed<u64, usize> for usize {
242    type Output = usize;
243    type Data = (usize, u64);
244
245    /// Precomputes data for modular exponentiation.
246    ///
247    /// See `mod_pow_precomputed` and
248    /// [`mod_pow_precomputed_assign`](super::traits::ModPowPrecomputedAssign).
249    ///
250    /// # Worst-case complexity
251    /// Constant time and additional memory.
252    fn precompute_mod_pow_data(&m: &usize) -> (usize, u64) {
253        if USIZE_IS_U32 {
254            let (inverse, shift) = u32::precompute_mod_pow_data(&u32::wrapping_from(m));
255            (usize::wrapping_from(inverse), shift)
256        } else {
257            let (inverse, shift) = u64::precompute_mod_pow_data(&u64::wrapping_from(m));
258            (usize::wrapping_from(inverse), shift)
259        }
260    }
261
262    /// Raises a number to a power modulo another number $m$. The base must be already reduced
263    /// modulo $m$.
264    ///
265    /// Some precomputed data is provided; this speeds up computations involving several modular
266    /// exponentiations with the same modulus. The precomputed data should be obtained using
267    /// [`precompute_mod_pow_data`](ModPowPrecomputed::precompute_mod_pow_data).
268    ///
269    /// # Worst-case complexity
270    /// $T(n) = O(n)$
271    ///
272    /// $M(n) = O(1)$
273    ///
274    /// where $T$ is time, $M$ is additional memory, and $n$ is `exp.significant_bits()`.
275    ///
276    /// # Panics
277    /// Panics if `self` is greater than or equal to `m`.
278    ///
279    /// # Examples
280    /// See [here](super::mod_pow#mod_pow_precomputed).
281    fn mod_pow_precomputed(self, exp: u64, m: usize, data: &(usize, u64)) -> usize {
282        let (inverse, shift) = *data;
283        if USIZE_IS_U32 {
284            usize::wrapping_from(u32::wrapping_from(self).mod_pow_precomputed(
285                exp,
286                u32::wrapping_from(m),
287                &(u32::wrapping_from(inverse), shift),
288            ))
289        } else {
290            usize::wrapping_from(u64::wrapping_from(self).mod_pow_precomputed(
291                exp,
292                u64::wrapping_from(m),
293                &(u64::wrapping_from(inverse), shift),
294            ))
295        }
296    }
297}
298
299macro_rules! impl_mod_pow {
300    ($t:ident) => {
301        impl ModPowPrecomputedAssign<u64, $t> for $t {
302            /// Raises a number to a power modulo another number $m$, in place. The base must be
303            /// already reduced modulo $m$.
304            ///
305            /// Some precomputed data is provided; this speeds up computations involving several
306            /// modular exponentiations with the same modulus. The precomputed data should be
307            /// obtained using
308            /// [`precompute_mod_pow_data`](ModPowPrecomputed::precompute_mod_pow_data).
309            ///
310            /// # Worst-case complexity
311            /// $T(n) = O(n)$
312            ///
313            /// $M(n) = O(1)$
314            ///
315            /// where $T$ is time, $M$ is additional memory, and $n$ is `exp.significant_bits()`.
316            ///
317            /// # Examples
318            /// See [here](super::mod_pow#mod_pow_precomputed_assign).
319            #[inline]
320            fn mod_pow_precomputed_assign(&mut self, exp: u64, m: $t, data: &Self::Data) {
321                *self = self.mod_pow_precomputed(exp, m, data);
322            }
323        }
324
325        impl ModPow<u64> for $t {
326            type Output = $t;
327
328            /// Raises a number to a power modulo another number $m$. The base must be already
329            /// reduced modulo $m$.
330            ///
331            /// $f(x, n, m) = y$, where $x, y < m$ and $x^n \equiv y \mod m$.
332            ///
333            /// # Worst-case complexity
334            /// $T(n) = O(n)$
335            ///
336            /// $M(n) = O(1)$
337            ///
338            /// where $T$ is time, $M$ is additional memory, and $n$ is `exp.significant_bits()`.
339            ///
340            /// # Panics
341            /// Panics if `self` is greater than or equal to `m`.
342            ///
343            /// # Examples
344            /// See [here](super::mod_pow#mod_pow).
345            #[inline]
346            fn mod_pow(self, exp: u64, m: $t) -> $t {
347                simple_binary_mod_pow(self, exp, m)
348            }
349        }
350
351        impl ModPowAssign<u64> for $t {
352            /// Raises a number to a power modulo another number $m$, in place. The base must be
353            /// already reduced modulo $m$.
354            ///
355            /// $x \gets y$, where $x, y < m$ and $x^n \equiv y \mod m$.
356            ///
357            /// # Worst-case complexity
358            /// $T(n) = O(n)$
359            ///
360            /// $M(n) = O(1)$
361            ///
362            /// where $T$ is time, $M$ is additional memory, and $n$ is `exp.significant_bits()`.
363            ///
364            /// # Panics
365            /// Panics if `self` is greater than or equal to `m`.
366            ///
367            /// # Examples
368            /// See [here](super::mod_pow#mod_pow_assign).
369            #[inline]
370            fn mod_pow_assign(&mut self, exp: u64, m: $t) {
371                *self = simple_binary_mod_pow(*self, exp, m);
372            }
373        }
374    };
375}
376apply_to_unsigneds!(impl_mod_pow);