strong_fermat_pseudoprimes_in_range.sf 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. #!/usr/bin/ruby
  2. # Daniel "Trizen" Șuteu
  3. # Date: 07 November 2022
  4. # https://github.com/trizen
  5. # Generate all the k-omega strong Fermat pseudoprimes in range [a,b]. (not in sorted order)
  6. # Definition:
  7. # k-omega primes are numbers n such that omega(n) = k.
  8. # See also:
  9. # https://en.wikipedia.org/wiki/Almost_prime
  10. # https://en.wikipedia.org/wiki/Prime_omega_function
  11. # https://trizenx.blogspot.com/2020/08/pseudoprimes-construction-methods-and.html
  12. #`(
  13. # PARI/GP program (slow):
  14. strong_fermat_psp(A, B, k, base) = A=max(A, vecprod(primes(k))); (f(m, l, p, j, k_exp, congr) = my(list=List()); forprime(q=p, sqrtnint(B\m, j), if(base%q != 0, my(tv=valuation(q-1, 2)); if(tv > k_exp && Mod(base, q)^(((q-1)>>tv)<<k_exp) == congr, my(v=m*q, t=q, r=nextprime(q+1)); while(v <= B, my(L=lcm(l, znorder(Mod(base, t)))); if(gcd(L, v) == 1, if(j==1, if(v>=A && if(k==1, !isprime(v), 1) && (v-1)%L == 0, listput(list, v)), if(v*r <= B, list=concat(list, f(v, L, r, j-1, k_exp, congr)))), break); v *= q; t *= q)))); list); my(r=f(1, 1, 2, k, 0, 1)); for(v=0, logint(B, 2), r=concat(r, f(1, 1, 2, k, v, -1))); vecsort(Vec(r));
  15. # PARI/GP program (fast):
  16. strong_check(p, base, e, r) = my(tv=valuation(p-1, 2)); tv > e && Mod(base, p)^((p-1)>>(tv-e)) == r;
  17. strong_fermat_psp(A, B, k, base) = A=max(A, vecprod(primes(k))); (f(m, l, lo, k, e, r) = my(list=List()); my(hi=sqrtnint(B\m, k)); if(lo > hi, return(list)); if(k==1, forstep(p=lift(1/Mod(m, l)), hi, l, if(isprimepower(p) && gcd(m*base, p) == 1 && strong_check(p, base, e, r), my(n=m*p); if(n >= A && (n-1) % znorder(Mod(base, p)) == 0, listput(list, n)))), forprime(p=lo, hi, base%p == 0 && next; strong_check(p, base, e, r) || next; my(z=znorder(Mod(base, p))); gcd(m,z) == 1 || next; my(q=p, v=m*p); while(v <= B, list=concat(list, f(v, lcm(l, z), p+1, k-1, e, r)); q *= p; Mod(base, q)^z == 1 || break; v *= p))); list); my(res=f(1, 1, 2, k, 0, 1)); for(v=0, logint(B, 2), res=concat(res, f(1, 1, 2, k, v, -1))); vecsort(Set(res));
  18. )
  19. func strong_fermat_pseudoprimes_in_range(A, B, k, base, callback) {
  20. A = max(k.pn_primorial, A)
  21. var seen = Hash()
  22. var generator = func (m, L, lo, j, k_exp, congr) {
  23. var hi = idiv(B,m).iroot(j)
  24. lo > hi && return nil
  25. if (j == 1) {
  26. if (L == 1) { # optimization
  27. each_prime(lo, hi, {|p|
  28. p.divides(base) && next
  29. var val2 = p.dec.valuation(2)
  30. val2 > k_exp || next
  31. powmod(base, p.dec>>(val2-k_exp), p).is_congruent(congr, p) || next
  32. for (var v = (m == 1 ? p*p : m*p); v <= B; v *= p) {
  33. v >= A || next
  34. v.is_strong_psp(base) || break
  35. callback(v) if !(seen{v} := 0 ++)
  36. }
  37. })
  38. return nil
  39. }
  40. var t = m.invmod(L)
  41. t > hi && return nil
  42. t += L*idiv_ceil(lo - t, L) if (t < lo)
  43. t > hi && return nil
  44. for p in (range(t, hi, L)) {
  45. p.is_prime_power || next
  46. p.is_coprime(m) || next
  47. p.is_coprime(base) || next
  48. var val2 = p.dec.valuation(2)
  49. val2 > k_exp || next
  50. powmod(base, p.dec>>(val2-k_exp), p).is_congruent(congr, p) || next
  51. var v = m*p
  52. v >= A || next
  53. if (znorder(base, p) `divides` v.dec) {
  54. callback(v) if !(seen{v} := 0 ++)
  55. }
  56. }
  57. return nil
  58. }
  59. each_prime(lo, hi, {|p|
  60. p.divides(base) && next
  61. var val2 = p.dec.valuation(2)
  62. val2 > k_exp || next
  63. powmod(base, p.dec>>(val2-k_exp), p).is_congruent(congr, p) || next
  64. var z = znorder(base, p)
  65. m.is_coprime(z) || next
  66. var q = p
  67. var v = m*p
  68. while (v <= B) {
  69. __FUNC__(v, lcm(L, z), p+1, j-1, k_exp, congr)
  70. q *= p
  71. powmod(base, z, q) == 1 || break
  72. v *= p
  73. }
  74. })
  75. }
  76. # Case where 2^d == 1 (mod p), where d is the odd part of p-1.
  77. generator(1, 1, 2, k, 0, 1)
  78. # Cases where 2^(d * 2^v) == -1 (mod p), for some v >= 0.
  79. for v in (0..B.ilog2) {
  80. generator(1, 1, 2, k, v, -1)
  81. }
  82. return callback
  83. }
  84. # Generate all the strong Fermat pseudoprimes to base 3 in range [1, 10^4]
  85. var from = 1
  86. var upto = 1e4
  87. var base = 3
  88. var arr = []
  89. for k in (1..100) {
  90. break if (k.pn_primorial > upto)
  91. strong_fermat_pseudoprimes_in_range(from, upto, k, base, { arr << _ })
  92. }
  93. say arr.sort
  94. # Run some tests
  95. if (false) { # true to run some tests
  96. for k in (1..5) {
  97. say "Testing k = #{k}"
  98. var lo = k.pn_primorial
  99. var hi = lo*1000
  100. for base in (2..100) {
  101. var this = k.strong_fermat_psp(base, lo, hi)
  102. #var this = k.omega_primes(lo,hi).grep{.is_strong_psp(base) && .is_composite }
  103. var that = gather {
  104. strong_fermat_pseudoprimes_in_range(lo, hi, k, base, func (n) { take(n) })
  105. }.sort
  106. this == that ||
  107. die "Error for k = #{k} and base = #{base} with hi = #{hi}\n#{this} != #{that}";
  108. }
  109. }
  110. }
  111. __END__
  112. [121, 703, 1891, 3281, 8401, 8911]