github.com/aergoio/aergo@v1.3.1/libtool/src/gmp-6.1.2/mpz/powm.c (about) 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, 2011, 2012 6 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.h" 36 #include "gmp-impl.h" 37 #include "longlong.h" 38 39 40 /* TODO 41 42 * Improve handling of buffers. It is pretty ugly now. 43 44 * For even moduli, we compute a binvert of its odd part both here and in 45 mpn_powm. How can we avoid this recomputation? 46 */ 47 48 /* 49 b ^ e mod m res 50 0 0 0 ? 51 0 e 0 ? 52 0 0 m ? 53 0 e m 0 54 b 0 0 ? 55 b e 0 ? 56 b 0 m 1 mod m 57 b e m b^e mod m 58 */ 59 60 #define HANDLE_NEGATIVE_EXPONENT 1 61 62 void 63 mpz_powm (mpz_ptr r, mpz_srcptr b, mpz_srcptr e, mpz_srcptr m) 64 { 65 mp_size_t n, nodd, ncnt; 66 int cnt; 67 mp_ptr rp, tp; 68 mp_srcptr bp, ep, mp; 69 mp_size_t rn, bn, es, en, itch; 70 mpz_t new_b; /* note: value lives long via 'b' */ 71 TMP_DECL; 72 73 n = ABSIZ(m); 74 if (UNLIKELY (n == 0)) 75 DIVIDE_BY_ZERO; 76 77 mp = PTR(m); 78 79 TMP_MARK; 80 81 es = SIZ(e); 82 if (UNLIKELY (es <= 0)) 83 { 84 if (es == 0) 85 { 86 /* b^0 mod m, b is anything and m is non-zero. 87 Result is 1 mod m, i.e., 1 or 0 depending on if m = 1. */ 88 SIZ(r) = n != 1 || mp[0] != 1; 89 PTR(r)[0] = 1; 90 TMP_FREE; /* we haven't really allocated anything here */ 91 return; 92 } 93 #if HANDLE_NEGATIVE_EXPONENT 94 MPZ_TMP_INIT (new_b, n + 1); 95 96 if (UNLIKELY (! mpz_invert (new_b, b, m))) 97 DIVIDE_BY_ZERO; 98 b = new_b; 99 es = -es; 100 #else 101 DIVIDE_BY_ZERO; 102 #endif 103 } 104 en = es; 105 106 bn = ABSIZ(b); 107 108 if (UNLIKELY (bn == 0)) 109 { 110 SIZ(r) = 0; 111 TMP_FREE; 112 return; 113 } 114 115 ep = PTR(e); 116 117 /* Handle (b^1 mod m) early, since mpn_pow* do not handle that case. */ 118 if (UNLIKELY (en == 1 && ep[0] == 1)) 119 { 120 rp = TMP_ALLOC_LIMBS (n); 121 bp = PTR(b); 122 if (bn >= n) 123 { 124 mp_ptr qp = TMP_ALLOC_LIMBS (bn - n + 1); 125 mpn_tdiv_qr (qp, rp, 0L, bp, bn, mp, n); 126 rn = n; 127 MPN_NORMALIZE (rp, rn); 128 129 if (SIZ(b) < 0 && rn != 0) 130 { 131 mpn_sub (rp, mp, n, rp, rn); 132 rn = n; 133 MPN_NORMALIZE (rp, rn); 134 } 135 } 136 else 137 { 138 if (SIZ(b) < 0) 139 { 140 mpn_sub (rp, mp, n, bp, bn); 141 rn = n; 142 rn -= (rp[rn - 1] == 0); 143 } 144 else 145 { 146 MPN_COPY (rp, bp, bn); 147 rn = bn; 148 } 149 } 150 goto ret; 151 } 152 153 /* Remove low zero limbs from M. This loop will terminate for correctly 154 represented mpz numbers. */ 155 ncnt = 0; 156 while (UNLIKELY (mp[0] == 0)) 157 { 158 mp++; 159 ncnt++; 160 } 161 nodd = n - ncnt; 162 cnt = 0; 163 if (mp[0] % 2 == 0) 164 { 165 mp_ptr newmp = TMP_ALLOC_LIMBS (nodd); 166 count_trailing_zeros (cnt, mp[0]); 167 mpn_rshift (newmp, mp, nodd, cnt); 168 nodd -= newmp[nodd - 1] == 0; 169 mp = newmp; 170 ncnt++; 171 } 172 173 if (ncnt != 0) 174 { 175 /* We will call both mpn_powm and mpn_powlo. */ 176 /* rp needs n, mpn_powlo needs 4n, the 2 mpn_binvert might need more */ 177 mp_size_t n_largest_binvert = MAX (ncnt, nodd); 178 mp_size_t itch_binvert = mpn_binvert_itch (n_largest_binvert); 179 itch = 3 * n + MAX (itch_binvert, 2 * n); 180 } 181 else 182 { 183 /* We will call just mpn_powm. */ 184 mp_size_t itch_binvert = mpn_binvert_itch (nodd); 185 itch = n + MAX (itch_binvert, 2 * n); 186 } 187 tp = TMP_ALLOC_LIMBS (itch); 188 189 rp = tp; tp += n; 190 191 bp = PTR(b); 192 mpn_powm (rp, bp, bn, ep, en, mp, nodd, tp); 193 194 rn = n; 195 196 if (ncnt != 0) 197 { 198 mp_ptr r2, xp, yp, odd_inv_2exp; 199 unsigned long t; 200 int bcnt; 201 202 if (bn < ncnt) 203 { 204 mp_ptr newbp = TMP_ALLOC_LIMBS (ncnt); 205 MPN_COPY (newbp, bp, bn); 206 MPN_ZERO (newbp + bn, ncnt - bn); 207 bp = newbp; 208 } 209 210 r2 = tp; 211 212 if (bp[0] % 2 == 0) 213 { 214 if (en > 1) 215 { 216 MPN_ZERO (r2, ncnt); 217 goto zero; 218 } 219 220 ASSERT (en == 1); 221 t = (ncnt - (cnt != 0)) * GMP_NUMB_BITS + cnt; 222 223 /* Count number of low zero bits in B, up to 3. */ 224 bcnt = (0x1213 >> ((bp[0] & 7) << 1)) & 0x3; 225 /* Note that ep[0] * bcnt might overflow, but that just results 226 in a missed optimization. */ 227 if (ep[0] * bcnt >= t) 228 { 229 MPN_ZERO (r2, ncnt); 230 goto zero; 231 } 232 } 233 234 mpn_powlo (r2, bp, ep, en, ncnt, tp + ncnt); 235 236 zero: 237 if (nodd < ncnt) 238 { 239 mp_ptr newmp = TMP_ALLOC_LIMBS (ncnt); 240 MPN_COPY (newmp, mp, nodd); 241 MPN_ZERO (newmp + nodd, ncnt - nodd); 242 mp = newmp; 243 } 244 245 odd_inv_2exp = tp + n; 246 mpn_binvert (odd_inv_2exp, mp, ncnt, tp + 2 * n); 247 248 mpn_sub (r2, r2, ncnt, rp, nodd > ncnt ? ncnt : nodd); 249 250 xp = tp + 2 * n; 251 mpn_mullo_n (xp, odd_inv_2exp, r2, ncnt); 252 253 if (cnt != 0) 254 xp[ncnt - 1] &= (CNST_LIMB(1) << cnt) - 1; 255 256 yp = tp; 257 if (ncnt > nodd) 258 mpn_mul (yp, xp, ncnt, mp, nodd); 259 else 260 mpn_mul (yp, mp, nodd, xp, ncnt); 261 262 mpn_add (rp, yp, n, rp, nodd); 263 264 ASSERT (nodd + ncnt >= n); 265 ASSERT (nodd + ncnt <= n + 1); 266 } 267 268 MPN_NORMALIZE (rp, rn); 269 270 if ((ep[0] & 1) && SIZ(b) < 0 && rn != 0) 271 { 272 mpn_sub (rp, PTR(m), n, rp, rn); 273 rn = n; 274 MPN_NORMALIZE (rp, rn); 275 } 276 277 ret: 278 MPZ_REALLOC (r, rn); 279 SIZ(r) = rn; 280 MPN_COPY (PTR(r), rp, rn); 281 282 TMP_FREE; 283 }