Coverage Report

Created: 2024-06-28 06:19

/src/gmp-6.2.1/mpn/perfpow.c
Line
Count
Source (jump to first uncovered line)
1
/* mpn_perfect_power_p -- mpn perfect power detection.
2
3
   Contributed to the GNU project by Martin Boij.
4
5
Copyright 2009, 2010, 2012, 2014 Free Software Foundation, Inc.
6
7
This file is part of the GNU MP Library.
8
9
The GNU MP Library is free software; you can redistribute it and/or modify
10
it under the terms of either:
11
12
  * the GNU Lesser General Public License as published by the Free
13
    Software Foundation; either version 3 of the License, or (at your
14
    option) any later version.
15
16
or
17
18
  * the GNU General Public License as published by the Free Software
19
    Foundation; either version 2 of the License, or (at your option) any
20
    later version.
21
22
or both in parallel, as here.
23
24
The GNU MP Library is distributed in the hope that it will be useful, but
25
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
26
or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
27
for more details.
28
29
You should have received copies of the GNU General Public License and the
30
GNU Lesser General Public License along with the GNU MP Library.  If not,
31
see https://www.gnu.org/licenses/.  */
32
33
#include "gmp-impl.h"
34
#include "longlong.h"
35
36
0
#define SMALL 20
37
0
#define MEDIUM 100
38
39
/* Return non-zero if {np,nn} == {xp,xn} ^ k.
40
   Algorithm:
41
       For s = 1, 2, 4, ..., s_max, compute the s least significant limbs of
42
       {xp,xn}^k. Stop if they don't match the s least significant limbs of
43
       {np,nn}.
44
45
   FIXME: Low xn limbs can be expected to always match, if computed as a mod
46
   B^{xn} root. So instead of using mpn_powlo, compute an approximation of the
47
   most significant (normalized) limb of {xp,xn} ^ k (and an error bound), and
48
   compare to {np, nn}. Or use an even cruder approximation based on fix-point
49
   base 2 logarithm.  */
50
static int
51
pow_equals (mp_srcptr np, mp_size_t n,
52
      mp_srcptr xp,mp_size_t xn,
53
      mp_limb_t k, mp_bitcnt_t f,
54
      mp_ptr tp)
55
0
{
56
0
  mp_bitcnt_t y, z;
57
0
  mp_size_t bn;
58
0
  mp_limb_t h, l;
59
60
0
  ASSERT (n > 1 || (n == 1 && np[0] > 1));
61
0
  ASSERT (np[n - 1] > 0);
62
0
  ASSERT (xn > 0);
63
64
0
  if (xn == 1 && xp[0] == 1)
65
0
    return 0;
66
67
0
  z = 1 + (n >> 1);
68
0
  for (bn = 1; bn < z; bn <<= 1)
69
0
    {
70
0
      mpn_powlo (tp, xp, &k, 1, bn, tp + bn);
71
0
      if (mpn_cmp (tp, np, bn) != 0)
72
0
  return 0;
73
0
    }
74
75
  /* Final check. Estimate the size of {xp,xn}^k before computing the power
76
     with full precision.  Optimization: It might pay off to make a more
77
     accurate estimation of the logarithm of {xp,xn}, rather than using the
78
     index of the MSB.  */
79
80
0
  MPN_SIZEINBASE_2EXP(y, xp, xn, 1);
81
0
  y -= 1;  /* msb_index (xp, xn) */
82
83
0
  umul_ppmm (h, l, k, y);
84
0
  h -= l == 0;  --l;  /* two-limb decrement */
85
86
0
  z = f - 1; /* msb_index (np, n) */
87
0
  if (h == 0 && l <= z)
88
0
    {
89
0
      mp_limb_t *tp2;
90
0
      mp_size_t i;
91
0
      int ans;
92
0
      mp_limb_t size;
93
0
      TMP_DECL;
94
95
0
      size = l + k;
96
0
      ASSERT_ALWAYS (size >= k);
97
98
0
      TMP_MARK;
99
0
      y = 2 + size / GMP_LIMB_BITS;
100
0
      tp2 = TMP_ALLOC_LIMBS (y);
101
102
0
      i = mpn_pow_1 (tp, xp, xn, k, tp2);
103
0
      if (i == n && mpn_cmp (tp, np, n) == 0)
104
0
  ans = 1;
105
0
      else
106
0
  ans = 0;
107
0
      TMP_FREE;
108
0
      return ans;
109
0
    }
110
111
0
  return 0;
112
0
}
113
114
115
/* Return non-zero if N = {np,n} is a kth power.
116
   I = {ip,n} = N^(-1) mod B^n.  */
117
static int
118
is_kth_power (mp_ptr rp, mp_srcptr np,
119
        mp_limb_t k, mp_srcptr ip,
120
        mp_size_t n, mp_bitcnt_t f,
121
        mp_ptr tp)
122
0
{
123
0
  mp_bitcnt_t b;
124
0
  mp_size_t rn, xn;
125
126
0
  ASSERT (n > 0);
127
0
  ASSERT ((k & 1) != 0 || k == 2);
128
0
  ASSERT ((np[0] & 1) != 0);
129
130
0
  if (k == 2)
131
0
    {
132
0
      b = (f + 1) >> 1;
133
0
      rn = 1 + b / GMP_LIMB_BITS;
134
0
      if (mpn_bsqrtinv (rp, ip, b, tp) != 0)
135
0
  {
136
0
    rp[rn - 1] &= (CNST_LIMB(1) << (b % GMP_LIMB_BITS)) - 1;
137
0
    xn = rn;
138
0
    MPN_NORMALIZE (rp, xn);
139
0
    if (pow_equals (np, n, rp, xn, k, f, tp) != 0)
140
0
      return 1;
141
142
    /* Check if (2^b - r)^2 == n */
143
0
    mpn_neg (rp, rp, rn);
144
0
    rp[rn - 1] &= (CNST_LIMB(1) << (b % GMP_LIMB_BITS)) - 1;
145
0
    MPN_NORMALIZE (rp, rn);
146
0
    if (pow_equals (np, n, rp, rn, k, f, tp) != 0)
147
0
      return 1;
148
0
  }
149
0
    }
150
0
  else
151
0
    {
152
0
      b = 1 + (f - 1) / k;
153
0
      rn = 1 + (b - 1) / GMP_LIMB_BITS;
154
0
      mpn_brootinv (rp, ip, rn, k, tp);
155
0
      if ((b % GMP_LIMB_BITS) != 0)
156
0
  rp[rn - 1] &= (CNST_LIMB(1) << (b % GMP_LIMB_BITS)) - 1;
157
0
      MPN_NORMALIZE (rp, rn);
158
0
      if (pow_equals (np, n, rp, rn, k, f, tp) != 0)
159
0
  return 1;
160
0
    }
161
0
  MPN_ZERO (rp, rn); /* Untrash rp */
162
0
  return 0;
163
0
}
164
165
static int
166
perfpow (mp_srcptr np, mp_size_t n,
167
   mp_limb_t ub, mp_limb_t g,
168
   mp_bitcnt_t f, int neg)
169
0
{
170
0
  mp_ptr ip, tp, rp;
171
0
  mp_limb_t k;
172
0
  int ans;
173
0
  mp_bitcnt_t b;
174
0
  gmp_primesieve_t ps;
175
0
  TMP_DECL;
176
177
0
  ASSERT (n > 0);
178
0
  ASSERT ((np[0] & 1) != 0);
179
0
  ASSERT (ub > 0);
180
181
0
  TMP_MARK;
182
0
  gmp_init_primesieve (&ps);
183
0
  b = (f + 3) >> 1;
184
185
0
  TMP_ALLOC_LIMBS_3 (ip, n, rp, n, tp, 5 * n);
186
187
0
  MPN_ZERO (rp, n);
188
189
  /* FIXME: It seems the inverse in ninv is needed only to get non-inverted
190
     roots. I.e., is_kth_power computes n^{1/2} as (n^{-1})^{-1/2} and
191
     similarly for nth roots. It should be more efficient to compute n^{1/2} as
192
     n * n^{-1/2}, with a mullo instead of a binvert. And we can do something
193
     similar for kth roots if we switch to an iteration converging to n^{1/k -
194
     1}, and we can then eliminate this binvert call. */
195
0
  mpn_binvert (ip, np, 1 + (b - 1) / GMP_LIMB_BITS, tp);
196
0
  if (b % GMP_LIMB_BITS)
197
0
    ip[(b - 1) / GMP_LIMB_BITS] &= (CNST_LIMB(1) << (b % GMP_LIMB_BITS)) - 1;
198
199
0
  if (neg)
200
0
    gmp_nextprime (&ps);
201
202
0
  ans = 0;
203
0
  if (g > 0)
204
0
    {
205
0
      ub = MIN (ub, g + 1);
206
0
      while ((k = gmp_nextprime (&ps)) < ub)
207
0
  {
208
0
    if ((g % k) == 0)
209
0
      {
210
0
        if (is_kth_power (rp, np, k, ip, n, f, tp) != 0)
211
0
    {
212
0
      ans = 1;
213
0
      goto ret;
214
0
    }
215
0
      }
216
0
  }
217
0
    }
218
0
  else
219
0
    {
220
0
      while ((k = gmp_nextprime (&ps)) < ub)
221
0
  {
222
0
    if (is_kth_power (rp, np, k, ip, n, f, tp) != 0)
223
0
      {
224
0
        ans = 1;
225
0
        goto ret;
226
0
      }
227
0
  }
228
0
    }
229
0
 ret:
230
0
  TMP_FREE;
231
0
  return ans;
232
0
}
233
234
static const unsigned short nrtrial[] = { 100, 500, 1000 };
235
236
/* Table of (log_{p_i} 2) values, where p_i is the (nrtrial[i] + 1)'th prime
237
   number.  */
238
static const double logs[] =
239
  { 0.1099457228193620, 0.0847016403115322, 0.0772048195144415 };
240
241
int
242
mpn_perfect_power_p (mp_srcptr np, mp_size_t n)
243
0
{
244
0
  mp_limb_t *nc, factor, g;
245
0
  mp_limb_t exp, d;
246
0
  mp_bitcnt_t twos, count;
247
0
  int ans, where, neg, trial;
248
0
  TMP_DECL;
249
250
0
  neg = n < 0;
251
0
  if (neg)
252
0
    {
253
0
      n = -n;
254
0
    }
255
256
0
  if (n == 0 || (n == 1 && np[0] == 1)) /* Valgrind doesn't like
257
             (n <= (np[0] == 1)) */
258
0
    return 1;
259
260
0
  TMP_MARK;
261
262
0
  count = 0;
263
264
0
  twos = mpn_scan1 (np, 0);
265
0
  if (twos != 0)
266
0
    {
267
0
      mp_size_t s;
268
0
      if (twos == 1)
269
0
  {
270
0
    return 0;
271
0
  }
272
0
      s = twos / GMP_LIMB_BITS;
273
0
      if (s + 1 == n && POW2_P (np[s]))
274
0
  {
275
0
    return ! (neg && POW2_P (twos));
276
0
  }
277
0
      count = twos % GMP_LIMB_BITS;
278
0
      n -= s;
279
0
      np += s;
280
0
      if (count > 0)
281
0
  {
282
0
    nc = TMP_ALLOC_LIMBS (n);
283
0
    mpn_rshift (nc, np, n, count);
284
0
    n -= (nc[n - 1] == 0);
285
0
    np = nc;
286
0
  }
287
0
    }
288
0
  g = twos;
289
290
0
  trial = (n > SMALL) + (n > MEDIUM);
291
292
0
  where = 0;
293
0
  factor = mpn_trialdiv (np, n, nrtrial[trial], &where);
294
295
0
  if (factor != 0)
296
0
    {
297
0
      if (count == 0) /* We did not allocate nc yet. */
298
0
  {
299
0
    nc = TMP_ALLOC_LIMBS (n);
300
0
  }
301
302
      /* Remove factors found by trialdiv.  Optimization: If remove
303
   define _itch, we can allocate its scratch just once */
304
305
0
      do
306
0
  {
307
0
    binvert_limb (d, factor);
308
309
    /* After the first round we always have nc == np */
310
0
    exp = mpn_remove (nc, &n, np, n, &d, 1, ~(mp_bitcnt_t)0);
311
312
0
    if (g == 0)
313
0
      g = exp;
314
0
    else
315
0
      g = mpn_gcd_1 (&g, 1, exp);
316
317
0
    if (g == 1)
318
0
      {
319
0
        ans = 0;
320
0
        goto ret;
321
0
      }
322
323
0
    if ((n == 1) & (nc[0] == 1))
324
0
      {
325
0
        ans = ! (neg && POW2_P (g));
326
0
        goto ret;
327
0
      }
328
329
0
    np = nc;
330
0
    factor = mpn_trialdiv (np, n, nrtrial[trial], &where);
331
0
  }
332
0
      while (factor != 0);
333
0
    }
334
335
0
  MPN_SIZEINBASE_2EXP(count, np, n, 1);   /* log (np) + 1 */
336
0
  d = (mp_limb_t) (count * logs[trial] + 1e-9) + 1;
337
0
  ans = perfpow (np, n, d, g, count, neg);
338
339
0
 ret:
340
0
  TMP_FREE;
341
0
  return ans;
342
0
}