squarefree_strong_fermat_pseudoprimes_in_range.sf 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. #!/usr/bin/ruby
  2. # Daniel "Trizen" Șuteu
  3. # Date: 07 November 2022
  4. # https://github.com/trizen
  5. # Generate all the squarefree strong Fermat pseudoprimes to a given 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. func squarefree_strong_fermat_pseudoprimes_in_range(A, B, k, base, callback) {
  10. A = max(k.pn_primorial, A)
  11. var generator = func (m, L, lo, k, k_exp, congr) {
  12. var hi = idiv(B,m).iroot(k)
  13. return nil if (lo > hi)
  14. if (k == 1) {
  15. lo = max(lo, idiv_ceil(A, m))
  16. lo > hi && return nil
  17. var t = m.invmod(L)
  18. t > hi && return nil
  19. t += L*idiv_ceil(lo - t, L) if (t < lo)
  20. t > hi && return nil
  21. for p in (range(t, hi, L)) {
  22. p.is_prime || next
  23. with (m*p) {|n|
  24. if (znorder(base, p) `divides` n.dec) {
  25. var v = p.dec.valuation(2)
  26. if (v > k_exp && powmod(base, p.dec>>(v-k_exp), p).is_congruent(congr, p)) {
  27. callback(n)
  28. }
  29. }
  30. }
  31. }
  32. return nil
  33. }
  34. each_prime(lo, hi, {|p|
  35. p.divides(base) && next
  36. var val2 = p.dec.valuation(2)
  37. val2 > k_exp || next
  38. powmod(base, p.dec>>(val2-k_exp), p).is_congruent(congr, p) || next
  39. var z = znorder(base, p)
  40. m.is_coprime(z) || next
  41. __FUNC__(m*p, lcm(L, z), p+1, k-1, k_exp, congr)
  42. })
  43. }
  44. # Case where 2^d == 1 (mod p), where d is the odd part of p-1.
  45. generator(1, 1, 2, k, 0, 1)
  46. # Cases where 2^(d * 2^v) == -1 (mod p), for some v >= 0.
  47. for v in (0..B.ilog2) {
  48. generator(1, 1, 2, k, v, -1)
  49. }
  50. return callback
  51. }
  52. # Generate all the squarefree strong Fermat pseudoprimes to base 5 with 4 prime factors in the range [1, 10^8]
  53. var k = 4
  54. var base = 5
  55. var from = 1
  56. var upto = 1e8
  57. say gather { squarefree_strong_fermat_pseudoprimes_in_range(from, upto, k, base, { take(_) }) }.sort
  58. __END__
  59. [4382191, 7267051, 9694351, 11921001, 18464761, 28351081, 33333091, 33567451, 38574199, 42151351, 48321001, 71107681, 80717131, 83420101]