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 { r } else { r.wrapping_sub(m) }
63}
64
65// m.get_highest_bit(), x < m
66//
67// This is equivalent to `n_powmod_ui_preinv` from ulong_extras/powmod_ui_preinv.c, FLINT 2.7.1.
68pub(crate) fn fast_mod_pow<
69    T: PrimitiveUnsigned,
70    DT: From<T> + HasHalf<Half = T> + JoinHalves + PrimitiveUnsigned + SplitInHalf,
71>(
72    mut x: T,
73    exp: u64,
74    m: T,
75    inverse: T,
76    shift: u64,
77) -> T {
78    assert!(x < m, "x must be reduced mod m, but {x} >= {m}");
79    if exp == 0 {
80        let x = T::power_of_2(shift);
81        if x == m { T::ZERO } else { x }
82    } else if x == T::ZERO {
83        T::ZERO
84    } else {
85        let mut bits = exp.bits();
86        let mut out = if bits.next().unwrap() {
87            x
88        } else {
89            T::power_of_2(shift)
90        };
91        for bit in bits {
92            x = mul_mod_helper::<T, DT>(x, x, m, inverse, shift);
93            if bit {
94                out = mul_mod_helper::<T, DT>(out, x, m, inverse, shift);
95            }
96        }
97        out
98    }
99}
100
101macro_rules! impl_mod_pow_precomputed_fast {
102    ($t:ident, $dt:ident, $invert_limb:ident) => {
103        impl ModPowPrecomputed<u64, $t> for $t {
104            type Output = $t;
105            type Data = ($t, u64);
106
107            /// Precomputes data for modular exponentiation.
108            ///
109            /// See `mod_pow_precomputed` and
110            /// [`mod_pow_precomputed_assign`](super::traits::ModPowPrecomputedAssign).
111            ///
112            /// # Worst-case complexity
113            /// Constant time and additional memory.
114            fn precompute_mod_pow_data(&m: &$t) -> ($t, u64) {
115                let leading_zeros = LeadingZeros::leading_zeros(m);
116                ($invert_limb(m << leading_zeros), leading_zeros)
117            }
118
119            /// Raises a number to a power modulo another number $m$. The base must be already
120            /// reduced modulo $m$.
121            ///
122            /// Some precomputed data is provided; this speeds up computations involving several
123            /// modular exponentiations with the same modulus. The precomputed data should be
124            /// obtained using
125            /// [`precompute_mod_pow_data`](ModPowPrecomputed::precompute_mod_pow_data).
126            ///
127            /// # Worst-case complexity
128            /// $T(n) = O(n)$
129            ///
130            /// $M(n) = O(1)$
131            ///
132            /// where $T$ is time, $M$ is additional memory, and $n$ is `exp.significant_bits()`.
133            ///
134            /// # Panics
135            /// Panics if `self` is greater than or equal to `m`.
136            ///
137            /// # Examples
138            /// See [here](super::mod_pow#mod_pow_precomputed).
139            fn mod_pow_precomputed(self, exp: u64, m: $t, data: &($t, u64)) -> $t {
140                let (inverse, shift) = *data;
141                fast_mod_pow::<$t, $dt>(self << shift, exp, m << shift, inverse, shift) >> shift
142            }
143        }
144    };
145}
146impl_mod_pow_precomputed_fast!(u32, u64, limbs_invert_limb_u32);
147impl_mod_pow_precomputed_fast!(u64, u128, limbs_invert_limb_u64);
148
149macro_rules! impl_mod_pow_precomputed_promoted {
150    ($t:ident) => {
151        impl ModPowPrecomputed<u64, $t> for $t {
152            type Output = $t;
153            type Data = (u32, u64);
154
155            /// Precomputes data for modular exponentiation.
156            ///
157            /// See `mod_pow_precomputed` and
158            /// [`mod_pow_precomputed_assign`](super::traits::ModPowPrecomputedAssign).
159            ///
160            /// # Worst-case complexity
161            /// Constant time and additional memory.
162            fn precompute_mod_pow_data(&m: &$t) -> (u32, u64) {
163                u32::precompute_mod_pow_data(&u32::from(m))
164            }
165
166            /// Raises a number to a power modulo another number $m$. The base must be already
167            /// reduced modulo $m$.
168            ///
169            /// Some precomputed data is provided; this speeds up computations involving several
170            /// modular exponentiations with the same modulus. The precomputed data should be
171            /// obtained using
172            /// [`precompute_mod_pow_data`](ModPowPrecomputed::precompute_mod_pow_data).
173            ///
174            /// # Worst-case complexity
175            /// $T(n) = O(n)$
176            ///
177            /// $M(n) = O(1)$
178            ///
179            /// where $T$ is time, $M$ is additional memory, and $n$ is `exp.significant_bits()`.
180            ///
181            /// # Panics
182            /// Panics if `self` is greater than or equal to `m`.
183            ///
184            /// # Examples
185            /// See [here](super::mod_pow#mod_pow_precomputed).
186            fn mod_pow_precomputed(self, exp: u64, m: $t, data: &(u32, u64)) -> $t {
187                $t::wrapping_from(u32::from(self).mod_pow_precomputed(exp, u32::from(m), data))
188            }
189        }
190    };
191}
192impl_mod_pow_precomputed_promoted!(u8);
193impl_mod_pow_precomputed_promoted!(u16);
194
195impl ModPowPrecomputed<u64, u128> for u128 {
196    type Output = u128;
197    type Data = ();
198
199    /// Precomputes data for modular exponentiation.
200    ///
201    /// See `mod_pow_precomputed` and
202    /// [`mod_pow_precomputed_assign`](super::traits::ModPowPrecomputedAssign).
203    ///
204    /// # Worst-case complexity
205    /// Constant time and additional memory.
206    fn precompute_mod_pow_data(_m: &u128) {}
207
208    /// Raises a number to a power modulo another number $m$. The base must be already reduced
209    /// modulo $m$.
210    ///
211    /// Some precomputed data is provided; this speeds up computations involving several modular
212    /// exponentiations with the same modulus. The precomputed data should be obtained using
213    /// [`precompute_mod_pow_data`](ModPowPrecomputed::precompute_mod_pow_data).
214    ///
215    /// # Worst-case complexity
216    /// $T(n) = O(n)$
217    ///
218    /// $M(n) = O(1)$
219    ///
220    /// where $T$ is time, $M$ is additional memory, and $n$ is `exp.significant_bits()`.
221    ///
222    /// # Panics
223    /// Panics if `self` is greater than or equal to `m`.
224    ///
225    /// # Examples
226    /// See [here](super::mod_pow#mod_pow_precomputed).
227    #[inline]
228    fn mod_pow_precomputed(self, exp: u64, m: u128, _data: &()) -> u128 {
229        simple_binary_mod_pow(self, exp, m)
230    }
231}
232
233impl ModPowPrecomputed<u64, usize> for usize {
234    type Output = usize;
235    type Data = (usize, u64);
236
237    /// Precomputes data for modular exponentiation.
238    ///
239    /// See `mod_pow_precomputed` and
240    /// [`mod_pow_precomputed_assign`](super::traits::ModPowPrecomputedAssign).
241    ///
242    /// # Worst-case complexity
243    /// Constant time and additional memory.
244    fn precompute_mod_pow_data(&m: &usize) -> (usize, u64) {
245        if USIZE_IS_U32 {
246            let (inverse, shift) = u32::precompute_mod_pow_data(&u32::wrapping_from(m));
247            (usize::wrapping_from(inverse), shift)
248        } else {
249            let (inverse, shift) = u64::precompute_mod_pow_data(&u64::wrapping_from(m));
250            (usize::wrapping_from(inverse), shift)
251        }
252    }
253
254    /// Raises a number to a power modulo another number $m$. The base must be already reduced
255    /// modulo $m$.
256    ///
257    /// Some precomputed data is provided; this speeds up computations involving several modular
258    /// exponentiations with the same modulus. The precomputed data should be obtained using
259    /// [`precompute_mod_pow_data`](ModPowPrecomputed::precompute_mod_pow_data).
260    ///
261    /// # Worst-case complexity
262    /// $T(n) = O(n)$
263    ///
264    /// $M(n) = O(1)$
265    ///
266    /// where $T$ is time, $M$ is additional memory, and $n$ is `exp.significant_bits()`.
267    ///
268    /// # Panics
269    /// Panics if `self` is greater than or equal to `m`.
270    ///
271    /// # Examples
272    /// See [here](super::mod_pow#mod_pow_precomputed).
273    fn mod_pow_precomputed(self, exp: u64, m: usize, data: &(usize, u64)) -> usize {
274        let (inverse, shift) = *data;
275        if USIZE_IS_U32 {
276            usize::wrapping_from(u32::wrapping_from(self).mod_pow_precomputed(
277                exp,
278                u32::wrapping_from(m),
279                &(u32::wrapping_from(inverse), shift),
280            ))
281        } else {
282            usize::wrapping_from(u64::wrapping_from(self).mod_pow_precomputed(
283                exp,
284                u64::wrapping_from(m),
285                &(u64::wrapping_from(inverse), shift),
286            ))
287        }
288    }
289}
290
291macro_rules! impl_mod_pow {
292    ($t:ident) => {
293        impl ModPowPrecomputedAssign<u64, $t> for $t {
294            /// Raises a number to a power modulo another number $m$, in place. The base must be
295            /// already reduced modulo $m$.
296            ///
297            /// Some precomputed data is provided; this speeds up computations involving several
298            /// modular exponentiations with the same modulus. The precomputed data should be
299            /// obtained using
300            /// [`precompute_mod_pow_data`](ModPowPrecomputed::precompute_mod_pow_data).
301            ///
302            /// # Worst-case complexity
303            /// $T(n) = O(n)$
304            ///
305            /// $M(n) = O(1)$
306            ///
307            /// where $T$ is time, $M$ is additional memory, and $n$ is `exp.significant_bits()`.
308            ///
309            /// # Examples
310            /// See [here](super::mod_pow#mod_pow_precomputed_assign).
311            #[inline]
312            fn mod_pow_precomputed_assign(&mut self, exp: u64, m: $t, data: &Self::Data) {
313                *self = self.mod_pow_precomputed(exp, m, data);
314            }
315        }
316
317        impl ModPow<u64> for $t {
318            type Output = $t;
319
320            /// Raises a number to a power modulo another number $m$. The base must be already
321            /// reduced modulo $m$.
322            ///
323            /// $f(x, n, m) = y$, where $x, y < m$ and $x^n \equiv y \mod m$.
324            ///
325            /// # Worst-case complexity
326            /// $T(n) = O(n)$
327            ///
328            /// $M(n) = O(1)$
329            ///
330            /// where $T$ is time, $M$ is additional memory, and $n$ is `exp.significant_bits()`.
331            ///
332            /// # Panics
333            /// Panics if `self` is greater than or equal to `m`.
334            ///
335            /// # Examples
336            /// See [here](super::mod_pow#mod_pow).
337            #[inline]
338            fn mod_pow(self, exp: u64, m: $t) -> $t {
339                simple_binary_mod_pow(self, exp, m)
340            }
341        }
342
343        impl ModPowAssign<u64> for $t {
344            /// Raises a number to a power modulo another number $m$, in place. The base must be
345            /// already reduced modulo $m$.
346            ///
347            /// $x \gets y$, where $x, y < m$ and $x^n \equiv y \mod m$.
348            ///
349            /// # Worst-case complexity
350            /// $T(n) = O(n)$
351            ///
352            /// $M(n) = O(1)$
353            ///
354            /// where $T$ is time, $M$ is additional memory, and $n$ is `exp.significant_bits()`.
355            ///
356            /// # Panics
357            /// Panics if `self` is greater than or equal to `m`.
358            ///
359            /// # Examples
360            /// See [here](super::mod_pow#mod_pow_assign).
361            #[inline]
362            fn mod_pow_assign(&mut self, exp: u64, m: $t) {
363                *self = simple_binary_mod_pow(*self, exp, m);
364            }
365        }
366    };
367}
368apply_to_unsigneds!(impl_mod_pow);