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);