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