is_almost_prime.pl 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. #!/usr/bin/perl
  2. # Author: Trizen
  3. # Date: 17 February 2023
  4. # https://github.com/trizen
  5. # A simple and fast method for checking if a given integer n has exactly k prime factors (i.e.: bigomega(n) = k).
  6. use 5.020;
  7. use warnings;
  8. use ntheory qw(:all);
  9. use experimental qw(signatures);
  10. use Math::GMPz;
  11. use Math::Prime::Util::GMP;
  12. use constant {
  13. TRIAL_LIMIT => 1e3,
  14. HAS_NEW_PRIME_UTIL => defined(&Math::Prime::Util::is_almost_prime),
  15. };
  16. my @SMALL_PRIMES = @{primes(TRIAL_LIMIT)};
  17. sub mpz_is_almost_prime ($n, $k) {
  18. state $z = Math::GMPz::Rmpz_init();
  19. state $t = Math::GMPz::Rmpz_init();
  20. if ($n == 0) {
  21. return 0;
  22. }
  23. Math::GMPz::Rmpz_set_str($z, "$n", 10);
  24. Math::GMPz::Rmpz_root($t, $z, $k);
  25. my $trial_limit = Math::GMPz::Rmpz_get_ui($t);
  26. if ($trial_limit > TRIAL_LIMIT or !Math::GMPz::Rmpz_fits_ulong_p($t)) {
  27. $trial_limit = TRIAL_LIMIT;
  28. }
  29. foreach my $p (@SMALL_PRIMES) {
  30. last if ($p > $trial_limit);
  31. if (Math::GMPz::Rmpz_divisible_ui_p($z, $p)) {
  32. Math::GMPz::Rmpz_set_ui($t, $p);
  33. $k -= Math::GMPz::Rmpz_remove($z, $z, $t);
  34. }
  35. ($k > 0) or last;
  36. if (HAS_NEW_PRIME_UTIL and Math::GMPz::Rmpz_fits_ulong_p($z)) {
  37. return Math::Prime::Util::is_almost_prime($k, Math::GMPz::Rmpz_get_ui($z));
  38. }
  39. }
  40. if ($k < 0) {
  41. return 0;
  42. }
  43. if (Math::GMPz::Rmpz_cmp_ui($z, 1) == 0) {
  44. return ($k == 0);
  45. }
  46. if ($k == 0) {
  47. return (Math::GMPz::Rmpz_cmp_ui($z, 1) == 0);
  48. }
  49. if ($k == 1) {
  50. if (Math::GMPz::Rmpz_fits_ulong_p($z)) {
  51. return is_prime(Math::GMPz::Rmpz_get_ui($z));
  52. }
  53. return Math::Prime::Util::GMP::is_prime(Math::GMPz::Rmpz_get_str($z, 10));
  54. }
  55. Math::GMPz::Rmpz_ui_pow_ui($t, next_prime($trial_limit), $k);
  56. if (Math::GMPz::Rmpz_cmp($z, $t) < 0) {
  57. return 0;
  58. }
  59. (HAS_NEW_PRIME_UTIL and Math::GMPz::Rmpz_fits_ulong_p($z))
  60. ? Math::Prime::Util::is_almost_prime($k, Math::GMPz::Rmpz_get_ui($z))
  61. : (factor(Math::GMPz::Rmpz_get_str($z, 10)) == $k);
  62. }
  63. foreach my $n (1 .. 100) {
  64. my $t = urandomb($n) + 1;
  65. say "Testing: $t";
  66. foreach my $k (1 .. 20) {
  67. if (HAS_NEW_PRIME_UTIL ? Math::Prime::Util::is_almost_prime($k, $t) : (factor($t) == $k)) {
  68. mpz_is_almost_prime($t, $k) || die "error for: ($t, $k)";
  69. }
  70. elsif (mpz_is_almost_prime($t, $k)) {
  71. die "counter-example: ($t, $k)";
  72. }
  73. }
  74. }