File: verify_primegap.pl

package info (click to toggle)
libmath-prime-util-gmp-perl 0.27-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 1,024 kB
  • ctags: 696
  • sloc: ansic: 10,302; perl: 2,855; sh: 158; makefile: 2
file content (139 lines) | stat: -rw-r--r-- 3,775 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#!/usr/bin/perl

# Verify a prime gap given the start (as number or expression)

# If start is less than 200 digits, then endpoints will be proven.
#
# Otherwise, all values will use BPSW + 1 M-R:
#    a) divisibility by small factors
#    b) SPSP-2
#    c) extra strong Lucas test
#    d) 1 Miller-Rabin test with a random base 3 .. N-2
#
# No composite has been found that passes (b) + (c), so the combination
# plus the extra M-R should give quite a bit of certainty that the
# endpoints really are prime.
#
# Runs in parallel for number larger than 200 digits.

# Examples:
#
#   verify_primegap.pl "9169*439#/55230 - 6926"
#   verify_primegap.pl "1931*1933#/7230 - 30244"  2
#   verify_primegap.pl "5557*4973#/2310 - 83542" 12

use warnings;
use strict;
use threads;
use threads::shared;
use feature 'say';
use Math::Prime::Util ':all';
use Math::Prime::Util::GMP;
use Math::BigInt try=>"GMP";
use Math::BigFloat;
$|=1;
my @mpu_funcs = (qw/next_prime prev_prime prime_count nth_prime random_prime
                    random_ndigit_prime random_nbit_prime random_strong_prime
                    random_maurer_prime primorial pn_primorial moebius mertens
                    euler_phi jordan_totient exp_mangoldt divisor_sum
                    consecutive_integer_lcm/);
my %mpu_func_map;

my $N = shift || dieusage();
my $nthreads = shift || 8;

$N =~ s/\s*$//;  $N =~ s/^\s*//;
$N = eval_expr($N) unless $N =~ /^\d+$/;
$N = Math::BigInt->new("$N") unless ref($N) eq 'Math::BigInt';
$nthreads = 1 if length($N) <= 200;
say_primality("start (" . length($N) . " digits)", $N);

if ($nthreads <= 1) {
  my $end = next_prime($N);
  my $gap = $end-$N;
  my $merit = merit($N, $gap);
  say_primality("Endpoint (merit $merit) n+$gap", $end);
  exit(0);
}

my $searchto :shared = 1;
my $found    :shared = 0;

my @threads;
push @threads, threads->create('search', $_) for 1 .. $nthreads;
$_->join() for (@threads);
{
  my $gap = $found;
  my $end = $N+$found;
  my $merit = merit($N, $gap);
  say_primality("Endpoint (merit $merit) n+$gap", $end);
  exit(0);
}

sub search {
  my $tnum = shift;
  while (!$found) {
    my ($j, $n);
    {
      lock($searchto);
      $j = $searchto++;
    }
    #print "  +$j($tnum)  ";
    if ( Math::Prime::Util::GMP::is_prime($N + $j) ) {
      lock($found);
      if ($found == 0 || $found > $j) {
        $found = $j;
      }
      last;
    }
  }
}


sub eval_expr {
  my $expr = shift;
  die "$expr cannot be evaluated" if $expr =~ /:/;  # Use : for escape
  if (scalar(keys %mpu_func_map) == 0) {
    my $n = 10;
    foreach my $func (@mpu_funcs) {
      $mpu_func_map{$func} = sprintf("%03d", $n++);
    }
  }
  $expr =~ s/\^/**/g;
  $expr =~ s/\b(\d+)#/primorial($1)/g;
  $expr =~ s/\blog\(/:001(/g;
  foreach my $func (@mpu_funcs) {
    $expr =~ s/\b$func\(/:$mpu_func_map{$func}(/g;
  }
  die "$expr cannot be evaluated" if $expr =~ tr|-0123456789+*/() :||c;
  $expr =~ s/:001/log/g;
  foreach my $func (@mpu_funcs) {
    $expr =~ s/:$mpu_func_map{$func}\(/ Math::BigInt->bone*Math::Prime::Util::$func(/g;
  }
  $expr =~ s/(\d+)/ Math::BigInt->new("$1") /g;
  my $res = eval $expr; ## no critic
  die "Cannot eval: $expr\n" if !defined $res;
  $res = int($res->bstr) if ref($res) eq 'Math::BigInt' && $res <= ~0;
  $res;
}

sub say_primality {
  my($text, $n) = @_;
  print "$text is ";
  my $is_prime = (length($n) <= 200) ? is_provable_prime($n) : is_prime($n);
  print "", ("composite",
             "probably prime (BPSW + 1 M-R)",
             "proven prime")[$is_prime];
  print "\n";
}

sub merit {
  my($n, $gap) = @_;
  my $fgap = Math::BigFloat->new("$gap");
  my $fn   = Math::BigFloat->new("$n");
  return sprintf("%7.4f", $fgap / log($fn));
}

sub dieusage {
  die "Usage: $0  \"expression\"  [number-of-threads]\n";
}