Coverage Report

Created: 2025-04-11 06:45

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