I've spent a while teaching myself Swift, and decided to take on the challenge of writing an unbounded Sieve of Eratosthenes to challenge myself. This is actually the first time I've written an unbounded sieve, as well as the first large Swift program I've written, so all comments are appreciated!
This was written using Swift 3 in Xcode 8 beta 3, so it should work on Swift 3.0 Preview 2 from swift.org, but I have not tested it.
public struct PrimeSequence: Sequence {
private let stop: StoppingPoint
/// Construct a sequence of primes up to a max.
///
/// - note: This constructor is eager.
///
/// - complexity: `O(log n)`
///
public init(max: Int) {
self.stop = .precompute([1] + sieveOfEratosthenes(n: max))
}
public init(terms: Int? = nil) {
self.stop = .terms(terms)
}
public func makeIterator() -> AnyIterator<Int> {
switch (stop) {
case .precompute(let primes):
return AnyIterator(primes.makeIterator())
case .terms(let terms):
var sieve = UnboundedSieve()
var term = 0
return AnyIterator {
term += 1
if let terms = terms, term > terms {
return nil
}
if term == 1 {
return 1
} else {
return sieve.next()
}
}
}
}
private enum StoppingPoint {
case precompute([Int])
case terms(Int?)
}
}
///
/// Lazy, Unbounded, Sieve of Erathosthenes.
///
/// See https://www.cs.hmc.edu/~oneill/papers/Sieve-JFP.pdf
///
private struct UnboundedSieve: IteratorProtocol {
var sieve: PriorityQueue<PrimeCounter>
var counter = 2
init() {
sieve = PriorityQueue { $0.composite < $1.composite }
}
private struct PrimeCounter {
let prime: Int
var composite: Int
func next() -> PrimeCounter {
return PrimeCounter(prime: prime, composite: composite + prime)
}
}
mutating func next() -> Int? {
while let catcher = sieve.peek(), catcher.composite == counter {
while let catcher = sieve.peek(), catcher.composite == counter {
sieve.replaceTop(with: catcher.next())
}
counter += 1
}
defer {
sieve.enqueue(PrimeCounter(prime: counter, composite: counter + counter))
counter += 1
}
return counter
}
}
///
/// Mutating Sieve of Erathosthenes.
///
/// Source: https://developer.apple.com/videos/play/wwdc2015/414/ @ 8:15
///
/// I would use the more "swifty" version @ 10:00, but this is the "true" sieve.
///
/// Complexity: `O(n log log n)`
///
private func sieveOfEratosthenes(n: Int) -> [Int] {
var numbers = [Int](2..<n)
for i in 0..<n-2 {
let prime = numbers[i]
guard prime > 0 else { continue }
for multiple in stride(from: 2 * prime-2, to: n-2, by: prime) {
numbers[multiple] = 0
}
}
return numbers.filter { $0 > 0 }
}
I'm using a heap-based PriorityQueue, whose implementation doesn't matter, but here's the protocol pseudocode:
protocol PriorityQueue<T> {
init(sort: (T, T) -> Bool)
enqueue(_ element: T)
dequeue() -> T?
peek() -> T?
replaceTop(with value: T)
}
For the purposes of this review, you can assume that it is accurate (but you can see it here if you want).