Coverage Report

Created: 2025-12-31 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gmp/mpz/powm.c
Line
Count
Source
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,
6
2011, 2012, 2015, 2019 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
/* TODO
40
41
 * Improve handling of buffers.  It is pretty ugly now.
42
43
 * For even moduli, we compute a binvert of its odd part both here and in
44
   mpn_powm.  How can we avoid this recomputation?
45
*/
46
47
/*
48
  b ^ e mod m   res
49
  0   0     0    ?
50
  0   e     0    ?
51
  0   0     m    ?
52
  0   e     m    0
53
  b   0     0    ?
54
  b   e     0    ?
55
  b   0     m    1 mod m
56
  b   e     m    b^e mod m
57
*/
58
59
#define HANDLE_NEGATIVE_EXPONENT 1
60
61
void
62
mpz_powm (mpz_ptr r, mpz_srcptr b, mpz_srcptr e, mpz_srcptr m)
63
52.5k
{
64
52.5k
  mp_size_t n, nodd, ncnt;
65
52.5k
  int cnt;
66
52.5k
  mp_ptr rp, tp;
67
52.5k
  mp_srcptr bp, ep, mp;
68
52.5k
  mp_size_t rn, bn, es, en, itch;
69
52.5k
  mpz_t new_b;      /* note: value lives long via 'b' */
70
52.5k
  TMP_DECL;
71
72
52.5k
  n = ABSIZ(m);
73
52.5k
  if (UNLIKELY (n == 0))
74
0
    DIVIDE_BY_ZERO;
75
76
52.5k
  mp = PTR(m);
77
78
52.5k
  TMP_MARK;
79
80
52.5k
  es = SIZ(e);
81
52.5k
  if (UNLIKELY (es <= 0))
82
58
    {
83
58
      if (es == 0)
84
58
  {
85
    /* b^0 mod m,  b is anything and m is non-zero.
86
       Result is 1 mod m, i.e., 1 or 0 depending on if m = 1.  */
87
58
    SIZ(r) = n != 1 || mp[0] != 1;
88
58
    MPZ_NEWALLOC (r, 1)[0] = 1;
89
58
    TMP_FREE; /* we haven't really allocated anything here */
90
58
    return;
91
58
  }
92
0
#if HANDLE_NEGATIVE_EXPONENT
93
0
      MPZ_TMP_INIT (new_b, n + 1);
94
95
0
      if (UNLIKELY (! mpz_invert (new_b, b, m)))
96
0
  DIVIDE_BY_ZERO;
97
0
      b = new_b;
98
0
      es = -es;
99
#else
100
      DIVIDE_BY_ZERO;
101
#endif
102
0
    }
103
52.5k
  en = es;
104
105
52.5k
  bn = ABSIZ(b);
106
107
52.5k
  if (UNLIKELY (bn == 0))
108
3
    {
109
3
      SIZ(r) = 0;
110
3
      TMP_FREE;
111
3
      return;
112
3
    }
113
114
52.5k
  ep = PTR(e);
115
116
  /* Handle (b^1 mod m) early, since mpn_pow* do not handle that case.  */
117
52.5k
  if (UNLIKELY (en == 1 && ep[0] == 1))
118
196
    {
119
196
      rp = TMP_ALLOC_LIMBS (n);
120
196
      bp = PTR(b);
121
196
      if (bn >= n)
122
154
  {
123
154
    mp_ptr qp = TMP_ALLOC_LIMBS (bn - n + 1);
124
154
    mpn_tdiv_qr (qp, rp, 0L, bp, bn, mp, n);
125
154
    rn = n;
126
154
    MPN_NORMALIZE (rp, rn);
127
128
154
    if (rn != 0 && SIZ(b) < 0)
129
0
      {
130
0
        mpn_sub (rp, mp, n, rp, rn);
131
0
        rn = n;
132
0
        MPN_NORMALIZE_NOT_ZERO (rp, rn);
133
0
      }
134
154
  }
135
42
      else
136
42
  {
137
42
    if (SIZ(b) < 0)
138
0
      {
139
0
        mpn_sub (rp, mp, n, bp, bn);
140
0
        rn = n;
141
0
        MPN_NORMALIZE_NOT_ZERO (rp, rn);
142
0
      }
143
42
    else
144
42
      {
145
42
        MPN_COPY (rp, bp, bn);
146
42
        rn = bn;
147
42
      }
148
42
  }
149
196
      goto ret;
150
196
    }
151
152
  /* Remove low zero limbs from M.  This loop will terminate for correctly
153
     represented mpz numbers.  */
154
52.3k
  ncnt = 0;
155
52.3k
  while (UNLIKELY (mp[0] == 0))
156
185k
    {
157
185k
      mp++;
158
185k
      ncnt++;
159
185k
    }
160
52.3k
  nodd = n - ncnt;
161
52.3k
  cnt = 0;
162
52.3k
  if (mp[0] % 2 == 0)
163
10.4k
    {
164
10.4k
      mp_ptr newmp = TMP_ALLOC_LIMBS (nodd);
165
10.4k
      count_trailing_zeros (cnt, mp[0]);
166
10.4k
      mpn_rshift (newmp, mp, nodd, cnt);
167
10.4k
      nodd -= newmp[nodd - 1] == 0;
168
10.4k
      mp = newmp;
169
10.4k
      ncnt++;
170
10.4k
    }
171
172
52.3k
  if (ncnt != 0)
173
11.0k
    {
174
      /* We will call both mpn_powm and mpn_powlo.  */
175
      /* rp needs n, mpn_powlo needs 4n, the 2 mpn_binvert might need more */
176
11.0k
      mp_size_t n_largest_binvert = MAX (ncnt, nodd);
177
11.0k
      mp_size_t itch_binvert = mpn_binvert_itch (n_largest_binvert);
178
11.0k
      itch = 3 * n + MAX (itch_binvert, 2 * n);
179
11.0k
    }
180
41.2k
  else
181
41.2k
    {
182
      /* We will call just mpn_powm.  */
183
41.2k
      mp_size_t itch_binvert = mpn_binvert_itch (nodd);
184
41.2k
      itch = n + MAX (itch_binvert, 2 * n);
185
41.2k
    }
186
52.3k
  tp = TMP_ALLOC_LIMBS (itch);
187
188
52.3k
  rp = tp;  tp += n;
189
190
52.3k
  bp = PTR(b);
191
52.3k
  mpn_powm (rp, bp, bn, ep, en, mp, nodd, tp);
192
193
52.3k
  rn = n;
194
195
52.3k
  if (ncnt != 0)
196
11.0k
    {
197
11.0k
      mp_ptr r2, xp, yp, odd_inv_2exp;
198
11.0k
      unsigned long t;
199
11.0k
      int bcnt;
200
201
11.0k
      if (bn < ncnt)
202
5.56k
  {
203
5.56k
    mp_ptr newbp = TMP_ALLOC_LIMBS (ncnt);
204
5.56k
    MPN_COPY (newbp, bp, bn);
205
5.56k
    MPN_ZERO (newbp + bn, ncnt - bn);
206
5.56k
    bp = newbp;
207
5.56k
  }
208
209
11.0k
      r2 = tp;
210
211
11.0k
      if (bp[0] % 2 == 0)
212
7.93k
  {
213
7.93k
    if (en > 1)
214
6.54k
      {
215
6.54k
        MPN_ZERO (r2, ncnt);
216
6.54k
        goto zero;
217
6.54k
      }
218
219
1.39k
    ASSERT (en == 1);
220
1.39k
    t = (ncnt - (cnt != 0)) * GMP_NUMB_BITS + cnt;
221
222
    /* Count number of low zero bits in B, up to 3.  */
223
1.39k
    bcnt = (0x1213 >> ((bp[0] & 7) << 1)) & 0x3;
224
    /* Note that ep[0] * bcnt might overflow, but that just results
225
       in a missed optimization.  */
226
1.39k
    if (ep[0] * bcnt >= t)
227
1.36k
      {
228
1.36k
        MPN_ZERO (r2, ncnt);
229
1.36k
        goto zero;
230
1.36k
      }
231
1.39k
  }
232
233
3.16k
      mpn_powlo (r2, bp, ep, en, ncnt, tp + ncnt);
234
235
11.0k
    zero:
236
11.0k
      if (nodd < ncnt)
237
3.00k
  {
238
3.00k
    mp_ptr newmp = TMP_ALLOC_LIMBS (ncnt);
239
3.00k
    MPN_COPY (newmp, mp, nodd);
240
3.00k
    MPN_ZERO (newmp + nodd, ncnt - nodd);
241
3.00k
    mp = newmp;
242
3.00k
  }
243
244
11.0k
      odd_inv_2exp = tp + n;
245
11.0k
      mpn_binvert (odd_inv_2exp, mp, ncnt, tp + 2 * n);
246
247
11.0k
      mpn_sub (r2, r2, ncnt, rp, nodd > ncnt ? ncnt : nodd);
248
249
11.0k
      xp = tp + 2 * n;
250
11.0k
      mpn_mullo_n (xp, odd_inv_2exp, r2, ncnt);
251
252
11.0k
      if (cnt != 0)
253
10.4k
  xp[ncnt - 1] &= (CNST_LIMB(1) << cnt) - 1;
254
255
11.0k
      yp = tp;
256
11.0k
      if (ncnt > nodd)
257
3.00k
  mpn_mul (yp, xp, ncnt, mp, nodd);
258
8.06k
      else
259
8.06k
  mpn_mul (yp, mp, nodd, xp, ncnt);
260
261
11.0k
      mpn_add (rp, yp, n, rp, nodd);
262
263
11.0k
      ASSERT (nodd + ncnt >= n);
264
11.0k
      ASSERT (nodd + ncnt <= n + 1);
265
11.0k
    }
266
267
52.3k
  MPN_NORMALIZE (rp, rn);
268
269
52.3k
  if ((ep[0] & 1) && SIZ(b) < 0 && rn != 0)
270
0
    {
271
0
      mpn_sub (rp, PTR(m), n, rp, rn);
272
0
      rn = n;
273
0
      MPN_NORMALIZE (rp, rn);
274
0
    }
275
276
52.5k
 ret:
277
52.5k
  MPZ_NEWALLOC (r, rn);
278
52.5k
  SIZ(r) = rn;
279
52.5k
  MPN_COPY (PTR(r), rp, rn);
280
281
52.5k
  TMP_FREE;
282
52.5k
}