squarefree_strong_fermat_pseudoprimes_in_range.pl 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  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 a given 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. sub divceil ($x, $y) { # ceil(x/y)
  14. (($x % $y == 0) ? 0 : 1) + divint($x, $y);
  15. }
  16. sub squarefree_strong_fermat_pseudoprimes_in_range ($A, $B, $k, $base) {
  17. $A = vecmax($A, pn_primorial($k));
  18. if ($A > $B) {
  19. return;
  20. }
  21. my @list;
  22. my $generator = sub ($m, $L, $lo, $k, $k_exp, $congr) {
  23. my $hi = rootint(divint($B, $m), $k);
  24. if ($lo > $hi) {
  25. return;
  26. }
  27. if ($k == 1) {
  28. $lo = vecmax($lo, divceil($A, $m));
  29. $lo > $hi && return;
  30. my $t = invmod($m, $L);
  31. $t > $hi && return;
  32. $t += $L * divceil($lo - $t, $L) if ($t < $lo);
  33. for (my $p = $t ; $p <= $hi ; $p += $L) {
  34. is_prime($p) || next;
  35. $base % $p == 0 and next;
  36. my $val = valuation($p - 1, 2);
  37. if ($val > $k_exp and powmod($base, ($p - 1) >> ($val - $k_exp), $p) == ($congr % $p)) {
  38. my $n = $m * $p;
  39. if (($n - 1) % znorder($base, $p) == 0) {
  40. push @list, $n;
  41. }
  42. }
  43. }
  44. return;
  45. }
  46. foreach my $p (@{primes($lo, $hi)}) {
  47. $base % $p == 0 and next;
  48. my $val = valuation($p - 1, 2);
  49. $val > $k_exp or next;
  50. powmod($base, ($p - 1) >> ($val - $k_exp), $p) == ($congr % $p) or next;
  51. my $z = znorder($base, $p);
  52. if (gcd($m, $z) == 1) {
  53. __SUB__->($m * $p, lcm($L, $z), $p + 1, $k - 1, $k_exp, $congr);
  54. }
  55. }
  56. };
  57. # Case where 2^d == 1 (mod p), where d is the odd part of p-1.
  58. $generator->(1, 1, 2, $k, 0, 1);
  59. # Cases where 2^(d * 2^v) == -1 (mod p), for some v >= 0.
  60. foreach my $v (0 .. logint($B, 2)) {
  61. $generator->(1, 1, 2, $k, $v, -1);
  62. }
  63. return sort { $a <=> $b } @list;
  64. }
  65. # Generate all the squarefree strong Fermat pseudoprimes to base 2 with 3 prime factors in the range [1, 10^8]
  66. my $k = 3;
  67. my $base = 2;
  68. my $from = 1;
  69. my $upto = 1e8;
  70. my @arr = squarefree_strong_fermat_pseudoprimes_in_range($from, $upto, $k, $base);
  71. say join(', ', @arr);
  72. __END__
  73. 15841, 29341, 52633, 74665, 252601, 314821, 476971, 635401, 1004653, 1023121, 1907851, 1909001, 2419385, 2953711, 3581761, 4335241, 4682833, 5049001, 5444489, 5599765, 5681809, 9069229, 13421773, 15247621, 15510041, 15603391, 17509501, 26254801, 26758057, 27966709, 29111881, 35703361, 36765901, 37769887, 38342071, 44963029, 47349373, 47759041, 53399449, 53711113, 54468001, 60155201, 61377109, 61755751, 66977281, 68154001, 70030501, 71572957, 74329399, 82273201, 91659283, 99036001