squarefree_strong_fermat_pseudoprimes_to_multiple_bases_in_range_mpz.pl 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. #!/usr/bin/perl
  2. # Daniel "Trizen" Șuteu
  3. # Date: 23 December 2023
  4. # https://github.com/trizen
  5. # Generate all the squarefree k-omega strong Fermat pseudoprimes in range [A,B] to multiple given bases. (not in sorted order)
  6. # See also:
  7. # https://trizenx.blogspot.com/2020/08/pseudoprimes-construction-methods-and.html
  8. use 5.036;
  9. use warnings;
  10. use Math::GMPz;
  11. use ntheory qw(:all);
  12. sub divceil ($x, $y) { # ceil(x/y)
  13. (($x % $y == 0) ? 0 : 1) + divint($x, $y);
  14. }
  15. sub k_squarefree_strong_fermat_pseudoprimes_in_range ($A, $B, $k, $bases) {
  16. $A = vecmax($A, pn_primorial($k));
  17. my @bases = @$bases;
  18. my $bases_lcm = lcm(@bases);
  19. $A = Math::GMPz->new("$A");
  20. $B = Math::GMPz->new("$B");
  21. my $u = Math::GMPz::Rmpz_init();
  22. my $v = Math::GMPz::Rmpz_init();
  23. my @list;
  24. my $generator = sub ($m, $L, $lo, $k) {
  25. Math::GMPz::Rmpz_tdiv_q($u, $B, $m);
  26. Math::GMPz::Rmpz_root($u, $u, $k);
  27. my $hi = Math::GMPz::Rmpz_get_ui($u);
  28. if ($lo > $hi) {
  29. return;
  30. }
  31. if ($k == 1) {
  32. Math::GMPz::Rmpz_cdiv_q($u, $A, $m);
  33. if (Math::GMPz::Rmpz_fits_ulong_p($u)) {
  34. $lo = vecmax($lo, Math::GMPz::Rmpz_get_ui($u));
  35. }
  36. elsif (Math::GMPz::Rmpz_cmp_ui($u, $lo) > 0) {
  37. if (Math::GMPz::Rmpz_cmp_ui($u, $hi) > 0) {
  38. return;
  39. }
  40. $lo = Math::GMPz::Rmpz_get_ui($u);
  41. }
  42. if ($lo > $hi) {
  43. return;
  44. }
  45. Math::GMPz::Rmpz_invert($v, $m, $L);
  46. if (Math::GMPz::Rmpz_cmp_ui($v, $hi) > 0) {
  47. return;
  48. }
  49. if (Math::GMPz::Rmpz_fits_ulong_p($L)) {
  50. $L = Math::GMPz::Rmpz_get_ui($L);
  51. }
  52. my $t = Math::GMPz::Rmpz_get_ui($v);
  53. $t > $hi && return;
  54. $t += $L * divceil($lo - $t, $L) if ($t < $lo);
  55. for (my $p = $t ; $p <= $hi ; $p += $L) {
  56. is_prime($p) || next;
  57. $bases_lcm % $p == 0 and next;
  58. Math::GMPz::Rmpz_mul_ui($v, $m, $p);
  59. Math::GMPz::Rmpz_sub_ui($u, $v, 1);
  60. if (vecall { is_strong_pseudoprime($v, $_) } @bases) {
  61. push(@list, Math::GMPz::Rmpz_init_set($v));
  62. }
  63. }
  64. return;
  65. }
  66. my $t = Math::GMPz::Rmpz_init();
  67. my $lcm = Math::GMPz::Rmpz_init();
  68. foreach my $p (@{primes($lo, $hi)}) {
  69. $bases_lcm % $p == 0 and next;
  70. my $z = lcm(map { znorder($_, $p) } @bases);
  71. Math::GMPz::Rmpz_gcd_ui($Math::GMPz::NULL, $m, $z) == 1 or next;
  72. Math::GMPz::Rmpz_lcm_ui($lcm, $L, $z);
  73. Math::GMPz::Rmpz_mul_ui($t, $m, $p);
  74. __SUB__->($t, $lcm, $p + 1, $k - 1);
  75. }
  76. }
  77. ->(Math::GMPz->new(1), Math::GMPz->new(1), 2, $k);
  78. return sort { $a <=> $b } @list;
  79. }
  80. sub squarefree_strong_fermat_pseudoprimes_in_range ($from, $upto, $bases) {
  81. my @arr;
  82. for (my $k = 2 ; ; ++$k) {
  83. last if pn_primorial($k) > $upto;
  84. push @arr, k_squarefree_strong_fermat_pseudoprimes_in_range($from, $upto, $k, $bases);
  85. }
  86. return sort { $a <=> $b } @arr;
  87. }
  88. my @bases = (17, 31);
  89. my $lo = Math::GMPz->new(2);
  90. my $hi = 2 * $lo;
  91. say ":: Searching for the smallest strong pseudoprime to bases: (@bases)";
  92. while (1) {
  93. say ":: Sieving range: [$lo, $hi]";
  94. my @arr = squarefree_strong_fermat_pseudoprimes_in_range($lo, $hi, \@bases);
  95. if (@arr) {
  96. say "\nFound: $arr[0]";
  97. say "All terms: @arr\n" if (@arr > 1);
  98. last;
  99. }
  100. $lo = $hi + 1;
  101. $hi = 2 * $lo;
  102. }
  103. __END__
  104. :: Searching for the smallest strong pseudoprime to bases: (17 31)
  105. :: Sieving range: [2, 4]
  106. :: Sieving range: [5, 10]
  107. :: Sieving range: [11, 22]
  108. :: Sieving range: [23, 46]
  109. :: Sieving range: [47, 94]
  110. :: Sieving range: [95, 190]
  111. :: Sieving range: [191, 382]
  112. :: Sieving range: [383, 766]
  113. :: Sieving range: [767, 1534]
  114. :: Sieving range: [1535, 3070]
  115. :: Sieving range: [3071, 6142]
  116. :: Sieving range: [6143, 12286]
  117. :: Sieving range: [12287, 24574]
  118. :: Sieving range: [24575, 49150]
  119. :: Sieving range: [49151, 98302]
  120. :: Sieving range: [98303, 196606]
  121. :: Sieving range: [196607, 393214]
  122. Found: 197209
  123. All terms: 197209 269011
  124. perl script.pl 0.19s user 0.01s system 98% cpu 0.205 total