carmichael-lucas-carmichael_from_factors_cached.pl 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. #!/usr/bin/perl
  2. # Try to generate a Carmichael number that is also Lucas-Carmichael.
  3. use 5.020;
  4. use strict;
  5. use warnings;
  6. use Storable;
  7. use Math::GMPz;
  8. use ntheory qw(:all);
  9. use Math::Prime::Util::GMP;
  10. use experimental qw(signatures);
  11. my $storable_file = "cache/factors-carmichael.storable";
  12. my $table = retrieve($storable_file);
  13. use 5.036;
  14. use Math::GMPz;
  15. use ntheory qw(:all);
  16. use List::Util qw(uniq);
  17. sub carmichael_from_multiple ($factors, $m, $callback, $reps = 1e4) {
  18. my $t = Math::GMPz::Rmpz_init();
  19. my $u = Math::GMPz::Rmpz_init();
  20. my $v = Math::GMPz::Rmpz_init();
  21. my @congr = uniq(map { $_ % 12 } @$factors);
  22. scalar(@congr) == 1 or return;
  23. my $L1 = Math::Prime::Util::GMP::lcm(map { subint($_, 1) } @$factors);
  24. my $L2 = Math::Prime::Util::GMP::lcm(map { addint($_, 1) } @$factors);
  25. Math::Prime::Util::GMP::gcd($L1, $L2) eq '2' or return;
  26. $m = Math::GMPz->new("$m");
  27. $L1 = Math::GMPz->new("$L1");
  28. $L2 = Math::GMPz->new("$L2");
  29. Math::GMPz::Rmpz_invert($v, $m, $L1) || return;
  30. my $x = Math::GMPz::Rmpz_init_set($v);
  31. Math::GMPz::Rmpz_invert($v, $m, $L2) || return;
  32. Math::GMPz::Rmpz_sub($v, $L2, $v);
  33. my $y = Math::GMPz::Rmpz_init_set($v);
  34. if (Math::GMPz::Rmpz_fits_ulong_p($L1)) {
  35. $L1 = Math::GMPz::Rmpz_get_ui($L1);
  36. }
  37. if (Math::GMPz::Rmpz_fits_ulong_p($L2)) {
  38. $L2 = Math::GMPz::Rmpz_get_ui($L2);
  39. }
  40. my $c = chinese([$x, $L1], [$y, $L2]) || return;
  41. my $L3 = Math::GMPz->new(lcm($L1, $L2));
  42. say "# Checking m = $m with c = $c";
  43. for (my $p = Math::GMPz::Rmpz_init_set_str("$c", 10) ; --$reps >= 0 ; Math::GMPz::Rmpz_add($p, $p, $L3)) {
  44. Math::GMPz::Rmpz_gcd($t, $m, $p);
  45. Math::GMPz::Rmpz_cmp_ui($t, 1) == 0 or next;
  46. Math::GMPz::Rmpz_mul($v, $m, $p);
  47. if (!Math::GMPz::Rmpz_fits_ulong_p($p)) {
  48. Math::Prime::Util::GMP::is_pseudoprime($v, 2) || next;
  49. }
  50. my @factors = factor_exp($p);
  51. (vecall { $_->[1] == 1 } @factors) || next;
  52. (vecall { modint($_->[0], 12) == $congr[0] } @factors) || next;
  53. Math::GMPz::Rmpz_sub_ui($u, $v, 1);
  54. Math::GMPz::Rmpz_set_str($t, lcm(map { subint($_->[0], 1) } @factors), 10);
  55. if (Math::GMPz::Rmpz_divisible_p($u, $t)) {
  56. $callback->(Math::GMPz::Rmpz_init_set($v));
  57. Math::GMPz::Rmpz_set_str($t, lcm(map { addint($_->[0], 1) } @factors), 10);
  58. Math::GMPz::Rmpz_add_ui($u, $v, 1);
  59. if (Math::GMPz::Rmpz_divisible_p($u, $t)) {
  60. die "Found counter-example: $v";
  61. }
  62. }
  63. }
  64. }
  65. while (my ($key, $value) = each %$table) {
  66. my @factors = split(' ', $value);
  67. #$factors[-1] < ~0 or next;
  68. $factors[-1] < 1e4 and next;
  69. $factors[-1] < 1e6 or next;
  70. my @list = ($key);
  71. while (@list) {
  72. my $m = shift(@list);
  73. carmichael_from_multiple(
  74. \@factors,
  75. $m,
  76. sub ($n) {
  77. if ($n > $m) {
  78. say $n;
  79. push @list, $n;
  80. }
  81. }
  82. );
  83. }
  84. }