squarefree_fermat_pseudoprimes_in_range.sf 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. #!/usr/bin/ruby
  2. # Daniel "Trizen" Șuteu
  3. # Date: 29 August 2022
  4. # https://github.com/trizen
  5. # Generate all the squarefree 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. # PARI/GP program:
  10. # squarefree_fermat(A, B, k, base=2) = A=max(A, vecprod(primes(k))); (f(m, l, p, k, u=0, v=0) = my(list=List()); if(k==1, forprime(p=u, v, if(base%p != 0, my(t=m*p); if((t-1)%l == 0 && (t-1)%znorder(Mod(base, p)) == 0, listput(list, t)))), forprime(q = p, sqrtnint(B\m, k), my(t = m*q); if (base%q != 0, my(L=lcm(l, znorder(Mod(base, q)))); if(gcd(L, t) == 1, my(u=ceil(A/t), v=B\t); if(u <= v, my(r=nextprime(q+1)); if(k==2 && r>u, u=r); list=concat(list, f(t, L, r, k-1, u, v))))))); list); vecsort(Vec(f(1, 1, 2, k)));
  11. func squarefree_fermat_pseudoprimes_in_range(a, b, k, base, callback) {
  12. a = max(k.pn_primorial, a)
  13. func (m, L, lo, k) {
  14. var hi = idiv(b,m).iroot(k)
  15. return nil if (lo > hi)
  16. if (k == 1) {
  17. lo = max(lo, idiv_ceil(a, m))
  18. lo > hi && return nil
  19. var t = m.invmod(L)
  20. t > hi && return nil
  21. t += L*idiv_ceil(lo - t, L) if (t < lo)
  22. t > hi && return nil
  23. for p in (range(t, hi, L)) {
  24. p.is_prime || next
  25. with (m*p) {|n|
  26. if (znorder(base, p) `divides` n.dec) {
  27. callback(n)
  28. }
  29. }
  30. }
  31. return nil
  32. }
  33. each_prime(lo, hi, {|p|
  34. p.divides(base) && next
  35. var t = lcm(L, znorder(base, p))
  36. m.is_coprime(t) || next
  37. __FUNC__(m*p, t, p+1, k-1)
  38. })
  39. }(1, 1, 2, k)
  40. return callback
  41. }
  42. # High-level implementation (unoptimized):
  43. func squarefree_fermat_pseudoprimes_in_range_unoptimized(A, B, k, base, callback) {
  44. func F(m, lambda, p, k) {
  45. if (k == 1) {
  46. each_prime(max(p, ceil(A/m)), floor(B/m), {|q|
  47. if (lcm(lambda, znorder(base, q)) `divides` (m*q - 1)) {
  48. callback(m*q)
  49. }
  50. })
  51. }
  52. elsif (k >= 2) {
  53. for q in (primes(p, (B/m)**(1/k))) {
  54. if (gcd(lcm(lambda, znorder(base, q)), m) == 1) {
  55. F(m*q, lcm(lambda, znorder(base, q)), q.next_prime, k-1)
  56. }
  57. }
  58. }
  59. }
  60. F(1, 1, 2, k)
  61. }
  62. # Generate all the squarefree Fermat pseudoprimes to base 2 with 5 prime factors in the range [100, 10^7]
  63. var k = 5
  64. var base = 2
  65. var from = 100
  66. var upto = 1e7
  67. say gather { squarefree_fermat_pseudoprimes_in_range(from, upto, k, base, { take(_) }) }.sort
  68. say gather { squarefree_fermat_pseudoprimes_in_range_unoptimized(from, upto, k, base, { take(_) }) }.sort
  69. __END__
  70. [825265, 1050985, 1275681, 2113665, 2503501, 2615977, 2882265, 3370641, 3755521, 4670029, 4698001, 4895065, 5034601, 6242685, 6973057, 7428421, 8322945, 9223401, 9224391, 9890881]