277 lines
7.0 KiB
C
277 lines
7.0 KiB
C
|
/* mpz_powm(res,base,exp,mod) -- Set RES to (base**exp) mod MOD.
|
||
|
|
||
|
Copyright (C) 1991, 1993, 1994, 1996 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 the GNU Library General Public License as published by
|
||
|
the Free Software Foundation; either version 2 of the License, or (at your
|
||
|
option) any later version.
|
||
|
|
||
|
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 Library General Public
|
||
|
License for more details.
|
||
|
|
||
|
You should have received a copy of the GNU Library General Public License
|
||
|
along with the GNU MP Library; see the file COPYING.LIB. If not, write to
|
||
|
the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||
|
MA 02111-1307, USA. */
|
||
|
|
||
|
#include "gmp.h"
|
||
|
#include "gmp-impl.h"
|
||
|
#include "longlong.h"
|
||
|
|
||
|
#ifndef BERKELEY_MP
|
||
|
void
|
||
|
#if __STDC__
|
||
|
mpz_powm (mpz_ptr res, mpz_srcptr base, mpz_srcptr exp, mpz_srcptr mod)
|
||
|
#else
|
||
|
mpz_powm (res, base, exp, mod)
|
||
|
mpz_ptr res;
|
||
|
mpz_srcptr base;
|
||
|
mpz_srcptr exp;
|
||
|
mpz_srcptr mod;
|
||
|
#endif
|
||
|
#else /* BERKELEY_MP */
|
||
|
void
|
||
|
#if __STDC__
|
||
|
pow (mpz_srcptr base, mpz_srcptr exp, mpz_srcptr mod, mpz_ptr res)
|
||
|
#else
|
||
|
pow (base, exp, mod, res)
|
||
|
mpz_srcptr base;
|
||
|
mpz_srcptr exp;
|
||
|
mpz_srcptr mod;
|
||
|
mpz_ptr res;
|
||
|
#endif
|
||
|
#endif /* BERKELEY_MP */
|
||
|
{
|
||
|
mp_ptr rp, ep, mp, bp;
|
||
|
mp_size_t esize, msize, bsize, rsize;
|
||
|
mp_size_t size;
|
||
|
int mod_shift_cnt;
|
||
|
int negative_result;
|
||
|
mp_limb_t *free_me = NULL;
|
||
|
size_t free_me_size;
|
||
|
TMP_DECL (marker);
|
||
|
|
||
|
esize = ABS (exp->_mp_size);
|
||
|
msize = ABS (mod->_mp_size);
|
||
|
size = 2 * msize;
|
||
|
|
||
|
rp = res->_mp_d;
|
||
|
ep = exp->_mp_d;
|
||
|
|
||
|
if (msize == 0)
|
||
|
msize = 1 / msize; /* provoke a signal */
|
||
|
|
||
|
if (esize == 0)
|
||
|
{
|
||
|
/* Exponent is zero, result is 1 mod MOD, i.e., 1 or 0
|
||
|
depending on if MOD equals 1. */
|
||
|
rp[0] = 1;
|
||
|
res->_mp_size = (msize == 1 && (mod->_mp_d)[0] == 1) ? 0 : 1;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
TMP_MARK (marker);
|
||
|
|
||
|
/* Normalize MOD (i.e. make its most significant bit set) as required by
|
||
|
mpn_divmod. This will make the intermediate values in the calculation
|
||
|
slightly larger, but the correct result is obtained after a final
|
||
|
reduction using the original MOD value. */
|
||
|
|
||
|
mp = (mp_ptr) TMP_ALLOC (msize * BYTES_PER_MP_LIMB);
|
||
|
count_leading_zeros (mod_shift_cnt, mod->_mp_d[msize - 1]);
|
||
|
if (mod_shift_cnt != 0)
|
||
|
mpn_lshift (mp, mod->_mp_d, msize, mod_shift_cnt);
|
||
|
else
|
||
|
MPN_COPY (mp, mod->_mp_d, msize);
|
||
|
|
||
|
bsize = ABS (base->_mp_size);
|
||
|
if (bsize > msize)
|
||
|
{
|
||
|
/* The base is larger than the module. Reduce it. */
|
||
|
|
||
|
/* Allocate (BSIZE + 1) with space for remainder and quotient.
|
||
|
(The quotient is (bsize - msize + 1) limbs.) */
|
||
|
bp = (mp_ptr) TMP_ALLOC ((bsize + 1) * BYTES_PER_MP_LIMB);
|
||
|
MPN_COPY (bp, base->_mp_d, bsize);
|
||
|
/* We don't care about the quotient, store it above the remainder,
|
||
|
at BP + MSIZE. */
|
||
|
mpn_divmod (bp + msize, bp, bsize, mp, msize);
|
||
|
bsize = msize;
|
||
|
/* Canonicalize the base, since we are going to multiply with it
|
||
|
quite a few times. */
|
||
|
MPN_NORMALIZE (bp, bsize);
|
||
|
}
|
||
|
else
|
||
|
bp = base->_mp_d;
|
||
|
|
||
|
if (bsize == 0)
|
||
|
{
|
||
|
res->_mp_size = 0;
|
||
|
TMP_FREE (marker);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (res->_mp_alloc < size)
|
||
|
{
|
||
|
/* We have to allocate more space for RES. If any of the input
|
||
|
parameters are identical to RES, defer deallocation of the old
|
||
|
space. */
|
||
|
|
||
|
if (rp == ep || rp == mp || rp == bp)
|
||
|
{
|
||
|
free_me = rp;
|
||
|
free_me_size = res->_mp_alloc;
|
||
|
}
|
||
|
else
|
||
|
(*_mp_free_func) (rp, res->_mp_alloc * BYTES_PER_MP_LIMB);
|
||
|
|
||
|
rp = (mp_ptr) (*_mp_allocate_func) (size * BYTES_PER_MP_LIMB);
|
||
|
res->_mp_alloc = size;
|
||
|
res->_mp_d = rp;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/* Make BASE, EXP and MOD not overlap with RES. */
|
||
|
if (rp == bp)
|
||
|
{
|
||
|
/* RES and BASE are identical. Allocate temp. space for BASE. */
|
||
|
bp = (mp_ptr) TMP_ALLOC (bsize * BYTES_PER_MP_LIMB);
|
||
|
MPN_COPY (bp, rp, bsize);
|
||
|
}
|
||
|
if (rp == ep)
|
||
|
{
|
||
|
/* RES and EXP are identical. Allocate temp. space for EXP. */
|
||
|
ep = (mp_ptr) TMP_ALLOC (esize * BYTES_PER_MP_LIMB);
|
||
|
MPN_COPY (ep, rp, esize);
|
||
|
}
|
||
|
if (rp == mp)
|
||
|
{
|
||
|
/* RES and MOD are identical. Allocate temporary space for MOD. */
|
||
|
mp = (mp_ptr) TMP_ALLOC (msize * BYTES_PER_MP_LIMB);
|
||
|
MPN_COPY (mp, rp, msize);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
MPN_COPY (rp, bp, bsize);
|
||
|
rsize = bsize;
|
||
|
|
||
|
{
|
||
|
mp_size_t i;
|
||
|
mp_ptr xp = (mp_ptr) TMP_ALLOC (2 * (msize + 1) * BYTES_PER_MP_LIMB);
|
||
|
int c;
|
||
|
mp_limb_t e;
|
||
|
mp_limb_t carry_limb;
|
||
|
|
||
|
negative_result = (ep[0] & 1) && base->_mp_size < 0;
|
||
|
|
||
|
i = esize - 1;
|
||
|
e = ep[i];
|
||
|
count_leading_zeros (c, e);
|
||
|
e = (e << c) << 1; /* shift the exp bits to the left, lose msb */
|
||
|
c = BITS_PER_MP_LIMB - 1 - c;
|
||
|
|
||
|
/* Main loop.
|
||
|
|
||
|
Make the result be pointed to alternately by XP and RP. This
|
||
|
helps us avoid block copying, which would otherwise be necessary
|
||
|
with the overlap restrictions of mpn_divmod. With 50% probability
|
||
|
the result after this loop will be in the area originally pointed
|
||
|
by RP (==RES->_mp_d), and with 50% probability in the area originally
|
||
|
pointed to by XP. */
|
||
|
|
||
|
for (;;)
|
||
|
{
|
||
|
while (c != 0)
|
||
|
{
|
||
|
mp_ptr tp;
|
||
|
mp_size_t xsize;
|
||
|
|
||
|
mpn_mul_n (xp, rp, rp, rsize);
|
||
|
xsize = 2 * rsize;
|
||
|
if (xsize > msize)
|
||
|
{
|
||
|
mpn_divmod (xp + msize, xp, xsize, mp, msize);
|
||
|
xsize = msize;
|
||
|
}
|
||
|
|
||
|
tp = rp; rp = xp; xp = tp;
|
||
|
rsize = xsize;
|
||
|
|
||
|
if ((mp_limb_signed_t) e < 0)
|
||
|
{
|
||
|
mpn_mul (xp, rp, rsize, bp, bsize);
|
||
|
xsize = rsize + bsize;
|
||
|
if (xsize > msize)
|
||
|
{
|
||
|
mpn_divmod (xp + msize, xp, xsize, mp, msize);
|
||
|
xsize = msize;
|
||
|
}
|
||
|
|
||
|
tp = rp; rp = xp; xp = tp;
|
||
|
rsize = xsize;
|
||
|
}
|
||
|
e <<= 1;
|
||
|
c--;
|
||
|
}
|
||
|
|
||
|
i--;
|
||
|
if (i < 0)
|
||
|
break;
|
||
|
e = ep[i];
|
||
|
c = BITS_PER_MP_LIMB;
|
||
|
}
|
||
|
|
||
|
/* We shifted MOD, the modulo reduction argument, left MOD_SHIFT_CNT
|
||
|
steps. Adjust the result by reducing it with the original MOD.
|
||
|
|
||
|
Also make sure the result is put in RES->_mp_d (where it already
|
||
|
might be, see above). */
|
||
|
|
||
|
if (mod_shift_cnt != 0)
|
||
|
{
|
||
|
carry_limb = mpn_lshift (res->_mp_d, rp, rsize, mod_shift_cnt);
|
||
|
rp = res->_mp_d;
|
||
|
if (carry_limb != 0)
|
||
|
{
|
||
|
rp[rsize] = carry_limb;
|
||
|
rsize++;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
MPN_COPY (res->_mp_d, rp, rsize);
|
||
|
rp = res->_mp_d;
|
||
|
}
|
||
|
|
||
|
if (rsize >= msize)
|
||
|
{
|
||
|
mpn_divmod (rp + msize, rp, rsize, mp, msize);
|
||
|
rsize = msize;
|
||
|
}
|
||
|
|
||
|
/* Remove any leading zero words from the result. */
|
||
|
if (mod_shift_cnt != 0)
|
||
|
mpn_rshift (rp, rp, rsize, mod_shift_cnt);
|
||
|
MPN_NORMALIZE (rp, rsize);
|
||
|
}
|
||
|
|
||
|
if (negative_result && rsize != 0)
|
||
|
{
|
||
|
if (mod_shift_cnt != 0)
|
||
|
mpn_rshift (mp, mp, msize, mod_shift_cnt);
|
||
|
mpn_sub (rp, mp, msize, rp, rsize);
|
||
|
rsize = msize;
|
||
|
MPN_NORMALIZE (rp, rsize);
|
||
|
}
|
||
|
res->_mp_size = rsize;
|
||
|
|
||
|
if (free_me != NULL)
|
||
|
(*_mp_free_func) (free_me, free_me_size * BYTES_PER_MP_LIMB);
|
||
|
TMP_FREE (marker);
|
||
|
}
|