From Code to Community: Sponsoring The Perl and Raku Conference 2025 Learn more

#!/usr/bin/perl -w
# 31B8fSs - prym created by Pip@CPAN.Org to find prime numbers for fun
# (as fast as I can in Perl using a sieve while saving all results)
#
# --> Press 'x' or Escape to eXit! <--
#
# 'v' toggles Visible progress
# 'b' toggles Base64 integers
#
# Desc: This was originally a UserRPL program on my HP48GX calculator
# called GetPrime. I rewrote it in 4Dos batch in 1999 && had fun
# with that for many hours. Now it will be reborn in Perl under a
# new name: prym. Finding prime numbers is a screen-saver of sorts
# for me. I like to watch it =). Specifically, I like to observe
# the percentages slowing descent alongside the MaxCheckDelta (MDlt).
# When MDlt gets to zero, the MaxCheckIndex (Mn) increments &&
# squares that prime number to get a new MaxCheck (MaxxCk) which
# makes the percentages jump up again && descend more slowly. If
# you run `prym p` it will explode Prym.txt (which just stores deltas
# in base64) into Prim.txt which will have one prime-per-line so the
# nth prime can be found on line n. `prym pN` where N is a base64
# number will print the Nth prime number from Prym.txt. Uppercase
# "P" before N is the same but can be the decimal index of the prime.
# I've added a much faster mode where it doesn't use Curses to draw
# anything. The flag for this is called $draw in the code below.
# $draw can be given on the command line like `prym 0`. If $draw is
# false (zero), prym will find primes in 65536 blocks before saving
# to disk. This way (without drawing every new found prime) seems to
# be finding about 250 new prime numbers per second on my machine
# while it was only finding about 3 new ones per second with draw on
# (but it is much more fun to watch the progress =) ). Press Ctrl-C
# to break out if you're not drawing (but it won't save the progress
# it has made on this loop if you do this so you may want to wait
# until it completes a block before breaking execution if you want
# progress to be saved... blocks take about 4 minutes each to
# complete on my P3-750MHz machine).
# 2do:
# if mxck surpasses @chkz, double csiz && reload chkz
# use Time::PT && Frame math instead of Time::HiRes::time
# mk prym faster
# add any new fields that might be interesting to observe
# add other keys
# cleanup code
# write help
use Math::BaseCnv qw(:all);
my $draw = shift; $draw = 1 unless(defined $draw);
# set draw to 0 for much faster calculation (it won't draw
# all the pretty stats or check for input so you have to Ctrl-C break it)
my @scrn = (); my @colz = (); my $qiii = "-1"; my $simp;
my $widt = 0; my $hite = 0; my $newl = "";
my $bsiz = shift || 65536; # rolling buffer size as the second param
my $csiz = shift || 4096; # check array size as third param
my $lupz = shift || 4096; # number of times to loop filling buffer as fourth param
my @chkz = (); my $buff = ""; my $temp = ""; my $cand = 0; my $done = 0; my $fndx = 0;
my $psiz = 0; my $prev = 0; my $pnum = 0; my $coun = 0; my $fail = 0; my $colr;
my $indx = 0; my $last = 0; my $totl = 0; my $mxnd = 0; my $mxck = 0; my $wich;
my $dttl = 0; my $davg = 0; my $time = 0; my $otim = 0;
my $dwid = 1; my $bs64 = 0; my $dtai = 1; my $bzip = 1; my $visi = 1;
my $tttl = 0; my $tavg = 0; my $fwid = 0; my $fdln;
my $pdfl = "PDsc.txt"; my $ppfl = "Prym.txt"; my $xpfl = "Prim.txt";
my @fdnm = ( "PipTime", "PrymNdx", "BfSiz", "Dl", "PrymNumb",
"MxNd", "MaxxChek", "MxDelt");
my @fdcl = ( "Wu", "Cb" , "Uu", "Pb", "Gu",
"Yb", "ou", "Rb");
my @flnm = ("AvgDelta", "FSecsDif", "AvgFSecs", "100th%Pr", "100th%Fl",
"Prym/Ndx");
my @flcl = ( "Wb", "Cu", "Ub", "Pu", "Gb",
"Yu");
my @fdvl = (); my @flvl = ();
if($bzip && -e $ppfl . ".bz2") { system("bunzip2 $ppfl.bz2"); }
if($bzip && -e $pdfl . ".bz2") { system("bunzip2 $pdfl.bz2"); }
if($draw =~ /^p(\w*)/i) { # generate Prim.txt from Prym.txt or just return Nth prime
$wich = $1 || undef;
if(defined $wich && $draw =~ /^p/) { $wich = b10($wich); }
if(-r $pdfl && -r $ppfl) { # read Prym
open(PDFL, "<$pdfl");
chomp($temp = <PDFL>);
close(PDFL);
($totl, $mxnd, $mxck, $last) = split(/\s+/, $temp);
$totl = b10($totl); $last = b10($last);
$mxnd = b10($mxnd); $mxck = b10($mxck);
$pnum = 1; $coun = 1; $done = 0; $dwid = 1;
open(PPFL, "<$ppfl");
open(XPFL, ">$xpfl") unless(defined $wich);
print XPFL "1\n" unless(defined $wich);
while(<PPFL>) {
while(length($_)) {
$_ =~ s/^(.{$dwid})//;
if(defined $1) {
if ($1 eq "\n") { $dwid = 2; if(length($_)) { $dwid += length($_); } $_ = ""; last; }
elsif($1 eq "0") { $dwid = 1; $pnum++; }
else { $dwid = 1; $pnum += (2 * b10($1)); }
$coun++;
if(defined $wich) {
if($coun == $wich) {
print $pnum;
$done = 1;
last;
}
} else {
print XPFL "$pnum\n";
}
}
}
last if($done);
}
close(XPFL) unless(defined $wich);
close(PPFL);
} else {
print "!EROR! Could not create $xpfl because $pdfl or $ppfl cannot be read!\n";
}
} else { # finding new primes...
if($draw) {
$simp = Curses::Simp->new('flagbkgr' => 1);
$widt = $simp->Widt(); $hite = $simp->Hite();
push(@scrn, "Loading check array..."); push(@colz, "wb" x $widt);
while(@scrn < $hite) {
push(@scrn, " " x $widt); push(@colz, "wb" x $widt);
}
$simp->Draw('text' => \@scrn, 'colr' => \@colz);
$temp = 0;
foreach(@fdnm) { $temp += length($_); }
$fwid = int(($widt - $temp) / @flnm);
}
if(-r $pdfl) { # read checks from PDsc.txt into @chkz
open(PDFL, "<$pdfl");
<PDFL>; # get rid of stats line
chomp(@chkz = <PDFL>); # snarf it all into checks
close(PDFL);
}
if(@chkz < $csiz) { # either PDsc.txt doesn't exist or isn't large enough to fill @chkz
if($draw) {
$scrn[0] .= "Generating first $csiz primes.";
$simp->Draw('text' => \@scrn, 'colr' => \@colz);
}
@chkz = ( 1..3 ); # so generate it
$mxnd = $#chkz;
$cand = $chkz[$mxnd];
$mxck = ($chkz[$mxnd] * $chkz[$mxnd]);
while(@chkz < $csiz) { # load chkz with first csiz primes
$cand += 2; # prime candidates can only be odd numbers from 3 on
while($cand >= $mxck) {
$mxnd++;
$mxck = ($chkz[$mxnd] * $chkz[$mxnd]);
}
$fail = 0;
foreach(@chkz[2..$mxnd]) { # don't check for 1 or 2
unless($cand % $_) { $fail = 1; last; }
}
unless($fail) {
push(@chkz, $cand);
if($draw && int($csiz / 64) && !(@chkz % int($csiz / 64))) {
$scrn[0] .= ".";
$simp->Draw('text' => \@scrn, 'colr' => \@colz);
}
}
}
}
if (-r $pdfl) { # get Prym.txt description info
open(PDFL, "<$pdfl");
chomp($temp = <PDFL>);
($totl, $mxnd, $mxck, $last) = split(/\s+/, $temp);
close(PDFL);
$totl = b10($totl); $last = b10($last);
$mxnd = b10($mxnd); $mxck = b10($mxck);
} elsif(-r $ppfl) { # or initialize description info from Prym.txt file (very slow!)
open(PPFL, "<$ppfl");
$coun = 0; $pnum = 0; $dwid = 1;
while(<PPFL>) {
while(length($_)) {
$_ =~ s/^(.{$dwid})//;
if(defined $1) {
if ($1 eq "\n") { $dwid = 2; if(length($_)) { $dwid += length($_); } $_ = ""; last; }
elsif($1 eq "0") { $dwid = 1; $pnum++; }
else { $dwid = 1; $pnum += (2 * b10($1)); }
$coun++;
}
}
}
close(PPFL);
($totl, $mxnd, $mxck, $last) = ($coun, 2, 4, $pnum);
} else { # or initialize description info for starting clean
($totl, $mxnd, $mxck, $last) = (3, 2, 4, 3);
}
if($draw) {
if(@scrn && $visi) {
$simp->Draw('text' => \@scrn, 'colr' => \@colz);
}
@scrn = (); @colz = ();
$newl = ""; $newc = "";
for($fndx = 0; $fndx < @fdnm; $fndx++) {
$fdln = length($fdnm[$fndx]);
$newl .= sprintf("%${fdln}s", $fdnm[$fndx]);
if($fndx) {
$colr = $fdcl[$fndx];
$newc .= $colr x $fdln;
} else {
$newc .= ptcc() . ';';
}
}
for($fndx = 0; $fndx < @flnm; $fndx++) {
$newl .= sprintf("%-${fwid}.${fwid}s", $flnm[$fndx]);
$colr = $flcl[$fndx];
$newc .= $colr x $fwid;
}
push(@scrn, $newl);
push(@colz, $newc);
$otim = Time::HiRes::time unless($otim);
}
for($indx = 0; $indx < $lupz; $indx++) {
$buff = '';
if($totl == 3) { $buff = '00'; $mxnd = 2; }
$cand = $last;
$mxck = ($chkz[$mxnd] * $chkz[$mxnd]);
while(lc($qiii) ne "x" && lc($qiii) ne "q" && ord($qiii) != 27 && length($buff) < $bsiz) { # load buff
$cand += 2;
while($cand >= $mxck) {
$mxnd++;
$mxck = ($chkz[$mxnd] * $chkz[$mxnd]);
}
$fail = 0;
foreach(@chkz[2..$mxnd]) { # don't check for 1 or 2
unless($cand % $_) { $fail = 1; last; }
}
unless($fail) {
$temp = (($cand - $last) / 2);
$dttl += $temp;
$temp = b64($temp);
$buff .= '\n' x (length($temp) - 1) if(length($temp) - 1);
$buff .= $temp;
$temp = (($cand - $last) / 2) unless($bs64);
$last = $cand;
$totl++;
if($draw && $dtai) {
if(($indx * $bsiz) + length($buff)) {
$davg = ($dttl / (($indx * $bsiz) + length($buff)));
}
$time = Time::HiRes::time;
$otim = ($time - $tavg) if($otim == -1);
$tttl += ($time - $otim) if(($time - $otim) < 4096);
$tavg = ($tttl / (($indx * $bsiz) + length($buff))) if(($indx * $bsiz) + length($buff));
$newl = "";
# @fdnm = ("PipTime","PrmNdx","BfSiz","Dl","B64Prime","MxNd","MaxxChek","MxDelt");
if($bs64) {
@fdvl = (pt, b64($totl), b64(length($buff)), $temp, b64($cand), b64($mxnd), b64($mxck),
b64($mxck - $cand));
} else {
@fdvl = (pt, $totl , length($buff) , $temp, $cand , $mxnd , $mxck ,
($mxck - $cand));
}
# @flnm = ("AvgDelta", "FSecsDif", "AvgFSecs", "100th%Pr", "100th%Fl", "Prym/Ndx");
@flvl = ($davg, ($time - $otim), $tavg, (($chkz[$mxnd] / $cand) * 10000),
(($mxnd / $totl) * 10000), ($cand / $totl));
for($fndx = 0; $fndx < @fdvl; $fndx++) {
$fdln = length($fdnm[$fndx]);
$newl .= sprintf("%${fdln}s", $fdvl[$fndx]);
}
for($fndx = 0; $fndx < @flvl; $fndx++) {
if($flvl[$fndx] =~ /^(\d+)\.?(\d*)$/) {
$temp = 0; $temp = $2 if(defined $2 && $2);
$newl .= sprintf("%-${fwid}.${fwid}s", $1 . '.' . $temp ); # decimal
} else {
$newl .= ' ' x $fwid;
}
}
$otim = $time;
push(@scrn, $newl);
if(@scrn > $hite) { $temp = shift(@scrn); shift(@scrn); unshift(@scrn, $temp); }
$newc = "";
for($fndx = 0; $fndx < @fdnm; $fndx++) {
if($fndx) {
$newc .= $fdcl[$fndx] x length($fdnm[$fndx]);
} else {
$newc .= ptcc() . ';';
}
}
for($fndx = 0; $fndx < @flnm; $fndx++) { $newc .= $flcl[$fndx] x $fwid; }
push(@colz, $newc);
if(@colz > $hite) { $temp = shift(@colz); shift(@colz); unshift(@colz, $temp); }
$simp->Draw('text' => \@scrn, 'colr' => \@colz) if($visi);
}
}
$qiii = $simp->GetK() if($draw);
if(lc($qiii) eq "b") { $bs64 ^= 1; } # toggle Base64 view
if(lc($qiii) eq "d") { $dtai ^= 1; $otim = -1; } # toggle Detailed view
if(lc($qiii) eq "v") { $visi ^= 1; } # toggle Visibility
}
open(PDFL, ">$pdfl"); # update PrimesDescription file
print PDFL b64($totl) . " " . b64($mxnd) . " " . b64($mxck) . " " . b64($last) . "\n";
foreach(@chkz) { print PDFL "$_\n"; }
close(PDFL);
open(PPFL, ">>$ppfl"); # append new Primes buffer
print PPFL $buff;
close(PPFL);
if($draw) {
unless($dtai) {
push(@scrn, "PipTime:" . pt . " DoneWithSet#:$indx BfSiz:$bsiz UpToPrime#:$totl = $last");
if(@scrn > $hite) { $temp = shift(@scrn); shift(@scrn); unshift(@scrn, $temp); }
push(@colz, $fdcl[0] x 8 . $fdcl[1] x 8 . $fdcl[2] x 13 . $fdcl[3] x length($indx) .
$fdcl[4] x 6 . $fdcl[5] x length($bsiz) . $fdcl[6] x 12 . $fdcl[7] x length($totl) .
$fdcl[0] x 3 . $fdcl[1] x length($last));
if(@colz > $hite) { $temp = shift(@colz); shift(@colz); unshift(@colz, $temp); }
$simp->Draw('text' => \@scrn, 'colr' => \@colz);
}
} else {
print "PipTime:" . pt . " DoneWithSet#:$indx BfSiz:$bsiz UpToPrime#:$totl = $last\n";
}
last if($qiii eq "x" || ord($qiii) == 27); # finish if x or Escape pressed
}
}
if($bzip && -e $ppfl) { system("bzip2 $ppfl"); }
if($bzip && -e $pdfl) { system("bzip2 $pdfl"); }