Skip to content

Commit afb4498

Browse files
committed
add lenstra eliptic curve factoring minor tidy
1 parent 23df5b3 commit afb4498

File tree

1 file changed

+73
-69
lines changed

1 file changed

+73
-69
lines changed

src/Primes.jl

Lines changed: 73 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -320,11 +320,10 @@ Base.isempty(f::FactorIterator) = f.n == 1
320320
# Uses a variety of algorithms depending on the size of n to find a factor.
321321
# https://en.algorithmica.org/hpc/algorithms/factorization
322322
# Cache of small factors for small numbers (n < 2^16)
323-
# Trial division of small (< 2^16) precomputed primes
324-
# Pollard rho's algorithm with Richard P. Brent optimizations
323+
# Trial division of small (< 2^16) precomputed primes and
324+
# Lenstra elliptic curve algorithm
325325
# https://en.wikipedia.org/wiki/Trial_division
326-
# https://en.wikipedia.org/wiki/Pollard%27s_rho_algorithm
327-
# http://maths-people.anu.edu.au/~brent/pub/pub051.html
326+
# https://en.wikipedia.org/wiki/Lenstra_elliptic-curve_factorization
328327
#
329328

330329
"""
@@ -387,8 +386,20 @@ function iterate(f::FactorIterator{T}, state=(f.n, T(3))) where T
387386
if n <= 2^32 || isprime(n)
388387
return (n, 1), (T(1), n)
389388
end
389+
# check for n=root^r
390+
r = cld(ndigits(n, base=2), ndigits(N_SMALL_FACTORS, base=2))
391+
root = iroot(n, r)
392+
while r >= 2
393+
if root^r == n
394+
isprime(root) && return (root, r), (n, root+2)
395+
396+
end
397+
r -= 1
398+
root = iroot(n, r)
399+
end
400+
390401
should_widen = T <: BigInt || widemul(n - 1, n - 1) typemax(n)
391-
p = should_widen ? pollardfactor(n) : pollardfactor(widen(n))
402+
p = should_widen ? lenstrafactor(n) : lenstrafactor(widen(n))
392403
num_p = 0
393404
while true
394405
q, r = divrem(n, p)
@@ -520,55 +531,65 @@ julia> radical(2*2*3)
520531
"""
521532
radical(n) = n==1 ? one(n) : prod(p for (p, num_p) in eachfactor(n))
522533

523-
function pollardfactor(n::T) where {T<:Integer}
524-
while true
525-
c::T = rand(1:(n - 1))
526-
G::T = 1
527-
r::T = 1
528-
y::T = rand(0:(n - 1))
529-
m::T = 100
530-
ys::T = 0
531-
q::T = 1
532-
x::T = 0
533-
while c == n - 2
534-
c = rand(1:(n - 1))
535-
end
536-
while G == 1
537-
x = y
538-
for i in 1:r
539-
y = y^2 % n
540-
y = (y + c) % n
541-
end
542-
k::T = 0
543-
G = 1
544-
while k < r && G == 1
545-
ys = y
546-
for i in 1:(m > (r - k) ? (r - k) : m)
547-
y = y^2 % n
548-
y = (y + c) % n
549-
q = (q * (x > y ? x - y : y - x)) % n
550-
end
551-
G = gcd(q, n)
552-
k += m
534+
function lenstrafactor(n::T) where{T<:Integer}
535+
# bounds and runs per bound taken from
536+
# https://www.rieselprime.de/ziki/Elliptic_curve_method
537+
B1s = Int[2e3, 11e3, 5e4, 25e4, 1e6, 3e6, 11e6,
538+
43e6, 11e7, 26e7, 85e7, 29e8, 76e8, 25e9]
539+
runs = Int[25, 90, 300, 700, 1800, 5100, 1800, 10600,
540+
19300, 49000, 124000, 210000, 340000, 10^6, 10^7]
541+
for (B1, run) in zip(B1s, runs)
542+
small_primes = primes(B1)
543+
for a in -run:run
544+
res = lenstra_get_factor(n, a, small_primes, B1)
545+
if res != 1
546+
return isprime(res) ? res : lenstrafactor(res)
553547
end
554-
r *= 2
555-
end
556-
G == n && (G = 1)
557-
while G == 1
558-
ys = ys^2 % n
559-
ys = (ys + c) % n
560-
G = gcd(x > ys ? x - ys : ys - x, n)
561548
end
562-
if G != n
563-
G2 = div(n,G)
564-
f = min(G, G2)
565-
_gcd = gcd(G, G2)
566-
if _gcd != 1
567-
f = _gcd
549+
end
550+
throw(ArgumentError("This number is too big to be factored with this algorith effectively"))
551+
end
552+
553+
function lenstra_get_factor(N::T, a, small_primes, plimit) where T <: Integer
554+
x, y = T(0), T(1)
555+
for B in small_primes
556+
t = B^trunc(Int64, log(B, plimit))
557+
xn, yn = T(0), T(0)
558+
sx, sy = x, y
559+
560+
first = true
561+
while t > 0
562+
if isodd(t)
563+
if first
564+
xn, yn = sx, sy
565+
first = false
566+
else
567+
g, u, _ = gcdx(sx-xn, N)
568+
g > 1 && (g == N ? break : return g)
569+
u += N
570+
L = (u*(sy-yn)) % N
571+
xs = (L*L - xn - sx) % N
572+
573+
yn = (L*(xn - xs) - yn) % N
574+
xn = xs
575+
end
568576
end
569-
return isprime(f) ? f : pollardfactor(f)
577+
g, u, _ = gcdx(2*sy, N)
578+
g > 1 && (g == N ? break : return g)
579+
u += N
580+
581+
L = (u*(3*sx*sx + a)) % N
582+
x2 = (L*L - 2*sx) % N
583+
584+
sy = (L*(sx - x2) - sy) % N
585+
sx = x2
586+
587+
sy == 0 && return T(1)
588+
t >>= 1
570589
end
590+
x, y = xn, yn
571591
end
592+
return T(1)
572593
end
573594

574595
"""
@@ -646,7 +667,7 @@ given by `f`. This method may be preferable to [`totient(::Integer)`](@ref)
646667
when the factorization can be reused for other purposes.
647668
"""
648669
function totient(f::Factorization{T}) where T <: Integer
649-
if iszero(sign(f))
670+
if !isempty(f) && first(first(f)) == 0
650671
throw(ArgumentError("ϕ(0) is not defined"))
651672
end
652673
result = one(T)
@@ -665,16 +686,8 @@ positive integers less than or equal to ``n`` that are relatively prime to
665686
The totient function of `n` when `n` is negative is defined to be
666687
`totient(abs(n))`.
667688
"""
668-
function totient(n::T) where T<:Integer
669-
n = abs(n)
670-
if n == 0
671-
throw(ArgumentError("ϕ(0) is not defined"))
672-
end
673-
result = one(T)
674-
for (p, k) in eachfactor(n)
675-
result *= p^(k-1) * (p - 1)
676-
end
677-
result
689+
function totient(n::Integer)
690+
totient(factor(abs(n)))
678691
end
679692

680693
# add: checked add (when makes sense), result of same type as first argument
@@ -964,19 +977,13 @@ prevprimes(start::T, n::Integer) where {T<:Integer} =
964977

965978
"""
966979
divisors(n::Integer) -> Vector
967-
968980
Return a vector with the positive divisors of `n`.
969-
970981
For a nonzero integer `n` with prime factorization `n = p₁^k₁ ⋯ pₘ^kₘ`, `divisors(n)`
971982
returns a vector of length (k₁ + 1)⋯(kₘ + 1) containing the divisors of `n` in
972983
lexicographic (rather than numerical) order.
973-
974984
`divisors(-n)` is equivalent to `divisors(n)`.
975-
976985
For convenience, `divisors(0)` returns `[]`.
977-
978986
# Example
979-
980987
```jldoctest; filter = r"(\\s+#.*)?"
981988
julia> divisors(60)
982989
12-element Vector{Int64}:
@@ -992,14 +999,12 @@ julia> divisors(60)
992999
15 # 5 * 3
9931000
30 # 5 * 3 * 2
9941001
60 # 5 * 3 * 2 * 2
995-
9961002
julia> divisors(-10)
9971003
4-element Vector{Int64}:
9981004
1
9991005
2
10001006
5
10011007
10
1002-
10031008
julia> divisors(0)
10041009
Int64[]
10051010
```
@@ -1017,7 +1022,6 @@ end
10171022

10181023
"""
10191024
divisors(f::Factorization) -> Vector
1020-
10211025
Return a vector with the positive divisors of the number whose factorization is `f`.
10221026
Divisors are sorted lexicographically, rather than numerically.
10231027
"""

0 commit comments

Comments
 (0)