fermat_pseudoprimes_in_range_with_prefix_mpz.pl 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  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.036;
  9. use ntheory qw(:all);
  10. use Math::GMPz;
  11. my $max_p = 1000000;
  12. my %znorder = map { $_ => znorder(2, $_) } @{primes($max_p)};
  13. sub fermat_pseudoprimes_in_range ($A, $B, $k, $base, $callback) {
  14. #my $m = "8833404609327838592895595408965";
  15. #my $m = "1614825036214963273306005";
  16. #my $m = Math::GMPz->new("19258022593463164626195195");
  17. #my $m = Math::GMPz->new("19976310800932286865"); # finds new abundant Fermat psp
  18. my $m = Math::GMPz->new("2799500171953451613547965"); # finds new abundant Fermat psp
  19. #my $m = Math::GMPz->new("551501533874829967868949105"); # finds new abundant Fermat psp
  20. #my $m = Math::GMPz->new("1389172629407632160878965"); # finds new abundant Fermat psp
  21. #my $m = Math::GMPz->new("3935333227783660512405"); # finds new abundant Fermat psp
  22. #my $m = Math::GMPz->new("15312580652854710165"); # finds new abundant Fermat psp
  23. #my $m = Math::GMPz->new("7051637712729097263345");
  24. #my $m = Math::GMPz->new("1256975577207099774483036285");
  25. #my $m = Math::GMPz->new("24383833295");
  26. my $L = znorder($base, $m);
  27. $L = Math::GMPz->new("$L");
  28. $A = $A*$m;
  29. $B = $B*$m;
  30. $A = vecmax($A, pn_primorial($k));
  31. $A = Math::GMPz->new("$A");
  32. $B = Math::GMPz->new("$B");
  33. if ($B > Math::GMPz->new("898943937249247967890084629421065")) {
  34. $B = Math::GMPz->new("898943937249247967890084629421065");
  35. }
  36. if ($A > $B) {
  37. return;
  38. }
  39. my $u = Math::GMPz::Rmpz_init();
  40. my $v = Math::GMPz::Rmpz_init();
  41. sub ($m, $L, $lo, $k) {
  42. Math::GMPz::Rmpz_tdiv_q($u, $B, $m);
  43. Math::GMPz::Rmpz_root($u, $u, $k);
  44. my $hi = Math::GMPz::Rmpz_get_ui($u);
  45. $hi = vecmin($max_p, $hi);
  46. if ($lo > $hi) {
  47. return;
  48. }
  49. if ($k == 1) {
  50. Math::GMPz::Rmpz_cdiv_q($u, $A, $m);
  51. if (Math::GMPz::Rmpz_fits_ulong_p($u)) {
  52. $lo = vecmax($lo, Math::GMPz::Rmpz_get_ui($u));
  53. }
  54. elsif (Math::GMPz::Rmpz_cmp_ui($u, $lo) > 0) {
  55. if (Math::GMPz::Rmpz_cmp_ui($u, $hi) > 0) {
  56. return;
  57. }
  58. $lo = Math::GMPz::Rmpz_get_ui($u);
  59. }
  60. if ($lo > $hi) {
  61. return;
  62. }
  63. Math::GMPz::Rmpz_invert($v, $m, $L) || return;
  64. if (Math::GMPz::Rmpz_cmp_ui($v, $hi) > 0) {
  65. return;
  66. }
  67. if (Math::GMPz::Rmpz_fits_ulong_p($L)) {
  68. $L = Math::GMPz::Rmpz_get_ui($L);
  69. }
  70. my $t = Math::GMPz::Rmpz_get_ui($v);
  71. $t > $hi && return;
  72. $t += $L while ($t < $lo);
  73. for (my $p = $t ; $p <= $hi ; $p += $L) {
  74. if (is_prime($p) and $base % $p != 0 and !Math::GMPz::Rmpz_divisible_ui_p($m, $p)) {
  75. Math::GMPz::Rmpz_mul_ui($v, $m, $p);
  76. Math::GMPz::Rmpz_sub_ui($u, $v, 1);
  77. my $z = ($znorder{$p} // znorder($base, $p));
  78. if (Math::GMPz::Rmpz_divisible_ui_p($u, $z)) {
  79. $callback->(Math::GMPz::Rmpz_init_set($v));
  80. }
  81. }
  82. }
  83. return;
  84. }
  85. my $t = Math::GMPz::Rmpz_init();
  86. my $lcm = Math::GMPz::Rmpz_init();
  87. foreach my $p (@{primes($lo, $hi)}) {
  88. Math::GMPz::Rmpz_divisible_ui_p($m, $p) and next;
  89. $base % $p == 0 and next;
  90. my $z = ($znorder{$p} // znorder($base, $p));
  91. Math::GMPz::Rmpz_gcd_ui($Math::GMPz::NULL, $m, $z) == 1 or next;
  92. Math::GMPz::Rmpz_lcm_ui($lcm, $L, $z);
  93. Math::GMPz::Rmpz_mul_ui($t, $m, $p);
  94. __SUB__->($t, $lcm, $p + 1, $k - 1);
  95. }
  96. }
  97. ->($m, $L, 3, $k);
  98. return 1;
  99. }
  100. my $base = 2;
  101. my $from = Math::GMPz->new("2");
  102. my $upto = 2*$from;
  103. while (1) {
  104. my $ok = 0;
  105. say "# Range: ($from, $upto)";
  106. foreach my $k (2..100) {
  107. fermat_pseudoprimes_in_range($from, $upto, $k, $base, sub ($n) { say $n }) or next;
  108. $ok = 1;
  109. }
  110. $ok || last;
  111. $from = $upto+1;
  112. $upto = 2*$from;
  113. }