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