Austin Schuh | dace2a6 | 2020-08-18 10:56:48 -0700 | [diff] [blame^] | 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 | { |
| 61 | mp_ptr qp = tp; |
| 62 | |
| 63 | if (dn == 1) |
| 64 | { |
| 65 | np[0] = mpn_divrem_1 (qp, (mp_size_t) 0, np, nn, dp[0]); |
| 66 | } |
| 67 | else if (dn == 2) |
| 68 | { |
| 69 | mpn_div_qr_2n_pi1 (qp, np, np, nn, dp[1], dp[0], dinv->inv32); |
| 70 | } |
| 71 | else if (BELOW_THRESHOLD (dn, DC_DIV_QR_THRESHOLD) || |
| 72 | BELOW_THRESHOLD (nn - dn, DC_DIV_QR_THRESHOLD)) |
| 73 | { |
| 74 | mpn_sbpi1_div_qr (qp, np, nn, dp, dn, dinv->inv32); |
| 75 | } |
| 76 | else if (BELOW_THRESHOLD (dn, MUPI_DIV_QR_THRESHOLD) || /* fast condition */ |
| 77 | BELOW_THRESHOLD (nn, 2 * MU_DIV_QR_THRESHOLD) || /* fast condition */ |
| 78 | (double) (2 * (MU_DIV_QR_THRESHOLD - MUPI_DIV_QR_THRESHOLD)) * dn /* slow... */ |
| 79 | + (double) MUPI_DIV_QR_THRESHOLD * nn > (double) dn * nn) /* ...condition */ |
| 80 | { |
| 81 | mpn_dcpi1_div_qr (qp, np, nn, dp, dn, dinv); |
| 82 | } |
| 83 | else |
| 84 | { |
| 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 | mp_ptr rp, scratch; |
| 89 | mp_size_t itch; |
| 90 | TMP_DECL; |
| 91 | TMP_MARK; |
| 92 | |
| 93 | itch = mpn_mu_div_qr_itch (nn, dn, 0); |
| 94 | rp = TMP_BALLOC_LIMBS (dn); |
| 95 | scratch = TMP_BALLOC_LIMBS (itch); |
| 96 | |
| 97 | mpn_mu_div_qr (qp, rp, np, nn, dp, dn, scratch); |
| 98 | MPN_COPY (np, rp, dn); |
| 99 | |
| 100 | TMP_FREE; |
| 101 | } |
| 102 | } |
| 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 | { |
| 109 | mp_ptr rp, scratch; |
| 110 | TMP_DECL; |
| 111 | TMP_MARK; |
| 112 | |
| 113 | TMP_ALLOC_LIMBS_2 (rp, an, scratch, an - mn + 1); |
| 114 | MPN_COPY (rp, ap, an); |
| 115 | mod (rp, an, mp, mn, dinv, scratch); |
| 116 | MPN_COPY (tp, rp, mn); |
| 117 | |
| 118 | TMP_FREE; |
| 119 | } |
| 120 | |
| 121 | void |
| 122 | mpz_powm_ui (mpz_ptr r, mpz_srcptr b, unsigned long int el, mpz_srcptr m) |
| 123 | { |
| 124 | if (el < 20) |
| 125 | { |
| 126 | mp_ptr xp, tp, mp, bp, scratch; |
| 127 | mp_size_t xn, tn, mn, bn; |
| 128 | int m_zero_cnt; |
| 129 | int c; |
| 130 | mp_limb_t e, m2; |
| 131 | gmp_pi1_t dinv; |
| 132 | TMP_DECL; |
| 133 | |
| 134 | mp = PTR(m); |
| 135 | mn = ABSIZ(m); |
| 136 | if (UNLIKELY (mn == 0)) |
| 137 | DIVIDE_BY_ZERO; |
| 138 | |
| 139 | if (el <= 1) |
| 140 | { |
| 141 | if (el == 1) |
| 142 | { |
| 143 | mpz_mod (r, b, m); |
| 144 | return; |
| 145 | } |
| 146 | /* Exponent is zero, result is 1 mod M, i.e., 1 or 0 depending on if |
| 147 | M equals 1. */ |
| 148 | SIZ(r) = mn != 1 || mp[0] != 1; |
| 149 | MPZ_NEWALLOC (r, 1)[0] = 1; |
| 150 | return; |
| 151 | } |
| 152 | |
| 153 | TMP_MARK; |
| 154 | |
| 155 | /* Normalize m (i.e. make its most significant bit set) as required by |
| 156 | division functions below. */ |
| 157 | count_leading_zeros (m_zero_cnt, mp[mn - 1]); |
| 158 | m_zero_cnt -= GMP_NAIL_BITS; |
| 159 | if (m_zero_cnt != 0) |
| 160 | { |
| 161 | mp_ptr new_mp = TMP_ALLOC_LIMBS (mn); |
| 162 | mpn_lshift (new_mp, mp, mn, m_zero_cnt); |
| 163 | mp = new_mp; |
| 164 | } |
| 165 | |
| 166 | m2 = mn == 1 ? 0 : mp[mn - 2]; |
| 167 | invert_pi1 (dinv, mp[mn - 1], m2); |
| 168 | |
| 169 | bn = ABSIZ(b); |
| 170 | bp = PTR(b); |
| 171 | if (bn > mn) |
| 172 | { |
| 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 | mp_ptr new_bp = TMP_ALLOC_LIMBS (mn); |
| 176 | reduce (new_bp, bp, bn, mp, mn, &dinv); |
| 177 | bp = new_bp; |
| 178 | bn = mn; |
| 179 | /* Canonicalize the base, since we are potentially going to multiply with |
| 180 | it quite a few times. */ |
| 181 | MPN_NORMALIZE (bp, bn); |
| 182 | } |
| 183 | |
| 184 | if (bn == 0) |
| 185 | { |
| 186 | SIZ(r) = 0; |
| 187 | TMP_FREE; |
| 188 | return; |
| 189 | } |
| 190 | |
| 191 | TMP_ALLOC_LIMBS_3 (xp, mn, scratch, mn + 1, tp, 2 * mn + 1); |
| 192 | |
| 193 | MPN_COPY (xp, bp, bn); |
| 194 | xn = bn; |
| 195 | |
| 196 | e = el; |
| 197 | count_leading_zeros (c, e); |
| 198 | e = (e << c) << 1; /* shift the exp bits to the left, lose msb */ |
| 199 | c = GMP_LIMB_BITS - 1 - c; |
| 200 | |
| 201 | ASSERT (c != 0); /* el > 1 */ |
| 202 | { |
| 203 | /* Main loop. */ |
| 204 | do |
| 205 | { |
| 206 | mpn_sqr (tp, xp, xn); |
| 207 | tn = 2 * xn; tn -= tp[tn - 1] == 0; |
| 208 | if (tn < mn) |
| 209 | { |
| 210 | MPN_COPY (xp, tp, tn); |
| 211 | xn = tn; |
| 212 | } |
| 213 | else |
| 214 | { |
| 215 | mod (tp, tn, mp, mn, &dinv, scratch); |
| 216 | MPN_COPY (xp, tp, mn); |
| 217 | xn = mn; |
| 218 | } |
| 219 | |
| 220 | if ((mp_limb_signed_t) e < 0) |
| 221 | { |
| 222 | mpn_mul (tp, xp, xn, bp, bn); |
| 223 | tn = xn + bn; tn -= tp[tn - 1] == 0; |
| 224 | if (tn < mn) |
| 225 | { |
| 226 | MPN_COPY (xp, tp, tn); |
| 227 | xn = tn; |
| 228 | } |
| 229 | else |
| 230 | { |
| 231 | mod (tp, tn, mp, mn, &dinv, scratch); |
| 232 | MPN_COPY (xp, tp, mn); |
| 233 | xn = mn; |
| 234 | } |
| 235 | } |
| 236 | e <<= 1; |
| 237 | c--; |
| 238 | } |
| 239 | while (c != 0); |
| 240 | } |
| 241 | |
| 242 | /* We shifted m left m_zero_cnt steps. Adjust the result by reducing it |
| 243 | with the original M. */ |
| 244 | if (m_zero_cnt != 0) |
| 245 | { |
| 246 | mp_limb_t cy; |
| 247 | cy = mpn_lshift (tp, xp, xn, m_zero_cnt); |
| 248 | tp[xn] = cy; xn += cy != 0; |
| 249 | |
| 250 | if (xn >= mn) |
| 251 | { |
| 252 | mod (tp, xn, mp, mn, &dinv, scratch); |
| 253 | xn = mn; |
| 254 | } |
| 255 | mpn_rshift (xp, tp, xn, m_zero_cnt); |
| 256 | } |
| 257 | MPN_NORMALIZE (xp, xn); |
| 258 | |
| 259 | if ((el & 1) != 0 && SIZ(b) < 0 && xn != 0) |
| 260 | { |
| 261 | mp = PTR(m); /* want original, unnormalized m */ |
| 262 | mpn_sub (xp, mp, mn, xp, xn); |
| 263 | xn = mn; |
| 264 | MPN_NORMALIZE (xp, xn); |
| 265 | } |
| 266 | MPZ_NEWALLOC (r, xn); |
| 267 | SIZ (r) = xn; |
| 268 | MPN_COPY (PTR(r), xp, xn); |
| 269 | |
| 270 | TMP_FREE; |
| 271 | } |
| 272 | else |
| 273 | { |
| 274 | /* For large exponents, fake an mpz_t exponent and deflect to the more |
| 275 | sophisticated mpz_powm. */ |
| 276 | mpz_t e; |
| 277 | mp_limb_t ep[LIMBS_PER_ULONG]; |
| 278 | MPZ_FAKE_UI (e, ep, el); |
| 279 | mpz_powm (r, b, e, m); |
| 280 | } |
| 281 | } |