Skip to content

Commit 2136a6a

Browse files
authored
Merge pull request #117 from JuliaMath/oscardssmith-MIN_FACTOR
switch `PRIMES` to `MIN_FACTOR`
2 parents 9067ce1 + db53e68 commit 2136a6a

File tree

2 files changed

+62
-17
lines changed

2 files changed

+62
-17
lines changed

src/Primes.jl

Lines changed: 58 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,26 @@ function primes(lo::Int, hi::Int)
130130
end
131131
primes(n::Int) = primes(1, n)
132132

133-
const PRIMES = primes(2^16)
133+
function _generate_min_factors(limit)
134+
function min_factor(n)
135+
n < 4 && return n
136+
for i in 3:2:isqrt(n)
137+
n%i == 0 && return i
138+
end
139+
return n
140+
end
141+
res = Int[]
142+
for i in 3:2:limit
143+
m = min_factor(i)
144+
push!(res, m==i ? 1 : m)
145+
end
146+
return res
147+
end
148+
149+
const N_SMALL_FACTORS = 2^16
150+
const _MIN_FACTOR = UInt8.(_generate_min_factors(N_SMALL_FACTORS))
151+
# _min_factor(n) = 1 if n is prime, otherwise the minimum factor of n
152+
_min_factor(n) = _MIN_FACTOR[n>>1]
134153

135154
"""
136155
isprime(n::Integer) -> Bool
@@ -146,10 +165,14 @@ function isprime(n::Integer)
146165
# Small precomputed primes + Miller-Rabin for primality testing:
147166
# https://en.wikipedia.org/wiki/Miller–Rabin_primality_test
148167
# https://github.com/JuliaLang/julia/issues/11594
149-
for m in (2, 3, 5, 7, 11, 13, 17, 19, 23)
150-
n % m == 0 && return n == m
168+
n < 2 && return false
169+
trailing_zeros(n) > 0 && return n==2
170+
if n < N_SMALL_FACTORS
171+
return _min_factor(n) == 1
172+
end
173+
for m in (3, 5, 7, 11, 13, 17, 19, 23)
174+
n % m == 0 && return false
151175
end
152-
n < 841 && return n > 1
153176
s = trailing_zeros(n - 1)
154177
d = (n - 1) >>> s
155178
for a in witnesses(n)::Tuple{Vararg{Int}}
@@ -226,10 +249,10 @@ witnesses(n::Integer) =
226249
isprime(n::UInt128) =
227250
n typemax(UInt64) ? isprime(UInt64(n)) : isprime(BigInt(n))
228251
isprime(n::Int128) = n < 2 ? false :
229-
n typemax(Int64) ? isprime(Int64(n)) : isprime(BigInt(n))
230-
252+
n typemax(UInt64) ? isprime(UInt64(n)) : isprime(BigInt(n))
231253

232-
# Trial division of small (< 2^16) precomputed primes +
254+
# Cache of small factors for tiny numbers (n < 2^16)
255+
# Trial division of small (< 2^16) precomputed primes
233256
# Pollard rho's algorithm with Richard P. Brent optimizations
234257
# https://en.wikipedia.org/wiki/Trial_division
235258
# https://en.wikipedia.org/wiki/Pollard%27s_rho_algorithm
@@ -238,22 +261,37 @@ isprime(n::Int128) = n < 2 ? false :
238261
function factor!(n::T, h::AbstractDict{K,Int}) where {T<:Integer,K<:Integer}
239262
# check for special cases
240263
if n < 0
241-
h[-1] = 1
264+
h[-1] = 1 - h[-1]
242265
if isa(n, BitSigned) && n == typemin(T)
243-
h[2] = 8 * sizeof(T) - 1
266+
h[2] += 8 * sizeof(T) - 1
244267
return h
245268
else
246269
return factor!(checked_neg(n), h)
247270
end
248-
elseif n == 1
249-
return h
250-
elseif n == 0 || isprime(n)
271+
elseif n == 0
251272
h[n] = 1
252273
return h
274+
elseif trailing_zeros(n) > 0
275+
tz = trailing_zeros(n)
276+
increment!(h, tz, 2)
277+
n >>= tz
278+
end
279+
if n <= N_SMALL_FACTORS
280+
while true
281+
n == 1 && return h
282+
if _min_factor(n)==1
283+
return increment!(h, 1, n)
284+
else
285+
increment!(h, 1, _min_factor(n))
286+
n = div(n, _min_factor(n))
287+
end
288+
end
289+
elseif isprime(n)
290+
return increment!(h, 1, n)
253291
end
254-
255292
local p::T
256-
for p in PRIMES
293+
for p in 3:2:N_SMALL_FACTORS
294+
_min_factor(p) == 1 || continue
257295
num_p = 0
258296
while true
259297
q, r = divrem(n, T(p)) # T(p) so julia <1.9 uses fast divrem for `BigInt`
@@ -262,11 +300,15 @@ function factor!(n::T, h::AbstractDict{K,Int}) where {T<:Integer,K<:Integer}
262300
n = q
263301
end
264302
# h[p] += num_p (about 2x faster, but the speed only matters for small numbers)
265-
num_p > 0 && increment!(h, num_p, p)
303+
if num_p > 0
304+
increment!(h, num_p, p)
305+
# if n is small, then recursing will hit the fast path.
306+
n < N_SMALL_FACTORS && return factor!(n, h)
307+
end
266308
p*p > n && break
267309
end
268310
n == 1 && return h
269-
isprime(n) && (h[n]=1; return h)
311+
isprime(n) && return increment!(h, 1, n)
270312
T <: BigInt || widemul(n - 1, n - 1) typemax(n) ? pollardfactors!(n, h) : pollardfactors!(widen(n), h)
271313
end
272314

src/factorization.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,10 @@ function increment!(f::Factorization{T}, e::Int, p::Integer) where T
5656
end
5757
f
5858
end
59-
increment!(f::AbstractDict, e::Int, p::Integer) = (f[p] = get(f, p, 0) + e)
59+
function increment!(f::AbstractDict, e::Int, p::Integer)
60+
f[p] = get(f, p, 0) + e
61+
return f
62+
end
6063

6164
Base.length(f::Factorization) = length(f.pe)
6265

0 commit comments

Comments
 (0)