search.pl 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. #!/usr/bin/perl
  2. # Search for a larger empirp, using a fast primality pretest.
  3. # See also:
  4. # https://en.wikipedia.org/wiki/Emirp
  5. # http://mathworld.wolfram.com/Emirp.html
  6. # https://www.primepuzzles.net/puzzles/puzz_020.htm
  7. # The largest known emirp is 10^10006+941992101×10^4999+1 (which has 10007 digits), found by Jens Kruse Andersen in October 2007.
  8. # 10^10006+941992101*10^4999+1
  9. # 10^10006+101299149*10^4999+1
  10. use 5.020;
  11. use strict;
  12. use warnings;
  13. use Math::GMPz;
  14. use POSIX qw(ULONG_MAX);
  15. use ntheory qw(:all);
  16. use IO::Uncompress::Bunzip2;
  17. use experimental qw(signatures declared_refs);
  18. local $| = 1;
  19. prime_precalc(1e7);
  20. sub primality_pretest ($n) {
  21. # Must be positive
  22. (Math::GMPz::Rmpz_sgn($n) > 0) || return;
  23. # Check for divisibilty by 2
  24. if (Math::GMPz::Rmpz_even_p($n)) {
  25. return (Math::GMPz::Rmpz_cmp_ui($n, 2) == 0);
  26. }
  27. # Return early if n is too small
  28. Math::GMPz::Rmpz_cmp_ui($n, 101) > 0 or return 1;
  29. # Check for very small factors
  30. if (ULONG_MAX >= 18446744073709551615) {
  31. Math::GMPz::Rmpz_gcd_ui($Math::GMPz::NULL, $n, 16294579238595022365) == 1 or return 0;
  32. Math::GMPz::Rmpz_gcd_ui($Math::GMPz::NULL, $n, 7145393598349078859) == 1 or return 0;
  33. }
  34. else {
  35. Math::GMPz::Rmpz_gcd_ui($Math::GMPz::NULL, $n, 3234846615) == 1 or return 0;
  36. }
  37. # Size of n in base-2
  38. my $size = Math::GMPz::Rmpz_sizeinbase($n, 2);
  39. # When n is large enough, try to find a small factor (up to 10^8)
  40. if ($size > 10_000) {
  41. state %cache;
  42. state $g = Math::GMPz::Rmpz_init_nobless();
  43. my @checks = (1e4);
  44. push(@checks, 1e6) if ($size > 15_000);
  45. push(@checks, 1e7) if ($size > 20_000);
  46. push(@checks, 1e8) if ($size > 30_000);
  47. my $prev;
  48. foreach my $k (@checks) {
  49. my $primorial = (
  50. $cache{$k} //= do {
  51. my $z = Math::GMPz::Rmpz_init_nobless();
  52. Math::GMPz::Rmpz_primorial_ui($z, $k);
  53. Math::GMPz::Rmpz_divexact($z, $z, $prev) if defined($prev);
  54. $z;
  55. }
  56. );
  57. Math::GMPz::Rmpz_gcd($g, $primorial, $n);
  58. if (Math::GMPz::Rmpz_cmp_ui($g, 1) > 0) {
  59. return 0;
  60. }
  61. $prev = $primorial;
  62. }
  63. }
  64. return 1;
  65. }
  66. my $z = Math::GMPz->new();
  67. my $e = '9'; # end digits
  68. my $d = '1'; # near end digits
  69. my $k = 5005;
  70. my %seen;
  71. foreach my $n (1 .. 1e10) {
  72. next if ($n eq reverse($n));
  73. if (exists $seen{reverse $n}) {
  74. next;
  75. }
  76. $seen{$n} = 1;
  77. my $p = $e . ($d x $k) . $n . ($d x $k) . $e;
  78. say "Testing: $n (length: ", length($p), ")";
  79. Math::GMPz::Rmpz_set_str($z, $p, 10);
  80. primality_pretest($z) || next;
  81. Math::GMPz::Rmpz_set_str($z, scalar(reverse($p)), 10);
  82. primality_pretest($z) || next;
  83. say "Strong candidate: $n";
  84. if (is_prob_prime($p) and is_prob_prime(scalar reverse($p))) {
  85. die "Found: $e . ($d x $k) . $n . ($d x $k) . $e";
  86. }
  87. }