[libmath-prime-util-perl] 08/54: is_power updates
Partha P. Mukherjee
ppm-guest at moszumanska.debian.org
Thu May 21 18:52:07 UTC 2015
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 <dana at 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(",", at 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
More information about the Pkg-perl-cvs-commits
mailing list