fermat_pseudoprimes_in_range_with_prefix.pl 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. #!/usr/bin/perl
  2. # Daniel "Trizen" Șuteu
  3. # Date: 28 August 2022
  4. # https://github.com/trizen
  5. # Generate all the squarefree 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. use 5.020;
  9. use ntheory qw(:all);
  10. use experimental qw(signatures);
  11. use Math::GMP qw(:constant);
  12. sub divceil ($x,$y) { # ceil(x/y)
  13. my $q = $x/$y;
  14. ($q*$y == $x) ? $q : ($q+1);
  15. }
  16. my $max_p = 1000000;
  17. my %znorder = map { $_ => znorder(2, $_) } @{primes($max_p)};
  18. sub fermat_pseudoprimes_in_range ($A, $B, $k, $base, $callback) {
  19. #my $m = "8833404609327838592895595408965";
  20. #my $m = "1614825036214963273306005";
  21. #my $m = Math::GMP->new("19258022593463164626195195");
  22. #my $m = Math::GMP->new("19976310800932286865"); # finds new abundant Fermat psp
  23. my $m = Math::GMP->new("2799500171953451613547965"); # finds new abundant Fermat psp
  24. #my $m = Math::GMP->new("551501533874829967868949105"); # finds new abundant Fermat psp
  25. #my $m = Math::GMP->new("1389172629407632160878965"); # finds new abundant Fermat psp
  26. #my $m = Math::GMP->new("3935333227783660512405"); # finds new abundant Fermat psp
  27. #my $m = Math::GMP->new("15312580652854710165"); # finds new abundant Fermat psp
  28. #my $m = Math::GMP->new("7051637712729097263345");
  29. #my $m = Math::GMP->new("1256975577207099774483036285");
  30. #my $m = Math::GMP->new("24383833295");
  31. my $L = znorder($base, $m);
  32. $A = $A*$m;
  33. $B = $B*$m;
  34. $A = vecmax($A, pn_primorial($k));
  35. $A = Math::GMP->new("$A");
  36. $B = Math::GMP->new("$B");
  37. if ($B > Math::GMP->new("898943937249247967890084629421065")) {
  38. $B = Math::GMP->new("898943937249247967890084629421065");
  39. }
  40. if ($A > $B) {
  41. return;
  42. }
  43. sub ($m, $L, $lo, $k) {
  44. my $hi = rootint($B/$m, $k);
  45. $hi = vecmin($max_p, $hi);
  46. if ($lo > $hi) {
  47. return;
  48. }
  49. if ($k == 1) {
  50. $lo = vecmax($lo, divceil($A, $m));
  51. $lo > $hi && return;
  52. my $t = invmod($m, $L);
  53. $t > $hi && return;
  54. $t += $L while ($t < $lo);
  55. for (my $p = $t ; $p <= $hi ; $p += $L) {
  56. if (is_prime($p)) {
  57. my $n = $m*$p;
  58. if (($n - 1) % $znorder{$p} == 0) {
  59. $callback->($n);
  60. }
  61. }
  62. }
  63. return;
  64. }
  65. foreach my $p (@{primes($lo, $hi)}) {
  66. if ($base % $p == 0) {
  67. next;
  68. }
  69. if ($m%$p == 0) {
  70. next;
  71. }
  72. my $z = $znorder{$p};
  73. #is_smooth($z, 13) || next;
  74. #is_smooth($z, 19) || next;
  75. gcd($m, $z) == 1 or next;
  76. __SUB__->($m*$p, lcm($L, $z), $p+1, $k-1);
  77. }
  78. }->($m, $L, 3, $k);
  79. return 1;
  80. }
  81. my $base = 2;
  82. my $from = 2;
  83. my $upto = 2*$from;
  84. while (1) {
  85. my $ok = 0;
  86. say "# Range: ($from, $upto)";
  87. foreach my $k (2..100) {
  88. fermat_pseudoprimes_in_range($from, $upto, $k, $base, sub ($n) { say $n }) or next;
  89. $ok = 1;
  90. }
  91. $ok || last;
  92. $from = $upto+1;
  93. $upto = 2*$from;
  94. }