continued_fraction_factorization_method.jl 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. #!/usr/bin/julia
  2. # A simple implementation of the continued fraction factorization method.
  3. # (variation of the Brillhart-Morrison algorithm).
  4. # See also:
  5. # https://en.wikipedia.org/wiki/Pell%27s_equation
  6. # https://en.wikipedia.org/wiki/Continued_fraction_factorization
  7. # https://trizenx.blogspot.com/2018/10/continued-fraction-factorization-method.html
  8. # "Gaussian elimination" algorithm from:
  9. # https://github.com/martani/Quadratic-Sieve
  10. using Primes
  11. const BIG_ONE = big"1"
  12. const BIG_ZERO = big"0"
  13. function issquare(n)
  14. isqrt(n)^2 == n
  15. end
  16. function issquarefree(n)
  17. for (p,e) in factor(n)
  18. (e >= 2) && return false
  19. end
  20. return true
  21. end
  22. function next_multiplier(n, k)
  23. k += 2
  24. while (!issquarefree(k) || gcd(k,n) != 1)
  25. k += 1
  26. end
  27. return k
  28. end
  29. function gaussian_elimination(A, n)
  30. m = length(A)
  31. I = [BIG_ONE << k for k in 0:m]
  32. nrow = 0
  33. for col in (1 : min(m, n))
  34. npivot = 0
  35. for row in (nrow+1 : m)
  36. if (((A[row] >> (col-1)) & 1) == 1)
  37. npivot = row
  38. nrow += 1
  39. break
  40. end
  41. end
  42. npivot == 0 && continue
  43. if (npivot != nrow)
  44. A[[npivot,nrow]] = A[[nrow, npivot]]
  45. I[[npivot,nrow]] = I[[nrow, npivot]]
  46. end
  47. for row in (nrow+1 : m)
  48. if (((A[row] >> (col-1)) & 1) == 1)
  49. A[row] = xor(A[row], A[nrow])
  50. I[row] = xor(I[row], I[nrow])
  51. end
  52. end
  53. end
  54. return I
  55. end
  56. function check_factor(n, g, factors)
  57. while (rem(n,g) == 0)
  58. n = div(n,g)
  59. push!(factors, g)
  60. if (isprime(n))
  61. push!(factors, n)
  62. return 1
  63. end
  64. end
  65. return n
  66. end
  67. function is_smooth_over_prod(n, k)
  68. g = gcd(n, k)
  69. while (g > 1)
  70. n = div(n, g)
  71. while (rem(n, g) == 0)
  72. n = div(n, g)
  73. end
  74. n == 1 && return true
  75. g = gcd(n, g)
  76. end
  77. n == 1
  78. end
  79. function jacobi(a, n)
  80. a %= n
  81. result = 1
  82. while a != 0
  83. while iseven(a)
  84. a >>= 1
  85. ((n % 8) in [3, 5]) && (result *= -1)
  86. end
  87. a, n = n, a
  88. (a % 4 == n % 4 == 3) && (result *= -1)
  89. a %= n
  90. end
  91. return n == 1 ? result : 0
  92. end
  93. function cffm(n, multiplier = 1)
  94. n <= 1 && return []
  95. isprime(n) && return [n]
  96. if (iseven(n))
  97. v = 0
  98. while (iseven(n))
  99. v += 1
  100. n >>= 1
  101. end
  102. arr1 = [big"2" for i in 1:v]
  103. arr2 = cffm(n)
  104. return append!(arr1, arr2)
  105. end
  106. if (issquare(n))
  107. f = cffm(isqrt(n))
  108. append!(f, f)
  109. return sort(f)
  110. end
  111. N = n*multiplier
  112. x = isqrt(N)
  113. y = x
  114. z = 1
  115. w = x+x
  116. r = w
  117. nf = round(Int64, exp(sqrt(log(n) * log(log(n))))^(sqrt(2) / 4) / 1.25)
  118. factor_base = [2]
  119. begin
  120. p = 3
  121. while (length(factor_base) < nf)
  122. if (jacobi(N, p) >= 0)
  123. push!(factor_base, p)
  124. end
  125. p = nextprime(p+1)
  126. end
  127. end
  128. # ~ B = round(Int64, exp(sqrt(log(n) * log(log(n))) / 2))
  129. # ~ factor_base = []
  130. # ~ for p in (primes(B))
  131. # ~ if (jacobi(N,p) >= 0)
  132. # ~ push!(factor_base, p)
  133. # ~ end
  134. # ~ end
  135. factor_prod = prod([big(k) for k in factor_base])
  136. factor_index = Dict{Int64, Int64}()
  137. for k in (1:length(factor_base))
  138. factor_index[factor_base[k]] = k-1
  139. end
  140. function exponent_signature(factors)
  141. sig = BIG_ZERO
  142. for (p,e) in factors
  143. if (isodd(e))
  144. sig |= (BIG_ONE << factor_index[p])
  145. end
  146. end
  147. return sig
  148. end
  149. L = length(factor_base)+1
  150. Q = []
  151. A = []
  152. (f1, f2) = (BIG_ONE, x)
  153. while (length(A) < L)
  154. y = (r*z - y)
  155. z = div((N - y*y), z)
  156. r = div((x + y), z)
  157. (f1, f2) = (f2, rem((r*f2 + f1), n))
  158. if (issquare(z))
  159. g = gcd(f1 - isqrt(z), n)
  160. if (g > 1 && g < n)
  161. arr1 = cffm(g)
  162. arr2 = cffm(div(n,g))
  163. return sort(append!(arr1, arr2))
  164. end
  165. end
  166. if (z > 1 && is_smooth_over_prod(z, factor_prod))
  167. push!(A, exponent_signature(factor(z)))
  168. push!(Q, [f1, z])
  169. end
  170. if (z == 1)
  171. println("z == 1 -> trying again with a multiplier...")
  172. return cffm(n, next_multiplier(n, multiplier))
  173. end
  174. end
  175. while (length(A) < L)
  176. push!(A, 0)
  177. end
  178. I = gaussian_elimination(A, length(A))
  179. LR = 0
  180. for k in (length(A):-1:1)
  181. if (A[k] != 0)
  182. LR = k+1
  183. break
  184. end
  185. end
  186. remainder = n
  187. factors = []
  188. function cfrac_find_factors(solution)
  189. X = BIG_ONE
  190. Y = BIG_ONE
  191. for i in 0:length(Q)-1
  192. if (((solution >> i) & 1) == 1)
  193. X *= Q[i+1][1]
  194. Y *= Q[i+1][2]
  195. X %= remainder
  196. g = gcd(X - isqrt(Y), remainder)
  197. if (g > 1 && g < remainder)
  198. remainder = check_factor(remainder, g, factors)
  199. if (remainder == 1)
  200. return true
  201. end
  202. end
  203. end
  204. end
  205. return false
  206. end
  207. for k in LR:length(I)
  208. cfrac_find_factors(I[k]) && break
  209. end
  210. final_factors = []
  211. for f in (factors)
  212. if (isprime(f))
  213. push!(final_factors, f)
  214. else
  215. append!(final_factors, cffm(f))
  216. end
  217. end
  218. if (remainder != 1)
  219. if (remainder != n)
  220. append!(final_factors, cffm(remainder))
  221. else
  222. push!(final_factors, remainder)
  223. end
  224. end
  225. # Failed to factorize n (try again with a multiplier)
  226. if (remainder == n)
  227. println("Trying again with a multiplier...")
  228. return cffm(n, next_multiplier(n, multiplier))
  229. end
  230. # Return all prime factors of n
  231. return sort(final_factors)
  232. end
  233. if (length(ARGS) >= 1)
  234. for n in (ARGS)
  235. println(n, " = ", cffm(parse(BigInt, n)))
  236. end
  237. else
  238. @time println("2^128 + 1 = ", cffm((big"2")^128 + 1))
  239. end