strong_fermat_pseudoprimes_in_range.jl 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. #!/usr/bin/julia
  2. # Daniel "Trizen" Șuteu
  3. # Date: 07 March 2023
  4. # https://github.com/trizen
  5. # Generate all the strong Fermat pseudoprimes to given a base 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. # https://trizenx.blogspot.com/2020/08/pseudoprimes-construction-methods-and.html
  9. # PARI/GP program (slow):
  10. # 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));
  11. # PARI/GP program (fast):
  12. # strong_check(p, base, e, r) = my(tv=valuation(p-1, 2)); tv > e && Mod(base, p)^((p-1)>>(tv-e)) == r;
  13. # 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));
  14. using Primes
  15. const BIG = false # true to use big integers
  16. function divisors(n)
  17. d = Int64[1]
  18. for (p,e) in factor(n)
  19. t = Int64[]
  20. r = 1
  21. for i in 1:e
  22. r *= p
  23. for u in d
  24. push!(t, u*r)
  25. end
  26. end
  27. append!(d, t)
  28. end
  29. return sort(d)
  30. end
  31. function isprimepower(n)
  32. length(factor(n)) == 1
  33. end
  34. function znorder(a, n)
  35. if isprime(n)
  36. for d in divisors(n-1)
  37. if (powermod(a, d, n) == 1)
  38. return d
  39. end
  40. end
  41. end
  42. f = factor(n)
  43. if (length(f) == 1) # is prime power
  44. p = first(first(f))
  45. z = znorder(a, p)
  46. while (powermod(a, z, n) != 1)
  47. z *= p
  48. end
  49. return z
  50. end
  51. pp_orders = Int64[]
  52. for (p,e) in f
  53. push!(pp_orders, znorder(a, p^e))
  54. end
  55. return lcm(pp_orders)
  56. end
  57. function big_prod(arr)
  58. BIG || return prod(arr)
  59. r = big"1"
  60. for n in (arr)
  61. r *= n
  62. end
  63. return r
  64. end
  65. function strong_check(p, base, e, r)
  66. tv = 0
  67. pm1 = p-1
  68. while (pm1 % 2 == 0)
  69. pm1 = fld(pm1, 2)
  70. tv += 1
  71. end
  72. tv > e && powermod(base, (p-1)>>(tv - e), p) == mod(r, p)
  73. end
  74. function strong_fermat_pseudoprimes_in_range(A, B, k, base, callback)
  75. seen = Dict()
  76. A = max(A, big_prod(primes(prime(k))))
  77. F = function(m, L, lo::Int64, k::Int64, e::Int64, r::Int64)
  78. hi = round(Int64, fld(B, m)^(1/k))
  79. if (lo > hi)
  80. return nothing
  81. end
  82. if (k == 1)
  83. if (L == 1)
  84. for p in primes(lo, hi)
  85. base % p == 0 && continue
  86. v = (m == 1 ? p*p : m*p)
  87. while (v <= B)
  88. if (v >= A && strong_check(p, base, e, r))
  89. powermod(base, v-1, v) == 1 || break
  90. if (!haskey(seen, v))
  91. callback(v)
  92. seen[v] = true
  93. end
  94. end
  95. v *= p
  96. end
  97. end
  98. return nothing
  99. end
  100. t = invmod(m, L)
  101. t > hi && return nothing
  102. if (t < lo)
  103. t += L*cld(lo - t, L)
  104. end
  105. t > hi && return nothing
  106. for p in t:L:hi
  107. if (isprimepower(p) && gcd(m, p) == 1 && gcd(base, p) == 1)
  108. n = m*p
  109. if (n >= A && strong_check(p,base,e,r) && (n-1) % znorder(base, p) == 0)
  110. if (!haskey(seen, n))
  111. callback(n)
  112. seen[n] = true
  113. end
  114. end
  115. end
  116. end
  117. return nothing
  118. end
  119. for p in primes(lo, hi)
  120. if (base % p != 0 && strong_check(p, base, e, r))
  121. z = znorder(base, p)
  122. if (gcd(m, z) == 1)
  123. v = m*p
  124. q = p
  125. while (v <= B)
  126. F(v, lcm(L, z), p+1, k-1, e, r)
  127. q *= p
  128. powermod(base, z, q) == 1 || break
  129. v *= p
  130. end
  131. end
  132. end
  133. end
  134. end
  135. # Case where 2^d == 1 (mod p), where d is the odd part of p-1.
  136. F((BIG ? big"1" : 1), (BIG ? big"1" : 1), 2, k, 0, 1)
  137. # Cases where 2^(d * 2^v) == -1 (mod p), for some v >= 0.
  138. for v in 0:round(Int64, log2(B))
  139. F((BIG ? big"1" : 1), (BIG ? big"1" : 1), 2, k, v, -1)
  140. end
  141. end
  142. # Generate all the strong Fermat pseudoprimes to base 3 in range [1, 10^5]
  143. from = 1
  144. upto = 10^5
  145. base = 3
  146. arr = []
  147. for k in 1:100
  148. prod(primes(prime(k))) > upto && break
  149. strong_fermat_pseudoprimes_in_range(from, upto, k, base, function (n) push!(arr, n) end)
  150. end
  151. sort!(arr)
  152. println(arr)