search.pl 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. #!/usr/bin/perl
  2. # Find the smallest prime factor of 2^(2^prime(n) - 1) - 1.
  3. # https://oeis.org/A309130
  4. # Known terms:
  5. # 7, 127, 2147483647, 170141183460469231731687303715884105727, 47, 338193759479, 231733529, 62914441, 2351, 1399, 295257526626031, 18287, 106937, 863, 4703, 138863, 22590223644617
  6. use 5.014;
  7. use strict;
  8. use warnings;
  9. use ntheory qw(:all);
  10. use Math::GMPz;
  11. use List::Util qw(uniq);
  12. use experimental qw(signatures);
  13. sub validate_factors ($n, @factors) {
  14. if (ref($n) ne 'Math::GMPz') {
  15. $n = Math::GMPz->new("$n");
  16. }
  17. @factors || return;
  18. @factors = map { (ref($_) eq 'Math::GMPz') ? $_ : Math::GMPz::Rmpz_init_set_str("$_", 10) } @factors;
  19. @factors = grep { ($_ > 1) and ($_ < $n) } @factors;
  20. @factors || return;
  21. uniq(sort { $a <=> $b } grep { $n % $_ == 0 } @factors);
  22. }
  23. sub parse_yafu_output ($n, $output) {
  24. my @factors;
  25. while ($output =~ /^[CP]\d+\s*=\s*(\d+)/mg) {
  26. push @factors, Math::GMPz->new($1);
  27. }
  28. return validate_factors($n, @factors);
  29. }
  30. sub parse_mpu_output ($n, $output) {
  31. my @factors;
  32. while ($output =~ /^(\d+)/mg) {
  33. push @factors, Math::GMPz->new($1);
  34. }
  35. return validate_factors($n, @factors);
  36. }
  37. sub trial_division ($n, $k = 1e5) { # trial division, using cached primorials + GCD
  38. state %cache;
  39. # Clear the cache when there are too many values cached
  40. if (scalar(keys(%cache)) > 100) {
  41. Math::GMPz::Rmpz_clear($_) for values(%cache);
  42. undef %cache;
  43. }
  44. my $B = (
  45. $cache{$k} //= do {
  46. my $t = Math::GMPz::Rmpz_init_nobless();
  47. Math::GMPz::Rmpz_primorial_ui($t, $k);
  48. $t;
  49. }
  50. );
  51. state $g = Math::GMPz::Rmpz_init_nobless();
  52. state $t = Math::GMPz::Rmpz_init_nobless();
  53. Math::GMPz::Rmpz_gcd($g, $n, $B);
  54. if (Math::GMPz::Rmpz_cmp_ui($g, 1) > 0) {
  55. Math::GMPz::Rmpz_set($t, $n);
  56. my %factor;
  57. foreach my $f (Math::Prime::Util::GMP::factor(Math::GMPz::Rmpz_get_str($g, 10))) {
  58. Math::GMPz::Rmpz_set_ui($g, $f);
  59. $factor{$f} = Math::GMPz::Rmpz_remove($t, $t, $g);
  60. }
  61. my @factors = keys %factor;
  62. if (Math::GMPz::Rmpz_cmp_ui($t, 1) > 0) {
  63. push @factors, Math::GMPz::Rmpz_init_set($t);
  64. }
  65. return validate_factors($n, @factors);
  66. }
  67. return;
  68. }
  69. sub ecm_one_factor ($n) {
  70. my @f = trial_division($n, 1e8);
  71. foreach my $f (@f) {
  72. while ($n % $f == 0) {
  73. $n /= $f;
  74. }
  75. }
  76. my @factors = @f;
  77. my $timeout = 10;
  78. my $prefer_yafu = 0; # true to prefer YAFU's ECM implementation
  79. eval {
  80. local $SIG{ALRM} = sub { die "alarm\n" };
  81. alarm $timeout;
  82. if ($prefer_yafu) {
  83. my $curves = $timeout >> 1;
  84. my $output = qx(yafu 'ecm($n, $curves)' -B1ecm 500000);
  85. push @factors, parse_yafu_output($n, $output);
  86. }
  87. my $output = `$^X -MMath::Prime::Util::GMP=ecm_factor -E 'say for ecm_factor("$n")'`;
  88. ## my $output = `$^X -MMath::Prime::Util=factor -E 'say for factor("$n")'`;
  89. push @factors, parse_mpu_output($n, $output);
  90. alarm 0;
  91. };
  92. if ($@ eq "alarm\n") {
  93. `pkill -P $$`;
  94. }
  95. return grep { length("$_") < 200 } uniq(@factors);
  96. }
  97. my $two = Math::GMPz->new(2);
  98. my $n = 18; # search for a(18)
  99. my $p = nth_prime($n);
  100. my $e = $two**$p - 1;
  101. my $r = znorder(2, $e);
  102. foreach my $i (1 .. 1e7) {
  103. my $k = ($i * $r + 1);
  104. is_prime($k) || next;
  105. if ($n == 18 and $k <= 50387) { # already searched this range
  106. next;
  107. }
  108. my @F = ecm_one_factor($two**$k - 1);
  109. say "[$k] Factors: @F";
  110. foreach my $f (@F) {
  111. my $t = powmod(2, $e, $f);
  112. if ($t == 1) {
  113. die "\n[$k] Found: $f is a factor of 2^(2^prime($n)-1)-1\n\n";
  114. }
  115. }
  116. }