carmichael_numbers_in_range_with_prefix_mpz.pl 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. #!/usr/bin/perl
  2. # Daniel "Trizen" Șuteu
  3. # Date: 27 August 2022
  4. # https://github.com/trizen
  5. # Generate all the Carmichael numbers with n prime factors in a given range [a,b]. (not in sorted order)
  6. # See also:
  7. # https://en.wikipedia.org/wiki/Almost_prime
  8. use 5.036;
  9. use ntheory qw(:all);
  10. use Math::GMPz;
  11. sub carmichael_numbers_in_range ($A, $B, $k, $callback) {
  12. #my $max_p = 313897;
  13. my $max_p = 1e6;
  14. #my $m = "1056687375767188465946114009917285";
  15. #my $m = Math::GMPz->new("6863588485053268178811679453193455");
  16. my $m = Math::GMPz->new("1049092636351906987863186392741166403295");
  17. #my $m = Math::GMPz->new("8035018770721572330061486952496026236686375478339885");
  18. #my $m = Math::GMPz->new("288796538380586656981514139972529852735632478655");
  19. #my $m = Math::GMPz->new("1127872835696879363649741868028740611132217832559978865049182075837136570515");
  20. my $L = lcm(map { $_ - 1 } factor($m));
  21. if ($L == 0) {
  22. $L = 1;
  23. }
  24. $L = Math::GMPz->new("$L");
  25. $A = $A * $m;
  26. $B = $B * $m;
  27. $A = Math::GMPz->new("$A");
  28. $B = Math::GMPz->new("$B");
  29. if ($B > Math::GMPz->new("2059832906607460252767290568443059994787898033540634712711845135488141590979778401392385")) {
  30. $B = Math::GMPz->new("2059832906607460252767290568443059994787898033540634712711845135488141590979778401392385");
  31. }
  32. $A = vecmax($A, pn_primorial($k + 1) >> 1);
  33. $A = Math::GMPz->new("$A");
  34. $B = Math::GMPz->new("$B");
  35. my $u = Math::GMPz::Rmpz_init();
  36. my $v = Math::GMPz::Rmpz_init();
  37. if ($A > $B) {
  38. return;
  39. }
  40. sub ($m, $L, $lo, $k) {
  41. Math::GMPz::Rmpz_tdiv_q($u, $B, $m);
  42. Math::GMPz::Rmpz_root($u, $u, $k);
  43. my $hi = Math::GMPz::Rmpz_get_ui($u);
  44. $hi = vecmin($max_p, $hi);
  45. if ($lo > $hi) {
  46. return;
  47. }
  48. if ($k == 1) {
  49. Math::GMPz::Rmpz_cdiv_q($u, $A, $m);
  50. if (Math::GMPz::Rmpz_fits_ulong_p($u)) {
  51. $lo = vecmax($lo, Math::GMPz::Rmpz_get_ui($u));
  52. }
  53. elsif (Math::GMPz::Rmpz_cmp_ui($u, $lo) > 0) {
  54. if (Math::GMPz::Rmpz_cmp_ui($u, $hi) > 0) {
  55. return;
  56. }
  57. $lo = Math::GMPz::Rmpz_get_ui($u);
  58. }
  59. if ($lo > $hi) {
  60. return;
  61. }
  62. Math::GMPz::Rmpz_invert($v, $m, $L) || return;
  63. if (Math::GMPz::Rmpz_cmp_ui($v, $hi) > 0) {
  64. return;
  65. }
  66. if (Math::GMPz::Rmpz_fits_ulong_p($L)) {
  67. $L = Math::GMPz::Rmpz_get_ui($L);
  68. }
  69. my $t = Math::GMPz::Rmpz_get_ui($v);
  70. $t > $hi && return;
  71. $t += $L while ($t < $lo);
  72. for (my $p = $t ; $p <= $hi ; $p += $L) {
  73. if (is_prime($p) and !Math::GMPz::Rmpz_divisible_ui_p($m, $p)) {
  74. Math::GMPz::Rmpz_mul_ui($v, $m, $p);
  75. Math::GMPz::Rmpz_sub_ui($u, $v, 1);
  76. if (Math::GMPz::Rmpz_divisible_ui_p($u, $p - 1)) {
  77. $callback->(Math::GMPz::Rmpz_init_set($v));
  78. }
  79. }
  80. }
  81. return;
  82. }
  83. my $z = Math::GMPz::Rmpz_init();
  84. my $lcm = Math::GMPz::Rmpz_init();
  85. foreach my $p (@{primes($lo, $hi)}) {
  86. Math::GMPz::Rmpz_divisible_ui_p($m, $p) and next;
  87. Math::GMPz::Rmpz_gcd_ui($Math::GMPz::NULL, $m, $p - 1) == 1 or next;
  88. Math::GMPz::Rmpz_lcm_ui($lcm, $L, $p - 1);
  89. Math::GMPz::Rmpz_mul_ui($z, $m, $p);
  90. __SUB__->($z, $lcm, $p + 1, $k - 1);
  91. }
  92. }
  93. ->($m, $L, 5, $k);
  94. return 1;
  95. }
  96. my $from = Math::GMPz->new(2);
  97. my $upto = 2 * $from;
  98. while (1) {
  99. my $ok = 0;
  100. say "# Range: ($from, $upto)";
  101. foreach my $k (10 .. 100) {
  102. carmichael_numbers_in_range($from, $upto, $k, sub ($n) { say $n }) or next;
  103. $ok = 1;
  104. }
  105. $ok || last;
  106. $from = $upto + 1;
  107. $upto = 2 * $from;
  108. }