verify_new_carmichael.pl 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. #!/usr/bin/perl
  2. # Partially verify the Carmichael numbers generated by Claude Goutier, using the various Carmichael numbers generated by myself in the range [2^64, 10^22].
  3. # Source of Carmichael numbers <= 10^22:
  4. # http://www-labs.iro.umontreal.ca/~goutier/OEIS/A055553/
  5. # https://oeis.org/A002997/a002997.7z
  6. use 5.036;
  7. use Storable;
  8. use Math::GMPz;
  9. use ntheory qw(:all);
  10. my $carmichael_file = "programs/cache/factors-carmichael.storable";
  11. say STDERR ":: Reading Storable file of Carmichael numbers...";
  12. my $min = Math::GMPz::Rmpz_init();
  13. my $max = Math::GMPz::Rmpz_init();
  14. Math::GMPz::Rmpz_ui_pow_ui($min, 2, 64);
  15. Math::GMPz::Rmpz_ui_pow_ui($max, 10, 22);
  16. my $z = Math::GMPz::Rmpz_init();
  17. my @valid_carmichael;
  18. {
  19. my $carmichael = retrieve($carmichael_file);
  20. while (my ($key, $value) = each %$carmichael) {
  21. Math::GMPz::Rmpz_set_str($z, $key, 10);
  22. if ($z > $min and $z < $max) {
  23. push @valid_carmichael, $key;
  24. }
  25. }
  26. undef $carmichael;
  27. }
  28. {
  29. my $t1 = Math::GMPz::Rmpz_init();
  30. my $t2 = Math::GMPz::Rmpz_init();
  31. sub compare ($n1, $n2) {
  32. Math::GMPz::Rmpz_set_str($t1, $n1, 10);
  33. Math::GMPz::Rmpz_set_str($t2, $n2, 10);
  34. $t1 <=> $t2;
  35. }
  36. }
  37. sub binary_search_file ($file, $word) {
  38. my $low = 0; # Guaranteed to be the start of a line.
  39. my $high = -s $file; # Might not be the start of a line.
  40. my $line;
  41. while ($high != $low) {
  42. my $mid = ($high + $low) >> 1;
  43. seek($file, $mid, 0);
  44. # $mid is probably in the middle of a line, so read the rest
  45. # and set $mid2 to that new position.
  46. scalar <$file>;
  47. my $mid2 = tell($file);
  48. if ($mid2 < $high) { # We're not near file's end, so read on.
  49. $mid = $mid2;
  50. $line = <$file>;
  51. }
  52. else { # $mid plunked us in the last line, so linear search.
  53. seek($file, $low, 0);
  54. while (defined($line = <$file>)) {
  55. chomp $line;
  56. last if compare($line, $word) >= 0;
  57. $low = tell($file);
  58. }
  59. last;
  60. }
  61. compare($line, $word) == -1
  62. ? do { $low = $mid }
  63. : do { $high = $mid };
  64. }
  65. compare($line, $word) == 0
  66. ? $low
  67. : undef;
  68. }
  69. my $input_file = $ARGV[0] // 'carm10e22.txt';
  70. open my $file, '<:raw:crlf', $input_file
  71. or die "Can't open file <<$input_file>>: $!";
  72. foreach my $n (@valid_carmichael) {
  73. my $pos = binary_search_file($file, $n);
  74. if (!defined($pos)) {
  75. die "Missing Carmichael: $n\n";
  76. }
  77. else {
  78. say "Found $n at position $pos";
  79. }
  80. }
  81. say STDERR ":: Done!";
  82. __END__
  83. ...
  84. Found 150296655073360091041 at position 207454961
  85. Found 3437007031250330646529 at position 749347096
  86. Found 55267646670479113921 at position 137945373
  87. Found 213552710204860549801 at position 239845171
  88. Found 8279200287270159984001 at position 1072707280
  89. Found 328497680817369112897 at position 286122988
  90. Found 7037389213489676764801 at position 1004136016
  91. Found 187438692737021648161 at position 227293519
  92. Found 9356343200621757918901 at position 1127295208
  93. :: Done!
  94. perl verify_new_carmichael.pl carm10e22.txt 75.94s user 33.41s system 69% cpu 2:37.50 total