Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 58 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,62 @@
*

### Allowed files and directories ###

!.gitignore
!.github/
!.github/workflows/
!.github/workflows/*.yml
!.github/dependabot.yml

!LICENSE.md
!README.md
!Project.toml

!src/
!src/*.jl

!test/
!test/*.jl

!docs/
!docs/src/
!docs/src/*.md
!docs/make.jl
!docs/Project.toml

!benchmarks/
!benchmarks/*.jl
!benchmarks/Project.toml
!benchmarks/README.md

### Denied even if allowed above ###

# Files generated by invoking Julia with --code-coverage
*.jl.cov
*.jl.*.cov

# Files generated by invoking Julia with --track-allocation
*.jl.mem
Manifest.toml
docs/build
docs/site

# System-specific files and directories generated by the BinaryProvider and BinDeps packages
# They contain absolute paths specific to the host computer, and so should not be committed
deps/deps.jl
deps/build.log
deps/downloads/
deps/usr/
deps/src/

# Build artifacts for creating documentation generated by the Documenter package
docs/build/
docs/site/
docs/Manifest.toml

# File generated by Pkg, the package manager, based on a corresponding Project.toml
# It records a fixed state of all packages used by the project. As such, it should not be
# committed for packages, but should be committed for applications that require a static
# environment.
Manifest*.toml

# File generated by the Preferences package to store local preferences
LocalPreferences.toml
JuliaLocalPreferences.toml
3 changes: 3 additions & 0 deletions docs/src/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ Primes.primes
Primes.nextprime
Primes.prevprime
Primes.prime
Primes.primes_from
Primes.primes_upto
Primes.PrimeIterator
```

## Identifying prime numbers
Expand Down
114 changes: 113 additions & 1 deletion src/Primes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ using Base.Checked: checked_neg
using IntegerMathUtils

export isprime, primes, primesmask, factor, eachfactor, divisors, ismersenneprime, isrieselprime,
nextprime, nextprimes, prevprime, prevprimes, prime, prodfactors, radical, totient
nextprime, nextprimes, prevprime, prevprimes, prime, prodfactors, radical, totient,
primes_from, primes_upto, PrimeIterator

include("factorization.jl")

Expand Down Expand Up @@ -968,6 +969,117 @@ julia> prevprimes(10, 10)
prevprimes(start::T, n::Integer) where {T<:Integer} =
collect(T, Iterators.take(prevprimes(start), n))

"""
PrimeIterator{T<:Integer, Up}

A lazy iterator over prime numbers. When `Up` is `true`, iterates in ascending order
(constructed via [`primes_from`](@ref)). When `Up` is `false`, iterates in descending
order (constructed via [`primes_upto`](@ref)).

The type parameter `T` determines the integer type of yielded primes, and `Up` is a
compile-time boolean controlling iteration direction.
"""
struct PrimeIterator{T<:Integer, Up}
start::T
function PrimeIterator{T, Up}(start::T) where {T<:Integer, Up}
start < zero(start) && throw(ArgumentError("start must be non-negative, got $start"))
new{T, Up}(start)
end
end

function iterate(it::PrimeIterator{T, true}, state=it.start) where {T}
p = nextprime(state)
(p, add(p, one(p)))
end

function iterate(it::PrimeIterator{T, false}, state=it.start) where {T}
state < T(2) && return nothing
p = prevprime(state)
(p, p - one(p))
end

IteratorSize(::Type{<:PrimeIterator{T, true}}) where {T} = Base.IsInfinite()
IteratorSize(::Type{<:PrimeIterator{T, false}}) where {T} = Base.SizeUnknown()
IteratorEltype(::Type{<:PrimeIterator}) = Base.HasEltype()
eltype(::Type{PrimeIterator{T, Up}}) where {T, Up} = T

function Base.show(io::IO, it::PrimeIterator{T, true}) where {T}
print(io, "primes_from(", it.start, ")")
end

function Base.show(io::IO, it::PrimeIterator{T, false}) where {T}
print(io, "primes_upto(", it.start, ")")
end

"""
primes_from(n::Integer)

Return a lazy, infinite iterator over all primes greater than or equal to `n`,
in ascending order.

# Examples

```jldoctest
julia> collect(Iterators.take(primes_from(10), 5))
5-element Vector{Int64}:
11
13
17
19
23

julia> first(primes_from(100))
101
```
"""
primes_from(n::T) where {T<:Integer} = PrimeIterator{T, true}(n)

"""
primes_from()

Return a lazy, infinite iterator over all primes starting from `2`.
Equivalent to `primes_from(2)`.

# Examples

```jldoctest
julia> collect(Iterators.take(primes_from(), 5))
5-element Vector{Int64}:
2
3
5
7
11
```
"""
primes_from() = primes_from(2)

"""
primes_upto(n::Integer)

Return a lazy iterator over all primes less than or equal to `n`,
in descending order. Terminates when no more primes remain.

# Examples

```jldoctest
julia> collect(primes_upto(20))
8-element Vector{Int64}:
19
17
13
11
7
5
3
2

julia> collect(primes_upto(1))
Int64[]
```
"""
primes_upto(n::T) where {T<:Integer} = PrimeIterator{T, false}(n)

"""
divisors(n::Integer) -> Vector

Expand Down
93 changes: 93 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,99 @@ end
@test Base.IteratorSize(prevprimes(10)) == Base.SizeUnknown()
end

@testset "PrimeIterator - primes_from" begin
@test first(primes_from(10)) == 11
@test first(primes_from(11)) == 11
@test collect(Iterators.take(primes_from(10), 4)) == [11, 13, 17, 19]

@test first(primes_from()) == 2
@test collect(Iterators.take(primes_from(), 5)) == [2, 3, 5, 7, 11]

@test primes_from(10) isa PrimeIterator{Int, true}
@test primes_from(Int32(10)) isa PrimeIterator{Int32, true}
@test primes_from(big(10)) isa PrimeIterator{BigInt, true}

@test Base.IteratorSize(primes_from(10)) == Base.IsInfinite()

@test eltype(primes_from(10)) == Int
@test eltype(primes_from(Int32(10))) == Int32
@test eltype(primes_from(big(10))) == BigInt

@test collect(Iterators.take(primes_from(100), 3)) == [101, 103, 107]

@test collect(Iterators.takewhile(p -> p < 20, primes_from(10))) == [11, 13, 17, 19]

for n in [2, 3, 10, 100, 1000]
@test first(primes_from(n)) == nextprime(n)
end

@test repr(primes_from(10)) == "primes_from(10)"
@test repr(primes_from(big(42))) == "primes_from(42)"

@test typeof(primes_from(10)) == PrimeIterator{Int, true}

@test_throws ArgumentError primes_from(-1)
@test_throws ArgumentError primes_from(-10)

for n in [2, 5, 10, 100]
for i in 1:5
@test nextprime(n, i) == collect(Iterators.take(primes_from(n), i))[end]
end
end
end

@testset "PrimeIterator - primes_upto" begin
@test first(primes_upto(10)) == 7
@test first(primes_upto(11)) == 11
@test collect(Iterators.take(primes_upto(20), 4)) == [19, 17, 13, 11]

@test primes_upto(10) isa PrimeIterator{Int, false}
@test primes_upto(Int32(10)) isa PrimeIterator{Int32, false}
@test primes_upto(big(10)) isa PrimeIterator{BigInt, false}

@test Base.IteratorSize(primes_upto(10)) == Base.SizeUnknown()
@test collect(primes_upto(10)) == [7, 5, 3, 2]
@test collect(primes_upto(2)) == [2]

@test eltype(primes_upto(10)) == Int
@test eltype(primes_upto(Int32(10))) == Int32
@test eltype(primes_upto(big(10))) == BigInt

@test collect(Iterators.take(primes_upto(100), 3)) == [97, 89, 83]

pairs = collect(Iterators.take(Iterators.zip(primes_from(10), primes_upto(100)), 3))
@test pairs == [(11, 97), (13, 89), (17, 83)]

for n in [2, 3, 10, 100, 1000]
@test first(primes_upto(n)) == prevprime(n)
end

@test repr(primes_upto(10)) == "primes_upto(10)"
@test repr(primes_upto(big(42))) == "primes_upto(42)"

@test_throws ArgumentError primes_upto(-1)
@test_throws ArgumentError primes_upto(-10)

@test collect(primes_upto(1)) == []
@test collect(primes_upto(0)) == []

big_primes = collect(Iterators.take(primes_upto(big(100)), 3))
@test big_primes == [prevprime(big(100), i) for i in 1:3]
end

@testset "PrimeIterator - overflow propagation" begin
@test_throws OverflowError collect(Iterators.take(primes_from(typemax(Int) - 1), 2))
end

@testset "PrimeIterator - existing API unchanged" begin
@test nextprime(10) == 11
@test nextprime(10, 3) == 17
@test prevprime(10) == 7
@test prevprime(10, 3) == 3
@test nextprimes(10, 3) == [11, 13, 17]
@test prevprimes(10, 3) == [7, 5, 3]
end

@testset "primes with huge arguments" begin
if Base.Sys.WORD_SIZE == 64
@test primes(2^63-200, 2^63-1) == [9223372036854775643, 9223372036854775783]
Expand Down
Loading