/src/gmp-6.2.1/mpz/powm.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* mpz_powm(res,base,exp,mod) -- Set R to (U^E) mod M. |
2 | | |
3 | | Contributed to the GNU project by Torbjorn Granlund. |
4 | | |
5 | | Copyright 1991, 1993, 1994, 1996, 1997, 2000-2002, 2005, 2008, 2009, |
6 | | 2011, 2012, 2015, 2019 Free Software Foundation, Inc. |
7 | | |
8 | | This file is part of the GNU MP Library. |
9 | | |
10 | | The GNU MP Library is free software; you can redistribute it and/or modify |
11 | | it under the terms of either: |
12 | | |
13 | | * the GNU Lesser General Public License as published by the Free |
14 | | Software Foundation; either version 3 of the License, or (at your |
15 | | option) any later version. |
16 | | |
17 | | or |
18 | | |
19 | | * the GNU General Public License as published by the Free Software |
20 | | Foundation; either version 2 of the License, or (at your option) any |
21 | | later version. |
22 | | |
23 | | or both in parallel, as here. |
24 | | |
25 | | The GNU MP Library is distributed in the hope that it will be useful, but |
26 | | WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
27 | | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
28 | | for more details. |
29 | | |
30 | | You should have received copies of the GNU General Public License and the |
31 | | GNU Lesser General Public License along with the GNU MP Library. If not, |
32 | | see https://www.gnu.org/licenses/. */ |
33 | | |
34 | | |
35 | | #include "gmp-impl.h" |
36 | | #include "longlong.h" |
37 | | |
38 | | |
39 | | /* TODO |
40 | | |
41 | | * Improve handling of buffers. It is pretty ugly now. |
42 | | |
43 | | * For even moduli, we compute a binvert of its odd part both here and in |
44 | | mpn_powm. How can we avoid this recomputation? |
45 | | */ |
46 | | |
47 | | /* |
48 | | b ^ e mod m res |
49 | | 0 0 0 ? |
50 | | 0 e 0 ? |
51 | | 0 0 m ? |
52 | | 0 e m 0 |
53 | | b 0 0 ? |
54 | | b e 0 ? |
55 | | b 0 m 1 mod m |
56 | | b e m b^e mod m |
57 | | */ |
58 | | |
59 | | #define HANDLE_NEGATIVE_EXPONENT 1 |
60 | | |
61 | | void |
62 | | mpz_powm (mpz_ptr r, mpz_srcptr b, mpz_srcptr e, mpz_srcptr m) |
63 | 1.86k | { |
64 | 1.86k | mp_size_t n, nodd, ncnt; |
65 | 1.86k | int cnt; |
66 | 1.86k | mp_ptr rp, tp; |
67 | 1.86k | mp_srcptr bp, ep, mp; |
68 | 1.86k | mp_size_t rn, bn, es, en, itch; |
69 | 1.86k | mpz_t new_b; /* note: value lives long via 'b' */ |
70 | 1.86k | TMP_DECL; |
71 | | |
72 | 1.86k | n = ABSIZ(m); |
73 | 1.86k | if (UNLIKELY (n == 0)) |
74 | 0 | DIVIDE_BY_ZERO; |
75 | | |
76 | 1.86k | mp = PTR(m); |
77 | | |
78 | 1.86k | TMP_MARK; |
79 | | |
80 | 1.86k | es = SIZ(e); |
81 | 1.86k | if (UNLIKELY (es <= 0)) |
82 | 25 | { |
83 | 25 | if (es == 0) |
84 | 25 | { |
85 | | /* b^0 mod m, b is anything and m is non-zero. |
86 | | Result is 1 mod m, i.e., 1 or 0 depending on if m = 1. */ |
87 | 25 | SIZ(r) = n != 1 || mp[0] != 1; |
88 | 25 | MPZ_NEWALLOC (r, 1)[0] = 1; |
89 | 25 | TMP_FREE; /* we haven't really allocated anything here */ |
90 | 25 | return; |
91 | 25 | } |
92 | 0 | #if HANDLE_NEGATIVE_EXPONENT |
93 | 0 | MPZ_TMP_INIT (new_b, n + 1); |
94 | | |
95 | 0 | if (UNLIKELY (! mpz_invert (new_b, b, m))) |
96 | 0 | DIVIDE_BY_ZERO; |
97 | 0 | b = new_b; |
98 | 0 | es = -es; |
99 | | #else |
100 | | DIVIDE_BY_ZERO; |
101 | | #endif |
102 | 0 | } |
103 | 1.83k | en = es; |
104 | | |
105 | 1.83k | bn = ABSIZ(b); |
106 | | |
107 | 1.83k | if (UNLIKELY (bn == 0)) |
108 | 9 | { |
109 | 9 | SIZ(r) = 0; |
110 | 9 | TMP_FREE; |
111 | 9 | return; |
112 | 9 | } |
113 | | |
114 | 1.82k | ep = PTR(e); |
115 | | |
116 | | /* Handle (b^1 mod m) early, since mpn_pow* do not handle that case. */ |
117 | 1.82k | if (UNLIKELY (en == 1 && ep[0] == 1)) |
118 | 3 | { |
119 | 3 | rp = TMP_ALLOC_LIMBS (n); |
120 | 3 | bp = PTR(b); |
121 | 3 | if (bn >= n) |
122 | 2 | { |
123 | 2 | mp_ptr qp = TMP_ALLOC_LIMBS (bn - n + 1); |
124 | 2 | mpn_tdiv_qr (qp, rp, 0L, bp, bn, mp, n); |
125 | 2 | rn = n; |
126 | 2 | MPN_NORMALIZE (rp, rn); |
127 | | |
128 | 2 | if (rn != 0 && SIZ(b) < 0) |
129 | 0 | { |
130 | 0 | mpn_sub (rp, mp, n, rp, rn); |
131 | 0 | rn = n; |
132 | 0 | MPN_NORMALIZE_NOT_ZERO (rp, rn); |
133 | 0 | } |
134 | 2 | } |
135 | 1 | else |
136 | 1 | { |
137 | 1 | if (SIZ(b) < 0) |
138 | 0 | { |
139 | 0 | mpn_sub (rp, mp, n, bp, bn); |
140 | 0 | rn = n; |
141 | 0 | MPN_NORMALIZE_NOT_ZERO (rp, rn); |
142 | 0 | } |
143 | 1 | else |
144 | 1 | { |
145 | 1 | MPN_COPY (rp, bp, bn); |
146 | 1 | rn = bn; |
147 | 1 | } |
148 | 1 | } |
149 | 3 | goto ret; |
150 | 3 | } |
151 | | |
152 | | /* Remove low zero limbs from M. This loop will terminate for correctly |
153 | | represented mpz numbers. */ |
154 | 1.82k | ncnt = 0; |
155 | 1.82k | while (UNLIKELY (mp[0] == 0)) |
156 | 7.13k | { |
157 | 7.13k | mp++; |
158 | 7.13k | ncnt++; |
159 | 7.13k | } |
160 | 1.82k | nodd = n - ncnt; |
161 | 1.82k | cnt = 0; |
162 | 1.82k | if (mp[0] % 2 == 0) |
163 | 1.39k | { |
164 | 1.39k | mp_ptr newmp = TMP_ALLOC_LIMBS (nodd); |
165 | 1.39k | count_trailing_zeros (cnt, mp[0]); |
166 | 1.39k | mpn_rshift (newmp, mp, nodd, cnt); |
167 | 1.39k | nodd -= newmp[nodd - 1] == 0; |
168 | 1.39k | mp = newmp; |
169 | 1.39k | ncnt++; |
170 | 1.39k | } |
171 | | |
172 | 1.82k | if (ncnt != 0) |
173 | 1.43k | { |
174 | | /* We will call both mpn_powm and mpn_powlo. */ |
175 | | /* rp needs n, mpn_powlo needs 4n, the 2 mpn_binvert might need more */ |
176 | 1.43k | mp_size_t n_largest_binvert = MAX (ncnt, nodd); |
177 | 1.43k | mp_size_t itch_binvert = mpn_binvert_itch (n_largest_binvert); |
178 | 1.43k | itch = 3 * n + MAX (itch_binvert, 2 * n); |
179 | 1.43k | } |
180 | 396 | else |
181 | 396 | { |
182 | | /* We will call just mpn_powm. */ |
183 | 396 | mp_size_t itch_binvert = mpn_binvert_itch (nodd); |
184 | 396 | itch = n + MAX (itch_binvert, 2 * n); |
185 | 396 | } |
186 | 1.82k | tp = TMP_ALLOC_LIMBS (itch); |
187 | | |
188 | 1.82k | rp = tp; tp += n; |
189 | | |
190 | 1.82k | bp = PTR(b); |
191 | 1.82k | mpn_powm (rp, bp, bn, ep, en, mp, nodd, tp); |
192 | | |
193 | 1.82k | rn = n; |
194 | | |
195 | 1.82k | if (ncnt != 0) |
196 | 1.43k | { |
197 | 1.43k | mp_ptr r2, xp, yp, odd_inv_2exp; |
198 | 1.43k | unsigned long t; |
199 | 1.43k | int bcnt; |
200 | | |
201 | 1.43k | if (bn < ncnt) |
202 | 360 | { |
203 | 360 | mp_ptr newbp = TMP_ALLOC_LIMBS (ncnt); |
204 | 360 | MPN_COPY (newbp, bp, bn); |
205 | 360 | MPN_ZERO (newbp + bn, ncnt - bn); |
206 | 360 | bp = newbp; |
207 | 360 | } |
208 | | |
209 | 1.43k | r2 = tp; |
210 | | |
211 | 1.43k | if (bp[0] % 2 == 0) |
212 | 868 | { |
213 | 868 | if (en > 1) |
214 | 561 | { |
215 | 561 | MPN_ZERO (r2, ncnt); |
216 | 561 | goto zero; |
217 | 561 | } |
218 | | |
219 | 307 | ASSERT (en == 1); |
220 | 307 | t = (ncnt - (cnt != 0)) * GMP_NUMB_BITS + cnt; |
221 | | |
222 | | /* Count number of low zero bits in B, up to 3. */ |
223 | 307 | bcnt = (0x1213 >> ((bp[0] & 7) << 1)) & 0x3; |
224 | | /* Note that ep[0] * bcnt might overflow, but that just results |
225 | | in a missed optimization. */ |
226 | 307 | if (ep[0] * bcnt >= t) |
227 | 173 | { |
228 | 173 | MPN_ZERO (r2, ncnt); |
229 | 173 | goto zero; |
230 | 173 | } |
231 | 307 | } |
232 | | |
233 | 696 | mpn_powlo (r2, bp, ep, en, ncnt, tp + ncnt); |
234 | | |
235 | 1.43k | zero: |
236 | 1.43k | if (nodd < ncnt) |
237 | 38 | { |
238 | 38 | mp_ptr newmp = TMP_ALLOC_LIMBS (ncnt); |
239 | 38 | MPN_COPY (newmp, mp, nodd); |
240 | 38 | MPN_ZERO (newmp + nodd, ncnt - nodd); |
241 | 38 | mp = newmp; |
242 | 38 | } |
243 | | |
244 | 1.43k | odd_inv_2exp = tp + n; |
245 | 1.43k | mpn_binvert (odd_inv_2exp, mp, ncnt, tp + 2 * n); |
246 | | |
247 | 1.43k | mpn_sub (r2, r2, ncnt, rp, nodd > ncnt ? ncnt : nodd); |
248 | | |
249 | 1.43k | xp = tp + 2 * n; |
250 | 1.43k | mpn_mullo_n (xp, odd_inv_2exp, r2, ncnt); |
251 | | |
252 | 1.43k | if (cnt != 0) |
253 | 1.39k | xp[ncnt - 1] &= (CNST_LIMB(1) << cnt) - 1; |
254 | | |
255 | 1.43k | yp = tp; |
256 | 1.43k | if (ncnt > nodd) |
257 | 38 | mpn_mul (yp, xp, ncnt, mp, nodd); |
258 | 1.39k | else |
259 | 1.39k | mpn_mul (yp, mp, nodd, xp, ncnt); |
260 | | |
261 | 1.43k | mpn_add (rp, yp, n, rp, nodd); |
262 | | |
263 | 1.43k | ASSERT (nodd + ncnt >= n); |
264 | 1.43k | ASSERT (nodd + ncnt <= n + 1); |
265 | 1.43k | } |
266 | | |
267 | 1.82k | MPN_NORMALIZE (rp, rn); |
268 | | |
269 | 1.82k | if ((ep[0] & 1) && SIZ(b) < 0 && rn != 0) |
270 | 0 | { |
271 | 0 | mpn_sub (rp, PTR(m), n, rp, rn); |
272 | 0 | rn = n; |
273 | 0 | MPN_NORMALIZE (rp, rn); |
274 | 0 | } |
275 | | |
276 | 1.82k | ret: |
277 | 1.82k | MPZ_NEWALLOC (r, rn); |
278 | 1.82k | SIZ(r) = rn; |
279 | 1.82k | MPN_COPY (PTR(r), rp, rn); |
280 | | |
281 | 1.82k | TMP_FREE; |
282 | 1.82k | } |