Coverage Report

Created: 2025-03-09 06:52

/src/gmp-6.2.1/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
50.0k
{
61
50.0k
  mp_ptr qp = tp;
62
63
50.0k
  if (dn == 1)
64
2.26k
    {
65
2.26k
      np[0] = mpn_divrem_1 (qp, (mp_size_t) 0, np, nn, dp[0]);
66
2.26k
    }
67
47.8k
  else if (dn == 2)
68
952
    {
69
952
      mpn_div_qr_2n_pi1 (qp, np, np, nn, dp[1], dp[0], dinv->inv32);
70
952
    }
71
46.8k
  else if (BELOW_THRESHOLD (dn, DC_DIV_QR_THRESHOLD) ||
72
46.8k
     BELOW_THRESHOLD (nn - dn, DC_DIV_QR_THRESHOLD))
73
45.4k
    {
74
45.4k
      mpn_sbpi1_div_qr (qp, np, nn, dp, dn, dinv->inv32);
75
45.4k
    }
76
1.36k
  else if (BELOW_THRESHOLD (dn, MUPI_DIV_QR_THRESHOLD) ||   /* fast condition */
77
1.36k
     BELOW_THRESHOLD (nn, 2 * MU_DIV_QR_THRESHOLD) || /* fast condition */
78
1.36k
     (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
1.36k
    {
81
1.36k
      mpn_dcpi1_div_qr (qp, np, nn, dp, dn, dinv);
82
1.36k
    }
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
50.0k
}
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
27
{
109
27
  mp_ptr rp, scratch;
110
27
  TMP_DECL;
111
27
  TMP_MARK;
112
113
27
  TMP_ALLOC_LIMBS_2 (rp, an, scratch, an - mn + 1);
114
27
  MPN_COPY (rp, ap, an);
115
27
  mod (rp, an, mp, mn, dinv, scratch);
116
27
  MPN_COPY (tp, rp, mn);
117
118
27
  TMP_FREE;
119
27
}
120
121
void
122
mpz_powm_ui (mpz_ptr r, mpz_srcptr b, unsigned long int el, mpz_srcptr m)
123
26.0k
{
124
26.0k
  if (el < 20)
125
26.0k
    {
126
26.0k
      mp_ptr xp, tp, mp, bp, scratch;
127
26.0k
      mp_size_t xn, tn, mn, bn;
128
26.0k
      int m_zero_cnt;
129
26.0k
      int c;
130
26.0k
      mp_limb_t e, m2;
131
26.0k
      gmp_pi1_t dinv;
132
26.0k
      TMP_DECL;
133
134
26.0k
      mp = PTR(m);
135
26.0k
      mn = ABSIZ(m);
136
26.0k
      if (UNLIKELY (mn == 0))
137
0
  DIVIDE_BY_ZERO;
138
139
26.0k
      if (el <= 1)
140
48
  {
141
48
    if (el == 1)
142
21
      {
143
21
        mpz_mod (r, b, m);
144
21
        return;
145
21
      }
146
    /* Exponent is zero, result is 1 mod M, i.e., 1 or 0 depending on if
147
       M equals 1.  */
148
27
    SIZ(r) = mn != 1 || mp[0] != 1;
149
27
    MPZ_NEWALLOC (r, 1)[0] = 1;
150
27
    return;
151
48
  }
152
153
25.9k
      TMP_MARK;
154
155
      /* Normalize m (i.e. make its most significant bit set) as required by
156
   division functions below.  */
157
25.9k
      count_leading_zeros (m_zero_cnt, mp[mn - 1]);
158
25.9k
      m_zero_cnt -= GMP_NAIL_BITS;
159
25.9k
      if (m_zero_cnt != 0)
160
24.0k
  {
161
24.0k
    mp_ptr new_mp = TMP_ALLOC_LIMBS (mn);
162
24.0k
    mpn_lshift (new_mp, mp, mn, m_zero_cnt);
163
24.0k
    mp = new_mp;
164
24.0k
  }
165
166
25.9k
      m2 = mn == 1 ? 0 : mp[mn - 2];
167
25.9k
      invert_pi1 (dinv, mp[mn - 1], m2);
168
169
25.9k
      bn = ABSIZ(b);
170
25.9k
      bp = PTR(b);
171
25.9k
      if (bn > mn)
172
27
  {
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
27
    mp_ptr new_bp = TMP_ALLOC_LIMBS (mn);
176
27
    reduce (new_bp, bp, bn, mp, mn, &dinv);
177
27
    bp = new_bp;
178
27
    bn = mn;
179
    /* Canonicalize the base, since we are potentially going to multiply with
180
       it quite a few times.  */
181
27
    MPN_NORMALIZE (bp, bn);
182
27
  }
183
184
25.9k
      if (bn == 0)
185
6
  {
186
6
    SIZ(r) = 0;
187
6
    TMP_FREE;
188
6
    return;
189
6
  }
190
191
25.9k
      TMP_ALLOC_LIMBS_3 (xp, mn, scratch, mn + 1, tp, 2 * mn + 1);
192
193
25.9k
      MPN_COPY (xp, bp, bn);
194
25.9k
      xn = bn;
195
196
25.9k
      e = el;
197
25.9k
      count_leading_zeros (c, e);
198
25.9k
      e = (e << c) << 1;    /* shift the exp bits to the left, lose msb */
199
25.9k
      c = GMP_LIMB_BITS - 1 - c;
200
201
25.9k
      ASSERT (c != 0); /* el > 1 */
202
25.9k
  {
203
    /* Main loop. */
204
25.9k
    do
205
26.0k
      {
206
26.0k
        mpn_sqr (tp, xp, xn);
207
26.0k
        tn = 2 * xn; tn -= tp[tn - 1] == 0;
208
26.0k
        if (tn < mn)
209
16
    {
210
16
      MPN_COPY (xp, tp, tn);
211
16
      xn = tn;
212
16
    }
213
25.9k
        else
214
25.9k
    {
215
25.9k
      mod (tp, tn, mp, mn, &dinv, scratch);
216
25.9k
      MPN_COPY (xp, tp, mn);
217
25.9k
      xn = mn;
218
25.9k
    }
219
220
26.0k
        if ((mp_limb_signed_t) e < 0)
221
44
    {
222
44
      mpn_mul (tp, xp, xn, bp, bn);
223
44
      tn = xn + bn; tn -= tp[tn - 1] == 0;
224
44
      if (tn < mn)
225
1
        {
226
1
          MPN_COPY (xp, tp, tn);
227
1
          xn = tn;
228
1
        }
229
43
      else
230
43
        {
231
43
          mod (tp, tn, mp, mn, &dinv, scratch);
232
43
          MPN_COPY (xp, tp, mn);
233
43
          xn = mn;
234
43
        }
235
44
    }
236
26.0k
        e <<= 1;
237
26.0k
        c--;
238
26.0k
      }
239
26.0k
    while (c != 0);
240
25.9k
  }
241
242
      /* We shifted m left m_zero_cnt steps.  Adjust the result by reducing it
243
   with the original M.  */
244
25.9k
      if (m_zero_cnt != 0)
245
24.0k
  {
246
24.0k
    mp_limb_t cy;
247
24.0k
    cy = mpn_lshift (tp, xp, xn, m_zero_cnt);
248
24.0k
    tp[xn] = cy; xn += cy != 0;
249
250
24.0k
    if (xn >= mn)
251
24.0k
      {
252
24.0k
        mod (tp, xn, mp, mn, &dinv, scratch);
253
24.0k
        xn = mn;
254
24.0k
      }
255
24.0k
    mpn_rshift (xp, tp, xn, m_zero_cnt);
256
24.0k
  }
257
25.9k
      MPN_NORMALIZE (xp, xn);
258
259
25.9k
      if ((el & 1) != 0 && SIZ(b) < 0 && xn != 0)
260
25
  {
261
25
    mp = PTR(m);      /* want original, unnormalized m */
262
25
    mpn_sub (xp, mp, mn, xp, xn);
263
25
    xn = mn;
264
25
    MPN_NORMALIZE (xp, xn);
265
25
  }
266
25.9k
      MPZ_NEWALLOC (r, xn);
267
25.9k
      SIZ (r) = xn;
268
25.9k
      MPN_COPY (PTR(r), xp, xn);
269
270
25.9k
      TMP_FREE;
271
25.9k
    }
272
66
  else
273
66
    {
274
      /* For large exponents, fake an mpz_t exponent and deflect to the more
275
   sophisticated mpz_powm.  */
276
66
      mpz_t e;
277
66
      mp_limb_t ep[LIMBS_PER_ULONG];
278
66
      MPZ_FAKE_UI (e, ep, el);
279
66
      mpz_powm (r, b, e, m);
280
66
    }
281
26.0k
}