fast_prog.pl 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. #!/usr/bin/perl
  2. # Daniel "Trizen" Șuteu
  3. # Date: 24 September 2022
  4. # https://github.com/trizen
  5. # New terms found (24 September 2022):
  6. # a(11) = 24325630440506854886701
  7. # a(12) = 27146803388402594456683201
  8. # a(13) = 4365221464536367089854499301
  9. # a(14) = 2162223198751674481689868383601
  10. # a(15) = 548097717006566233800428685318301
  11. =for comment
  12. PARI/GP program:
  13. strong_fermat_psp(A, B, k, base) = A=max(A, vecprod(primes(k))); (f(m, l, p, j, k_exp, congr) = my(list=List()); forprime(q=p, sqrtnint(B\m, j), if(base%q != 0, my(tv=valuation(q-1, 2)); if(tv > k_exp && Mod(base, q)^(((q-1)>>tv)<<k_exp) == congr, my(v=m*q, t=q, r=nextprime(q+1)); while(v <= B, my(L=lcm(l, znorder(Mod(base, t)))); if(gcd(L, v) == 1, if(j==1, if(v>=A && if(k==1, !isprime(v), 1) && (v-1)%L == 0, listput(list, v)), if(v*r <= B, list=concat(list, f(v, L, r, j-1, k_exp, congr)))), break); v *= q; t *= q)))); list); my(res=f(1, 1, 2, k, 0, 1)); for(v=0, logint(B, 2), res=concat(res, f(1, 1, 2, k, v, -1))); vecsort(Vec(res));
  14. a(n) = if(n < 2, return()); my(x=vecprod(primes(n)), y=2*x); while(1, my(v=strong_fermat_psp(x, y, n, 2)); if(#v >= 1, return(v[1])); x=y+1; y=2*x); \\ ~~~~
  15. =cut
  16. use 5.020;
  17. use warnings;
  18. use ntheory qw(:all);
  19. use experimental qw(signatures);
  20. use Math::GMPz;
  21. sub divceil ($x, $y) { # ceil(x/y)
  22. my $q = ($x / $y);
  23. ($q * $y == $x) ? $q : ($q + 1);
  24. }
  25. sub squarefree_strong_fermat_pseudoprimes_in_range ($A, $B, $k, $base, $callback) {
  26. $A = vecmax($A, Math::GMPz->new(pn_primorial($k)));
  27. $A > $B and return;
  28. my $generator = sub ($m, $lambda, $p, $k, $k_exp, $congr, $u = undef, $v = undef) {
  29. if ($k == 1) {
  30. forprimes {
  31. my $valuation = valuation($_ - 1, 2);
  32. if ($valuation > $k_exp and powmod($base, (($_ - 1) >> $valuation) << $k_exp, $_) == ($congr % $_)) {
  33. my $t = $m * $_;
  34. if (Math::GMPz::Rmpz_divisible_ui_p($t - 1, $lambda) and Math::GMPz::Rmpz_divisible_ui_p($t - 1, znorder($base, $_))) {
  35. say "# Found: $t";
  36. $callback->($t);
  37. $B = $t if ($t < $B);
  38. }
  39. }
  40. } $u, $v;
  41. return;
  42. }
  43. my $s = rootint(($B / $m), $k);
  44. for (my $r ; $p <= $s ; $p = $r) {
  45. $r = next_prime($p);
  46. $base % $p == 0 and next;
  47. my $valuation = valuation($p - 1, 2);
  48. $valuation > $k_exp or next;
  49. powmod($base, (($p - 1) >> $valuation) << $k_exp, $p) == ($congr % $p) or next;
  50. my $z = znorder($base, $p);
  51. my $L = lcm($lambda, $z);
  52. gcd($L, $m) == 1 or next;
  53. my $t = $m * $p;
  54. my $u = divceil($A, $t);
  55. my $v = ($B / $t);
  56. if ($u <= $v) {
  57. __SUB__->($t, $L, $r, $k - 1, $k_exp, $congr, (($k == 2 && $r > $u) ? $r : $u), $v);
  58. }
  59. }
  60. };
  61. say "# Sieving range: [$A, $B]";
  62. # Case where 2^d == 1 (mod p), where d is the odd part of p-1.
  63. $generator->(Math::GMPz->new(1), 1, 2, $k, 0, 1);
  64. # Cases where 2^(d * 2^v) == -1 (mod p), for some v >= 0.
  65. foreach my $v (0 .. logint($B, 2)) {
  66. say "# Generating with v = $v";
  67. $generator->(Math::GMPz->new(1), 1, 2, $k, $v, -1);
  68. }
  69. }
  70. my $k = 10;
  71. my $from = Math::GMPz->new(2);
  72. my $upto = Math::GMPz->new(pn_primorial($k));
  73. while (1) {
  74. my @found;
  75. squarefree_strong_fermat_pseudoprimes_in_range($from, $upto, $k, 2, sub ($n) { push @found, $n });
  76. if (@found) {
  77. @found = sort {$a <=> $b} @found;
  78. say "Terms: @found";
  79. say "a($k) = $found[0]";
  80. last;
  81. }
  82. $from = $upto+1;
  83. $upto = 2*$from;
  84. }