Blame mpz/powm_ui.c

Packit 5c3484
/* mpz_powm_ui(res,base,exp,mod) -- Set R to (B^E) mod M.
Packit 5c3484
Packit 5c3484
   Contributed to the GNU project by Torbjörn Granlund.
Packit 5c3484
Packit 5c3484
Copyright 1991, 1993, 1994, 1996, 1997, 2000-2002, 2005, 2008, 2009, 2011-2013
Packit 5c3484
Free Software Foundation, Inc.
Packit 5c3484
Packit 5c3484
This file is part of the GNU MP Library.
Packit 5c3484
Packit 5c3484
The GNU MP Library is free software; you can redistribute it and/or modify
Packit 5c3484
it under the terms of either:
Packit 5c3484
Packit 5c3484
  * the GNU Lesser General Public License as published by the Free
Packit 5c3484
    Software Foundation; either version 3 of the License, or (at your
Packit 5c3484
    option) any later version.
Packit 5c3484
Packit 5c3484
or
Packit 5c3484
Packit 5c3484
  * the GNU General Public License as published by the Free Software
Packit 5c3484
    Foundation; either version 2 of the License, or (at your option) any
Packit 5c3484
    later version.
Packit 5c3484
Packit 5c3484
or both in parallel, as here.
Packit 5c3484
Packit 5c3484
The GNU MP Library is distributed in the hope that it will be useful, but
Packit 5c3484
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
Packit 5c3484
or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
Packit 5c3484
for more details.
Packit 5c3484
Packit 5c3484
You should have received copies of the GNU General Public License and the
Packit 5c3484
GNU Lesser General Public License along with the GNU MP Library.  If not,
Packit 5c3484
see https://www.gnu.org/licenses/.  */
Packit 5c3484
Packit 5c3484
Packit 5c3484
#include "gmp.h"
Packit 5c3484
#include "gmp-impl.h"
Packit 5c3484
#include "longlong.h"
Packit 5c3484
Packit 5c3484
Packit 5c3484
/* This code is very old, and should be rewritten to current GMP standard.  It
Packit 5c3484
   is slower than mpz_powm for large exponents, but also for small exponents
Packit 5c3484
   when the mod argument is small.
Packit 5c3484
Packit 5c3484
   As an intermediate solution, we now deflect to mpz_powm for exponents >= 20.
Packit 5c3484
*/
Packit 5c3484
Packit 5c3484
/*
Packit 5c3484
  b ^ e mod m   res
Packit 5c3484
  0   0     0    ?
Packit 5c3484
  0   e     0    ?
Packit 5c3484
  0   0     m    ?
Packit 5c3484
  0   e     m    0
Packit 5c3484
  b   0     0    ?
Packit 5c3484
  b   e     0    ?
Packit 5c3484
  b   0     m    1 mod m
Packit 5c3484
  b   e     m    b^e mod m
Packit 5c3484
*/
Packit 5c3484
Packit 5c3484
static void
Packit 5c3484
mod (mp_ptr np, mp_size_t nn, mp_srcptr dp, mp_size_t dn, gmp_pi1_t *dinv, mp_ptr tp)
Packit 5c3484
{
Packit 5c3484
  mp_ptr qp;
Packit 5c3484
  TMP_DECL;
Packit 5c3484
  TMP_MARK;
Packit 5c3484
Packit 5c3484
  qp = tp;
Packit 5c3484
Packit 5c3484
  if (dn == 1)
Packit 5c3484
    {
Packit 5c3484
      np[0] = mpn_divrem_1 (qp, (mp_size_t) 0, np, nn, dp[0]);
Packit 5c3484
    }
Packit 5c3484
  else if (dn == 2)
Packit 5c3484
    {
Packit 5c3484
      mpn_div_qr_2n_pi1 (qp, np, np, nn, dp[1], dp[0], dinv->inv32);
Packit 5c3484
    }
Packit 5c3484
  else if (BELOW_THRESHOLD (dn, DC_DIV_QR_THRESHOLD) ||
Packit 5c3484
	   BELOW_THRESHOLD (nn - dn, DC_DIV_QR_THRESHOLD))
Packit 5c3484
    {
Packit 5c3484
      mpn_sbpi1_div_qr (qp, np, nn, dp, dn, dinv->inv32);
Packit 5c3484
    }
Packit 5c3484
  else if (BELOW_THRESHOLD (dn, MUPI_DIV_QR_THRESHOLD) ||   /* fast condition */
Packit 5c3484
	   BELOW_THRESHOLD (nn, 2 * MU_DIV_QR_THRESHOLD) || /* fast condition */
Packit 5c3484
	   (double) (2 * (MU_DIV_QR_THRESHOLD - MUPI_DIV_QR_THRESHOLD)) * dn /* slow... */
Packit 5c3484
	   + (double) MUPI_DIV_QR_THRESHOLD * nn > (double) dn * nn)    /* ...condition */
Packit 5c3484
    {
Packit 5c3484
      mpn_dcpi1_div_qr (qp, np, nn, dp, dn, dinv);
Packit 5c3484
    }
Packit 5c3484
  else
Packit 5c3484
    {
Packit 5c3484
      /* We need to allocate separate remainder area, since mpn_mu_div_qr does
Packit 5c3484
	 not handle overlap between the numerator and remainder areas.
Packit 5c3484
	 FIXME: Make it handle such overlap.  */
Packit 5c3484
      mp_ptr rp = TMP_BALLOC_LIMBS (dn);
Packit 5c3484
      mp_size_t itch = mpn_mu_div_qr_itch (nn, dn, 0);
Packit 5c3484
      mp_ptr scratch = TMP_BALLOC_LIMBS (itch);
Packit 5c3484
      mpn_mu_div_qr (qp, rp, np, nn, dp, dn, scratch);
Packit 5c3484
      MPN_COPY (np, rp, dn);
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  TMP_FREE;
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
/* Compute t = a mod m, a is defined by (ap,an), m is defined by (mp,mn), and
Packit 5c3484
   t is defined by (tp,mn).  */
Packit 5c3484
static void
Packit 5c3484
reduce (mp_ptr tp, mp_srcptr ap, mp_size_t an, mp_srcptr mp, mp_size_t mn, gmp_pi1_t *dinv)
Packit 5c3484
{
Packit 5c3484
  mp_ptr rp, scratch;
Packit 5c3484
  TMP_DECL;
Packit 5c3484
  TMP_MARK;
Packit 5c3484
Packit 5c3484
  rp = TMP_ALLOC_LIMBS (an);
Packit 5c3484
  scratch = TMP_ALLOC_LIMBS (an - mn + 1);
Packit 5c3484
  MPN_COPY (rp, ap, an);
Packit 5c3484
  mod (rp, an, mp, mn, dinv, scratch);
Packit 5c3484
  MPN_COPY (tp, rp, mn);
Packit 5c3484
Packit 5c3484
  TMP_FREE;
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
void
Packit 5c3484
mpz_powm_ui (mpz_ptr r, mpz_srcptr b, unsigned long int el, mpz_srcptr m)
Packit 5c3484
{
Packit 5c3484
  if (el < 20)
Packit 5c3484
    {
Packit 5c3484
      mp_ptr xp, tp, mp, bp, scratch;
Packit 5c3484
      mp_size_t xn, tn, mn, bn;
Packit 5c3484
      int m_zero_cnt;
Packit 5c3484
      int c;
Packit 5c3484
      mp_limb_t e, m2;
Packit 5c3484
      gmp_pi1_t dinv;
Packit 5c3484
      TMP_DECL;
Packit 5c3484
Packit 5c3484
      mp = PTR(m);
Packit 5c3484
      mn = ABSIZ(m);
Packit 5c3484
      if (UNLIKELY (mn == 0))
Packit 5c3484
	DIVIDE_BY_ZERO;
Packit 5c3484
Packit 5c3484
      if (el == 0)
Packit 5c3484
	{
Packit 5c3484
	  /* Exponent is zero, result is 1 mod M, i.e., 1 or 0 depending on if
Packit 5c3484
	     M equals 1.  */
Packit 5c3484
	  SIZ(r) = (mn == 1 && mp[0] == 1) ? 0 : 1;
Packit 5c3484
	  PTR(r)[0] = 1;
Packit 5c3484
	  return;
Packit 5c3484
	}
Packit 5c3484
Packit 5c3484
      TMP_MARK;
Packit 5c3484
Packit 5c3484
      /* Normalize m (i.e. make its most significant bit set) as required by
Packit 5c3484
	 division functions below.  */
Packit 5c3484
      count_leading_zeros (m_zero_cnt, mp[mn - 1]);
Packit 5c3484
      m_zero_cnt -= GMP_NAIL_BITS;
Packit 5c3484
      if (m_zero_cnt != 0)
Packit 5c3484
	{
Packit 5c3484
	  mp_ptr new_mp = TMP_ALLOC_LIMBS (mn);
Packit 5c3484
	  mpn_lshift (new_mp, mp, mn, m_zero_cnt);
Packit 5c3484
	  mp = new_mp;
Packit 5c3484
	}
Packit 5c3484
Packit 5c3484
      m2 = mn == 1 ? 0 : mp[mn - 2];
Packit 5c3484
      invert_pi1 (dinv, mp[mn - 1], m2);
Packit 5c3484
Packit 5c3484
      bn = ABSIZ(b);
Packit 5c3484
      bp = PTR(b);
Packit 5c3484
      if (bn > mn)
Packit 5c3484
	{
Packit 5c3484
	  /* Reduce possibly huge base.  Use a function call to reduce, since we
Packit 5c3484
	     don't want the quotient allocation to live until function return.  */
Packit 5c3484
	  mp_ptr new_bp = TMP_ALLOC_LIMBS (mn);
Packit 5c3484
	  reduce (new_bp, bp, bn, mp, mn, &dinv);
Packit 5c3484
	  bp = new_bp;
Packit 5c3484
	  bn = mn;
Packit 5c3484
	  /* Canonicalize the base, since we are potentially going to multiply with
Packit 5c3484
	     it quite a few times.  */
Packit 5c3484
	  MPN_NORMALIZE (bp, bn);
Packit 5c3484
	}
Packit 5c3484
Packit 5c3484
      if (bn == 0)
Packit 5c3484
	{
Packit 5c3484
	  SIZ(r) = 0;
Packit 5c3484
	  TMP_FREE;
Packit 5c3484
	  return;
Packit 5c3484
	}
Packit 5c3484
Packit 5c3484
      tp = TMP_ALLOC_LIMBS (2 * mn + 1);
Packit 5c3484
      xp = TMP_ALLOC_LIMBS (mn);
Packit 5c3484
      scratch = TMP_ALLOC_LIMBS (mn + 1);
Packit 5c3484
Packit 5c3484
      MPN_COPY (xp, bp, bn);
Packit 5c3484
      xn = bn;
Packit 5c3484
Packit 5c3484
      e = el;
Packit 5c3484
      count_leading_zeros (c, e);
Packit 5c3484
      e = (e << c) << 1;		/* shift the exp bits to the left, lose msb */
Packit 5c3484
      c = GMP_LIMB_BITS - 1 - c;
Packit 5c3484
Packit 5c3484
      if (c == 0)
Packit 5c3484
	{
Packit 5c3484
	  /* If m is already normalized (high bit of high limb set), and b is
Packit 5c3484
	     the same size, but a bigger value, and e==1, then there's no
Packit 5c3484
	     modular reductions done and we can end up with a result out of
Packit 5c3484
	     range at the end. */
Packit 5c3484
	  if (xn == mn && mpn_cmp (xp, mp, mn) >= 0)
Packit 5c3484
	    mpn_sub_n (xp, xp, mp, mn);
Packit 5c3484
	}
Packit 5c3484
      else
Packit 5c3484
	{
Packit 5c3484
	  /* Main loop. */
Packit 5c3484
	  do
Packit 5c3484
	    {
Packit 5c3484
	      mpn_sqr (tp, xp, xn);
Packit 5c3484
	      tn = 2 * xn; tn -= tp[tn - 1] == 0;
Packit 5c3484
	      if (tn < mn)
Packit 5c3484
		{
Packit 5c3484
		  MPN_COPY (xp, tp, tn);
Packit 5c3484
		  xn = tn;
Packit 5c3484
		}
Packit 5c3484
	      else
Packit 5c3484
		{
Packit 5c3484
		  mod (tp, tn, mp, mn, &dinv, scratch);
Packit 5c3484
		  MPN_COPY (xp, tp, mn);
Packit 5c3484
		  xn = mn;
Packit 5c3484
		}
Packit 5c3484
Packit 5c3484
	      if ((mp_limb_signed_t) e < 0)
Packit 5c3484
		{
Packit 5c3484
		  mpn_mul (tp, xp, xn, bp, bn);
Packit 5c3484
		  tn = xn + bn; tn -= tp[tn - 1] == 0;
Packit 5c3484
		  if (tn < mn)
Packit 5c3484
		    {
Packit 5c3484
		      MPN_COPY (xp, tp, tn);
Packit 5c3484
		      xn = tn;
Packit 5c3484
		    }
Packit 5c3484
		  else
Packit 5c3484
		    {
Packit 5c3484
		      mod (tp, tn, mp, mn, &dinv, scratch);
Packit 5c3484
		      MPN_COPY (xp, tp, mn);
Packit 5c3484
		      xn = mn;
Packit 5c3484
		    }
Packit 5c3484
		}
Packit 5c3484
	      e <<= 1;
Packit 5c3484
	      c--;
Packit 5c3484
	    }
Packit 5c3484
	  while (c != 0);
Packit 5c3484
	}
Packit 5c3484
Packit 5c3484
      /* We shifted m left m_zero_cnt steps.  Adjust the result by reducing it
Packit 5c3484
	 with the original M.  */
Packit 5c3484
      if (m_zero_cnt != 0)
Packit 5c3484
	{
Packit 5c3484
	  mp_limb_t cy;
Packit 5c3484
	  cy = mpn_lshift (tp, xp, xn, m_zero_cnt);
Packit 5c3484
	  tp[xn] = cy; xn += cy != 0;
Packit 5c3484
Packit 5c3484
	  if (xn < mn)
Packit 5c3484
	    {
Packit 5c3484
	      MPN_COPY (xp, tp, xn);
Packit 5c3484
	    }
Packit 5c3484
	  else
Packit 5c3484
	    {
Packit 5c3484
	      mod (tp, xn, mp, mn, &dinv, scratch);
Packit 5c3484
	      MPN_COPY (xp, tp, mn);
Packit 5c3484
	      xn = mn;
Packit 5c3484
	    }
Packit 5c3484
	  mpn_rshift (xp, xp, xn, m_zero_cnt);
Packit 5c3484
	}
Packit 5c3484
      MPN_NORMALIZE (xp, xn);
Packit 5c3484
Packit 5c3484
      if ((el & 1) != 0 && SIZ(b) < 0 && xn != 0)
Packit 5c3484
	{
Packit 5c3484
	  mp = PTR(m);			/* want original, unnormalized m */
Packit 5c3484
	  mpn_sub (xp, mp, mn, xp, xn);
Packit 5c3484
	  xn = mn;
Packit 5c3484
	  MPN_NORMALIZE (xp, xn);
Packit 5c3484
	}
Packit 5c3484
      MPZ_REALLOC (r, xn);
Packit 5c3484
      SIZ (r) = xn;
Packit 5c3484
      MPN_COPY (PTR(r), xp, xn);
Packit 5c3484
Packit 5c3484
      TMP_FREE;
Packit 5c3484
    }
Packit 5c3484
  else
Packit 5c3484
    {
Packit 5c3484
      /* For large exponents, fake an mpz_t exponent and deflect to the more
Packit 5c3484
	 sophisticated mpz_powm.  */
Packit 5c3484
      mpz_t e;
Packit 5c3484
      mp_limb_t ep[LIMBS_PER_ULONG];
Packit 5c3484
      MPZ_FAKE_UI (e, ep, el);
Packit 5c3484
      mpz_powm (r, b, e, m);
Packit 5c3484
    }
Packit 5c3484
}