continued_fraction_factorization_method_nemo.jl 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  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 Nemo
  11. const BIG_ONE = ZZ(1)
  12. const BIG_ZERO = ZZ(0)
  13. function next_multiplier(n, k)
  14. k += 2
  15. while (!is_squarefree(k) || gcd(k,n) != 1)
  16. k += 1
  17. end
  18. return k
  19. end
  20. function gaussian_elimination(A, n)
  21. m = length(A)
  22. I = [BIG_ONE << k for k in 0:m]
  23. nrow = 0
  24. for col in (1 : min(m, n))
  25. npivot = 0
  26. for row in (nrow+1 : m)
  27. if (((A[row] >> (col-1)) & BIG_ONE) == 1)
  28. npivot = row
  29. nrow += 1
  30. break
  31. end
  32. end
  33. npivot == 0 && continue
  34. if (npivot != nrow)
  35. A[[npivot,nrow]] = A[[nrow, npivot]]
  36. I[[npivot,nrow]] = I[[nrow, npivot]]
  37. end
  38. for row in (nrow+1 : m)
  39. if (((A[row] >> (col-1)) & BIG_ONE) == 1)
  40. A[row] = xor(A[row], A[nrow])
  41. I[row] = xor(I[row], I[nrow])
  42. end
  43. end
  44. end
  45. return I
  46. end
  47. function check_factor(n, g, factors)
  48. while (rem(n,g) == 0)
  49. n = div(n,g)
  50. push!(factors, g)
  51. if (is_prime(n))
  52. push!(factors, n)
  53. return 1
  54. end
  55. end
  56. return n
  57. end
  58. function is_smooth_over_prod(n, k)
  59. g = gcd(n, k)
  60. while (g > 1)
  61. n = div(n, g)
  62. while (rem(n, g) == 0)
  63. n = div(n, g)
  64. end
  65. n == 1 && return true
  66. g = gcd(n, g)
  67. end
  68. n == 1
  69. end
  70. function jacobi(a, n)
  71. kronecker_symbol(ZZ(a), ZZ(n))
  72. end
  73. function cffm(n, multiplier = 1)
  74. n <= 1 && return []
  75. is_prime(n) && return [n]
  76. if (iseven(n))
  77. v = 0
  78. while (iseven(n))
  79. v += 1
  80. n >>= 1
  81. end
  82. arr1 = [ZZ(2) for i in 1:v]
  83. arr2 = cffm(n)
  84. return append!(arr1, arr2)
  85. end
  86. if (is_square(n))
  87. f = cffm(isqrt(n))
  88. append!(f, f)
  89. return sort(f)
  90. end
  91. N = n*multiplier
  92. x = isqrt(N)
  93. y = x
  94. z = 1
  95. w = x+x
  96. r = w
  97. nf = round(Int64, exp(sqrt(log(n) * log(log(n))))^(sqrt(2) / 4) / 1.25)
  98. factor_base = [2]
  99. begin
  100. p = 3
  101. while (length(factor_base) < nf)
  102. if (jacobi(N, p) >= 0)
  103. push!(factor_base, p)
  104. end
  105. p = next_prime(p+1)
  106. end
  107. end
  108. factor_prod = prod([ZZ(k) for k in factor_base])
  109. factor_index = Dict{Int64, Int64}()
  110. for k in (1:length(factor_base))
  111. factor_index[factor_base[k]] = k-1
  112. end
  113. function exponent_signature(factors)
  114. sig = BIG_ZERO
  115. for (p,e) in factors
  116. if (isodd(e))
  117. sig |= (BIG_ONE << factor_index[p])
  118. end
  119. end
  120. return sig
  121. end
  122. L = length(factor_base)+1
  123. Q = []
  124. A = []
  125. (f1, f2) = (BIG_ONE, x)
  126. while (length(A) < L)
  127. y = (r*z - y)
  128. z = div((N - y*y), z)
  129. r = div((x + y), z)
  130. (f1, f2) = (f2, rem((r*f2 + f1), n))
  131. if (is_square(z))
  132. g = gcd(f1 - isqrt(z), n)
  133. if (g > 1 && g < n)
  134. arr1 = cffm(g)
  135. arr2 = cffm(div(n,g))
  136. return sort(append!(arr1, arr2))
  137. end
  138. end
  139. if (z > 1 && is_smooth_over_prod(z, factor_prod))
  140. push!(A, exponent_signature(factor(z)))
  141. push!(Q, [f1, z])
  142. end
  143. if (z == 1)
  144. println("z == 1 -> trying again with a multiplier...")
  145. return cffm(n, next_multiplier(n, multiplier))
  146. end
  147. end
  148. while (length(A) < L)
  149. push!(A, 0)
  150. end
  151. I = gaussian_elimination(A, length(A))
  152. LR = 0
  153. for k in (length(A):-1:1)
  154. if (A[k] != 0)
  155. LR = k+1
  156. break
  157. end
  158. end
  159. remainder = n
  160. factors = []
  161. function cfrac_find_factors(solution)
  162. X = BIG_ONE
  163. Y = BIG_ONE
  164. for i in 0:length(Q)-1
  165. if (((solution >> i) & BIG_ONE) == 1)
  166. X *= Q[i+1][1]
  167. Y *= Q[i+1][2]
  168. X %= remainder
  169. g = gcd(X - isqrt(Y), remainder)
  170. if (g > 1 && g < remainder)
  171. remainder = check_factor(remainder, g, factors)
  172. if (remainder == 1)
  173. return true
  174. end
  175. end
  176. end
  177. end
  178. return false
  179. end
  180. for k in LR:length(I)
  181. cfrac_find_factors(I[k]) && break
  182. end
  183. final_factors = []
  184. for f in (factors)
  185. if (is_prime(f))
  186. push!(final_factors, f)
  187. else
  188. append!(final_factors, cffm(f))
  189. end
  190. end
  191. if (remainder != 1)
  192. if (remainder != n)
  193. append!(final_factors, cffm(remainder))
  194. else
  195. push!(final_factors, remainder)
  196. end
  197. end
  198. # Failed to factorize n (try again with a multiplier)
  199. if (remainder == n)
  200. println("Trying again with a multiplier...")
  201. return cffm(n, next_multiplier(n, multiplier))
  202. end
  203. # Return all prime factors of n
  204. return sort(final_factors)
  205. end
  206. if (length(ARGS) >= 1)
  207. for n in (ARGS)
  208. println(n, " = ", cffm(ZZ(n)))
  209. end
  210. else
  211. @time println("2^128 + 1 = ", cffm(ZZ(2)^128 + 1))
  212. end