quadratic_frobenius_primality_test.pl 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. #!/usr/bin/perl
  2. # A simple implemenetation of the Frobenius Quadratic pseudoprimality test.
  3. # Conditions:
  4. # 1. Make sure n is odd and is not a perfect power.
  5. # 2. Find the smallest odd prime p such that kronecker(p, n) = -1.
  6. # 3. Check if (1 + sqrt(p))^n == (1 - sqrt(p)) mod n.
  7. # Generalized test:
  8. # 1. Make sure n is odd and is not a perfect power.
  9. # 2. Find the smallest squarefree number c such that kronecker(c, n) = -1.
  10. # 3. Check if (a + b*sqrt(c))^n == (a - b*sqrt(c)) mod n, where a,b,c are all coprime with n.
  11. # No counter-examples are known to this test.
  12. # See also:
  13. # https://en.wikipedia.org/wiki/Quadratic_integer
  14. use 5.020;
  15. use warnings;
  16. use Math::GMPz;
  17. use ntheory qw(:all);
  18. use experimental qw(signatures);
  19. sub quadratic_powmod ($x, $y, $a, $b, $w, $n, $m) {
  20. state $t = Math::GMPz::Rmpz_init_nobless();
  21. foreach my $bit (split(//, scalar reverse Math::GMPz::Rmpz_get_str($n, 2))) {
  22. if ($bit) {
  23. # (x, y) = ((a*x + b*y*w) % m, (a*y + b*x) % m)
  24. Math::GMPz::Rmpz_mul_ui($t, $b, $w);
  25. Math::GMPz::Rmpz_mul($t, $t, $y);
  26. Math::GMPz::Rmpz_addmul($t, $a, $x);
  27. Math::GMPz::Rmpz_mul($y, $y, $a);
  28. Math::GMPz::Rmpz_addmul($y, $x, $b);
  29. Math::GMPz::Rmpz_mod($x, $t, $m);
  30. Math::GMPz::Rmpz_mod($y, $y, $m);
  31. }
  32. # (a, b) = ((a*a + b*b*w) % m, (2*a*b) % m)
  33. Math::GMPz::Rmpz_mul($t, $a, $b);
  34. Math::GMPz::Rmpz_mul_2exp($t, $t, 1);
  35. Math::GMPz::Rmpz_powm_ui($a, $a, 2, $m);
  36. Math::GMPz::Rmpz_powm_ui($b, $b, 2, $m);
  37. Math::GMPz::Rmpz_addmul_ui($a, $b, $w);
  38. Math::GMPz::Rmpz_mod($b, $t, $m);
  39. }
  40. }
  41. sub find_discriminant ($n) {
  42. for (my $p = 3 ; ; $p = next_prime($p)) {
  43. my $k = Math::GMPz::Rmpz_ui_kronecker($p, $n);
  44. if ($k == 0 and $p != $n) {
  45. return undef;
  46. }
  47. elsif ($k == -1) {
  48. return $p;
  49. }
  50. }
  51. }
  52. sub is_quadratic_frobenius_pseudoprime ($n) {
  53. if (ref($n) ne 'Math::GMPz') {
  54. $n = Math::GMPz->new("$n");
  55. }
  56. return 0 if ($n <= 1);
  57. return 1 if ($n == 2);
  58. return 0 if Math::GMPz::Rmpz_even_p($n);
  59. return 0 if Math::GMPz::Rmpz_perfect_power_p($n);
  60. my $c = find_discriminant($n) // return 0;
  61. state $a = Math::GMPz::Rmpz_init();
  62. state $b = Math::GMPz::Rmpz_init();
  63. state $w = Math::GMPz::Rmpz_init();
  64. state $x = Math::GMPz::Rmpz_init();
  65. state $y = Math::GMPz::Rmpz_init();
  66. Math::GMPz::Rmpz_set_ui($a, 1);
  67. Math::GMPz::Rmpz_set_ui($b, 1);
  68. Math::GMPz::Rmpz_set_ui($w, $c);
  69. Math::GMPz::Rmpz_set_ui($x, 1);
  70. Math::GMPz::Rmpz_set_ui($y, 0);
  71. quadratic_powmod($x, $y, $a, $b, $w, $n, $n);
  72. Math::GMPz::Rmpz_congruent_p($x, $n - $n + 1, $n)
  73. && Math::GMPz::Rmpz_congruent_p($y, $n - 1, $n);
  74. }
  75. my $count = 0;
  76. foreach my $n (1 .. 1e5) {
  77. if (is_quadratic_frobenius_pseudoprime($n)) {
  78. ++$count;
  79. if (!is_prime($n)) {
  80. die "Counter-example: $n";
  81. }
  82. }
  83. elsif (is_prime($n)) {
  84. die "Missed prime: $n";
  85. }
  86. }
  87. say "Count: $count"; #=> Count: 9592