diff options
Diffstat (limited to 'vendor/gmp-6.3.0/mpz/powm.c')
-rw-r--r-- | vendor/gmp-6.3.0/mpz/powm.c | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/vendor/gmp-6.3.0/mpz/powm.c b/vendor/gmp-6.3.0/mpz/powm.c new file mode 100644 index 0000000..f1bf8e3 --- /dev/null +++ b/vendor/gmp-6.3.0/mpz/powm.c @@ -0,0 +1,282 @@ +/* mpz_powm(res,base,exp,mod) -- Set R to (U^E) mod M. + + Contributed to the GNU project by Torbjorn Granlund. + +Copyright 1991, 1993, 1994, 1996, 1997, 2000-2002, 2005, 2008, 2009, +2011, 2012, 2015, 2019 Free Software Foundation, Inc. + +This file is part of the GNU MP Library. + +The GNU MP Library is free software; you can redistribute it and/or modify +it under the terms of either: + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at your + option) any later version. + +or + + * the GNU General Public License as published by the Free Software + Foundation; either version 2 of the License, or (at your option) any + later version. + +or both in parallel, as here. + +The GNU MP Library is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received copies of the GNU General Public License and the +GNU Lesser General Public License along with the GNU MP Library. If not, +see https://www.gnu.org/licenses/. */ + + +#include "gmp-impl.h" +#include "longlong.h" + + +/* TODO + + * Improve handling of buffers. It is pretty ugly now. + + * For even moduli, we compute a binvert of its odd part both here and in + mpn_powm. How can we avoid this recomputation? +*/ + +/* + b ^ e mod m res + 0 0 0 ? + 0 e 0 ? + 0 0 m ? + 0 e m 0 + b 0 0 ? + b e 0 ? + b 0 m 1 mod m + b e m b^e mod m +*/ + +#define HANDLE_NEGATIVE_EXPONENT 1 + +void +mpz_powm (mpz_ptr r, mpz_srcptr b, mpz_srcptr e, mpz_srcptr m) +{ + mp_size_t n, nodd, ncnt; + int cnt; + mp_ptr rp, tp; + mp_srcptr bp, ep, mp; + mp_size_t rn, bn, es, en, itch; + mpz_t new_b; /* note: value lives long via 'b' */ + TMP_DECL; + + n = ABSIZ(m); + if (UNLIKELY (n == 0)) + DIVIDE_BY_ZERO; + + mp = PTR(m); + + TMP_MARK; + + es = SIZ(e); + if (UNLIKELY (es <= 0)) + { + if (es == 0) + { + /* b^0 mod m, b is anything and m is non-zero. + Result is 1 mod m, i.e., 1 or 0 depending on if m = 1. */ + SIZ(r) = n != 1 || mp[0] != 1; + MPZ_NEWALLOC (r, 1)[0] = 1; + TMP_FREE; /* we haven't really allocated anything here */ + return; + } +#if HANDLE_NEGATIVE_EXPONENT + MPZ_TMP_INIT (new_b, n + 1); + + if (UNLIKELY (! mpz_invert (new_b, b, m))) + DIVIDE_BY_ZERO; + b = new_b; + es = -es; +#else + DIVIDE_BY_ZERO; +#endif + } + en = es; + + bn = ABSIZ(b); + + if (UNLIKELY (bn == 0)) + { + SIZ(r) = 0; + TMP_FREE; + return; + } + + ep = PTR(e); + + /* Handle (b^1 mod m) early, since mpn_pow* do not handle that case. */ + if (UNLIKELY (en == 1 && ep[0] == 1)) + { + rp = TMP_ALLOC_LIMBS (n); + bp = PTR(b); + if (bn >= n) + { + mp_ptr qp = TMP_ALLOC_LIMBS (bn - n + 1); + mpn_tdiv_qr (qp, rp, 0L, bp, bn, mp, n); + rn = n; + MPN_NORMALIZE (rp, rn); + + if (rn != 0 && SIZ(b) < 0) + { + mpn_sub (rp, mp, n, rp, rn); + rn = n; + MPN_NORMALIZE_NOT_ZERO (rp, rn); + } + } + else + { + if (SIZ(b) < 0) + { + mpn_sub (rp, mp, n, bp, bn); + rn = n; + MPN_NORMALIZE_NOT_ZERO (rp, rn); + } + else + { + MPN_COPY (rp, bp, bn); + rn = bn; + } + } + goto ret; + } + + /* Remove low zero limbs from M. This loop will terminate for correctly + represented mpz numbers. */ + ncnt = 0; + while (UNLIKELY (mp[0] == 0)) + { + mp++; + ncnt++; + } + nodd = n - ncnt; + cnt = 0; + if (mp[0] % 2 == 0) + { + mp_ptr newmp = TMP_ALLOC_LIMBS (nodd); + count_trailing_zeros (cnt, mp[0]); + mpn_rshift (newmp, mp, nodd, cnt); + nodd -= newmp[nodd - 1] == 0; + mp = newmp; + ncnt++; + } + + if (ncnt != 0) + { + /* We will call both mpn_powm and mpn_powlo. */ + /* rp needs n, mpn_powlo needs 4n, the 2 mpn_binvert might need more */ + mp_size_t n_largest_binvert = MAX (ncnt, nodd); + mp_size_t itch_binvert = mpn_binvert_itch (n_largest_binvert); + itch = 3 * n + MAX (itch_binvert, 2 * n); + } + else + { + /* We will call just mpn_powm. */ + mp_size_t itch_binvert = mpn_binvert_itch (nodd); + itch = n + MAX (itch_binvert, 2 * n); + } + tp = TMP_ALLOC_LIMBS (itch); + + rp = tp; tp += n; + + bp = PTR(b); + mpn_powm (rp, bp, bn, ep, en, mp, nodd, tp); + + rn = n; + + if (ncnt != 0) + { + mp_ptr r2, xp, yp, odd_inv_2exp; + unsigned long t; + int bcnt; + + if (bn < ncnt) + { + mp_ptr newbp = TMP_ALLOC_LIMBS (ncnt); + MPN_COPY (newbp, bp, bn); + MPN_ZERO (newbp + bn, ncnt - bn); + bp = newbp; + } + + r2 = tp; + + if (bp[0] % 2 == 0) + { + if (en > 1) + { + MPN_ZERO (r2, ncnt); + goto zero; + } + + ASSERT (en == 1); + t = (ncnt - (cnt != 0)) * GMP_NUMB_BITS + cnt; + + /* Count number of low zero bits in B, up to 3. */ + bcnt = (0x1213 >> ((bp[0] & 7) << 1)) & 0x3; + /* Note that ep[0] * bcnt might overflow, but that just results + in a missed optimization. */ + if (ep[0] * bcnt >= t) + { + MPN_ZERO (r2, ncnt); + goto zero; + } + } + + mpn_powlo (r2, bp, ep, en, ncnt, tp + ncnt); + + zero: + if (nodd < ncnt) + { + mp_ptr newmp = TMP_ALLOC_LIMBS (ncnt); + MPN_COPY (newmp, mp, nodd); + MPN_ZERO (newmp + nodd, ncnt - nodd); + mp = newmp; + } + + odd_inv_2exp = tp + n; + mpn_binvert (odd_inv_2exp, mp, ncnt, tp + 2 * n); + + mpn_sub (r2, r2, ncnt, rp, nodd > ncnt ? ncnt : nodd); + + xp = tp + 2 * n; + mpn_mullo_n (xp, odd_inv_2exp, r2, ncnt); + + if (cnt != 0) + xp[ncnt - 1] &= (CNST_LIMB(1) << cnt) - 1; + + yp = tp; + if (ncnt > nodd) + mpn_mul (yp, xp, ncnt, mp, nodd); + else + mpn_mul (yp, mp, nodd, xp, ncnt); + + mpn_add (rp, yp, n, rp, nodd); + + ASSERT (nodd + ncnt >= n); + ASSERT (nodd + ncnt <= n + 1); + } + + MPN_NORMALIZE (rp, rn); + + if ((ep[0] & 1) && SIZ(b) < 0 && rn != 0) + { + mpn_sub (rp, PTR(m), n, rp, rn); + rn = n; + MPN_NORMALIZE (rp, rn); + } + + ret: + MPZ_NEWALLOC (r, rn); + SIZ(r) = rn; + MPN_COPY (PTR(r), rp, rn); + + TMP_FREE; +} |