chernick-carmichael_with_n_factors_sieve_int128.jl 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. #!/usr/bin/julia
  2. # Sieve for Chernick's "universal form" Carmichael number with n prime factors.
  3. # Inspired by the PARI program by David A. Corneth from OEIS A372238.
  4. # Version with 128-bit integers, to potentially find a(12).
  5. # See also:
  6. # https://oeis.org/A318646
  7. # https://oeis.org/A372238/a372238.gp.txt
  8. using Primes
  9. function isrem(m::UInt128, p, n)
  10. (( 6 * m + 1) % p == 0) && return false
  11. ((12 * m + 1) % p == 0) && return false
  12. ((18 * m + 1) % p == 0) && return false
  13. for k in 2 : n-2
  14. if (((9 * m << k) + 1) % p == 0)
  15. return false
  16. end
  17. end
  18. return true
  19. end
  20. function remaindersmodp(p, n)
  21. filter(m -> isrem(m |> UInt128, p, n), 0:p-1)
  22. end
  23. function mulmod(a::UInt128, b::UInt128, m::Integer)
  24. # Function from:
  25. # https://discourse.julialang.org/t/modular-multiplication-without-overflow/90421/4
  26. magic1 = (UInt128(0xFFFFFFFF) << 32)
  27. magic2 = UInt128(0x8000000000000000)
  28. if iszero(((a | b) & magic1))
  29. return (a * b) % m
  30. end
  31. d = zero(UInt128)
  32. mp2 = m >> 1
  33. if a >= m; a %= m; end
  34. if b >= m; b %= m; end
  35. for _ in 1:64
  36. (d > mp2) ? d = ((d << 1) - m) : d = (d << 1)
  37. if !iszero(a & magic2)
  38. d += b
  39. end
  40. if d >= m
  41. d -= m
  42. end
  43. a <<= 1
  44. end
  45. return d
  46. end
  47. function mulmod(a::Integer, b::Integer, m::Integer)
  48. sa, sb = UInt128.(unsigned.((a,b)))
  49. return mulmod(sa, sb, m)
  50. end
  51. function chinese(a1, m1, a2, m2)
  52. M = lcm(m1, m2)
  53. return [
  54. (mulmod(mulmod(a1, invmod(div(M, m1), m1), M), div(M, m1), M) +
  55. mulmod(mulmod(a2, invmod(div(M, m2), m2), M), div(M, m2), M)) % M, M
  56. ]
  57. end
  58. function remainders_for_primes(n, primes)
  59. res = [[0, 1]]
  60. for p in primes
  61. rems = remaindersmodp(p, n)
  62. if (length(rems) == 0)
  63. rems = [0]
  64. end
  65. nres = []
  66. for r in res
  67. a = r[1]
  68. m = r[2]
  69. for rem in rems
  70. push!(nres, chinese(a, m, rem, p))
  71. end
  72. end
  73. res = nres
  74. end
  75. res = map(x -> x[1], res)
  76. sort!(res)
  77. return res
  78. end
  79. function is(m::UInt128, n)
  80. isprime( 6 * m + 1) || return false
  81. isprime(12 * m + 1) || return false
  82. isprime(18 * m + 1) || return false
  83. for k in 2:n-2
  84. isprime((9 * m << k) + 1) || return false
  85. end
  86. return true
  87. end
  88. function deltas(arr)
  89. prev = 0
  90. D = []
  91. for k in arr
  92. push!(D, k - prev)
  93. prev = k
  94. end
  95. return D
  96. end
  97. function chernick_carmichael_with_n_factors(n)
  98. maxp = 11
  99. n >= 8 && (maxp = 17)
  100. n >= 10 && (maxp = 29)
  101. n >= 12 && (maxp = 31)
  102. P = primes(maxp)
  103. R = remainders_for_primes(n, P)
  104. D = deltas(R)
  105. s = prod(P)
  106. while (D[1] == 0)
  107. popfirst!(D)
  108. end
  109. push!(D, R[1] + s - R[length(R)])
  110. m = R[1] |> UInt128
  111. D_len = length(D)
  112. two_power = max(1 << (n - 4), 1)
  113. for j in 0:10^18
  114. if (m % two_power == 0 && is(m, n))
  115. return m |> Int128
  116. end
  117. if (j % 10^8 == 0 && j > 0)
  118. println("Searching for a($n) with m = $m")
  119. end
  120. m += D[(j % D_len) + 1]
  121. end
  122. end
  123. for n in 3:9
  124. println([n, chernick_carmichael_with_n_factors(n)])
  125. end