Line | Count | Source (jump to first uncovered line) |
1 | | /* mpz_powm_ui(res,base,exp,mod) -- Set R to (B^E) mod M. |
2 | | |
3 | | Contributed to the GNU project by Torbjörn Granlund. |
4 | | |
5 | | Copyright 1991, 1993, 1994, 1996, 1997, 2000-2002, 2005, 2008, 2009, |
6 | | 2011-2013, 2015 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 | | /* This code is very old, and should be rewritten to current GMP standard. It |
40 | | is slower than mpz_powm for large exponents, but also for small exponents |
41 | | when the mod argument is small. |
42 | | |
43 | | As an intermediate solution, we now deflect to mpz_powm for exponents >= 20. |
44 | | */ |
45 | | |
46 | | /* |
47 | | b ^ e mod m res |
48 | | 0 0 0 ? |
49 | | 0 e 0 ? |
50 | | 0 0 m ? |
51 | | 0 e m 0 |
52 | | b 0 0 ? |
53 | | b e 0 ? |
54 | | b 0 m 1 mod m |
55 | | b e m b^e mod m |
56 | | */ |
57 | | |
58 | | static void |
59 | | mod (mp_ptr np, mp_size_t nn, mp_srcptr dp, mp_size_t dn, gmp_pi1_t *dinv, mp_ptr tp) |
60 | 0 | { |
61 | 0 | mp_ptr qp = tp; |
62 | |
|
63 | 0 | if (dn == 1) |
64 | 0 | { |
65 | 0 | np[0] = mpn_divrem_1 (qp, (mp_size_t) 0, np, nn, dp[0]); |
66 | 0 | } |
67 | 0 | else if (dn == 2) |
68 | 0 | { |
69 | 0 | mpn_div_qr_2n_pi1 (qp, np, np, nn, dp[1], dp[0], dinv->inv32); |
70 | 0 | } |
71 | 0 | else if (BELOW_THRESHOLD (dn, DC_DIV_QR_THRESHOLD) || |
72 | 0 | BELOW_THRESHOLD (nn - dn, DC_DIV_QR_THRESHOLD)) |
73 | 0 | { |
74 | 0 | mpn_sbpi1_div_qr (qp, np, nn, dp, dn, dinv->inv32); |
75 | 0 | } |
76 | 0 | else if (BELOW_THRESHOLD (dn, MUPI_DIV_QR_THRESHOLD) || /* fast condition */ |
77 | 0 | BELOW_THRESHOLD (nn, 2 * MU_DIV_QR_THRESHOLD) || /* fast condition */ |
78 | 0 | (double) (2 * (MU_DIV_QR_THRESHOLD - MUPI_DIV_QR_THRESHOLD)) * dn /* slow... */ |
79 | 0 | + (double) MUPI_DIV_QR_THRESHOLD * nn > (double) dn * nn) /* ...condition */ |
80 | 0 | { |
81 | 0 | mpn_dcpi1_div_qr (qp, np, nn, dp, dn, dinv); |
82 | 0 | } |
83 | 0 | else |
84 | 0 | { |
85 | | /* We need to allocate separate remainder area, since mpn_mu_div_qr does |
86 | | not handle overlap between the numerator and remainder areas. |
87 | | FIXME: Make it handle such overlap. */ |
88 | 0 | mp_ptr rp, scratch; |
89 | 0 | mp_size_t itch; |
90 | 0 | TMP_DECL; |
91 | 0 | TMP_MARK; |
92 | |
|
93 | 0 | itch = mpn_mu_div_qr_itch (nn, dn, 0); |
94 | 0 | rp = TMP_BALLOC_LIMBS (dn); |
95 | 0 | scratch = TMP_BALLOC_LIMBS (itch); |
96 | |
|
97 | 0 | mpn_mu_div_qr (qp, rp, np, nn, dp, dn, scratch); |
98 | 0 | MPN_COPY (np, rp, dn); |
99 | |
|
100 | 0 | TMP_FREE; |
101 | 0 | } |
102 | 0 | } |
103 | | |
104 | | /* Compute t = a mod m, a is defined by (ap,an), m is defined by (mp,mn), and |
105 | | t is defined by (tp,mn). */ |
106 | | static void |
107 | | reduce (mp_ptr tp, mp_srcptr ap, mp_size_t an, mp_srcptr mp, mp_size_t mn, gmp_pi1_t *dinv) |
108 | 0 | { |
109 | 0 | mp_ptr rp, scratch; |
110 | 0 | TMP_DECL; |
111 | 0 | TMP_MARK; |
112 | |
|
113 | 0 | TMP_ALLOC_LIMBS_2 (rp, an, scratch, an - mn + 1); |
114 | 0 | MPN_COPY (rp, ap, an); |
115 | 0 | mod (rp, an, mp, mn, dinv, scratch); |
116 | 0 | MPN_COPY (tp, rp, mn); |
117 | |
|
118 | 0 | TMP_FREE; |
119 | 0 | } |
120 | | |
121 | | void |
122 | | mpz_powm_ui (mpz_ptr r, mpz_srcptr b, unsigned long int el, mpz_srcptr m) |
123 | 0 | { |
124 | 0 | if (el < 20) |
125 | 0 | { |
126 | 0 | mp_ptr xp, tp, mp, bp, scratch; |
127 | 0 | mp_size_t xn, tn, mn, bn; |
128 | 0 | int m_zero_cnt; |
129 | 0 | int c; |
130 | 0 | mp_limb_t e, m2; |
131 | 0 | gmp_pi1_t dinv; |
132 | 0 | TMP_DECL; |
133 | |
|
134 | 0 | mp = PTR(m); |
135 | 0 | mn = ABSIZ(m); |
136 | 0 | if (UNLIKELY (mn == 0)) |
137 | 0 | DIVIDE_BY_ZERO; |
138 | | |
139 | 0 | if (el <= 1) |
140 | 0 | { |
141 | 0 | if (el == 1) |
142 | 0 | { |
143 | 0 | mpz_mod (r, b, m); |
144 | 0 | return; |
145 | 0 | } |
146 | | /* Exponent is zero, result is 1 mod M, i.e., 1 or 0 depending on if |
147 | | M equals 1. */ |
148 | 0 | SIZ(r) = mn != 1 || mp[0] != 1; |
149 | 0 | MPZ_NEWALLOC (r, 1)[0] = 1; |
150 | 0 | return; |
151 | 0 | } |
152 | | |
153 | 0 | TMP_MARK; |
154 | | |
155 | | /* Normalize m (i.e. make its most significant bit set) as required by |
156 | | division functions below. */ |
157 | 0 | count_leading_zeros (m_zero_cnt, mp[mn - 1]); |
158 | 0 | m_zero_cnt -= GMP_NAIL_BITS; |
159 | 0 | if (m_zero_cnt != 0) |
160 | 0 | { |
161 | 0 | mp_ptr new_mp = TMP_ALLOC_LIMBS (mn); |
162 | 0 | mpn_lshift (new_mp, mp, mn, m_zero_cnt); |
163 | 0 | mp = new_mp; |
164 | 0 | } |
165 | |
|
166 | 0 | m2 = mn == 1 ? 0 : mp[mn - 2]; |
167 | 0 | invert_pi1 (dinv, mp[mn - 1], m2); |
168 | |
|
169 | 0 | bn = ABSIZ(b); |
170 | 0 | bp = PTR(b); |
171 | 0 | if (bn > mn) |
172 | 0 | { |
173 | | /* Reduce possibly huge base. Use a function call to reduce, since we |
174 | | don't want the quotient allocation to live until function return. */ |
175 | 0 | mp_ptr new_bp = TMP_ALLOC_LIMBS (mn); |
176 | 0 | reduce (new_bp, bp, bn, mp, mn, &dinv); |
177 | 0 | bp = new_bp; |
178 | 0 | bn = mn; |
179 | | /* Canonicalize the base, since we are potentially going to multiply with |
180 | | it quite a few times. */ |
181 | 0 | MPN_NORMALIZE (bp, bn); |
182 | 0 | } |
183 | |
|
184 | 0 | if (bn == 0) |
185 | 0 | { |
186 | 0 | SIZ(r) = 0; |
187 | 0 | TMP_FREE; |
188 | 0 | return; |
189 | 0 | } |
190 | | |
191 | 0 | TMP_ALLOC_LIMBS_3 (xp, mn, scratch, mn + 1, tp, 2 * mn + 1); |
192 | |
|
193 | 0 | MPN_COPY (xp, bp, bn); |
194 | 0 | xn = bn; |
195 | |
|
196 | 0 | e = el; |
197 | 0 | count_leading_zeros (c, e); |
198 | 0 | e = (e << c) << 1; /* shift the exp bits to the left, lose msb */ |
199 | 0 | c = GMP_LIMB_BITS - 1 - c; |
200 | |
|
201 | 0 | ASSERT (c != 0); /* el > 1 */ |
202 | 0 | { |
203 | | /* Main loop. */ |
204 | 0 | do |
205 | 0 | { |
206 | 0 | mpn_sqr (tp, xp, xn); |
207 | 0 | tn = 2 * xn; tn -= tp[tn - 1] == 0; |
208 | 0 | if (tn < mn) |
209 | 0 | { |
210 | 0 | MPN_COPY (xp, tp, tn); |
211 | 0 | xn = tn; |
212 | 0 | } |
213 | 0 | else |
214 | 0 | { |
215 | 0 | mod (tp, tn, mp, mn, &dinv, scratch); |
216 | 0 | MPN_COPY (xp, tp, mn); |
217 | 0 | xn = mn; |
218 | 0 | } |
219 | |
|
220 | 0 | if ((mp_limb_signed_t) e < 0) |
221 | 0 | { |
222 | 0 | mpn_mul (tp, xp, xn, bp, bn); |
223 | 0 | tn = xn + bn; tn -= tp[tn - 1] == 0; |
224 | 0 | if (tn < mn) |
225 | 0 | { |
226 | 0 | MPN_COPY (xp, tp, tn); |
227 | 0 | xn = tn; |
228 | 0 | } |
229 | 0 | else |
230 | 0 | { |
231 | 0 | mod (tp, tn, mp, mn, &dinv, scratch); |
232 | 0 | MPN_COPY (xp, tp, mn); |
233 | 0 | xn = mn; |
234 | 0 | } |
235 | 0 | } |
236 | 0 | e <<= 1; |
237 | 0 | c--; |
238 | 0 | } |
239 | 0 | while (c != 0); |
240 | 0 | } |
241 | | |
242 | | /* We shifted m left m_zero_cnt steps. Adjust the result by reducing it |
243 | | with the original M. */ |
244 | 0 | if (m_zero_cnt != 0) |
245 | 0 | { |
246 | 0 | mp_limb_t cy; |
247 | 0 | cy = mpn_lshift (tp, xp, xn, m_zero_cnt); |
248 | 0 | tp[xn] = cy; xn += cy != 0; |
249 | |
|
250 | 0 | if (xn >= mn) |
251 | 0 | { |
252 | 0 | mod (tp, xn, mp, mn, &dinv, scratch); |
253 | 0 | xn = mn; |
254 | 0 | } |
255 | 0 | mpn_rshift (xp, tp, xn, m_zero_cnt); |
256 | 0 | } |
257 | 0 | MPN_NORMALIZE (xp, xn); |
258 | |
|
259 | 0 | if ((el & 1) != 0 && SIZ(b) < 0 && xn != 0) |
260 | 0 | { |
261 | 0 | mp = PTR(m); /* want original, unnormalized m */ |
262 | 0 | mpn_sub (xp, mp, mn, xp, xn); |
263 | 0 | xn = mn; |
264 | 0 | MPN_NORMALIZE (xp, xn); |
265 | 0 | } |
266 | 0 | MPZ_NEWALLOC (r, xn); |
267 | 0 | SIZ (r) = xn; |
268 | 0 | MPN_COPY (PTR(r), xp, xn); |
269 | |
|
270 | 0 | TMP_FREE; |
271 | 0 | } |
272 | 0 | else |
273 | 0 | { |
274 | | /* For large exponents, fake an mpz_t exponent and deflect to the more |
275 | | sophisticated mpz_powm. */ |
276 | 0 | mpz_t e; |
277 | 0 | mp_limb_t ep[LIMBS_PER_ULONG]; |
278 | 0 | MPZ_FAKE_UI (e, ep, el); |
279 | 0 | mpz_powm (r, b, e, m); |
280 | 0 | } |
281 | 0 | } |