Skip to content

Commit 4098c5c

Browse files
committed
add eachfactor
1 parent db53e68 commit 4098c5c

File tree

1 file changed

+91
-49
lines changed

1 file changed

+91
-49
lines changed

src/Primes.jl

Lines changed: 91 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ using Base: BitSigned
99
using Base.Checked: checked_neg
1010
using IntegerMathUtils
1111

12-
export isprime, primes, primesmask, factor, ismersenneprime, isrieselprime,
12+
export isprime, primes, primesmask, factor, eachfactor, ismersenneprime, isrieselprime,
1313
nextprime, nextprimes, prevprime, prevprimes, prime, prodfactors, radical, totient
1414

1515
include("factorization.jl")
@@ -148,8 +148,11 @@ end
148148

149149
const N_SMALL_FACTORS = 2^16
150150
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]
151+
# _min_factor(n) = the minimum factor of n for odd n, if 1<n<N_SMALL_FACTORS
152+
function _min_factor(n::T) where T<:Integer
153+
m = _MIN_FACTOR[n>>1]
154+
return m==1 ? n : T(m)
155+
end
153156

154157
"""
155158
isprime(n::Integer) -> Bool
@@ -168,7 +171,7 @@ function isprime(n::Integer)
168171
n < 2 && return false
169172
trailing_zeros(n) > 0 && return n==2
170173
if n < N_SMALL_FACTORS
171-
return _min_factor(n) == 1
174+
return _min_factor(n) == n
172175
end
173176
for m in (3, 5, 7, 11, 13, 17, 19, 23)
174177
n % m == 0 && return false
@@ -251,65 +254,93 @@ isprime(n::UInt128) =
251254
isprime(n::Int128) = n < 2 ? false :
252255
n typemax(UInt64) ? isprime(UInt64(n)) : isprime(BigInt(n))
253256

254-
# Cache of small factors for tiny numbers (n < 2^16)
257+
struct FactorIterator{T<:Integer}
258+
n::T
259+
FactorIterator(n::T) where {T} = new{T}(n)
260+
end
261+
262+
IteratorSize(::Type{<:FactorIterator}) = Base.SizeUnknown()
263+
IteratorEltype(::Type{<:FactorIterator}) = Base.HasEltype()
264+
eltype(::Type{FactorIterator{T}}) where {T} = T
265+
Base.isempty(f::FactorIterator) = f.n == 1
266+
267+
# Iterates over the factors of n in an arbitrary order
268+
# Uses a variety of algorithms depending on the size of n to find a factor.
269+
# https://en.algorithmica.org/hpc/algorithms/factorization
270+
# Cache of small factors for small numbers (n < 2^16)
255271
# Trial division of small (< 2^16) precomputed primes
256272
# Pollard rho's algorithm with Richard P. Brent optimizations
257273
# https://en.wikipedia.org/wiki/Trial_division
258274
# https://en.wikipedia.org/wiki/Pollard%27s_rho_algorithm
259275
# http://maths-people.anu.edu.au/~brent/pub/pub051.html
260276
#
261-
function factor!(n::T, h::AbstractDict{K,Int}) where {T<:Integer,K<:Integer}
262-
# check for special cases
263-
if n < 0
264-
h[-1] = 1 - h[-1]
265-
if isa(n, BitSigned) && n == typemin(T)
266-
h[2] += 8 * sizeof(T) - 1
267-
return h
268-
else
269-
return factor!(checked_neg(n), h)
277+
eachfactor(n::Integer) = FactorIterator(n)
278+
279+
# state[1] is the current number to factor (this decreases when factors are found)
280+
# state[2] is the prime to start trial division with.
281+
function iterate(f::FactorIterator{T}, state=(f.n, T(3))) where T
282+
n, p::T = state
283+
if n <= p
284+
n == 1 && return nothing
285+
if n < 0
286+
# if n is typemin, we can't negate it properly
287+
# instead we set p=n which we can detect here.
288+
if isa(n, BitSigned) && n == typemin(T)
289+
if n != p
290+
return (T(-1), 1), (n, n)
291+
end
292+
return (T(2), 8 * sizeof(T) - 1), T.((1, 1))
293+
end
294+
return (T(-1), 1), (-n, p)
270295
end
271-
elseif n == 0
272-
h[n] = 1
273-
return h
274-
elseif trailing_zeros(n) > 0
275-
tz = trailing_zeros(n)
276-
increment!(h, tz, 2)
277-
n >>= tz
296+
n == 0 && return (T(n), 1), (T(1), p)
278297
end
298+
tz = trailing_zeros(n)
299+
tz>0 && return (T(2), tz), (n >> tz, p)
279300
if n <= N_SMALL_FACTORS
301+
p = _min_factor(n)
302+
num_p = 1
280303
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
304+
n = div(n, p)
305+
n == 1 && break
306+
_min_factor(n) == p || break
307+
num_p += 1
288308
end
309+
return (p, num_p), (n, p)
289310
elseif isprime(n)
290-
return increment!(h, 1, n)
311+
return (n, 1), (T(1), n)
291312
end
292-
local p::T
293-
for p in 3:2:N_SMALL_FACTORS
294-
_min_factor(p) == 1 || continue
313+
for p in p:2:N_SMALL_FACTORS
314+
isprime(p) || continue
295315
num_p = 0
296316
while true
297317
q, r = divrem(n, T(p)) # T(p) so julia <1.9 uses fast divrem for `BigInt`
298318
r == 0 || break
299319
num_p += 1
300320
n = q
301321
end
302-
# h[p] += num_p (about 2x faster, but the speed only matters for small numbers)
303322
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)
323+
return (p, num_p), (n, p)
307324
end
308325
p*p > n && break
309326
end
310-
n == 1 && return h
311-
isprime(n) && return increment!(h, 1, n)
312-
T <: BigInt || widemul(n - 1, n - 1) typemax(n) ? pollardfactors!(n, h) : pollardfactors!(widen(n), h)
327+
isprime(n) && (n, 1), (T(1), n)
328+
should_widen = T <: BigInt || widemul(n - 1, n - 1) typemax(n)
329+
p = should_widen ? pollardfactor(n) : pollardfactor(widen(n))
330+
num_p = 0
331+
while true
332+
q, r = divrem(n, p)
333+
r != 0 && return (p, num_p), (n, p)
334+
num_p += 1
335+
n = q
336+
end
337+
end
338+
339+
function factor!(n::T, h::AbstractDict{K,Int}) where {T<:Integer,K<:Integer}
340+
for (p, num_p) in eachfactor(n)
341+
increment!(h, num_p, p)
342+
end
343+
return h
313344
end
314345

315346

@@ -425,15 +456,15 @@ julia> radical(2*2*3)
425456
6
426457
```
427458
"""
428-
radical(n) = prod(factor(Set, n))
459+
radical(n) = n==1 ? one(n) : prod(p for (p, num_p) in eachfactor(n))
429460

430-
function pollardfactors!(n::T, h::AbstractDict{K,Int}) where {T<:Integer,K<:Integer}
461+
function pollardfactor(n::T) where {T<:Integer}
431462
while true
432463
c::T = rand(1:(n - 1))
433464
G::T = 1
434-
r::K = 1
465+
r::T = 1
435466
y::T = rand(0:(n - 1))
436-
m::K = 100
467+
m::T = 100
437468
ys::T = 0
438469
q::T = 1
439470
x::T = 0
@@ -446,7 +477,7 @@ function pollardfactors!(n::T, h::AbstractDict{K,Int}) where {T<:Integer,K<:Inte
446477
y = y^2 % n
447478
y = (y + c) % n
448479
end
449-
k::K = 0
480+
k::T = 0
450481
G = 1
451482
while k < r && G == 1
452483
ys = y
@@ -467,10 +498,13 @@ function pollardfactors!(n::T, h::AbstractDict{K,Int}) where {T<:Integer,K<:Inte
467498
G = gcd(x > ys ? x - ys : ys - x, n)
468499
end
469500
if G != n
470-
isprime(G) ? h[G] = get(h, G, 0) + 1 : pollardfactors!(G, h)
471501
G2 = div(n,G)
472-
isprime(G2) ? h[G2] = get(h, G2, 0) + 1 : pollardfactors!(G2, h)
473-
return h
502+
f = min(G, G2)
503+
_gcd = gcd(G, G2)
504+
if _gcd != 1
505+
f = _gcd
506+
end
507+
return isprime(f) ? f : pollardfactor(f)
474508
end
475509
end
476510
end
@@ -569,8 +603,16 @@ positive integers less than or equal to ``n`` that are relatively prime to
569603
The totient function of `n` when `n` is negative is defined to be
570604
`totient(abs(n))`.
571605
"""
572-
function totient(n::Integer)
573-
totient(factor(abs(n)))
606+
function totient(n::T) where T<:Integer
607+
n = abs(n)
608+
if n == 0
609+
throw(ArgumentError("ϕ(0) is not defined"))
610+
end
611+
result = one(T)
612+
for (p, k) in eachfactor(n)
613+
result *= p^(k-1) * (p - 1)
614+
end
615+
result
574616
end
575617

576618
# add: checked add (when makes sense), result of same type as first argument

0 commit comments

Comments
 (0)