carmichael_strong_fermat_pseudoprimes_in_range.sf 3.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. #!/usr/bin/ruby
  2. # Daniel "Trizen" Șuteu
  3. # Date: 07 November 2022
  4. # https://github.com/trizen
  5. # Generate all the Carmichael numbers with n prime factors in a given range [A,B] that are also strong Fermat pseudoprimes to a given base. (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. # carmichael_strong_psp(A, B, k, base) = A=max(A, vecprod(primes(k+1))\2); (f(m, l, p, k, k_exp, congr, u=0, v=0) = my(list=List()); if(k==1, forprime(q=u, v, my(t=m*q); if((t-1)%l == 0 && (t-1)%(q-1) == 0, my(tv=valuation(q-1, 2)); if(tv > k_exp && Mod(base, q)^(((q-1)>>tv)<<k_exp) == congr, listput(list, t)))), forprime(q = p, sqrtnint(B\m, k), if(base%q != 0, my(tv=valuation(q-1, 2)); if(tv > k_exp && Mod(base, q)^(((q-1)>>tv)<<k_exp) == congr, my(L=lcm(l, q-1)); if(gcd(L, m) == 1, my(t = m*q, 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, k_exp, congr, u, v)))))))); list); my(res=f(1, 1, 3, k, 0, 1)); for(v=0, logint(B, 2), res=concat(res, f(1, 1, 3, k, v, -1))); vecsort(Vec(res));
  11. func carmichael_strong_fermat_in_range(A, B, k, base, callback) {
  12. A = max(k.pn_primorial, A)
  13. # Largest possisble prime factor for Carmichael numbers <= B
  14. var max_p = ((1 + isqrt(8*B + 1))>>2)
  15. var generator = func (m, L, lo, k, k_exp, congr) {
  16. var hi = idiv(B,m).iroot(k)
  17. if (lo > hi) {
  18. return nil
  19. }
  20. if (k == 1) {
  21. hi = max_p if (hi > max_p)
  22. lo = max(lo, idiv_ceil(A, m))
  23. lo > hi && return nil
  24. var t = m.invmod(L)
  25. t > hi && return nil
  26. t += L*idiv_ceil(lo - t, L) if (t < lo)
  27. t > hi && return nil
  28. for p in (range(t, hi, L)) {
  29. p.is_prime || next
  30. with (m*p) {|n|
  31. if (p.dec `divides` n.dec) {
  32. var v = p.dec.valuation(2)
  33. if (v > k_exp && powmod(base, p.dec>>(v-k_exp), p).is_congruent(congr, p)) {
  34. callback(n)
  35. }
  36. }
  37. }
  38. }
  39. return nil
  40. }
  41. each_prime(lo, hi, {|p|
  42. p.divides(base) && next
  43. var val2 = p.dec.valuation(2)
  44. val2 > k_exp || next
  45. powmod(base, p.dec>>(val2-k_exp), p).is_congruent(congr, p) || next
  46. var t = lcm(L, p-1)
  47. m.is_coprime(t) || next
  48. __FUNC__(m*p, t, p+1, k-1, k_exp, congr)
  49. })
  50. }
  51. # Case where 2^d == 1 (mod p), where d is the odd part of p-1.
  52. generator(1, 1, 2, k, 0, 1)
  53. # Cases where 2^(d * 2^v) == -1 (mod p), for some v >= 0.
  54. for v in (0..B.ilog2) {
  55. generator(1, 1, 2, k, v, -1)
  56. }
  57. return callback
  58. }
  59. # Generate all the 3-Carmichael numbers in the range [1, 10^7] that are also strong pseudoprimes to base 2.
  60. var k = 3
  61. var base = 2
  62. var from = 1
  63. var upto = 1e7
  64. say gather { carmichael_strong_fermat_in_range(from, upto, k, base, { take(_) }) }.sort
  65. __END__
  66. [15841, 29341, 52633, 252601, 314821, 1909001, 3581761, 4335241, 5049001, 5444489]