[libmath-prime-util-perl] 21/50: More primes.pl changes, and script test does multiple filters
Partha P. Mukherjee
ppm-guest at moszumanska.debian.org
Thu May 21 18:45:35 UTC 2015
This is an automated email from the git hooks/post-receive script.
ppm-guest pushed a commit to annotated tag v0.13
in repository libmath-prime-util-perl.
commit 050e1858073a16c81d4a3d05ec3ef9e3709909f9
Author: Dana Jacobsen <dana at acm.org>
Date: Fri Oct 19 01:54:48 2012 -0600
More primes.pl changes, and script test does multiple filters
---
bin/primes.pl | 171 ++++++++++++++++++++++++-----------------
examples/test-primes-script.pl | 44 ++++++++---
2 files changed, 135 insertions(+), 80 deletions(-)
diff --git a/bin/primes.pl b/bin/primes.pl
index 80bfb7d..5d7a427 100755
--- a/bin/primes.pl
+++ b/bin/primes.pl
@@ -65,7 +65,6 @@ GetOptions(\%opts,
die_usage() if exists $opts{'help'};
Math::Prime::Util::prime_set_config(gmp=>0) if exists $opts{'nompugmp'};
-
# Get the start and end values. Verify they're positive integers.
die_usage() unless @ARGV == 2;
my ($start, $end) = @ARGV;
@@ -81,13 +80,58 @@ if ( ($start >= ~0 && $start ne ~0) || ($end >= ~0 && $end ne ~0) ) {
$end = Math::BigInt->new($end);
}
+# Calculate the mod 210 pre-test. This helps with the individual filters,
+# but the real benefit is that it convolves the pretests, which can speed
+# up even more.
+my ($min_pass, %mod_pass) = find_mod210_restriction();
+# Find out if they've filtered so much nothing passes (e.g. cousin quad)
+if (scalar keys %mod_pass == 0) {
+ $end = $min_pass if $end > $min_pass;
+}
+
+if ($start > $end) {
+ # Do nothing
+} elsif (exists $opts{'lucas'} ||
+ exists $opts{'fibonacci'} ||
+ exists $opts{'euclid'} ||
+ exists $opts{'mersenne'}
+ ) {
+ my @p = gen_and_filter($start, $end);
+ print join("\n", @p), "\n" if scalar @p > 0;
+} else {
+ my @p;
+ while ($start <= $end) {
+
+ # Adjust segment sizes for some cases
+ $segment_size = 10000 if $start > ~0; # small if doing bigints
+ if (exists $opts{'pillai'}) {
+ $segment_size = ($start < 10000) ? 100 : 1000; # very small for Pillai
+ }
+ if (exists $opts{'palindromic'}) {
+ $segment_size = 10**length($start) - $start - 1; # all n-digit numbers
+ }
+
+ my $seg_start = $start;
+ my $seg_end = $start + $segment_size;
+ $seg_end = $end if $end < $seg_end;
+ $start = $seg_end+1;
+
+ @p = gen_and_filter($seg_start, $seg_end);
+
+ # print this segment
+ print join("\n", @p), "\n" if scalar @p > 0;
+ }
+}
+
+
# Fibonacci numbers
{
- my @fibs = (Math::BigInt->new(0), Math::BigInt->new(1));
+ my @fibs;
sub fib {
my $n = shift;
return $n if $n < 2;
if (!defined $fibs[$n]) {
+ @fibs = (Math::BigInt->new(0), Math::BigInt->new(1)) if scalar @fibs == 0;
my ($nm2, $nm1) = ($fibs[-2],$fibs[-1]);
for (scalar @fibs .. $n) {
($nm2, $nm1) = ($nm1, $nm2 + $nm1);
@@ -265,44 +309,46 @@ sub gen_and_filter {
}
}
+ if (exists $opts{'twin'} && !defined $gen) {
+ @p = @{primes($start, $end)};
+ push @p, is_prime($p[-1]+2) ? $p[-1]+2 : 0;
+ my @twin;
+ my $prime = shift @p;
+ foreach my $next (@p) {
+ push @twin, $prime if $prime+2 == $next;
+ $prime = $next;
+ }
+ @p = @twin;
+ $gen = 'twin';
+ }
+
if (!defined $gen) {
@p = @{primes($start, $end)};
$gen = 'primes';
}
- if (exists $opts{'twin'}) {
- if ($gen ne 'primes') {
- @p = grep { is_prime( $_+2 ); } @p;
- } elsif (scalar @p > 0) {
- # All primes in the range are here, so just look in the array.
- push @p, is_prime($p[-1]+2) ? $p[-1]+2 : 0;
- my @twin;
- my $prime = shift @p;
- foreach my $next (@p) {
- push @twin, $prime if $prime+2 == $next;
- $prime = $next;
- }
- @p = @twin;
- }
+ # Apply the mod 210 pretest
+ if ($min_pass > 0) {
+ @p = grep { $_ <= $min_pass || exists $mod_pass{$_ % 210} } @p;
}
- if (exists $opts{'triplet'}) { # could be optimized like twin
+ if (exists $opts{'twin'} && $gen ne 'twin') {
+ @p = grep { is_prime( $_+2 ); } @p;
+ }
+
+ if (exists $opts{'triplet'}) {
@p = grep { is_prime($_+6) && (is_prime($_+2) || is_prime($_+4)); } @p;
}
- if (exists $opts{'quadruplet'}) { # could be optimized like twin
- @p = grep { is_prime($_+2) && is_prime($_+6) && is_prime($_+8); }
- grep { $_ <= 5 || ($_ % 30) == 11; }
- @p;
+ if (exists $opts{'quadruplet'}) {
+ @p = grep { is_prime($_+2) && is_prime($_+6) && is_prime($_+8); } @p;
}
- if (exists $opts{'cousin'}) { # could be optimized like twin
- @p = grep { is_prime($_+4); }
- grep { ($_ <= 3) || ($_ % 6) == 1; }
- @p;
+ if (exists $opts{'cousin'}) {
+ @p = grep { is_prime($_+4); } @p;
}
- if (exists $opts{'sexy'}) { # could be optimized like twin
+ if (exists $opts{'sexy'}) {
@p = grep { is_prime($_+6); } @p;
}
@@ -312,24 +358,14 @@ sub gen_and_filter {
@p;
}
if (exists $opts{'sophie'}) {
- my %mod210;
- undef @mod210{11,23,29,41,53,71,83,89,113,131,149,173,179,191,209};
- @p = grep { is_prime( 2*$_+1 ); }
- grep { $_ <= 5 || exists $mod210{$_ % 210} }
- @p;
+ @p = grep { is_prime( 2*$_+1 ); } @p;
}
if (exists $opts{'cuban1'}) {
- my %mod210;
- undef @mod210{1,19,37,61,79,121,127,169,187};
- @p = grep { my $n = sqrt((4*$_-1)/3); $n == int($n); }
- #grep { ($_%3) == 1 }
- grep { $_ <= 7 || exists $mod210{$_ % 210} }
- @p;
+ #@p = grep { my $n = sqrt((4*$_-1)/3); $n == int($n); } @p;
+ @p = grep { my $n = sqrt((4*$_-1)/3); 4*$_ == int($n)*int($n)*3+1; } @p;
}
if (exists $opts{'cuban2'}) {
- @p = grep { my $n = sqrt(($_-1)/3); $n == int($n); }
- grep { ($_%3) == 1 }
- @p;
+ @p = grep { my $n = sqrt(($_-1)/3); $_ == int($n)*int($n)*3+1; } @p;
}
if (exists $opts{'pnm1'}) {
@p = grep { is_prime( primorial(Math::BigInt->new($_))-1 ) } @p;
@@ -346,40 +382,37 @@ sub gen_and_filter {
@p;
}
-if (exists $opts{'lucas'} ||
- exists $opts{'fibonacci'} ||
- exists $opts{'euclid'} ||
- exists $opts{'mersenne'}) {
- my @p = gen_and_filter($start, $end);
- print join("\n", @p), "\n" if scalar @p > 0;
-} else {
- my @p;
- while ($start <= $end) {
-
- # Adjust segment sizes for some cases
- $segment_size = 10000 if $start > ~0; # small if doing bigints
- if (exists $opts{'pillai'}) {
- $segment_size = ($start < 10000) ? 100 : 1000; # very small for Pillai
- }
- if (exists $opts{'palindromic'}) {
- $segment_size = 10**length($start) - $start - 1; # all n-digit numbers
+sub find_mod210_restriction {
+ my %mods_left;
+ undef @mods_left{ grep { ($_%2) && ($_%3) && ($_%5) && ($_%7) } (0..209) };
+
+ my %mod210_restrict = (
+ cuban1 => {min=> 7, mod=>[1,19,37,61,79,121,127,169,187]},
+ cuban2 => {min=> 2, mod=>[1,13,43,109,139,151,169,181,193]},
+ twin => {min=> 5, mod=>[11,17,29,41,59,71,101,107,137,149,167,179,191,197,209]},
+ triplet => {min=> 7, mod=>[11,13,17,37,41,67,97,101,103,107,137,163,167,187,191,193]},
+ quadruplet => {min=> 5, mod=>[11,101,191]},
+ cousin => {min=> 7, mod=>[13,19,37,43,67,79,97,103,109,127,139,163,169,187,193]},
+ sexy => {min=> 7, mod=>[11,13,17,23,31,37,41,47,53,61,67,73,83,97,101,103,107,121,131,137,143,151,157,163,167,173,181,187,191,193]},
+ safe => {min=>11, mod=>[17,23,47,53,59,83,89,107,137,143,149,167,173,179,209]},
+ sophie => {min=> 5, mod=>[11,23,29,41,53,71,83,89,113,131,149,173,179,191,209]},
+ # Nothing for good, pillai, palindromic, fib, lucas, mersenne, primorials
+ );
+
+ my $min = 0;
+ while (my($filter,$data) = each %mod210_restrict) {
+ next unless exists $opts{$filter};
+ $min = $data->{min} if $min < $data->{min};
+ my %thismod;
+ undef @thismod{ @{$data->{mod}} };
+ foreach my $m (keys %mods_left) {
+ delete $mods_left{$m} unless exists $thismod{$m};
}
-
- my $seg_start = $start;
- my $seg_end = $start + $segment_size;
- $seg_end = $end if $end < $seg_end;
- $start = $seg_end+1;
-
- @p = gen_and_filter($seg_start, $seg_end);
-
- # print this segment
- print join("\n", @p), "\n" if scalar @p > 0;
}
+ return ($min, %mods_left);
}
-
-
sub die_usage {
die <<EOU;
Usage: $0 [options] START END
diff --git a/examples/test-primes-script.pl b/examples/test-primes-script.pl
index e91ccdb..7f3390f 100755
--- a/examples/test-primes-script.pl
+++ b/examples/test-primes-script.pl
@@ -31,16 +31,27 @@ my @test_data = (
[18239, "Euclid", "--euclid", 0],
);
my %oeis_name = map { $_->[0] => $_->[1] } @test_data;
+my %oeis_number = map { my $n=$_->[2]; $n=~s/^--//; $n => $_->[0] } @test_data;
+
+# Verify additional filters
+my @additional_filters;
+foreach my $name (@ARGV) {
+ $name =~ s/^--//;
+ die "Unknown filter: $name\n" unless defined $oeis_number{$name};
+ push @additional_filters, $name;
+}
my $test_data_hash = read_script_data('script-test-data.bs');
+if (@additional_filters > 0) {
+ print "Additional Filters: ", join(" ", @additional_filters), "\n";
+}
foreach my $test (@test_data) {
my $oeis_no = $test->[0];
- my $test_data = $test_data_hash->{$oeis_no};
- if (!defined $test_data) {
+ if (!defined $test_data_hash->{$oeis_no}) {
die "No test data found for OEIS $oeis_no : $test->[1] primes\n";
}
- test_oeis(@$test, $test_data);
+ test_oeis(@$test, $test_data_hash);
}
@@ -49,6 +60,9 @@ foreach my $test (@test_data) {
sub read_script_data {
my ($filename) = @_;
+ die "Can't find test file: $filename\nRun make-script-test-data.pl\n"
+ unless -r $filename;
+
my $stream = Data::BitStream::XS->new( file => $filename, mode => 'ro' );
my %hash;
@@ -89,20 +103,28 @@ sub read_script_data {
}
sub test_oeis {
- my($oeis_no, $name, $script_arg, $restrict, $ref_data) = @_;
+ my($oeis_no, $name, $script_arg, $restrict, $test_data_hash) = @_;
+
+ my @ref = @{ $test_data_hash->{$oeis_no} };
+ my $end = $ref[-1];
+
+ foreach my $filter_name (@additional_filters) {
+ my $filter_no = $oeis_number{$filter_name};
+ my %filter_data;
+ undef @filter_data{ @{$test_data_hash->{$filter_no}} };
+ my $filter_end = $test_data_hash->{$filter_no}->[-1];
+ @ref = grep { exists $filter_data{$_} } @ref;
+ $script_arg .= " --$filter_name";
+ $end = $filter_end if $end > $filter_end; # bring endpoint down
+ }
- my @ref = @$ref_data;
printf "%12s primes (OEIS A%06d): generating..", $name, $oeis_no;
- #print "\n";
- #print "reference data:\n";
- #print " $_\n" for @ref;
- #print "primes.pl $script_arg 1 $ref[-1]\n";
my $start = [gettimeofday];
- my @scr = split /\s+/, qx+$FindBin::Bin/../bin/primes.pl $script_arg 1 $ref[-1]+;
+ my @scr = split /\s+/, qx+$FindBin::Bin/../bin/primes.pl $script_arg 1 $end+;
{
no bigint;
- my $num_generated = scalar @scr;
+ my $num_generated = scalar @scr || 0.1;
my $seconds = tv_interval($start);
my $msperprime = ($seconds * 1000.0) / $num_generated;
printf " %7d. %7.2f ms/prime\n", $num_generated, $msperprime;
--
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