Coverage Report

Created: 2025-03-18 06:55

/src/gmp/mpz/powm_ui.c
Line
Count
Source (jump to first uncovered line)
1
/* mpz_powm_ui(res,base,exp,mod) -- Set R to (B^E) mod M.
2
3
   Contributed to the GNU project by Torbjörn Granlund.
4
5
Copyright 1991, 1993, 1994, 1996, 1997, 2000-2002, 2005, 2008, 2009,
6
2011-2013, 2015 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-impl.h"
36
#include "longlong.h"
37
38
39
/* This code is very old, and should be rewritten to current GMP standard.  It
40
   is slower than mpz_powm for large exponents, but also for small exponents
41
   when the mod argument is small.
42
43
   As an intermediate solution, we now deflect to mpz_powm for exponents >= 20.
44
*/
45
46
/*
47
  b ^ e mod m   res
48
  0   0     0    ?
49
  0   e     0    ?
50
  0   0     m    ?
51
  0   e     m    0
52
  b   0     0    ?
53
  b   e     0    ?
54
  b   0     m    1 mod m
55
  b   e     m    b^e mod m
56
*/
57
58
static void
59
mod (mp_ptr np, mp_size_t nn, mp_srcptr dp, mp_size_t dn, gmp_pi1_t *dinv, mp_ptr tp)
60
0
{
61
0
  mp_ptr qp = tp;
62
63
0
  if (dn == 1)
64
0
    {
65
0
      np[0] = mpn_divrem_1 (qp, (mp_size_t) 0, np, nn, dp[0]);
66
0
    }
67
0
  else if (dn == 2)
68
0
    {
69
0
      mpn_div_qr_2n_pi1 (qp, np, np, nn, dp[1], dp[0], dinv->inv32);
70
0
    }
71
0
  else if (BELOW_THRESHOLD (dn, DC_DIV_QR_THRESHOLD) ||
72
0
     BELOW_THRESHOLD (nn - dn, DC_DIV_QR_THRESHOLD))
73
0
    {
74
0
      mpn_sbpi1_div_qr (qp, np, nn, dp, dn, dinv->inv32);
75
0
    }
76
0
  else if (BELOW_THRESHOLD (dn, MUPI_DIV_QR_THRESHOLD) ||   /* fast condition */
77
0
     BELOW_THRESHOLD (nn, 2 * MU_DIV_QR_THRESHOLD) || /* fast condition */
78
0
     (double) (2 * (MU_DIV_QR_THRESHOLD - MUPI_DIV_QR_THRESHOLD)) * dn /* slow... */
79
0
     + (double) MUPI_DIV_QR_THRESHOLD * nn > (double) dn * nn)    /* ...condition */
80
0
    {
81
0
      mpn_dcpi1_div_qr (qp, np, nn, dp, dn, dinv);
82
0
    }
83
0
  else
84
0
    {
85
      /* We need to allocate separate remainder area, since mpn_mu_div_qr does
86
   not handle overlap between the numerator and remainder areas.
87
   FIXME: Make it handle such overlap.  */
88
0
      mp_ptr rp, scratch;
89
0
      mp_size_t itch;
90
0
      TMP_DECL;
91
0
      TMP_MARK;
92
93
0
      itch = mpn_mu_div_qr_itch (nn, dn, 0);
94
0
      rp = TMP_BALLOC_LIMBS (dn);
95
0
      scratch = TMP_BALLOC_LIMBS (itch);
96
97
0
      mpn_mu_div_qr (qp, rp, np, nn, dp, dn, scratch);
98
0
      MPN_COPY (np, rp, dn);
99
100
0
      TMP_FREE;
101
0
    }
102
0
}
103
104
/* Compute t = a mod m, a is defined by (ap,an), m is defined by (mp,mn), and
105
   t is defined by (tp,mn).  */
106
static void
107
reduce (mp_ptr tp, mp_srcptr ap, mp_size_t an, mp_srcptr mp, mp_size_t mn, gmp_pi1_t *dinv)
108
0
{
109
0
  mp_ptr rp, scratch;
110
0
  TMP_DECL;
111
0
  TMP_MARK;
112
113
0
  TMP_ALLOC_LIMBS_2 (rp, an, scratch, an - mn + 1);
114
0
  MPN_COPY (rp, ap, an);
115
0
  mod (rp, an, mp, mn, dinv, scratch);
116
0
  MPN_COPY (tp, rp, mn);
117
118
0
  TMP_FREE;
119
0
}
120
121
void
122
mpz_powm_ui (mpz_ptr r, mpz_srcptr b, unsigned long int el, mpz_srcptr m)
123
0
{
124
0
  if (el < 20)
125
0
    {
126
0
      mp_ptr xp, tp, mp, bp, scratch;
127
0
      mp_size_t xn, tn, mn, bn;
128
0
      int m_zero_cnt;
129
0
      int c;
130
0
      mp_limb_t e, m2;
131
0
      gmp_pi1_t dinv;
132
0
      TMP_DECL;
133
134
0
      mp = PTR(m);
135
0
      mn = ABSIZ(m);
136
0
      if (UNLIKELY (mn == 0))
137
0
  DIVIDE_BY_ZERO;
138
139
0
      if (el <= 1)
140
0
  {
141
0
    if (el == 1)
142
0
      {
143
0
        mpz_mod (r, b, m);
144
0
        return;
145
0
      }
146
    /* Exponent is zero, result is 1 mod M, i.e., 1 or 0 depending on if
147
       M equals 1.  */
148
0
    SIZ(r) = mn != 1 || mp[0] != 1;
149
0
    MPZ_NEWALLOC (r, 1)[0] = 1;
150
0
    return;
151
0
  }
152
153
0
      TMP_MARK;
154
155
      /* Normalize m (i.e. make its most significant bit set) as required by
156
   division functions below.  */
157
0
      count_leading_zeros (m_zero_cnt, mp[mn - 1]);
158
0
      m_zero_cnt -= GMP_NAIL_BITS;
159
0
      if (m_zero_cnt != 0)
160
0
  {
161
0
    mp_ptr new_mp = TMP_ALLOC_LIMBS (mn);
162
0
    mpn_lshift (new_mp, mp, mn, m_zero_cnt);
163
0
    mp = new_mp;
164
0
  }
165
166
0
      m2 = mn == 1 ? 0 : mp[mn - 2];
167
0
      invert_pi1 (dinv, mp[mn - 1], m2);
168
169
0
      bn = ABSIZ(b);
170
0
      bp = PTR(b);
171
0
      if (bn > mn)
172
0
  {
173
    /* Reduce possibly huge base.  Use a function call to reduce, since we
174
       don't want the quotient allocation to live until function return.  */
175
0
    mp_ptr new_bp = TMP_ALLOC_LIMBS (mn);
176
0
    reduce (new_bp, bp, bn, mp, mn, &dinv);
177
0
    bp = new_bp;
178
0
    bn = mn;
179
    /* Canonicalize the base, since we are potentially going to multiply with
180
       it quite a few times.  */
181
0
    MPN_NORMALIZE (bp, bn);
182
0
  }
183
184
0
      if (bn == 0)
185
0
  {
186
0
    SIZ(r) = 0;
187
0
    TMP_FREE;
188
0
    return;
189
0
  }
190
191
0
      TMP_ALLOC_LIMBS_3 (xp, mn, scratch, mn + 1, tp, 2 * mn + 1);
192
193
0
      MPN_COPY (xp, bp, bn);
194
0
      xn = bn;
195
196
0
      e = el;
197
0
      count_leading_zeros (c, e);
198
0
      e = (e << c) << 1;    /* shift the exp bits to the left, lose msb */
199
0
      c = GMP_LIMB_BITS - 1 - c;
200
201
0
      ASSERT (c != 0); /* el > 1 */
202
0
  {
203
    /* Main loop. */
204
0
    do
205
0
      {
206
0
        mpn_sqr (tp, xp, xn);
207
0
        tn = 2 * xn; tn -= tp[tn - 1] == 0;
208
0
        if (tn < mn)
209
0
    {
210
0
      MPN_COPY (xp, tp, tn);
211
0
      xn = tn;
212
0
    }
213
0
        else
214
0
    {
215
0
      mod (tp, tn, mp, mn, &dinv, scratch);
216
0
      MPN_COPY (xp, tp, mn);
217
0
      xn = mn;
218
0
    }
219
220
0
        if ((mp_limb_signed_t) e < 0)
221
0
    {
222
0
      mpn_mul (tp, xp, xn, bp, bn);
223
0
      tn = xn + bn; tn -= tp[tn - 1] == 0;
224
0
      if (tn < mn)
225
0
        {
226
0
          MPN_COPY (xp, tp, tn);
227
0
          xn = tn;
228
0
        }
229
0
      else
230
0
        {
231
0
          mod (tp, tn, mp, mn, &dinv, scratch);
232
0
          MPN_COPY (xp, tp, mn);
233
0
          xn = mn;
234
0
        }
235
0
    }
236
0
        e <<= 1;
237
0
        c--;
238
0
      }
239
0
    while (c != 0);
240
0
  }
241
242
      /* We shifted m left m_zero_cnt steps.  Adjust the result by reducing it
243
   with the original M.  */
244
0
      if (m_zero_cnt != 0)
245
0
  {
246
0
    mp_limb_t cy;
247
0
    cy = mpn_lshift (tp, xp, xn, m_zero_cnt);
248
0
    tp[xn] = cy; xn += cy != 0;
249
250
0
    if (xn >= mn)
251
0
      {
252
0
        mod (tp, xn, mp, mn, &dinv, scratch);
253
0
        xn = mn;
254
0
      }
255
0
    mpn_rshift (xp, tp, xn, m_zero_cnt);
256
0
  }
257
0
      MPN_NORMALIZE (xp, xn);
258
259
0
      if ((el & 1) != 0 && SIZ(b) < 0 && xn != 0)
260
0
  {
261
0
    mp = PTR(m);      /* want original, unnormalized m */
262
0
    mpn_sub (xp, mp, mn, xp, xn);
263
0
    xn = mn;
264
0
    MPN_NORMALIZE (xp, xn);
265
0
  }
266
0
      MPZ_NEWALLOC (r, xn);
267
0
      SIZ (r) = xn;
268
0
      MPN_COPY (PTR(r), xp, xn);
269
270
0
      TMP_FREE;
271
0
    }
272
0
  else
273
0
    {
274
      /* For large exponents, fake an mpz_t exponent and deflect to the more
275
   sophisticated mpz_powm.  */
276
0
      mpz_t e;
277
0
      mp_limb_t ep[LIMBS_PER_ULONG];
278
0
      MPZ_FAKE_UI (e, ep, el);
279
0
      mpz_powm (r, b, e, m);
280
0
    }
281
0
}