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