The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/env perl
use strict;
use warnings;
use Math::Prime::Util qw/:all/;
use Math::Prime::Util::PrimeArray;
use Math::NumSeq::Primes;
use Math::Prime::TiedArray;
use Benchmark qw/:all/;
use List::Util qw/min max/;
my $count = shift || -2;

my ($s, $nlimit, $ilimit, $expect);

if (1) {
print "summation to 100k\n";
$nlimit = 100000;
$ilimit = prime_count($nlimit)-1;
$expect = 0; forprimes { $expect += $_ } $nlimit;

cmpthese($count,{
  'primes'    => sub { $s=0; $s += $_ for @{primes($nlimit)}; die unless $s == $expect; },
  'forprimes' => sub { $s=0; forprimes { $s += $_ } $nlimit;  die unless $s == $expect; },
  'iterator'  => sub { $s=0; my $it = prime_iterator();
                       $s += $it->() for 0..$ilimit;
                       die unless $s == $expect; },
  'pa index'  => sub { $s=0; tie my @primes, "Math::Prime::Util::PrimeArray";
                       $s += $primes[$_] for 0..$ilimit;
                       die unless $s == $expect; },
  'pa loop'   => sub { $s=0; tie my @primes, "Math::Prime::Util::PrimeArray";
                       for (@primes) { last if $_ > $nlimit; $s += $_; }
                       die $s unless $s == $expect; },
  'pa slice'  => sub { $s=0; tie my @primes, "Math::Prime::Util::PrimeArray";
                       $s += $_ for @primes[0..$ilimit];
                       die unless $s == $expect; },
  'pa each'   => sub { $s=0; tie my @primes, "Math::Prime::Util::PrimeArray";
                       while(my(undef,$v) = each @primes) { last if $v > $nlimit; $s += $v; }
                       die $s unless $s == $expect; },
  'pa shift'  => sub { $s=0; tie my @primes, "Math::Prime::Util::PrimeArray";
                       while ((my $p = shift @primes) <= $nlimit) { $s += $p; }
                       die unless $s == $expect; },
  'numseq'    => sub { $s=0; my $seq = Math::NumSeq::Primes->new;
                       while (1) { my($undev,$v) = $seq->next; last if $v > $nlimit; $s += $v; }
                       die $s unless $s == $expect; },
  # This was slightly faster than slice or shift
  'tiedarray'  => sub { $s=0; tie my @primes, "Math::Prime::TiedArray", extend_step => 1000;
                       $s += $primes[$_] for 0..$ilimit;
                       die unless $s == $expect; },
});
}

if (0) {
print "summation to 10M\n";
print "Skipping Math::Prime::TiedArray as it will take too long\n";
$nlimit = 10_000_000;
$ilimit = prime_count($nlimit)-1;
$expect = 0; forprimes { $expect += $_ } $nlimit;

cmpthese($count,{
  'primes'    => sub { $s=0; $s += $_ for @{primes($nlimit)}; die unless $s == $expect; },
  'forprimes' => sub { $s=0; forprimes { $s += $_ } $nlimit;  die unless $s == $expect; },
  'pa index'  => sub { $s=0; tie my @primes, "Math::Prime::Util::PrimeArray";
                       $s += $primes[$_] for 0..$ilimit;
                       die unless $s == $expect; },
  'pa loop'   => sub { $s=0; tie my @primes, "Math::Prime::Util::PrimeArray";
                       for (@primes) { last if $_ > $nlimit; $s += $_; }
                       die $s unless $s == $expect; },
  'pa slice'  => sub { $s=0; tie my @primes, "Math::Prime::Util::PrimeArray";
                       $s += $_ for @primes[0..$ilimit];
                       die unless $s == $expect; },
  'pa each'   => sub { $s=0; tie my @primes, "Math::Prime::Util::PrimeArray";
                       while(my(undef,$v) = each @primes) { last if $v > $nlimit; $s += $v; }
                       die $s unless $s == $expect; },
  'pa shift'  => sub { $s=0; tie my @primes, "Math::Prime::Util::PrimeArray";
                       while ((my $p = shift @primes) <= $nlimit) { $s += $p; }
                       die unless $s == $expect; },
  'numseq'    => sub { $s=0; my $seq = Math::NumSeq::Primes->new;
                       while (1) { my($undev,$v) = $seq->next; last if $v > $nlimit; $s += $v; }
                       die $s unless $s == $expect; },
});
}

if (1) {
print "Walk primes backwards from 1M\n";
$nlimit = 1_000_000;
$ilimit = prime_count($nlimit)-1;
$expect = 0; forprimes { $expect += $_ } $nlimit;

cmpthese($count,{
  'rev primes'=> sub { $s=0; $s += $_ for reverse @{primes($nlimit)}; die unless $s == $expect; },
  'nthprime'  => sub { $s=0; $s += nth_prime($_) for reverse 1..$ilimit+1; die unless $s == $expect; },
  'pa index'  => sub { $s=0; tie my @primes, "Math::Prime::Util::PrimeArray";
                       $s += $primes[$_] for reverse 0..$ilimit;
                       die unless $s == $expect; },
  'tiedarray' => sub { $s=0; tie my @primes, "Math::Prime::TiedArray", extend_step => 1000;
                       $s += $primes[$_] for reverse 0..$ilimit;
                       die unless $s == $expect; },
});
}

if (1) {
print "Random walk in 1M\n";
srand(29);
my @rindex;
do { push @rindex, int(rand(1000000)) } for 1..10000;
$expect = 0; $expect += nth_prime($_+1) for @rindex;

cmpthese($count,{
  'nthprime'  => sub { $s=0; $s += nth_prime($_+1) for @rindex; },
  'pa index'  => sub { $s=0; tie my @primes, "Math::Prime::Util::PrimeArray";
                       $s += $primes[$_] for @rindex;
                       die unless $s == $expect; },
   # Argh!  Is it possible to write a slower sieve than the one MPTA uses?
  #'tiedarray' => sub { $s=0; tie my @primes, "Math::Prime::TiedArray", extend_step => 10000;
  #                     $s += $primes[$_] for @rindex;
  #                     die unless $s == $expect; },
});
}