#!perl use warnings; use strict; use Inline "C", <<__ENDC; /* mulmod(a, b, m) gives (a*b)%c */ long mulmod (long a, long b, long m) { long long t = (long long)a * b; return (long)(t % m); } __ENDC { # Miller--Rabin test my @smallprimes = ( 2, 3, 5, 7, 11, 13, 17, 19, ); #### # primep($x) returns whether $x is a prime sub primep_rm { my($cand, $base, $p, $iter, $exp, $odd, $t, $x, $xprev); $cand = 0 + $_[0]; 1 < $cand && $cand == int($cand) or die "error: invalid number to primep_rm: $cand"; for $p (@smallprimes) { 0 != $cand % $p or return $p == $cand; } ($exp, $odd) = (0, $cand - 1); while (0 == $odd % 2) { ($exp, $odd) = ($exp + 1, $odd >> 1); } for $iter (1 .. 52) { $base = 1 + int(rand($cand - 1)); $x = powmod($base, $odd, $cand); SQUARING: for $t (1 .. $exp) { ($xprev, $x) = ($x, mulmod($x, $x, $cand)); $x == 1 and do { $xprev == 1 || $xprev == $cand - 1 or return; last; } } # KEY STEP: $x now contains $base**($cand - 1) % $cand # which should be 1 because of the Fermat theorem. $x == 1 or return; } return 1; } #### # powmod($a, $f, $m) calculates $a**$f % $m, # precondition: 1 < $m, 0 < $f, $a are integers representable as a perl integer sub powmod { my($a, $f, $m) = @_; my $r = 1; for (;;) { 0 != ($f & 1) and $r = mulmod($r, $a, $m); $f >>= 1; 0 == $f and last; $a = mulmod($a, $a, $m); } $r; } } { # simple factoring my @primes; { my($n, $p, $sqrp); NLOOP: for $n (2 .. 100_100) { $sqrp = sqrt($n) + 0.5; for $p (@primes) { $sqrp < $p and last; 0 == $n % $p and next NLOOP; } push @primes, $n } } my $greatest_prime_squared = $primes[-1]**2; #### sub primep_div { my $cand = 0 + $_[0]; my($p, $sqr); 1 < $cand && $cand == int($cand) or die "error: invalid number to primep_div: $cand"; $cand < $greatest_prime_squared or die "error: number too large in primep_div: $cand"; $sqr = sqrt($cand) + 0.5; for $p (@primes) { $p <= $sqr or return 1; 0 == $cand % $p and return $p == $cand; } die "internal error: shouldn't fall through here"; } #### } if (1) { my $test = sub { my($n, $p_rm, $p_div); $n = $_[0]; $p_rm = !!primep_rm($n); $p_div = !!primep_div($n); $p_rm == $p_div or die "error: primality tests don't match: $n"; }; my($n, $r, $l); warn "begin serial tests"; for $n (2 .. 100000) { &$test($n); } warn "begin random tests"; for $l (3 .. 9) { for $r (1 .. 10000) { $n = 2 + int(rand(10**$l)); &$test($n); } } warn "all ok"; } use Benchmark ":all"; if (1) { warn "start benchmarks"; for my $len (3 .. 9) { my($r_rm, $r_div); my $ntries = $len < 8 ? 50_000 : 20_000; $ntries /= 5; my @tries = map { 10**($len - 1) + 1 + 2 * int(rand((10**$len - 10**($len - 1))/2)); } 0 .. $ntries; warn "benchmarking $len digit odd numbers, $ntries in total"; cmpthese(1, { "primep_rm", sub { my $r; $r += primep_rm($_) ? 1 : 0 for @tries; $r_rm = $r; }, "primep_div", sub { my $r; $r += primep_div($_) ? 1 : 0 for @tries; $r_div = $r; }, }); $r_rm == $r_div or die "error: primality tests don't agree in total"; } } __END__ #### begin serial tests at p line 125. begin random tests at p line 129. all ok at p line 136. start benchmarks at p line 143. benchmarking 3 digit odd numbers, 10000 in total at p line 151. (warning: too few iterations for a reliable count) (warning: too few iterations for a reliable count) benchmarking 4 digit odd numbers, 10000 in total at p line 151. s/iter primep_rm primep_div primep_rm 11.2 -- -97% primep_div 0.300 3637% -- (warning: too few iterations for a reliable count) (warning: too few iterations for a reliable count) benchmarking 5 digit odd numbers, 10000 in total at p line 151. s/iter primep_rm primep_div primep_rm 10.0 -- -96% primep_div 0.370 2614% -- (warning: too few iterations for a reliable count) (warning: too few iterations for a reliable count) benchmarking 6 digit odd numbers, 10000 in total at p line 151. s/iter primep_rm primep_div primep_rm 9.63 -- -95% primep_div 0.490 1865% -- (warning: too few iterations for a reliable count) (warning: too few iterations for a reliable count) benchmarking 7 digit odd numbers, 10000 in total at p line 151. s/iter primep_rm primep_div primep_rm 9.04 -- -92% primep_div 0.740 1122% -- (warning: too few iterations for a reliable count) (warning: too few iterations for a reliable count) benchmarking 8 digit odd numbers, 4000 in total at p line 151. s/iter primep_rm primep_div primep_rm 8.87 -- -85% primep_div 1.34 562% -- (warning: too few iterations for a reliable count) (warning: too few iterations for a reliable count) benchmarking 9 digit odd numbers, 4000 in total at p line 151. s/iter primep_rm primep_div primep_rm 3.36 -- -68% primep_div 1.08 211% -- (warning: too few iterations for a reliable count) (warning: too few iterations for a reliable count) s/iter primep_rm primep_div primep_rm 3.41 -- -30% primep_div 2.37 44% --