Skip to content

Commit 2685b72

Browse files
committed
add lenstra eliptic curve factoring
faster and more modular minor tidy
1 parent d789490 commit 2685b72

File tree

1 file changed

+78
-48
lines changed

1 file changed

+78
-48
lines changed

src/Primes.jl

Lines changed: 78 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -230,10 +230,9 @@ isprime(n::Int128) = n < 2 ? false :
230230

231231

232232
# Trial division of small (< 2^16) precomputed primes +
233-
# Pollard rho's algorithm with Richard P. Brent optimizations
233+
# Lenstra elliptic curve algorithm
234234
# https://en.wikipedia.org/wiki/Trial_division
235-
# https://en.wikipedia.org/wiki/Pollard%27s_rho_algorithm
236-
# http://maths-people.anu.edu.au/~brent/pub/pub051.html
235+
# https://en.wikipedia.org/wiki/Lenstra_elliptic-curve_factorization
237236
#
238237
function factor!(n::T, h::AbstractDict{K,Int}) where {T<:Integer,K<:Integer}
239238
# check for special cases
@@ -257,18 +256,17 @@ function factor!(n::T, h::AbstractDict{K,Int}) where {T<:Integer,K<:Integer}
257256
for p in PRIMES
258257
p > nsqrt && break
259258
if n % p == 0
260-
h[p] = get(h, p, 0) + 1
261-
n = div(n, p)
262-
while n % p == 0
259+
while true
263260
h[p] = get(h, p, 0) + 1
264-
n = div(n, p)
261+
n, r = divrem(n, p)
262+
r != 0 && break
265263
end
266264
n == 1 && return h
267265
nsqrt = isqrt(n)
268266
end
269267
end
270268
isprime(n) && (h[n]=1; return h)
271-
T <: BigInt || widemul(n - 1, n - 1) typemax(n) ? pollardfactors!(n, h) : pollardfactors!(widen(n), h)
269+
lenstrafactors!(widen(n), h)
272270
end
273271

274272

@@ -386,52 +384,84 @@ julia> radical(2*2*3)
386384
"""
387385
radical(n) = prod(factor(Set, n))
388386

389-
function pollardfactors!(n::T, h::AbstractDict{K,Int}) where {T<:Integer,K<:Integer}
390-
while true
391-
c::T = rand(1:(n - 1))
392-
G::T = 1
393-
r::K = 1
394-
y::T = rand(0:(n - 1))
395-
m::K = 100
396-
ys::T = 0
397-
q::T = 1
398-
x::T = 0
399-
while c == n - 2
400-
c = rand(1:(n - 1))
387+
function primepowerfactor!(n::T, h::AbstractDict{K,Int}) where{T<:Integer,K<:Integer}
388+
r = ceil(Int, inv(log(n, Primes.PRIMES[end])))+1
389+
root = inthroot(n, r)
390+
while r >= 2
391+
if root^r == n && isprime(root)
392+
h[root] = get(h, root, 0) + r
393+
return true
401394
end
402-
while G == 1
403-
x = y
404-
for i in 1:r
405-
y = y^2 % n
406-
y = (y + c) % n
395+
r -= 1
396+
root = inthroot(n, r)
397+
end
398+
return false
399+
end
400+
401+
function lenstrafactors!(n::T, h::AbstractDict{K,Int}) where{T<:Integer,K<:Integer}
402+
isprime(n) && (h[n] = get(h, n, 0)+1; return h)
403+
primepowerfactor!(n::T, h) && return h
404+
# bounds and runs per bound taken from
405+
# https://www.rieselprime.de/ziki/Elliptic_curve_method
406+
B1s = Int[2e3, 11e3, 5e4, 25e4, 1e6, 3e6, 11e6,
407+
43e6, 11e7, 26e7, 85e7, 29e8, 76e8, 25e9]
408+
runs = (74, 221, 453, 984, 2541, 4949, 8266, 20158,
409+
47173, 77666, 42057, 69471, 102212, 188056, 265557)
410+
for (B1, run) in zip(B1s, runs)
411+
small_primes = primes(B1)
412+
for a in -run:run
413+
res = lenstra_get_factor(n, a, small_primes, B1)
414+
if res != 1
415+
isprime(res) ? h[res] = get(h, res, 0) + 1 : lenstrafactors!(res, h)
416+
n = div(n,res)
417+
primepowerfactor!(n::T, h) && return h
418+
isprime(n) && (h[n] = get(h, n, 0) + 1; return h)
407419
end
408-
k::K = 0
409-
G = 1
410-
while k < r && G == 1
411-
ys = y
412-
for i in 1:(m > (r - k) ? (r - k) : m)
413-
y = y^2 % n
414-
y = (y + c) % n
415-
q = (q * (x > y ? x - y : y - x)) % n
420+
end
421+
end
422+
throw(ArgumentError("This number is too big to be factored with this algorith effectively"))
423+
end
424+
425+
function lenstra_get_factor(N::T, a, small_primes, plimit) where T <: Integer
426+
x, y = T(0), T(1)
427+
for B in small_primes
428+
t = B^trunc(Int64, log(B, plimit))
429+
xn, yn = T(0), T(0)
430+
sx, sy = x, y
431+
432+
first = true
433+
while t > 0
434+
if isodd(t)
435+
if first
436+
xn, yn = sx, sy
437+
first = false
438+
else
439+
g, u, _ = gcdx(sx-xn, N)
440+
g > 1 && (g == N ? break : return g)
441+
u += N
442+
L = (u*(sy-yn)) % N
443+
xs = (L*L - xn - sx) % N
444+
445+
yn = (L*(xn - xs) - yn) % N
446+
xn = xs
416447
end
417-
G = gcd(q, n)
418-
k += m
419448
end
420-
r *= 2
421-
end
422-
G == n && (G = 1)
423-
while G == 1
424-
ys = ys^2 % n
425-
ys = (ys + c) % n
426-
G = gcd(x > ys ? x - ys : ys - x, n)
427-
end
428-
if G != n
429-
isprime(G) ? h[G] = get(h, G, 0) + 1 : pollardfactors!(G, h)
430-
G2 = div(n,G)
431-
isprime(G2) ? h[G2] = get(h, G2, 0) + 1 : pollardfactors!(G2, h)
432-
return h
449+
g, u, _ = gcdx(2*sy, N)
450+
g > 1 && (g == N ? break : return g)
451+
u += N
452+
453+
L = (u*(3*sx*sx + a)) % N
454+
x2 = (L*L - 2*sx) % N
455+
456+
sy = (L*(sx - x2) - sy) % N
457+
sx = x2
458+
459+
sy == 0 && return T(1)
460+
t >>= 1
433461
end
462+
x, y = xn, yn
434463
end
464+
return T(1)
435465
end
436466

437467
"""

0 commit comments

Comments
 (0)