fib-tzn_primality_test.pl 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. #!/usr/bin/perl
  2. # Daniel "Trizen" Șuteu
  3. # Date: 01 August 2017
  4. # https://github.com/trizen
  5. # An efficient implementation of a new primality test, inspired from the AKS primality test.
  6. # When n>2 is a (pseudo)prime:
  7. #
  8. # (2 + sqrt(-1))^n - (sqrt(-1))^n - 2 = 0 (mod n)
  9. #
  10. # By breaking the formula into pieces, we get the following equivalent statements:
  11. #
  12. # 5^(n/2) * cos(n * atan(1/2)) = 2 (mod n)
  13. # 5^(n/2) * sin(n * atan(1/2)) = { n-1 if n=3 (mod 4)
  14. # 1 if n=1 (mod 4) } (mod n)
  15. #
  16. # Additionally, we have the following two identities:
  17. #
  18. # cos(n * atan(1/2)) = (((2+i)/sqrt(5))^n + exp(-1 * log((2+i)/sqrt(5)) * n))/2
  19. # sin(n * atan(1/2)) = (((2+i)/sqrt(5))^n - exp(-1 * log((2+i)/sqrt(5)) * n))/(2i)
  20. #
  21. # For numbers of the form `2n+1`, the above formulas simplify to:
  22. #
  23. # cos((2*n + 1) * atan(1/2)) = a(n)/(sqrt(5) * 5^n)
  24. # sin((2*n + 1) * atan(1/2)) = b(n)/(sqrt(5) * 5^n)
  25. #
  26. # where `a(n)` and `b(n)` are integers given by:
  27. #
  28. # a(n) = real((2 + sqrt(-1))^n)
  29. # b(n) = imag((2 + sqrt(-1))^n)
  30. #
  31. # Defined recursively as:
  32. #
  33. # a(1) = 2; a(2) = 3; a(n) = 4*a(n-1) - 5*a(n-2)
  34. # b(1) = 1; b(2) = 4; b(n) = 4*b(n-1) - 5*b(n-2)
  35. #
  36. # Currently, we use only the `b(n)` branch, as it is strong enough to reject most composites.
  37. # Known counter-examples (in order):
  38. # [1105, 1729, 2465, 10585, 15841, 29341, 38081, 40501, 41041, 46657, ...]
  39. use 5.020;
  40. use strict;
  41. use warnings;
  42. no warnings 'recursion';
  43. use ntheory qw(is_prime);
  44. use experimental qw(signatures);
  45. sub mulmod {
  46. my ($n, $k, $mod) = @_;
  47. ref($mod)
  48. ? ((($n % $mod) * $k) % $mod)
  49. : ntheory::mulmod($n, $k, $mod);
  50. }
  51. sub modulo_test($n, $mod) {
  52. my %cache;
  53. sub ($n) {
  54. $n == 0 && return 1;
  55. $n == 1 && return 4;
  56. if (exists $cache{$n}) {
  57. return $cache{$n};
  58. }
  59. my $k = $n >> 1;
  60. #<<<
  61. $cache{$n} = (
  62. $n % 2 == 0
  63. ? (mulmod(__SUB__->($k), __SUB__->($k), $mod) - mulmod(mulmod(5, __SUB__->($k - 1), $mod), __SUB__->($k - 1), $mod)) % $mod
  64. : (mulmod(__SUB__->($k), __SUB__->($k + 1), $mod) - mulmod(mulmod(5, __SUB__->($k - 1), $mod), __SUB__->($k), $mod)) % $mod
  65. );
  66. #>>>
  67. }->($n - 1);
  68. }
  69. sub is_probably_prime($n) {
  70. $n <= 1 && return 0;
  71. $n == 2 && return 1;
  72. my $r = modulo_test($n, $n);
  73. ($n % 4 == 3) ? ($r == $n - 1) : ($r == 1);
  74. }
  75. #
  76. ## Run a few tests
  77. #
  78. say is_probably_prime(6760517005636313) ? 'prime' : 'error'; #=> prime
  79. say is_probably_prime(204524538079257577) ? 'prime' : 'error'; #=> prime
  80. say is_probably_prime(904935283655003749) ? 'prime' : 'error'; #=> prime
  81. # Big integers
  82. eval {
  83. require Math::GMPz;
  84. say is_probably_prime(Math::GMPz->new('90123127846128741241234935283655003749')) ? 'prime' : 'error'; #=> prime
  85. say is_probably_prime(Math::GMPz->new('793534607085486631526003804503819188867498912352777')) ? 'prime' : 'error'; #=> prime
  86. say is_probably_prime(Math::GMPz->new('6297842947207644396587450668076662882608856575233692384596461')) ? 'prime' : 'error'; #=> prime
  87. say is_probably_prime(Math::GMPz->new('396090926269155174167385236415542573007935497117155349994523806173')) ? 'prime' : 'error'; #=> prime
  88. say "=> Testing large Mersenne primes...";
  89. # Mersenne primes
  90. say is_probably_prime(Math::GMPz->new(2)**127 - 1) ? 'prime' : 'error'; #=> prime
  91. say is_probably_prime(Math::GMPz->new(2)**521 - 1) ? 'prime' : 'error'; #=> prime
  92. say is_probably_prime(Math::GMPz->new(2)**1279 - 1) ? 'prime' : 'error'; #=> prime
  93. say is_probably_prime(Math::GMPz->new(2)**3217 - 1) ? 'prime' : 'error'; #=> prime
  94. say is_probably_prime(Math::GMPz->new(2)**4423 - 1) ? 'prime' : 'error'; #=> prime
  95. };
  96. # Find counter-examples
  97. foreach my $n (1 .. 2500) {
  98. if (is_probably_prime($n)) {
  99. if (not is_prime($n)) {
  100. warn "Counter-example: $n\n";
  101. }
  102. }
  103. elsif (is_prime($n)) {
  104. # This should never happen.
  105. warn "Missed a prime: $n\n";
  106. }
  107. }