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  }