carmichael-lucas-carmichael_number_generation_mpz.pl 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. #!/usr/bin/perl
  2. # Conjecture:
  3. # Letting this program run long enough, it will find a Carmichael number that is also a Lucas-Carmichael number.
  4. use 5.036;
  5. use ntheory qw(:all);
  6. use Math::GMPz;
  7. sub carmichael_numbers_in_range ($A, $B, $k, $callback) {
  8. my $max_prime = ~0;
  9. my $max_lambda = 1e9;
  10. my $m = Math::GMPz->new("1");
  11. my $L1 = lcm(map { $_ - 1 } factor($m));
  12. my $L2 = lcm(map { $_ + 1 } factor($m));
  13. if ($L1 == 0) {
  14. $L1 = 1;
  15. }
  16. if ($L2 == 0) {
  17. $L2 = 1;
  18. }
  19. $L1 = Math::GMPz->new("$L1");
  20. $L2 = Math::GMPz->new("$L2");
  21. $A = $A * $m;
  22. $B = $B * $m;
  23. $A = vecmax($A, pn_primorial($k + 1) >> 1);
  24. $A = Math::GMPz->new("$A");
  25. $B = Math::GMPz->new("$B");
  26. my $u = Math::GMPz::Rmpz_init();
  27. my $v = Math::GMPz::Rmpz_init();
  28. if ($A > $B) {
  29. return;
  30. }
  31. sub ($m, $L1, $L2, $lo, $k) {
  32. Math::GMPz::Rmpz_tdiv_q($u, $B, $m);
  33. Math::GMPz::Rmpz_root($u, $u, $k);
  34. my $hi = Math::GMPz::Rmpz_get_ui($u);
  35. $hi = vecmin($max_prime, $hi);
  36. if ($lo > $hi) {
  37. return;
  38. }
  39. if ($k == 1) {
  40. Math::GMPz::Rmpz_cdiv_q($u, $A, $m);
  41. if (Math::GMPz::Rmpz_fits_ulong_p($u)) {
  42. $lo = vecmax($lo, Math::GMPz::Rmpz_get_ui($u));
  43. }
  44. elsif (Math::GMPz::Rmpz_cmp_ui($u, $lo) > 0) {
  45. if (Math::GMPz::Rmpz_cmp_ui($u, $hi) > 0) {
  46. return;
  47. }
  48. $lo = Math::GMPz::Rmpz_get_ui($u);
  49. }
  50. if ($lo > $hi) {
  51. return;
  52. }
  53. Math::GMPz::Rmpz_invert($v, $m, $L1) || return;
  54. if (Math::GMPz::Rmpz_cmp_ui($v, $hi) > 0) {
  55. return;
  56. }
  57. my $x = Math::GMPz::Rmpz_get_ui($v);
  58. Math::GMPz::Rmpz_invert($v, $m, $L2) || return;
  59. Math::GMPz::Rmpz_sub($v, $L2, $v);
  60. if (Math::GMPz::Rmpz_cmp_ui($v, $hi) > 0) {
  61. return;
  62. }
  63. my $y = Math::GMPz::Rmpz_get_ui($v);
  64. if (Math::GMPz::Rmpz_fits_ulong_p($L1)) {
  65. $L1 = Math::GMPz::Rmpz_get_ui($L1);
  66. }
  67. if (Math::GMPz::Rmpz_fits_ulong_p($L2)) {
  68. $L2 = Math::GMPz::Rmpz_get_ui($L2);
  69. }
  70. my $t = chinese([$x, $L1], [$y, $L2]) || return;
  71. $t > $hi && return;
  72. #$t += $L2 while ($t < $lo);
  73. say "# Checking t = $t with [$L1, $L2] and m = $m";
  74. my $L3 = lcm($L1, $L2);
  75. for (my $p = $t ; $p <= $hi ; $p += $L3) {
  76. if (is_prime($p) and !Math::GMPz::Rmpz_divisible_ui_p($m, $p)) {
  77. Math::GMPz::Rmpz_mul_ui($v, $m, $p);
  78. Math::GMPz::Rmpz_add_ui($u, $v, 1);
  79. if (Math::GMPz::Rmpz_divisible_ui_p($u, $p + 1)) {
  80. $callback->(Math::GMPz::Rmpz_init_set($v));
  81. Math::GMPz::Rmpz_sub_ui($u, $v, 1);
  82. if (Math::GMPz::Rmpz_divisible_ui_p($u, $p - 1)) {
  83. die "Found counter-example: $v";
  84. $callback->(Math::GMPz::Rmpz_init_set($v));
  85. }
  86. }
  87. }
  88. }
  89. return;
  90. for (my $p = $t ; $p <= $hi ; $p += $L1) {
  91. if (is_prime($p) and !Math::GMPz::Rmpz_divisible_ui_p($m, $p)) {
  92. Math::GMPz::Rmpz_mul_ui($v, $m, $p);
  93. Math::GMPz::Rmpz_sub_ui($u, $v, 1);
  94. if (Math::GMPz::Rmpz_divisible_ui_p($u, $p - 1)) {
  95. $callback->(Math::GMPz::Rmpz_init_set($v));
  96. Math::GMPz::Rmpz_add_ui($u, $v, 1);
  97. if (Math::GMPz::Rmpz_divisible_ui_p($u, $p + 1)) {
  98. die "Found counter-example: $v";
  99. $callback->(Math::GMPz::Rmpz_init_set($v));
  100. }
  101. }
  102. }
  103. }
  104. for (my $p = $t ; $p <= $hi ; $p += $L2) {
  105. if (is_prime($p) and !Math::GMPz::Rmpz_divisible_ui_p($m, $p)) {
  106. Math::GMPz::Rmpz_mul_ui($v, $m, $p);
  107. Math::GMPz::Rmpz_add_ui($u, $v, 1);
  108. if (Math::GMPz::Rmpz_divisible_ui_p($u, $p + 1)) {
  109. $callback->(Math::GMPz::Rmpz_init_set($v));
  110. Math::GMPz::Rmpz_sub_ui($u, $v, 1);
  111. if (Math::GMPz::Rmpz_divisible_ui_p($u, $p - 1)) {
  112. die "Found counter-example: $v";
  113. $callback->(Math::GMPz::Rmpz_init_set($v));
  114. }
  115. }
  116. }
  117. }
  118. return;
  119. }
  120. my $z = Math::GMPz::Rmpz_init();
  121. my $lcm1 = Math::GMPz::Rmpz_init();
  122. my $lcm2 = Math::GMPz::Rmpz_init();
  123. my @primes = @{primes($lo, $hi)};
  124. foreach my $congr (1, 3, 5, 7, 11) {
  125. foreach my $p (@primes) {
  126. $p % 12 == $congr or next; # prime factors must be congruent to each other modulo 12
  127. gcd($p - 1, $p + 1) == 2 or next;
  128. Math::GMPz::Rmpz_divisible_ui_p($m, $p) and next;
  129. #is_smooth(($p-1)*($p+1), 13) || next;
  130. # All prime factors p must satisfy: (p^2 - 1)/2 == 0 (mod 12)
  131. modint(divint(subint(mulint($p, $p), 1), 2), 12) == 0 or next;
  132. Math::GMPz::Rmpz_gcd_ui($Math::GMPz::NULL, $m, $p - 1) == 1 or next;
  133. Math::GMPz::Rmpz_gcd_ui($Math::GMPz::NULL, $m, $p + 1) == 1 or next;
  134. Math::GMPz::Rmpz_lcm_ui($lcm1, $L1, $p - 1);
  135. Math::GMPz::Rmpz_lcm_ui($lcm2, $L2, $p + 1);
  136. Math::GMPz::Rmpz_gcd($z, $lcm1, $lcm2);
  137. Math::GMPz::Rmpz_cmp_ui($z, 2) == 0 or next;
  138. #Math::GMPz::Rmpz_fits_ulong_p($lcm) || next;
  139. #Math::GMPz::Rmpz_fits_ulong_p($lcm2) || next;
  140. Math::GMPz::Rmpz_cmp_ui($lcm1, $max_lambda) <= 0 or next;
  141. Math::GMPz::Rmpz_cmp_ui($lcm2, $max_lambda) <= 0 or next;
  142. Math::GMPz::Rmpz_lcm($z, $lcm1, $lcm2);
  143. Math::GMPz::Rmpz_cmp_ui($z, $max_prime) <= 0 or next;
  144. #Math::GMPz::Rmpz_cmp_ui($lcm, 1e4) < 0 or next;
  145. Math::GMPz::Rmpz_mul_ui($z, $m, $p);
  146. __SUB__->($z, $lcm1, $lcm2, $p + 1, $k - 1);
  147. }
  148. }
  149. }
  150. ->($m, $L1, $L2, 3, $k);
  151. return 1;
  152. }
  153. my $from = Math::GMPz->new(2)**68;
  154. #my $upto = Math::GMPz->new("713211736645623197793013755552001");
  155. my $upto = 10 * $from;
  156. while (1) {
  157. my $ok = 0;
  158. say "# Range: ($from, $upto)";
  159. foreach my $k (5 .. 100) {
  160. $k % 2 == 1 or next;
  161. #$k % 2 == 0 or next;
  162. carmichael_numbers_in_range($from, $upto, $k, sub ($n) { say $n }) or next;
  163. $ok = 1;
  164. }
  165. $ok || last;
  166. $from = $upto + 1;
  167. $upto = 10 * $from;
  168. }