This is an automated email from the git hooks/post-receive script.

ppm-guest pushed a commit to annotated tag v0.38
in repository libmath-prime-util-perl.

commit 25dc64a8ab401675d451ccb747fc1abf03530520
Author: Dana Jacobsen <d...@acm.org>
Date:   Thu Jan 30 14:29:46 2014 -0800

    is_power updates
---
 Changes                     |   7 +++
 TODO                        |   6 --
 XS.xs                       |   2 +-
 aks.c                       |   2 +-
 lib/Math/Prime/Util/PP.pm   |  15 +++--
 lib/Math/Prime/Util/PPFE.pm |   7 +++
 t/19-moebius.t              |  27 ++++++++-
 t/81-bignum.t               |   4 ++
 util.c                      | 140 +++++++++++++++++++++-----------------------
 util.h                      |   3 +-
 10 files changed, 126 insertions(+), 87 deletions(-)

diff --git a/Changes b/Changes
index f3bbbc7..fedc152 100644
--- a/Changes
+++ b/Changes
@@ -1,5 +1,12 @@
 Revision history for Perl module Math::Prime::Util
 
+0.38  2014-02
+
+    [ADDED]
+
+    - is_power         Returns max k if n=p^k.  See Pari 2.4.x.
+
+
 0.37  2014-01-26
 
     [FUNCTIONALITY AND PERFORMANCE]
diff --git a/TODO b/TODO
index 21f8915..904b6ca 100644
--- a/TODO
+++ b/TODO
@@ -74,9 +74,3 @@
 - Ensure a fast path for Math::GMP from MPU -> MPU:GMP -> GMP, and back.
 
 - znlog better implementation
-
-- consider following Pari and doing is_power($n) / is_power($n, $k) instead
-  of the three:  is_perfect_power, is_perfect_square, is_perfect_cube.
-  Document power.  Tests for power.
-  From Sage:  
ispower(3089265681159475043336839581081873360674602365963130114355701114591322241990483812812582393906477998611814245513881)
 should return 14.
-  Pari's ispower had some problems in 2.4
diff --git a/XS.xs b/XS.xs
index 666e28a..1523068 100644
--- a/XS.xs
+++ b/XS.xs
@@ -562,7 +562,7 @@ is_prime(IN SV* svn, ...)
           case 5:  ret = _XS_is_lucas_pseudoprime(n, 2); break;
           case 6:  ret = _XS_is_frobenius_underwood_pseudoprime(n); break;
           case 7:  ret = _XS_is_aks_prime(n); break;
-          case 8:  ret = (a == 0) ? is_power(n) : !(is_power(n) % a);  break;
+          case 8:  ret = is_power(n, a); break;
           case 9:  ret = _XS_is_pseudoprime(n, (items == 1) ? 2 : a); break;
           case 10:
           default: ret = _XS_is_almost_extra_strong_lucas_pseudoprime
diff --git a/aks.c b/aks.c
index 85f40c7..d5bf5e9 100644
--- a/aks.c
+++ b/aks.c
@@ -194,7 +194,7 @@ int _XS_is_aks_prime(UV n)
   if (n == 2)
     return 1;
 
-  if (is_perfect_power(n))
+  if (is_power(n, 0))
     return 0;
 
   sqrtn = isqrt(n);
diff --git a/lib/Math/Prime/Util/PP.pm b/lib/Math/Prime/Util/PP.pm
index c29351f..27576e2 100644
--- a/lib/Math/Prime/Util/PP.pm
+++ b/lib/Math/Prime/Util/PP.pm
@@ -1427,16 +1427,21 @@ sub _gcd_ui {
   $x;
 }
 
-sub is_perfect_power {
-  my $n = shift;
+sub is_power {
+  my ($n, $a) = @_;
   return 0 if $n <= 3 || $n != int($n);
-  return 1 if ($n & ($n-1)) == 0;                       # Power of 2
+  return !(is_power($n) % $a) if defined $a && $a != 0;
+  #return 2 if _is_perfect_square($n);
   $n = Math::BigInt->new("$n") unless ref($n) eq 'Math::BigInt';
   # Perl 5.6.2 chokes on this, so do it via as_bin
   # my $log2n = 0; { my $num = $n; $log2n++ while $num >>= 1; }
   my $log2n = length($n->as_bin) - 2;
   for (my $e = 2; $e <= $log2n; $e = next_prime($e)) {
-    return 1 if $n->copy()->broot($e)->bpow($e) == $n;
+    my $root = $n->copy()->broot($e);
+    if ($root->copy->bpow($e) == $n) {
+      my $next = is_power($root);
+      return ($next == 0) ? $e : $e * $next;
+    }
   }
   0;
 }
@@ -2097,7 +2102,7 @@ sub _test_anr {
 
 sub is_aks_prime {
   my $n = shift;
-  return 0 if $n < 2 || is_perfect_power($n);
+  return 0 if $n < 2 || is_power($n);
 
   my($log2n, $limit);
   if ($n > 2**48) {
diff --git a/lib/Math/Prime/Util/PPFE.pm b/lib/Math/Prime/Util/PPFE.pm
index 5787af5..f33f78c 100644
--- a/lib/Math/Prime/Util/PPFE.pm
+++ b/lib/Math/Prime/Util/PPFE.pm
@@ -314,6 +314,13 @@ sub chebyshev_psi {
   return Math::Prime::Util::PP::chebyshev_psi($n);
 }
 
+sub is_power {
+  my($x, $a) = @_;
+  _validate_positive_integer($x);
+  _validate_positive_integer($a) if defined $a;
+  return Math::Prime::Util::PP::is_power($x, $a);
+}
+
 #############################################################################
 
 sub forprimes (&$;$) {    ## no critic qw(ProhibitSubroutinePrototypes)
diff --git a/t/19-moebius.t b/t/19-moebius.t
index aee8695..eaf8975 100644
--- a/t/19-moebius.t
+++ b/t/19-moebius.t
@@ -6,7 +6,7 @@ use Test::More;
 use Math::Prime::Util
    qw/moebius mertens euler_phi jordan_totient divisor_sum exp_mangoldt
       chebyshev_theta chebyshev_psi carmichael_lambda znorder liouville
-      znprimroot znlog kronecker legendre_phi gcd lcm
+      znprimroot znlog kronecker legendre_phi gcd lcm is_power
      /;
 
 my $extra = defined $ENV{EXTENDED_TESTING} && $ENV{EXTENDED_TESTING};
@@ -339,6 +339,21 @@ if ($usexs) {
   push @znlogs, [ [5678,5,10007], 8620];  # 5678 = 5^8620 mod 10007
 }
 
+my %powers = (
+  0 => [-2, -1, 0, 1, 2, 3, 5, 6, 7, 10, 11, 12, 13, 14, 15, 17, 18, 19],
+  2 => [4, 9, 25, 36, 49],
+  3 => [8, 27, 125, 343, 17576],
+  4 => [16, 38416],
+  9 => [19683, 1000000000],
+);
+if ($use64) {
+  push @{$powers{0}}, 9908918038843197151;
+  push @{$powers{2}}, 18446743927680663841;
+  push @{$powers{3}}, 2250923753991375;
+  push @{$powers{4}}, 1150530828529256001;
+  push @{$powers{9}}, 118587876497;
+}
+
 # These are slow with XS, and *really* slow with PP.
 if (!$usexs) {
   %big_mertens = map { $_ => $big_mertens{$_} }
@@ -371,6 +386,7 @@ plan tests => 0 + 1
                 + scalar(@mult_orders)
                 + scalar(@znlogs)
                 + scalar(@legendre_sums)
+                + scalar(keys %powers)
                 + scalar(keys %primroots) + 2
                 + scalar(keys %jordan_totients)
                 + 2  # Dedekind psi calculated two ways
@@ -566,6 +582,15 @@ foreach my $r (@legendre_sums) {
   is( legendre_phi($x, $a), $exp, "legendre_phi($x,$a) = $exp" );
 }
 
+###### is_power
+while (my($e, $vals) = each (%powers)) {
+  my @fail;
+  foreach my $val (@$vals) {
+    push @fail, $val unless is_power($val) == $e;
+  }
+  ok( @fail == 0, "is_power returns $e for " . join(",",@fail) );
+}
+
 sub cmp_closeto {
   my $got = shift;
   my $expect = shift;
diff --git a/t/81-bignum.t b/t/81-bignum.t
index 39ae952..0684139 100644
--- a/t/81-bignum.t
+++ b/t/81-bignum.t
@@ -80,6 +80,7 @@ plan tests =>  0
              + 2   # liouville
              + 3   # gcd
              + 3   # lcm
+             + 1   # ispower
              + 15  # random primes
              + 2   # miller-rabin random
              + 1;
@@ -112,6 +113,7 @@ use Math::Prime::Util qw/
   liouville
   gcd
   lcm
+  is_power
   pn_primorial
   ExponentialIntegral
   LogarithmicIntegral
@@ -283,6 +285,8 @@ is( lcm(9999999998987,10000000001011), 
99999999999979999998975857, "lcm(p1,p2)"
 is( lcm(892478777297173184633,892478777297173184633), 892478777297173184633, 
"lcm(p1,p1)" );
 is( lcm(23498324,32497832409432,328732487324,328973248732,3487234897324), 
1124956497899814324967019145509298020838481660295598696, "lcm(a,b,c,d,e)" );
 
+is( 
is_power(3089265681159475043336839581081873360674602365963130114355701114591322241990483812812582393906477998611814245513881),
 14, "ispower(150607571^14) == 14" );
+
 ###############################################################################
 
 my $randprime;
diff --git a/util.c b/util.c
index 9ad1a76..c681dc6 100644
--- a/util.c
+++ b/util.c
@@ -978,85 +978,81 @@ IV mertens(UV n) {
   return sum;
 }
 
-int is_perfect_cube(UV n)
-{
-  UV r = icbrt(n);
-  return (r*r*r == n);
-}
-
-/* All 32-bit perfect powers, and all for k=17,19,23,31,37 */
-static const UV perfect_powers[] =
- {243,2187,3125,7776,16807,78125,100000,161051,177147,248832,279936,371293,
-  537824,759375,823543,1419857,1594323,1889568,2476099,3200000,4084101,5153632,
-  6436343,7962624,10000000,11881376,17210368,19487171,20511149,24300000,
-  28629151,33554432,35831808,39135393,45435424,48828125,52521875,62748517,
-  69343957,79235168,90224199,102400000,105413504,115856201,129140163,130691232,
-  147008443,164916224,170859375,184528125,205962976,229345007,254803968,
-  312500000,345025251,362797056,380204032,410338673,418195493,459165024,
-  503284375,550731776,601692057,612220032,656356768,714924299,777600000,
-  844596301,893871739,916132832,992436543,1160290625,1162261467,1220703125,
-  1252332576,1280000000,1350125107,1453933568,1564031349,1680700000,1801088541,
-  1804229351,1934917632,1977326743,2073071593,
-  UVCONST(2219006624), UVCONST(2373046875), UVCONST(2494357888),
-  UVCONST(2535525376), UVCONST(2706784157), UVCONST(2887174368),
-  UVCONST(3077056399), UVCONST(3276800000), UVCONST(3404825447),
-  UVCONST(3707398432), UVCONST(3939040643), UVCONST(4182119424)
-#if BITS_PER_WORD == 64
- ,UVCONST(         94143178827), UVCONST(       762939453125),
-  UVCONST(      16926659444736), UVCONST(     19073486328125),
-  UVCONST(      68630377364883), UVCONST(    232630513987207),
-  UVCONST(     609359740010496), UVCONST(    617673396283947),
-  UVCONST(   11398895185373143), UVCONST(  11920928955078125),
-  UVCONST(  100000000000000000), UVCONST( 450283905890997363),
-  UVCONST(  505447028499293771), UVCONST( 789730223053602816),
-  UVCONST( 2218611106740436992), UVCONST(8650415919381337933),
-  UVCONST(10000000000000000000)
-#endif
-};
-#define NPOWERS (sizeof(perfect_powers)/sizeof(perfect_powers[0]))
-
-/* TODO: This has to be redone to properly return the highest power */
-int is_power(UV n) {
-  int next;
-  if ((n <= 3) || (n == UV_MAX)) return 0;
+/* There are at least 4 ways to do this, plus hybrids.
+ * 1) use a table.  Great for 32-bit, too big for 64-bit.
+ * 2) Use pow() to check.  Relatively slow and FP is always dangerous.
+ * 3) factor or trial factor.  Slow for 64-bit.
+ * 4) Dietzfelbinger algorithm 2.3.5.  Quite slow.
+ * This currently uses a hybrid of 1 and 2.
+ */
+int powerof(UV n) {
+  int ib;
+  const int iblast = (n > UVCONST(4294967295)) ? 6 : 4;
+  if ((n <= 3) || (n == UV_MAX)) return 1;
   if ((n & (n-1)) == 0)          return ctz(n);  /* powers of 2    */
-  if (is_perfect_square(n)) {
-    next = is_power(isqrt(n));
-    return (next == 0) ? 2 : 2*next;
+  if (is_perfect_square(n))      return 2 * powerof(isqrt(n));
+  { UV cb = icbrt(n);  if (cb*cb*cb==n) return 3 * powerof(cb); }
+  for (ib = 3; ib <= iblast; ib++) { /* prime exponents from 5 to 7-or-13 */
+    UV k, pk, root, b = primes_small[ib];
+    root = (UV) ( pow(n, 1.0 / b ) + 0.01 );
+    pk = root * root * root * root * root;
+    for (k = 5; k < b; k++)
+      pk *= root;
+    if (n == pk) return b * powerof(root);
   }
-  if (is_perfect_cube(n)) {
-    next = is_power(icbrt(n));
-    return (next == 0) ? 3 : 3*next;
-  }
-  {
-    UV lo = 0;
-    UV hi = NPOWERS-1;
-    while (lo < hi) {
-      UV mid = lo + (hi-lo)/2;
-      if (perfect_powers[mid] < n) lo = mid+1;
-      else                         hi = mid;
+  if (n > 177146) {
+    switch (n) { /* Check for powers of 11, 13, 17, 19 within 32 bits */
+      case 177147: case 48828125: case 362797056: case 1977326743: return 11;
+      case 1594323: case 1220703125: return 13;
+      case 129140163: return 17;
+      case 1162261467: return 19;
+      default:  break;
     }
-    if (n <= UVCONST(4294967295) || perfect_powers[lo] == n)
-      return (perfect_powers[lo] == n);
-  }
 #if BITS_PER_WORD == 64
-  {  /* n > 2**32.  If n = p^k, then p in (3 .. 7131) and k in (5,7,11,13) */
-    int ib;
-    for (ib = 3; ib <= 6; ib++) { /* prime exponents from 5 to 13 */
-      UV k, b = primes_small[ib];
-      UV root = (UV) ( powl(n, 1.0L / b ) + 0.01 );
-      UV pk = root * root * root * root * root;
-      for (k = 5; k < b; k++)
-        pk *= root;
-      if (n == pk) {
-        next = is_power(root);
-        return (next == 0) ? b : b*next;
-      }
+    if (n > UVCONST(4294967295)) {
+    switch (n) {
+      case UVCONST(762939453125):
+      case UVCONST(16926659444736):
+      case UVCONST(232630513987207):
+      case UVCONST(100000000000000000):
+      case UVCONST(505447028499293771):
+      case UVCONST(2218611106740436992):
+      case UVCONST(8650415919381337933):  return 17;
+      case UVCONST(19073486328125):
+      case UVCONST(609359740010496):
+      case UVCONST(11398895185373143):
+      case UVCONST(10000000000000000000): return 19;
+      case UVCONST(94143178827):
+      case UVCONST(11920928955078125):
+      case UVCONST(789730223053602816):   return 23;
+      case UVCONST(68630377364883):       return 29;
+      case UVCONST(617673396283947):      return 31;
+      case UVCONST(450283905890997363):   return 37;
+      default:  break;
+    }
     }
-  }
 #endif
-  return 0;
+  }
+  return 1;
 }
+int is_power(UV n, UV a)
+{
+  int ret;
+  if (a > 0) {
+    if (a == 1 || n <= 1) return 1;
+    if ((a % 2) == 0)
+      return is_perfect_square(n) ? is_power(isqrt(n),a>>1) : 0;
+    if ((a % 3) == 0)
+      { UV cb = icbrt(n); return (cb*cb*cb == n) ? is_power(cb, a/3) : 0; }
+    if ((a % 5) == 0)
+      { UV r5 = (UV)(pow(n,0.2) + 0.1);
+        return (r5*r5*r5*r5*r5 == n) ? is_power(r5, a/5) : 0; }
+  }
+  ret = powerof(n);
+  if (a == 0 && ret == 1) ret = 0;
+  return (a == 0) ? ret : !(ret % a);
+}
+
 
 /* How many times does 2 divide n? */
 #define padic2(n)  ctz(n)
diff --git a/util.h b/util.h
index 1243fa0..dd8684e 100644
--- a/util.h
+++ b/util.h
@@ -21,7 +21,8 @@ extern UV  prime_count_lower(UV x);
 extern UV  prime_count_upper(UV x);
 extern UV  prime_count_approx(UV x);
 
-extern int is_power(UV n);
+extern int powerof(UV n);
+extern int is_power(UV n, UV a);
 
 extern signed char* _moebius_range(UV low, UV high);
 extern UV*    _totient_range(UV low, UV high);

-- 
Alioth's /usr/local/bin/git-commit-notice on 
/srv/git.debian.org/git/pkg-perl/packages/libmath-prime-util-perl.git

_______________________________________________
Pkg-perl-cvs-commits mailing list
Pkg-perl-cvs-commits@lists.alioth.debian.org
http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-perl-cvs-commits

Reply via email to