1use crate::{
2 arch::word::Word,
3 div_const::{ConstDoubleDivisor, ConstSingleDivisor},
4 primitive::{split_dword, WORD_BITS},
5 repr::TypedReprRef::*,
6 ubig::UBig,
7};
8
9use super::repr::{Reduced, ReducedDword, ReducedRepr, ReducedWord};
10use num_modular::Reducer;
11
12impl<'a> Reduced<'a> {
13 #[inline]
30 pub fn pow(&self, exp: &UBig) -> Reduced<'a> {
31 match self.repr() {
32 ReducedRepr::Single(raw, ring) => {
33 Reduced::from_single(single::pow(ring, *raw, exp), ring)
34 }
35 ReducedRepr::Double(raw, ring) => {
36 Reduced::from_double(double::pow(ring, *raw, exp), ring)
37 }
38 ReducedRepr::Large(raw, ring) => Reduced::from_large(large::pow(ring, raw, exp), ring),
39 }
40 }
41}
42
43macro_rules! impl_mod_pow_for_primitive {
44 ($ns:ident, $ring:ty, $raw:ident) => {
45 mod $ns {
46 use super::*;
47
48 #[inline]
49 pub(super) fn pow_word(ring: &$ring, raw: $raw, exp: Word) -> $raw {
50 match exp {
51 0 => <$raw>::one(ring),
52 1 => raw, 2 => $raw(ring.0.sqr(raw.0)),
54 _ => {
55 let bits = WORD_BITS - 1 - exp.leading_zeros();
56 pow_helper(ring, raw, raw, exp, bits)
57 }
58 }
59 }
60
61 #[inline]
63 fn pow_helper(ring: &$ring, lhs: $raw, rhs: $raw, exp: Word, mut bits: u32) -> $raw {
64 let mut res = lhs;
65 while bits > 0 {
66 res.0 = ring.0.sqr(res.0);
67 bits -= 1;
68 if exp & (1 << bits) != 0 {
69 res.0 = ring.0.mul(&res.0, &rhs.0);
70 }
71 }
72 res
73 }
74
75 #[inline]
77 pub(super) fn pow(ring: &$ring, raw: $raw, exp: &UBig) -> $raw {
78 match exp.repr() {
79 RefSmall(dword) => {
80 let (lo, hi) = split_dword(dword);
81 if hi == 0 {
82 pow_word(ring, raw, lo)
83 } else {
84 let res = pow_word(ring, raw, hi);
85 pow_helper(ring, res, raw, lo, WORD_BITS)
86 }
87 }
88 RefLarge(words) => pow_nontrivial(ring, raw, words),
89 }
90 }
91
92 fn pow_nontrivial(ring: &$ring, raw: $raw, exp_words: &[Word]) -> $raw {
93 let mut n = exp_words.len() - 1;
94 let mut res = pow_word(ring, raw, exp_words[n]); while n != 0 {
96 n -= 1;
97 res = pow_helper(ring, res, raw, exp_words[n], WORD_BITS);
98 }
99 res
100 }
101 }
102 };
103}
104impl_mod_pow_for_primitive!(single, ConstSingleDivisor, ReducedWord);
105impl_mod_pow_for_primitive!(double, ConstDoubleDivisor, ReducedDword);
106
107mod large {
108 use dashu_base::BitTest;
109
110 use super::{
111 super::mul::{mul_memory_requirement, mul_normalized, sqr_in_place},
112 *,
113 };
114 use crate::{
115 div_const::ConstLargeDivisor,
116 error::panic_allocate_too_much,
117 math,
118 memory::{self, MemoryAllocation},
119 modular::repr::ReducedLarge,
120 primitive::{double_word, split_dword, PrimitiveUnsigned, WORD_BITS_USIZE},
121 };
122
123 pub(super) fn pow(ring: &ConstLargeDivisor, raw: &ReducedLarge, exp: &UBig) -> ReducedLarge {
124 if exp.is_zero() {
125 ReducedLarge::one(ring)
126 } else if exp.is_one() {
127 raw.clone()
128 } else {
129 pow_nontrivial(ring, raw, exp)
130 }
131 }
132
133 fn pow_nontrivial(ring: &ConstLargeDivisor, raw: &ReducedLarge, exp: &UBig) -> ReducedLarge {
134 let n = ring.normalized_divisor.len();
135 let window_len = choose_pow_window_len(exp.bit_len());
136
137 #[allow(clippy::redundant_closure)]
139 let table_words = ((1usize << (window_len - 1)) - 1)
140 .checked_mul(n)
141 .unwrap_or_else(|| panic_allocate_too_much());
142
143 let memory_requirement = memory::add_layout(
144 memory::array_layout::<Word>(table_words),
145 memory::max_layout(mul_memory_requirement(ring), crate::sqr::sqr_memory_requirement(n)),
149 );
150 let mut allocation = MemoryAllocation::new(memory_requirement);
151 let mut memory = allocation.memory();
152 let (table, mut memory) = memory.allocate_slice_fill::<Word>(table_words, 0);
153
154 let mut val = raw.clone();
156 sqr_in_place(ring, &mut val, &mut memory);
157
158 for i in 1..(1 << (window_len - 1)) {
160 let (prev, cur) = if i == 1 {
161 (raw.0.as_ref(), &mut table[0..n])
162 } else {
163 let (prev, cur) = table[(i - 2) * n..i * n].split_at_mut(n);
164 (&*prev, cur)
165 };
166 cur.copy_from_slice(mul_normalized(ring, prev, &val.0, &mut memory));
167 }
168
169 let exp_words = exp.as_words();
170 let mut bit = exp.bit_len() - 2;
173
174 loop {
175 let word_idx = bit / WORD_BITS_USIZE;
177 let bit_idx = (bit % WORD_BITS_USIZE) as u32;
178 let cur_word = exp_words[word_idx];
179 if cur_word & (1 << bit_idx) != 0 {
180 let next_word = if word_idx == 0 {
181 0
182 } else {
183 exp_words[word_idx - 1]
184 };
185 let (mut window, _) = split_dword(
187 double_word(next_word, cur_word) >> (bit_idx + 1 + WORD_BITS - window_len),
188 );
189 window &= math::ones_word(window_len);
190 let num_bits = window_len - window.trailing_zeros();
192 window >>= window_len - num_bits;
193 for _ in 0..num_bits - 1 {
195 sqr_in_place(ring, &mut val, &mut memory);
196 }
197 bit -= (num_bits as usize) - 1;
198 debug_assert!(window & 1 == 1);
201 let entry_idx = (window >> 1) as usize;
202 let entry = if entry_idx == 0 {
203 &raw.0
204 } else {
205 &table[(entry_idx - 1) * n..entry_idx * n]
206 };
207 let prod = mul_normalized(ring, &val.0, entry, &mut memory);
208 val.0.copy_from_slice(prod);
209 }
210 if bit == 0 {
212 break;
213 }
214 bit -= 1;
215 sqr_in_place(ring, &mut val, &mut memory);
216 }
217 val
218 }
219
220 fn choose_pow_window_len(n: usize) -> u32 {
223 let cost = |window_size| (1usize << (window_size - 1)) - 1 + n / (window_size as usize + 1);
226 let mut window_size = 1;
227 let mut c = cost(window_size);
228 while window_size + 1 < WORD_BITS.min(usize::BIT_SIZE) {
229 let c2 = cost(window_size + 1);
230 if c <= c2 {
231 break;
232 }
233 window_size += 1;
234 c = c2;
235 }
236 window_size
237 }
238}
239
240#[cfg(test)]
241mod tests {
242 use super::*;
243
244 #[test]
245 fn test_pow_word() {
246 let ring = ConstSingleDivisor::new(100);
247 let modulo = ReducedWord(ring.0.transform(17));
248 assert_eq!(single::pow_word(&ring, modulo, 0).residue(&ring), 1);
249 assert_eq!(single::pow_word(&ring, modulo, 15).residue(&ring), 93);
250 }
251}