Permalink
Cannot retrieve contributors at this time
executable file
398 lines (353 sloc)
16.9 KB
| #!/usr/bin/perl -w | |
| #!D:\strawberry\perl\bin\perl.exe -w | |
| # | |
| # Factordb composite evaluator version 1.5.2 - 21 April 2015 | |
| # Based on original Perl script by yoyo - http://www.rechenkraft.net/wiki/Benutzer_Diskussion:Yoyo/factordb | |
| # Modified and extended by Antonio - http://mersenneforum.org/ | |
| # Written using Strawberry Perl 5.20.2.1-64bit | |
| # Linux compatibility and formatting by ChristianB - http://mersenneforum.org/ | |
| use warnings; | |
| use strict; | |
| use LWP::Simple; # Used to read/write web data | |
| use Term::ReadKey; # Used to read terminal key presses without requiring 'Enter' to be pressed | |
| use Time::HiRes qw(sleep time); # Need better than 1sec resolution for time() and sleep() | |
| use Getopt::Std; # Handle command line (single letter) options | |
| use IO::Handle qw( ); # Used to flush display buffer | |
| my $mindig = 70; # Default minimum size of composite to request, smaller composites are handled by the FactorDB server | |
| my $maxdig = 98; # Default maximum size of composite to factor (my SIQS/GNFS crossover) | |
| my $range = 100; # Default maximum random offset into composite data list, used to reduce collisions between multiple users | |
| my $rangeinc = 0; # Auto increment maximum random offset by this amount if a worker collision occurs | |
| my $yafutext = 1; # Default to displaying YAFU progress messages | |
| my $pagerate = 1500; # Maximum page request rate (per hour) allowed by Factordb.com | |
| my $pcrate = 3600/($pagerate*1.2); # per composite rate limit | |
| my $ltrate = 3600/($pagerate*0.96); # long term rate limit | |
| my $strate = 3600/($pagerate*0.98); # short term rate limit | |
| my $yafupath = "./"; # Default yafu path on linux | |
| my $logpath = "./"; # Default log file path on linux | |
| if ($^O ne "linux") { | |
| # For Windows:- can be set if not in the same directory as yafu e.g. $yafupath = "F:\\yafu\\" | |
| # Note:- must use '\\' to get'\' in Perl text string. | |
| $yafupath = ""; # Default yafu path on Windows | |
| $logpath = ""; # Default log file path on Windows | |
| } | |
| my $fdburl = "http://factordb.com"; # URL to get composites from | |
| my $fdbcookie = ""; # Cookie to use with the URL above | |
| my $logfile = $logpath . "Fdb.csv"; # Default log file | |
| my %numtoget = (); # Composites to get per call, | |
| # altered by the throttling code when hourly page request limit is being approached or exceeded | |
| # Normally we get one composite at a time at a cost of 2 page requests per composite (1 for the 'get' + 1 for the report) | |
| # Increasing the number of composites we get each time increases the chance of worker collisions, but, it also reduces | |
| # the page request rate (1 for the 'get n' + 1 for each report) so a total of 1+n page requests for each 'get n' | |
| $numtoget{'normal'} = 1; # 2 page requests per composite | |
| $numtoget{'throttle'} = 10; # 11 page requests per 10 composites (normal would cause 20 page requests) | |
| $numtoget{'current'} = $numtoget{'normal'}; # Start in 'normal' mode | |
| my $shortstop = 60; # Number of samples to hold for short term rate calculations | |
| my @short; # Holds the time that each of the last ($shortstop) composite requests occurred | |
| my @page; # Holds the page requests invoked by each of the last ($shortstop) composite requests | |
| my @nfa; # Holds the new factors added by each of the last ($shortstop) composite requests | |
| my @composites; | |
| my $pages = 0; # Holds a running total of the last ($shortstop) page requests | |
| my $nfas = 0; # Holds a running total of the last ($shortstop) new factors added to database | |
| my $delay = 0; # Memory for smoothing out forced delays when page request rate is high | |
| my $waitrequests = 0; # Page requests while waiting for valid composite size | |
| my $logging; # Flag - are we going to write to log file. | |
| my $mincomposite = 1000; | |
| my $maxcomposite = 0; | |
| my %total = (); # Store for all running totals displayed | |
| $total{'composites'} = 0; | |
| $total{'added'} = 0; | |
| $total{'known'} = 0; | |
| $total{'small'} = 0; | |
| $total{'nofactors'} = 0; | |
| $total{'nocomposites'} = 0; | |
| $total{'noresults'} = 0; | |
| $total{'only_small'} = 0; | |
| $total{'collision'} = 0; | |
| $total{'queries'} = 0; | |
| $total{'no_ack'} = 0; | |
| $total{'wait_for_comp'} = 0; | |
| my $key; # Store for terminal key press | |
| ReadMode 'raw'; | |
| # declare the perl command line flags/options | |
| my %options = (); | |
| getopts("hm:M:r:a:fql", \%options); | |
| # test for the existence of the options on the command line. | |
| if (defined $options{h}) { | |
| print "\tfdb_comp_evaluator ver. 1.5.2\n\n"; | |
| print "\t-m xx\tset minimum composite size to xx(digits) (default=$mindig)\n"; | |
| print "\t-M xx\tset maximum composite size to xx(digits) (default=$maxdig)\n"; | |
| print "\t-r xx\tset maximum random offset into composite list (range is 0 to xx) (default=$range)\n"; | |
| print "\t-a xx\tset auto-increment of maximum random offset if a collision occurs (default=0)\n"; | |
| print "\t-f\tflag - if present YAFU log files (session.log & factor.log)\n\t\tare deleted when the program terminates.\n"; | |
| print "\t-q\tflag - if present YAFU progress text is suppressed.\n"; | |
| print "\t-l\tflag - if present log data to Fdb.csv\n\t\t(composite size, number of prime factors, time to factor in seconds).\n\n"; | |
| die "\n"; | |
| } | |
| if (defined $options{m}) {$mindig = int($options{m});} | |
| if (defined $options{M}) {$maxdig = int($options{M});} | |
| my $addrange = 0; | |
| if (defined $options{r}) {$range = int($options{r});} | |
| if (defined $options{a}) {$rangeinc = int($options{a});} | |
| print "\tminimum composite size= $mindig digits\n\tmaximum composite size= $maxdig digits\n"; | |
| my $maxinc = ($maxdig - $mindig)*$rangeinc; # upper limit for the extra random offset that can be added | |
| if ($rangeinc > 0) { | |
| print "\tadditional random offset increment set to $rangeinc if worker collision occurs\n"; | |
| print "\trandom offset into composite list variable from 0 to $range up to 0 to " . sprintf ("%d", ($range + $maxinc)) . "\n"; | |
| }else { | |
| print "\trandom offset into composite list from 0 to $range)\n"; | |
| } | |
| if (defined $options{f}) {print "\tDelete YAFU log files when done.\n";} | |
| if (defined $options{q}) { | |
| print "\tSuppress YAFU progress display.\n"; | |
| $yafutext=0; | |
| } | |
| if (defined $options{l}) { | |
| print "\tLogging results to $logfile\n"; | |
| $logging=1; | |
| }else { | |
| $logging=0; | |
| } | |
| print "\n\tCtrl-Q to exit after factoring composite(s) already queued.\n"; | |
| sleep(3); | |
| # Allow to exit now if command line not as intended | |
| $key = ReadKey(-1); | |
| if ($key and (ord($key) == 17)) { | |
| ReadMode 'restore'; | |
| die "\n\tCtrl-Q detected\n"; | |
| } | |
| my $startime = time(); | |
| do { | |
| my $current = time(); | |
| # Store the start time for up to '$shortstop' queries so we can calculate the short term average rate later | |
| if (scalar(@short) == $shortstop) {shift(@short);} | |
| push(@short,$current); | |
| my $rangenow = $range + int($addrange+0.5); | |
| my $rand = int(rand($rangenow+1)); | |
| { | |
| (my $sec,my $min,my $hour,my $mday,my $mon,my $year,my $wday,my $yday,my $isdst) = localtime(); | |
| print "\n" . sprintf("%02d:%02d:%02d", $hour, $min, $sec) . "> get composite"; | |
| ($numtoget{'current'}>1) ? (print "s (offset=$rand/$rangenow)\n") : (print " (offset=$rand/$rangenow)\n"); | |
| } | |
| $waitrequests = 0; | |
| my $idle = 0; | |
| do { | |
| (my $sec,my $min,my $hour,my $mday,my $mon,my $year,my $wday,my $yday,my $isdst) = localtime(); | |
| my $contents = get("$fdburl/listtype.php?t=3&mindig=$mindig&perpage=$numtoget{'current'}&start=$rand&download=1"); | |
| ++$waitrequests; | |
| if (!defined $contents or $contents =~ /[a-z]/) { | |
| $total{'nocomposites'} +=1; | |
| print sprintf("%02d:%02d:%02d", $hour, $min, $sec) . "> No composite(s) received, waiting......($waitrequests)\r"; | |
| STDOUT->flush(); | |
| $idle = 1; | |
| }else { | |
| @composites = split(/\s/, $contents); | |
| my $compositelen = $maxdig+1; | |
| foreach my $composite (@composites) { | |
| my $comlen = length($composite); | |
| if ($comlen< $compositelen) {$compositelen = $comlen;} | |
| } | |
| if ($compositelen > $maxdig) { | |
| # pause while all composites are larger than maximum allowed | |
| print sprintf("%02d:%02d:%02d", $hour, $min, $sec) . "> Max composite size exceeded, waiting...($waitrequests)\r"; | |
| STDOUT->flush(); | |
| # All composites are too big so look at the start of the list from now on, | |
| # in case we missed some due to the random step into the list. | |
| $rand = 0; | |
| $addrange *= 0.5; # decrease additional random offset into composite list | |
| $idle = 1; | |
| }else { | |
| if ($idle) { | |
| # If we are going from idle state back to active state | |
| (my $sec,my $min,my $hour,my $mday,my $mon,my $year,my $wday,my $yday,my $isdst) = localtime(); | |
| print "\n" . sprintf("%02d:%02d:%02d", $hour, $min, $sec) . ">"; | |
| $idle = 0; | |
| } | |
| } | |
| } | |
| if ($idle) { | |
| for (my $i=0; $i<60; $i++) { | |
| sleep(10); | |
| # Now check for key press | |
| $key = ReadKey(-1); | |
| if ($key and (ord($key) == 17)) { | |
| if (defined $options{f}) { | |
| # delete YAFU log files | |
| unlink("factor.log"); | |
| unlink("session.log"); | |
| } | |
| ReadMode 'restore'; | |
| die "\n\n\tCtrl-Q detected\n"; | |
| } | |
| } | |
| } | |
| }while ($idle); | |
| my $pageinc = $numtoget{'current'}+$waitrequests; | |
| $total{'wait_for_comp'} += ($waitrequests-1); | |
| $total{'queries'} += $waitrequests; | |
| push(@page,$pageinc); | |
| push(@nfa,0); | |
| $pages += $pageinc; | |
| if (scalar(@page) > $shortstop) { | |
| $pages -= shift(@page); | |
| $nfas -= shift(@nfa); | |
| } | |
| # Sort into ascending size - if more than one composite is requested | |
| if ($numtoget{'current'} > 1) {@composites = sort{$a<=>$b}@composites;} | |
| foreach my $composite (@composites) { | |
| my $localt = time(); | |
| my $compositelen = length($composite); | |
| last if ($compositelen > $maxdig); | |
| if ($compositelen > $maxcomposite) {$maxcomposite = $compositelen;} | |
| if ($compositelen < $mincomposite) {$mincomposite = $compositelen;} | |
| print "\nFactoring C$compositelen: $composite\n"; | |
| my @results; | |
| open(YAFU, "${yafupath}yafu \"factor($composite)\" -p|") or die "\n\tCouldn't start yafu!"; | |
| while (<YAFU>) { | |
| if ($yafutext) {print "$_";} | |
| chomp; | |
| if (/^[CP].*? = (\d+)/) { | |
| push(@results, $1); | |
| if (!$yafutext) {print "$_\n";} | |
| } | |
| } | |
| print "*****\n"; | |
| close(YAFU); | |
| unlink("siqs.dat"); # I have seen this file re-used when factoring, so delete it here to redo entire factorization! | |
| if (scalar(@results) > 0) { | |
| # Sort factors into descending size order for reporting to database | |
| @results = sort{$b<=>$a}@results; | |
| ++$total{'composites'}; | |
| my $compute_time = time()-$localt; | |
| my $url = "$fdburl/report.php?report=".$composite."%3D".join('*',@results); | |
| my $contents = get($url); | |
| ++$total{'queries'}; | |
| my $elapse = time()-$startime; | |
| my $hours = int($elapse /3600); | |
| my $minutes = int($elapse/60)%60; | |
| my $seconds = $elapse%60; | |
| if (defined $contents) { | |
| my $nofactors = ($contents =~ s/Does not divide//g); | |
| my $already_known = ($contents =~ s/Factor already known//g); | |
| my $added = ($contents =~ s/Factor added//g); | |
| my $small = ($contents =~ s/Small factor//g); | |
| if ($logging) { | |
| my $numfac = $already_known+$added+$small; | |
| open (my $filehandle, '>>', $logfile); | |
| print $filehandle sprintf("%4d",$compositelen) . "," . sprintf("%4d",$numfac) . ", " . sprintf("%.3f",$compute_time) . "\n"; | |
| close ($filehandle); | |
| } | |
| my $whrs = int($total{'wait_for_comp'}/6); | |
| my $wmin = ($total{'wait_for_comp'}%6)*10; | |
| if ($nofactors) {$total{'nofactors'} += $nofactors;} | |
| if ($already_known) {$total{'known'} += $already_known;} | |
| if ($small) {$total{'small'} += $small;} | |
| if ($added) { | |
| $total{'added'} += $added; | |
| push(@nfa,pop(@nfa)+$added); | |
| $nfas += $added; | |
| } | |
| if (!$added and !$already_known and $small) {++$total{'only_small'};} | |
| $addrange *= 0.933; # reduce any existing additional random offset (halves after 10 consecutive non-collisions) | |
| if (!$added and $already_known) { | |
| ++$total{'collision'}; | |
| #increase additional random offset if number of collisions > 5% of composites factored | |
| if (($total{'collision'}/$total{'composites'})>0.05) { | |
| $addrange += $rangeinc; | |
| if($addrange > $maxinc) {$addrange = $maxinc;} # limit the additional random offset | |
| } | |
| } | |
| print "============================================================\n"; | |
| print "Runtime (H:M:S).....................: " . sprintf("%04d",$hours) . ":" . sprintf("%02d",$minutes) . ":" . sprintf("%02d",$seconds) . "\n"; | |
| print "Time waiting for composites (H:M)...: " . sprintf("%04d",$whrs) . ":" . sprintf("%02d",$wmin) . "\n"; | |
| print "Composite range.....................: $mincomposite - $maxcomposite digits\n\n"; | |
| print "Report factors for composite #......: " . $total{'composites'} . "\n"; | |
| print "Factored C$compositelen in...................."; | |
| if ($compositelen < 10) { | |
| print "..: "; | |
| }elsif ($compositelen < 100) { | |
| print ".: "; | |
| }else { | |
| print ": "; | |
| } | |
| print sprintf("%.1f",$compute_time) . " sec.\n\n"; | |
| print "\tNew factors added...........: " . ($added ? $added : 0) . " / " . $total{'added'} . "\n"; | |
| print "\tFactors already known.......: " . ($already_known ? $already_known : 0) . " / " . $total{'known'} . "\n"; | |
| print "\tSmall factors...............: " . ($small ? $small : 0) . " / " . $total{'small'} . "\n\n"; | |
| print "\tOnly small factors..........: " . $total{'only_small'} . "\n"; | |
| print "\tWorker collisions...........: " . $total{'collision'} . "\n"; | |
| if ($total{'nocomposites'} || $total{'noresults'} || $total{'no_ack'} || $total{'nofactors'}) {print "\n"} | |
| if ($total{'nocomposites'}) {print "\tNo composites received......: " . $total{'nocomposites'} . "\n";} | |
| if ($total{'noresults'}) {print "\tNo results from YAFU........: " . $total{'noresults'} . "\n";} | |
| if ($total{'no_ack'}) {print "\tResults not acknowledged....: " . $total{'no_ack'} . "\n";} | |
| if ($total{'nofactors'}) { | |
| print "\tErrors......................: " . ($nofactors ? $nofactors : 0) . " / " . $total{'nofactors'} . "\n"; | |
| print "\t(Factor did not divide composite)\n"; | |
| } | |
| print "\n\tTotal page requests.........: " . $total{'queries'} . "\n"; | |
| print "============================================================\n"; | |
| }else { | |
| print "\nError, no response from FactorDB when reporting results\n"; | |
| ++$total{'no_ack'}; | |
| #sleep(60); | |
| } | |
| }else { | |
| print "Error in YAFU, no factors returned\n"; | |
| ++$total{'noresults'}; | |
| } | |
| } | |
| # 'per composite request' rate limiting: | |
| # Limit the maximum rate by adding extra delay. Will be negative for slow factorisations | |
| # this should help to average out large changes in factorization times. | |
| $delay += ($pcrate*($numtoget{'current'}+1)-(time()-$current)); | |
| # Display current database query rate | |
| $current = time(); | |
| my $elapsed = $current-$startime; # Time to process all composites so far | |
| print sprintf("%7.1f",$total{'composites'}*3600/$elapsed) . " composites/hr\n"; | |
| my $longterm = $total{'queries'}*3600/$elapsed; | |
| print sprintf("%7.1f",$longterm) . " page requests/hr\n"; | |
| # Now try to ensure average rate does not exceed the database page requests per hour limit | |
| # Use average of last '$shortstop' "get composite(s)" times for short term rate calculations. | |
| # Each "get composite(s)" request and each factored composite report generates a page request in the database | |
| # $pages holds the number of page requests invoked by the last '$shortstop' "get composite(s)" | |
| if (scalar(@short) == $shortstop) { | |
| my $shorterm = (3600*$pages) / ($current-$short[0]); | |
| my $shortnfas = (3600*$nfas) / ($current-$short[0]); | |
| print sprintf("%7.1f", $shorterm) . " page requests/hr (last $shortstop composite requests)\n"; | |
| print sprintf("%7.1f", $shortnfas) . " new factors added/hr (last $shortstop composite requests)\n"; | |
| if ($numtoget{'current'} == $numtoget{'throttle'}) { | |
| if ($longterm > $pagerate) { | |
| $delay += ($ltrate*$total{'queries'} - $elapsed); # add to any previous delay(s) | |
| if ($delay > 0) {print "Waiting(Long term rate limit)..." . sprintf("%6.2f",$delay) . " sec.\n";} | |
| }elsif ($shorterm > $pagerate) { | |
| $delay += ($strate*$pages-($current-$short[0])); # add to any previous delay(s) | |
| if ($delay > 0) { print "Waiting(Short term rate limit)." . sprintf("%6.2f",$delay) . " sec.\n";} | |
| } elsif ($delay > 0) { | |
| # Use any remaining delay - | |
| # from the smoothing function and the 'per request' rate limiting | |
| print "Waiting(Smoothing function)......................" . sprintf("%6.2f",$delay) . " sec.\n"; | |
| } | |
| if ($delay > 0) {sleep($delay);} | |
| if ($shorterm < (0.86*$pagerate)) {$numtoget{'current'} = $numtoget{'normal'};} | |
| }elsif ($shorterm > (0.93*$pagerate)) { | |
| $numtoget{'current'} = $numtoget{'throttle'}; | |
| } | |
| } | |
| # This is a sort of smoothing function | |
| # divide by 10 and round, retain 2 digits after decimal, old delays will decay to zero | |
| # Under sustained throttling this will increase calculated delays by approx. 11% | |
| if ($delay > 0) { | |
| $delay = int($delay*10+0.5)/100; | |
| }elsif ($delay < 0) { | |
| $delay = int($delay*10-0.5)/100; | |
| } | |
| # Now check for key press | |
| $key = ReadKey(-1); | |
| # Check to see if Ctrl-Q has been pressed, if not then continue otherwise quit program | |
| }while (!$key or (ord($key) != 17)); | |
| if (defined $options{f}) { | |
| # delete YAFU log files | |
| unlink("factor.log"); | |
| unlink("session.log"); | |
| } | |
| ReadMode 'restore'; | |
| die "\n\tCtrl-Q detected\n"; |