prog_squarefree.pl 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. #!/usr/bin/perl
  2. # Smallest strong pseudoprime to base 2 with n prime factors.
  3. # https://oeis.org/A180065
  4. # Known terms:
  5. # 2047, 15841, 800605, 293609485, 10761055201, 5478598723585, 713808066913201, 90614118359482705, 5993318051893040401
  6. # New terms found (24 September 2022):
  7. # a(11) = 24325630440506854886701
  8. # a(12) = 27146803388402594456683201
  9. # a(13) = 4365221464536367089854499301
  10. # a(14) = 2162223198751674481689868383601
  11. # a(15) = 548097717006566233800428685318301
  12. # New terms found (01 March 2023):
  13. # a(16) = 348613808580816298169781820233637261
  14. # a(17) = 179594694484889004052891417528244514541
  15. # Lower-bounds:
  16. # a(18) > 4225759284680910908812751551690679779327
  17. # Upper-bounds:
  18. # a(18) <= 402705517727804564796340190090616337175101
  19. # It took 1 hour and 2 minutes to find a(16).
  20. # It took 5 hours and 39 minutes to find a(17).
  21. use 5.036;
  22. use Math::GMPz;
  23. use ntheory qw(:all);
  24. sub squarefree_strong_fermat_pseudoprimes_in_range ($A, $B, $k, $base, $callback) {
  25. $A = vecmax($A, pn_primorial($k));
  26. $A = Math::GMPz->new("$A");
  27. $B = Math::GMPz->new("$B");
  28. my $u = Math::GMPz::Rmpz_init();
  29. my $v = Math::GMPz::Rmpz_init();
  30. my $generator = sub ($m, $L, $lo, $k, $k_exp, $congr) {
  31. Math::GMPz::Rmpz_tdiv_q($u, $B, $m);
  32. Math::GMPz::Rmpz_root($u, $u, $k);
  33. my $hi = Math::GMPz::Rmpz_get_ui($u);
  34. if ($lo > $hi) {
  35. return;
  36. }
  37. if ($k == 1) {
  38. Math::GMPz::Rmpz_cdiv_q($u, $A, $m);
  39. if (Math::GMPz::Rmpz_fits_ulong_p($u)) {
  40. $lo = vecmax($lo, Math::GMPz::Rmpz_get_ui($u));
  41. }
  42. elsif (Math::GMPz::Rmpz_cmp_ui($u, $lo) > 0) {
  43. if (Math::GMPz::Rmpz_cmp_ui($u, $hi) > 0) {
  44. return;
  45. }
  46. $lo = Math::GMPz::Rmpz_get_ui($u);
  47. }
  48. if ($lo > $hi) {
  49. return;
  50. }
  51. Math::GMPz::Rmpz_invert($v, $m, $L);
  52. if (Math::GMPz::Rmpz_cmp_ui($v, $hi) > 0) {
  53. return;
  54. }
  55. if (Math::GMPz::Rmpz_fits_ulong_p($L)) {
  56. $L = Math::GMPz::Rmpz_get_ui($L);
  57. }
  58. my $t = Math::GMPz::Rmpz_get_ui($v);
  59. $t > $hi && return;
  60. $t += $L while ($t < $lo);
  61. for (my $p = $t ; $p <= $hi ; $p += $L) {
  62. is_prime($p) || next;
  63. $base % $p == 0 and next;
  64. my $val = valuation($p - 1, 2);
  65. if ($val > $k_exp and powmod($base, ($p - 1) >> ($val - $k_exp), $p) == ($congr % $p)) {
  66. Math::GMPz::Rmpz_mul_ui($v, $m, $p);
  67. Math::GMPz::Rmpz_sub_ui($u, $v, 1);
  68. if (Math::GMPz::Rmpz_divisible_ui_p($u, znorder($base, $p))) {
  69. my $value = Math::GMPz::Rmpz_init_set($v);
  70. say "Found upper-bound: $value";
  71. $B = $value if ($value < $B);
  72. $callback->($value);
  73. }
  74. }
  75. }
  76. return;
  77. }
  78. my $t = Math::GMPz::Rmpz_init();
  79. my $lcm = Math::GMPz::Rmpz_init();
  80. foreach my $p (@{primes($lo, $hi)}) {
  81. $base % $p == 0 and next;
  82. my $val = valuation($p - 1, 2);
  83. $val > $k_exp or next;
  84. powmod($base, ($p - 1) >> ($val - $k_exp), $p) == ($congr % $p) or next;
  85. my $z = znorder($base, $p);
  86. Math::GMPz::Rmpz_gcd_ui($Math::GMPz::NULL, $m, $z) == 1 or next;
  87. Math::GMPz::Rmpz_lcm_ui($lcm, $L, $z);
  88. Math::GMPz::Rmpz_mul_ui($t, $m, $p);
  89. __SUB__->($t, $lcm, $p + 1, $k - 1, $k_exp, $congr);
  90. }
  91. };
  92. # Cases where 2^(d * 2^v) == -1 (mod p), for some v >= 0.
  93. foreach my $v (reverse(0 .. logint($B, 2))) {
  94. $generator->(Math::GMPz->new(1), Math::GMPz->new(1), 2, $k, $v, -1);
  95. }
  96. # Case where 2^d == 1 (mod p), where d is the odd part of p-1.
  97. $generator->(Math::GMPz->new(1), Math::GMPz->new(1), 2, $k, 0, 1);
  98. }
  99. sub a($n) {
  100. if ($n == 0) {
  101. return 1;
  102. }
  103. say "Searching for a($n)";
  104. #my $x = Math::GMPz->new(pn_primorial($n));
  105. my $x = Math::GMPz->new("4225759284680910908812751551690679779327");
  106. my $y = 2*$x;
  107. while (1) {
  108. say("Sieving range: [$x, $y]");
  109. my @v;
  110. squarefree_strong_fermat_pseudoprimes_in_range($x, $y, $n, 2, sub ($k) {
  111. push @v, $k;
  112. });
  113. @v = sort { $a <=> $b } @v;
  114. if (scalar(@v) > 0) {
  115. return $v[0];
  116. }
  117. $x = $y+1;
  118. $y = 2*$x;
  119. my $max = Math::GMPz->new("402705517727804564796340190090616337175101");
  120. $y = $max if ($y > $max);
  121. }
  122. }
  123. foreach my $n (18) {
  124. say "a($n) = ", a($n);
  125. }