squarefree_strong_fermat_psp_with_n_prime_factors.pl 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. #!/usr/bin/perl
  2. # Daniel "Trizen" Șuteu
  3. # Date: 24 September 2022
  4. # https://github.com/trizen
  5. # Generate all the squarefree strong Fermat pseudoprimes to given a base with n prime factors in a given range [A,B]. (not in sorted order)
  6. # See also:
  7. # https://en.wikipedia.org/wiki/Almost_prime
  8. # https://trizenx.blogspot.com/2020/08/pseudoprimes-construction-methods-and.html
  9. use 5.020;
  10. use warnings;
  11. use ntheory qw(:all);
  12. use experimental qw(signatures);
  13. use Math::GMPz;
  14. sub divceil ($x, $y) { # ceil(x/y)
  15. my $q = ($x / $y);
  16. ($q * $y == $x) ? $q : ($q + 1);
  17. }
  18. sub squarefree_strong_fermat_pseudoprimes_in_range ($A, $B, $k, $base, $callback) {
  19. $A = vecmax($A, Math::GMPz->new(pn_primorial($k)));
  20. $A > $B and return;
  21. my $generator = sub ($m, $lambda, $p, $k, $k_exp, $congr, $u = undef, $v = undef) {
  22. if ($k == 1) {
  23. forprimes {
  24. my $valuation = valuation($_ - 1, 2);
  25. if ($valuation > $k_exp
  26. and powmod($base, (($_ - 1) >> $valuation) << $k_exp, $_) == ($congr % $_)) {
  27. my $t = $m * $_;
  28. if (($t - 1) % $lambda == 0 and ($t - 1) % znorder($base, $_) == 0) {
  29. say "# Found: $t";
  30. $callback->($t);
  31. }
  32. }
  33. } $u, $v;
  34. return;
  35. }
  36. my $s = rootint($B / $m, $k);
  37. for (my $r ; $p <= $s ; $p = $r) {
  38. $r = next_prime($p);
  39. $base % $p == 0 and next;
  40. my $valuation = valuation($p - 1, 2);
  41. $valuation > $k_exp or next;
  42. powmod($base, (($p - 1) >> $valuation) << $k_exp, $p) == ($congr % $p) or next;
  43. my $z = znorder($base, $p);
  44. my $L = lcm($lambda, $z);
  45. gcd($L, $m) == 1 or next;
  46. my $t = $m * $p;
  47. my $u = divceil($A, $t);
  48. my $v = ($B / $t);
  49. if ($u <= $v) {
  50. __SUB__->($t, $L, $r, $k - 1, $k_exp, $congr, (($k == 2 && $r > $u) ? $r : $u), $v);
  51. }
  52. }
  53. };
  54. say "# Sieving range: [$A, $B]";
  55. # Case where 2^d == 1 (mod p), where d is the odd part of p-1.
  56. $generator->(Math::GMPz->new(1), 1, 2, $k, 0, 1);
  57. # Cases where 2^(d * 2^v) == -1 (mod p), for some v >= 0.
  58. foreach my $v (0 .. logint($B, 2)) {
  59. say "# Generating with v = $v";
  60. $generator->(Math::GMPz->new(1), 1, 2, $k, $v, -1);
  61. }
  62. }
  63. # Generate all the squarefree strong Fermat pseudoprimes to base 2 with 3 prime factors in the range [1, 10^7]
  64. my $k = 10;
  65. my $from = Math::GMPz->new(2);
  66. my $upto = Math::GMPz->new(pn_primorial($k));
  67. while (1) {
  68. my @found;
  69. squarefree_strong_fermat_pseudoprimes_in_range($from, $upto, $k, 2, sub ($n) { push @found, $n });
  70. if (@found) {
  71. @found = sort {$a <=> $b} @found;
  72. say "Terms: @found";
  73. say "a($k) = $found[0]";
  74. last;
  75. }
  76. $from = $upto+1;
  77. $upto = 2*$from;
  78. }