[libmath-prime-util-perl] 13/16: Add PrimeArray tied array

Partha P. Mukherjee ppm-guest at moszumanska.debian.org
Thu May 21 18:44:35 UTC 2015


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

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

commit 1bcf403717edef5a1f904e293517d413384b49ac
Author: Dana Jacobsen <dana at acm.org>
Date:   Thu Jun 21 20:54:50 2012 -0600

    Add PrimeArray tied array
---
 Changes                           |   1 +
 MANIFEST                          |   3 +
 Makefile.PL                       |   6 +-
 TODO                              |   2 +-
 examples/sophie_germain.pl        |  18 +++
 examples/twin_primes.pl           |  21 ++++
 lib/Math/Prime/Util.pm            |   2 +
 lib/Math/Prime/Util/PrimeArray.pm | 240 ++++++++++++++++++++++++++++++++++++++
 8 files changed, 288 insertions(+), 5 deletions(-)

diff --git a/Changes b/Changes
index 5e4f3f4..0d48120 100644
--- a/Changes
+++ b/Changes
@@ -8,6 +8,7 @@ Revision history for Perl extension Math::Prime::Util.
     - Static presieve for 7, 11, and 13.  1k of ROM used for prefilling sieve
       memory, meaning we can skip the 7, 11, and 13 loops.  ~15% speedup.
     - Add all_factors function and added tests to t/50-factoring.t.
+    - Add tied array module Math::Prime::Util::PrimeArray.
 
 0.07  17 June 2012
     - Fixed a bug in next_prime found by Lou Godio (thank you VERY much!).
diff --git a/MANIFEST b/MANIFEST
index 89667bb..22f2963 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -1,5 +1,6 @@
 Changes
 lib/Math/Prime/Util.pm
+lib/Math/Prime/Util/PrimeArray.pm
 LICENSE
 Makefile.PL
 MANIFEST
@@ -32,6 +33,8 @@ examples/test-primes-yafu.pl
 examples/test-holf.pl
 examples/test-nthapprox.pl
 examples/test-pcapprox.pl
+examples/sophie_germain.pl
+examples/twin_primes.pl
 t/01-load.t
 t/02-can.t
 t/03-init.t
diff --git a/Makefile.PL b/Makefile.PL
index f3e2d28..f11bea2 100644
--- a/Makefile.PL
+++ b/Makefile.PL
@@ -7,9 +7,6 @@ WriteMakefile1(
     LICENSE      => 'perl',
     AUTHOR       => 'Dana A Jacobsen <dana at acm.org>',
 
-    LIBS         => [''],   # e.g., '-lm'
-    DEFINE       => '',     # e.g., '-DHAVE_SOMETHING'
-    INC          => '',     # e.g., '-I/usr/include/other'
     OBJECT       => 'cache.o ' .
                     'factor.o '  .
                     'sieve.o '  .
@@ -22,7 +19,8 @@ WriteMakefile1(
     PREREQ_PM    => {
                       'Exporter'         => '5.562',
                       'XSLoader'         => '0.01',
-                      'Carp'             => '0',
+                      'Carp'             => 0,
+                      'Tie::Array'       => 0,
                     },
 
     MIN_PERL_VERSION => 5.006002,
diff --git a/TODO b/TODO
index 8369292..683023d 100644
--- a/TODO
+++ b/TODO
@@ -25,4 +25,4 @@
 - Move .c / .h files into separate directory.
   version does it in a painful way.  Something simpler to be had?
 
-- Iterator or tie?
+- tests for tie
diff --git a/examples/sophie_germain.pl b/examples/sophie_germain.pl
new file mode 100755
index 0000000..ed7dde3
--- /dev/null
+++ b/examples/sophie_germain.pl
@@ -0,0 +1,18 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+
+use Math::Prime::Util qw/next_prime is_prime/;
+
+my $count = shift || 20;
+
+# Simple little loop looking for Sophie Germain primes (numbers where
+# p and 2p+1 are both prime).  Calculating the first 100k runs 17x faster than
+# Math::NumSeq::SophieGermainPrimes (about 3x faster if the latter's algorithm
+# is changed).
+my $prime = 2;
+for (1..$count) {
+  $prime = next_prime($prime) while (!is_prime(2*$prime+1));
+  print "$prime\n";
+  $prime = next_prime($prime);
+}
diff --git a/examples/twin_primes.pl b/examples/twin_primes.pl
new file mode 100755
index 0000000..2b3fbab
--- /dev/null
+++ b/examples/twin_primes.pl
@@ -0,0 +1,21 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+
+use Math::Prime::Util qw/next_prime is_prime/;
+
+my $count = shift || 20;
+
+# Simple little loop looking for twin primes (numbers where p and p+2 are
+# both prime).  Print them both.  About 3x faster than Math::NumSeq::TwinPrimes.
+my $prime = 2;
+my $next;
+for (1..$count) {
+  while (1) {
+    $next = next_prime($prime);
+    last if ($next-2) == $prime;
+    $prime = $next;
+  }
+  print $prime, ",", $prime+2, "\n";
+  $prime = $next;
+}
diff --git a/lib/Math/Prime/Util.pm b/lib/Math/Prime/Util.pm
index dceb2ff..d3873f8 100644
--- a/lib/Math/Prime/Util.pm
+++ b/lib/Math/Prime/Util.pm
@@ -42,6 +42,8 @@ END {
 
 my $_maxparam  = (_maxbits == 32) ? 4294967295 : 18446744073709551615;
 my $_maxdigits = (_maxbits == 32) ? 10 : 20;
+my $_maxprime  = (_maxbits == 32) ? 4294967291 : 18446744073709551557;
+my $_maxprimeidx=(_maxbits == 32) ?  203280221 :   425656284035217743;
 
 sub primes {
   my $optref = {};  $optref = shift if ref $_[0] eq 'HASH';
diff --git a/lib/Math/Prime/Util/PrimeArray.pm b/lib/Math/Prime/Util/PrimeArray.pm
new file mode 100644
index 0000000..af57788
--- /dev/null
+++ b/lib/Math/Prime/Util/PrimeArray.pm
@@ -0,0 +1,240 @@
+package Math::Prime::Util::PrimeArray;
+use strict;
+use warnings;
+
+BEGIN {
+  $Math::Prime::Util::PrimeArray::AUTHORITY = 'cpan:DANAJ';
+  $Math::Prime::Util::PrimeArray::VERSION = '0.07';
+}
+
+# parent is cleaner, and in the Perl 5.10.1 / 5.12.0 core, but not earlier.
+# use parent qw( Exporter );
+use base qw( Exporter );
+our @EXPORT_OK = qw( );
+our %EXPORT_TAGS = (all => [ @EXPORT_OK ]);
+
+
+use Math::Prime::Util qw/nth_prime nth_prime_upper primes/;
+use Tie::Array;
+use Carp qw/carp croak confess/;
+
+sub TIEARRAY {
+  my $class = shift;
+  if (@_) {
+    croak "usage: tie ARRAY, '" . __PACKAGE__ . "";
+  }
+  return bless {
+    # used to keep track of shift
+    SHIFTINDEX => 0,
+    # Remove all extra prime memory when we go out of scope
+    MEMFREE    => Math::Prime::Util::MemFree->new,
+    # A chunk of primes
+    PRIMES     => [2, 3, 5, 7, 11, 13, 17],
+    # What's the index of the first one?
+    BEG_INDEX  => 0,
+    # What's the index of the last one?
+    END_INDEX  => 6,
+  }, $class;
+}
+sub STORE     { carp "You cannot write to the prime array"; }
+sub DELETE    { carp "You cannot write to the prime array"; }
+sub STORESIZE { carp "You cannot write to the prime array"; }
+sub EXISTS    { 1 }
+sub EXTEND    { 1 }
+sub FETCHSIZE { 0x7FFF_FFFF }   # Even on 64-bit
+# Simple FETCH:
+# sub FETCH { return nth_prime($_[1]+1); }
+sub FETCH {
+  my $self = shift;
+  my $index = shift;
+  # TODO: negative index?
+  $index += $self->{SHIFTINDEX};  # take into account any shifts
+  if ( ($index < $self->{BEG_INDEX}) || ($index > $self->{END_INDEX}) ) {
+    # We're going to get a chunk of primes starting 1000 before the one
+    # asked for, and extending at _least_ 1000 past it.  So given a number
+    # past index 1000, we would expect to have ~2000 primes with the one
+    # requested being about in the middle.  This should be reasonably fast
+    # for the siever and amortize the cost over a lot of calls if they're
+    # walking us.  Yes, we'll get well over 2000 primes as the numbers get
+    # very large, but that's ok (e.g. 80k primes at index 400M).  Calling
+    # nth_prime on indices that large is very time consuming.
+    my $start_idx = ($index < 1000) ? 0 : $index-1000;
+    my $start_prime = nth_prime($start_idx+1);       # This is exact
+    my $end_prime   = nth_prime_upper($index+5000);  # This is a bound
+    #print "calling primes from $start_idx/$start_prime to $end_prime\n";
+    $self->{PRIMES} = primes($start_prime, $end_prime);
+    $self->{BEG_INDEX} = $start_idx;
+    $self->{END_INDEX} = $start_idx + scalar @{$self->{PRIMES}} - 1;;
+  }
+  return $self->{PRIMES}->[ $index - $self->{BEG_INDEX} ];
+}
+
+# Fake out shift and unshift
+sub SHIFT {
+  my $self = shift;
+  my $head = $self->FETCH(0);
+  $self->{SHIFTINDEX}++;
+  $head;
+}
+sub UNSHIFT {
+  my $self = shift;
+  my $shiftamount = defined $_[0] ? shift : 1;
+  $self->{SHIFTINDEX} = ($shiftamount >= $self->{SHIFTINDEX})
+                        ? 0
+                        : $self->{SHIFTINDEX} - $shiftamount;
+  $self->FETCHSIZE;
+}
+# CLEAR this
+# PUSH this, LIST
+# POP this
+# SPLICE this, offset, len, LIST
+# DESTROY this
+# UNTIE this
+
+1;
+
+__END__
+
+
+# ABSTRACT: A tied array for primes
+
+=pod
+
+=head1 NAME
+
+Math::Prime::Util::PrimeArray - A tied array for primes
+
+
+=head1 VERSION
+
+Version 0.07
+
+
+=head1 SYNOPSIS
+
+  use Math::Prime::Util::PrimeArray;
+
+  # Create:
+  my @primes;  tie @primes, 'Math::Prime::Util::PrimeArray';
+
+  # Use in a loop by index:
+  for my $n (1..10) {
+    print "prime $n = $primes[$n]\n";
+  }
+
+  # Use in a loop over array:
+  for my $p (@primes) {
+    print "$p\n";
+    last if $p > $limit;   # stop sometime
+  }
+
+  # Use via array slice:
+  print join(",", @primes[0..50]), "\n";
+
+  # Use via each:
+  use 5.012;
+  while( my($index,$value) = each @primes ) {
+    print "The ${index}th prime is $value\n";
+    last if $p > $limit;   # stop sometime
+  }
+
+  # Use with shift:
+  while ((my $p = shift @primes) < $limit) {
+    print "$p\n";
+  }
+
+
+=head1 DESCRIPTION
+
+An array that acts like the infinite set of primes.  This may be more
+convenient than using L<Math::Prime::Util> directly, and in some cases it can
+be faster than calling C<next_prime> and C<prev_prime>.
+
+Internally when an index is accessed, an area surrounding the index is sieved
+if necessary, then the result returned.  This means random access will be a
+little slower than using C<nth_prime> directly, but not by very much.
+Random access in a small window (1000 or so primes in either direction) will
+be very fast, as will sequential access in either direction.
+
+Shifting acts like the array is losing elements at the front, so after two
+shifts, C<$primes[0]> == 5>.  Unshift will move the internal shift index back
+one, unless given an argument which is the number to move back (it silently
+truncates so it does not shift past the beginning).
+Example:
+
+  say shift @primes;     # 2
+  say shift @primes;     # 3
+  say shift @primes;     # 5
+  say $primes[0];        # 7
+  unshift @primes;       #     back up one
+  say $primes[0];        # 5
+  unshift @primes, 2;    #     back up two
+  say $primes[0];        # 2
+  
+
+=head1 LIMITATIONS
+
+The size of the array will always be shown as 2147483647 (IV32 max), even in
+a 64-bit environment where primes through C<2^64> are available.
+
+
+=head1 PERFORMANCE
+
+Summing the first 0.1M primes via walking the array:
+
+      40ms   3.3 MB    $sum += $_ for @{primes(nth_prime(100_000))};
+     300ms   0.6 MB    Math::Prime::Util::PrimeArray
+     230ms   2.2 MB    Math::NumSeq::Primes
+   10300ms  36.2 MB    Math::Primes::TiedArray (extend 1k)
+
+Summing the first 1M primes via walking the array:
+
+     0.3s   35.5 MB    $sum += $_ for @{primes(nth_prime(1_000_000))};
+     3.9s    1.3 MB    Math::Prime::Util::PrimeArray
+     9.6s    2.2 MB    Math::NumSeq::Primes
+   146.7s  444.9 MB    Math::Primes::TiedArray (extend 1k)
+
+Summing the first 10M primes via walking the array:
+
+       4s    365 MB    $sum += $_ for @{primes(nth_prime(10_000_000))};
+     2m        2 MB    Math::Prime::Util::PrimeArray
+    85m        2 MB    Math::NumSeq::Primes
+           >2048 MB    Math::Primes::TiedArray (extend 1k)
+
+Using L<Math::Prime::Util> directly in a naive fashion uses lots of memory,
+but is extremely fast.  Sieving segments at a time would control the memory
+use, which is one thing the C<PrimeArray> tie is trying to do for you.
+
+L<Math::NumSeq::Primes> offers an iterator alternative, and works quite well
+for reasonably small numbers.  It does not, however, support random access.
+There seems to be a 2MB fixed overhead, but after that the memory use is 
+is well constrained.  It is very fast for small values, but clearly is
+getting slower as we sum to 1 million, and takes well over an hour to count
+to 10 million.
+
+L<Math::Primes::TiedArray> is remarkably impractical for anything other
+than very small numbers.  I believe the times and memory use in the above
+tables illustrate this.
+
+
+=head1 SEE ALSO
+
+This module uses L<Math::Prime::Util> to do all the work.  If you're doing
+anything but retrieving primes, you should examine that module to see if it
+has functionality you can use directly, as it may be a lot faster or easier.
+
+Similar functionality can be had from L<Math::NumSeq>
+and L<Math::Prime::TiedArray>.
+
+=head1 AUTHORS
+
+Dana Jacobsen E<lt>dana at acm.orgE<gt>
+
+
+=head1 COPYRIGHT
+
+Copyright 2012 by Dana Jacobsen E<lt>dana at acm.orgE<gt>
+
+This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
+
+=cut

-- 
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