Skip to content

Commit f721b61

Browse files
committed
Added words(_:) page size to NBKPrimeSieve. Cleanup (#114).
1 parent d62aa1f commit f721b61

File tree

2 files changed

+161
-72
lines changed

2 files changed

+161
-72
lines changed

Sources/NBKCoreKit/Models/NBKPrimeSieve.swift

Lines changed: 93 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,11 @@ public final class NBKPrimeSieve: CustomStringConvertible {
5454
///
5555
@usableFromInline var cache: Cache
5656

57-
/// A cyclical pattern used to skip small prime multiples.
57+
/// A cyclical pattern used to skip multiples of small primes.
5858
@usableFromInline let wheel: Wheel
5959

60-
/// A cyclical pattern used to cull small prime multiples.
61-
@usableFromInline let small: [UInt]
60+
/// A cyclical pattern used to cull multiples of small primes.
61+
@usableFromInline let small: Small
6262

6363
//=------------------------------------------------------------------------=
6464
// MARK: Initializers
@@ -71,7 +71,7 @@ public final class NBKPrimeSieve: CustomStringConvertible {
7171
public init(size: Size) {
7272
self.cache = Cache(size: size)
7373
self.wheel = Wheel(primes: [2, 3, 5, 7])
74-
self.small = Self.pattern(primes: Array(wheel.primes[1...]))
74+
self.small = Small(primes: Array(wheel.primes[1...]))
7575
self.state = Self.makeInitialState(&cache, wheel, small)
7676
}
7777

@@ -94,11 +94,6 @@ public final class NBKPrimeSieve: CustomStringConvertible {
9494
self.cache.count &<< 1 as UInt // OK, see size
9595
}
9696

97-
/// The number of bits in one number page.
98-
@inlinable public var size: UInt {
99-
self.cache.count
100-
}
101-
10297
//=------------------------------------------------------------------------=
10398
// MARK: Utilities
10499
//=------------------------------------------------------------------------=
@@ -135,7 +130,7 @@ public final class NBKPrimeSieve: CustomStringConvertible {
135130
///
136131
/// It represents at most `Int.max` bits so the odd number stride fits.
137132
///
138-
/// - Requires: `words * UInt.bitWidth <= Int.max`
133+
/// - Requires: `2 * UInt.bitWidth * words <= UInt.max`
139134
///
140135
@usableFromInline let words: Int
141136

@@ -146,7 +141,7 @@ public final class NBKPrimeSieve: CustomStringConvertible {
146141
@usableFromInline init(@NBK.MoreThanZero words: Int) {
147142
self.words = words
148143
precondition(words <= (Int.max / UInt.bitWidth),
149-
"number of elements per page must fit in UInt")
144+
"the prime sieve's increment must fit in UInt")
150145
}
151146

152147
//=--------------------------------------------------------------------=
@@ -155,7 +150,12 @@ public final class NBKPrimeSieve: CustomStringConvertible {
155150

156151
/// The size per page measured in KiB (i.e. 1024 B).
157152
@inlinable public static func KiB(_ count: Int) -> Self {
158-
Self(words: 8 * 1024 / UInt.bitWidth * count)
153+
Self(words: count * (8 * 1024 / UInt.bitWidth))
154+
}
155+
156+
/// The size per page measured in words.
157+
@inlinable public static func words(_ count: Int) -> Self {
158+
Self(words: count)
159159
}
160160
}
161161
}
@@ -182,7 +182,7 @@ extension NBKPrimeSieve {
182182
// mark composites not hit by the wheel
183183
//=--------------------------------------=
184184
precull: do {
185-
var pattern = NBK.CyclicIterator(self.small)!
185+
var pattern = NBK.CyclicIterator(self.small.pattern)!
186186
pattern.set(iteration: 1 &+ NBK.PBI.quotient(dividing: NBK.ZeroOrMore(self.limit &>> 1), by: NBK.PowerOf2(bitWidth: UInt.self)))
187187
self.cache.sieve(pattern: pattern)
188188
}
@@ -236,7 +236,7 @@ extension NBKPrimeSieve {
236236
}
237237
}
238238

239-
@inline(never) @inlinable static func makeInitialState(_ cache: inout Cache, _ wheel: Wheel, _ small: [UInt]) -> State {
239+
@inline(never) @inlinable static func makeInitialState(_ cache: inout Cache, _ wheel: Wheel, _ small: Small) -> State {
240240
Swift.assert(cache.base.allSatisfy({ $0.onesComplement().isZero }))
241241
//=--------------------------------------=
242242
let limit = cache.count * 2 - 1 // OK, see size
@@ -247,7 +247,7 @@ extension NBKPrimeSieve {
247247
// mark each number in: 1, small
248248
//=--------------------------------------=
249249
cache.base[cache.base.startIndex] = ~1
250-
cache.sieve(pattern: NBK.CyclicIterator(small)!)
250+
cache.sieve(pattern: NBK.CyclicIterator(small.pattern)!)
251251
//=--------------------------------------=
252252
// mark each number in: composites
253253
//=--------------------------------------=
@@ -311,7 +311,7 @@ extension NBKPrimeSieve {
311311

312312
/// ### Development
313313
///
314-
/// The possibility of size overflow is preconditioned by the size model.
314+
/// Size overflow is prevented by the size model's preconditions.
315315
///
316316
@frozen @usableFromInline struct Cache {
317317

@@ -325,19 +325,8 @@ extension NBKPrimeSieve {
325325
// MARK: Initializers
326326
//=--------------------------------------------------------------------=
327327

328-
/// Rounds the given `size` down to at least one machine word.
329-
///
330-
/// ### On a 64-bit machine
331-
///
332-
/// ```
333-
/// bits: 000 ..< 064 -> 064
334-
/// bits: 064 ..< 128 -> 064
335-
/// bits: 128 ..< 192 -> 128
336-
/// bits: 192 ..< 256 -> 192
337-
/// ```
338-
///
339328
@inlinable init(size: Size) {
340-
self.base = Array(repeating: UInt.max, count: Int(size.words))
329+
self.base = Array(repeating: UInt.max, count: size.words)
341330
}
342331

343332
//=--------------------------------------------------------------------=
@@ -403,7 +392,7 @@ extension NBKPrimeSieve {
403392
// MARK: State
404393
//=--------------------------------------------------------------------=
405394

406-
/// The primes used to create this wheel.
395+
/// The primes used to create this instance.
407396
@usableFromInline let primes: [UInt]
408397

409398
/// Returns the next `value` from the `index`.
@@ -484,54 +473,90 @@ extension NBKPrimeSieve {
484473
}
485474

486475
//=----------------------------------------------------------------------------=
487-
// MARK: + Miscellaneous
476+
// MARK: + Details x Small
488477
//=----------------------------------------------------------------------------=
489478

490479
extension NBKPrimeSieve {
491480

492-
//=------------------------------------------------------------------------=
493-
// MARK: Utilities
494-
//=------------------------------------------------------------------------=
481+
//*========================================================================*
482+
// MARK: * Small
483+
//*========================================================================*
495484

496-
@usableFromInline static func pattern(primes: [UInt]) -> [UInt] {
497-
var pattern = [UInt](repeating: UInt.max, count: Int(primes.reduce(1, *)))
498-
var next:(prime: UInt, product: UInt); next.prime = primes.first!; next.product = next.prime
499-
var primeIndex = primes.startIndex; while primeIndex < primes.endIndex {
500-
//=----------------------------------=
501-
let current: (prime: UInt, product: UInt) = next
502-
var patternIndex = NBK.PBI.dividing(NBK.ZeroOrMore(current.prime &>> 1), by: NBK.PowerOf2(bitWidth: UInt.self))
503-
while patternIndex.quotient < current.prime {
504-
var chunk = UInt.zero
505-
506-
while patternIndex.remainder < UInt.bitWidth {
507-
chunk |= 1 &<< patternIndex.remainder
508-
patternIndex.remainder &+= current.prime
509-
}; chunk.formOnesComplement()
510-
511-
var destination = patternIndex.quotient; while destination < current.product {
512-
pattern[Int(bitPattern: destination)] &= chunk
513-
destination &+= current.prime
485+
/// A cyclical pattern used to cull multiples of small primes.
486+
///
487+
/// ### Development
488+
///
489+
/// Consider multiple patterns formed by chunking:
490+
///
491+
/// [03, 67], [05, 61], [07, 59],
492+
/// [11, 53], [13, 47], [17, 43],
493+
/// [19, 41], [23, 37], [29, 31],
494+
///
495+
@frozen @usableFromInline struct Small {
496+
497+
//=--------------------------------------------------------------------=
498+
// MARK: State
499+
//=--------------------------------------------------------------------=
500+
501+
/// The primes used to create this instance.
502+
@usableFromInline var primes: [UInt]
503+
504+
/// A cyclical pattern used to cull multiples of small primes.
505+
@usableFromInline var pattern: [UInt]
506+
507+
//=--------------------------------------------------------------------=
508+
// MARK: Initializers
509+
//=--------------------------------------------------------------------=
510+
511+
@inlinable init(primes: [UInt]) {
512+
self.primes = primes
513+
self.pattern = Self.pattern(primes: primes)
514+
}
515+
516+
//=--------------------------------------------------------------------=
517+
// MARK: Utilities
518+
//=--------------------------------------------------------------------=
519+
520+
@usableFromInline static func pattern(primes: [UInt]) -> [UInt] {
521+
var pattern = [UInt](repeating: UInt.max, count: Int(primes.reduce(1, *)))
522+
var next:(prime: UInt, product: UInt); next.prime = primes.first!; next.product = next.prime
523+
var primeIndex = primes.startIndex; while primeIndex < primes.endIndex {
524+
//=----------------------------------=
525+
let current: (prime: UInt, product: UInt) = next
526+
var patternIndex = NBK.PBI.dividing(NBK.ZeroOrMore(current.prime &>> 1), by: NBK.PowerOf2(bitWidth: UInt.self))
527+
while patternIndex.quotient < current.prime {
528+
var chunk = UInt.zero
529+
530+
while patternIndex.remainder < UInt.bitWidth {
531+
chunk |= 1 &<< patternIndex.remainder
532+
patternIndex.remainder &+= current.prime
533+
}; chunk.formOnesComplement()
534+
535+
var destination = patternIndex.quotient; while destination < current.product {
536+
pattern[Int(bitPattern: destination)] &= chunk
537+
destination &+= current.prime
538+
}
539+
540+
patternIndex.quotient &+= NBK.PBI.quotient (dividing: NBK.ZeroOrMore(patternIndex.remainder), by: NBK.PowerOf2(bitWidth: UInt.self))
541+
patternIndex.remainder = NBK.PBI.remainder(dividing: NBK.ZeroOrMore(patternIndex.remainder), by: NBK.PowerOf2(bitWidth: UInt.self))
542+
}
543+
//=----------------------------------=
544+
primes.formIndex(after: &primeIndex)
545+
//=----------------------------------=
546+
if primeIndex < primes.endIndex {
547+
next.prime = primes[primeIndex]
548+
next.product &*= next.prime
514549
}
515550

516-
patternIndex.quotient &+= NBK.PBI.quotient (dividing: NBK.ZeroOrMore(patternIndex.remainder), by: NBK.PowerOf2(bitWidth: UInt.self))
517-
patternIndex.remainder = NBK.PBI.remainder(dividing: NBK.ZeroOrMore(patternIndex.remainder), by: NBK.PowerOf2(bitWidth: UInt.self))
518-
}
519-
//=----------------------------------=
520-
primes.formIndex(after: &primeIndex)
521-
//=----------------------------------=
522-
if primeIndex < primes.endIndex {
523-
next.prime = primes[primeIndex]
524-
next.product &*= next.prime
525-
}
526-
527-
var destination = current.product; while destination < next.product {
528-
for source in 0 as UInt ..< current.product {
529-
pattern[Int(bitPattern: destination)] &= pattern[Int(bitPattern: source)]
530-
destination += 1 as UInt
551+
var destination = current.product; while destination < next.product {
552+
for source in 0 as UInt ..< current.product {
553+
pattern[Int(bitPattern: destination)] &= pattern[Int(bitPattern: source)]
554+
destination += 1 as UInt
555+
}
531556
}
532557
}
558+
559+
return pattern as [UInt] as [UInt] as [UInt]
533560
}
534-
535-
return pattern as [UInt] as [UInt] as [UInt]
536561
}
537562
}

Tests/NBKCoreKitTests/Models/NBKPrimeSieve.swift

Lines changed: 68 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -151,8 +151,69 @@ final class NBKPrimeSieveTests: XCTestCase {
151151
// MARK: Tests
152152
//=------------------------------------------------------------------------=
153153

154-
func test001KiB() {
154+
func testBit064() {
155+
let ((sieve)) = T(size: .words( 0064 / UInt.bitWidth))
156+
check(sieve, limit: 0127, count: 0031, last: 127)
157+
158+
sieve.increment()
159+
check(sieve, limit: 0255, count: 0054, last: 251)
160+
161+
sieve.increment()
162+
check(sieve, limit: 0383, count: 0076, last: 383)
163+
164+
sieve.increment()
165+
check(sieve, limit: 0511, count: 0097, last: 509)
166+
}
167+
168+
func testBit128() {
169+
let ((sieve)) = T(size: .words( 0128 / UInt.bitWidth))
170+
check(sieve, limit: 0255, count: 0054, last: 0251)
171+
172+
sieve.increment()
173+
check(sieve, limit: 0511, count: 0097, last: 0509)
174+
175+
sieve.increment()
176+
check(sieve, limit: 0767, count: 0135, last: 0761)
177+
178+
sieve.increment()
179+
check(sieve, limit: 1023, count: 0172, last: 1021)
180+
}
181+
182+
func testBit192() {
183+
let ((sieve)) = T(size: .words( 0192 / UInt.bitWidth))
184+
check(sieve, limit: 0383, count: 0076, last: 0383)
185+
186+
sieve.increment()
187+
check(sieve, limit: 0767, count: 0135, last: 0761)
188+
189+
sieve.increment()
190+
check(sieve, limit: 1151, count: 0190, last: 1151)
191+
192+
sieve.increment()
193+
check(sieve, limit: 1535, count: 0242, last: 1531)
194+
}
195+
196+
func testBit256() {
197+
let ((sieve)) = T(size: .words( 0256 / UInt.bitWidth))
198+
check(sieve, limit: 0511, count: 0097, last: 0509)
199+
200+
sieve.increment()
201+
check(sieve, limit: 1023, count: 0172, last: 1021)
202+
203+
sieve.increment()
204+
check(sieve, limit: 1535, count: 0242, last: 1531)
205+
206+
sieve.increment()
207+
check(sieve, limit: 2047, count: 0309, last: 2039)
208+
}
209+
210+
//=------------------------------------------------------------------------=
211+
// MARK: Tests x KiB
212+
//=------------------------------------------------------------------------=
213+
214+
func testKiB001() {
155215
let ((sieve)) = T(size: .KiB(001))
216+
XCTAssertEqual(sieve.stride,16384)
156217
check(sieve, limit: 016383, count: 01900, last: 016381)
157218

158219
sieve.increment()
@@ -165,8 +226,9 @@ final class NBKPrimeSieveTests: XCTestCase {
165226
check(sieve, limit: 065535, count: 06542, last: 065521)
166227
}
167228

168-
func test002KiB() {
229+
func testKiB002() {
169230
let ((sieve)) = T(size: .KiB(002))
231+
XCTAssertEqual(sieve.stride,32768)
170232
check(sieve, limit: 032767, count: 03512, last: 032749)
171233

172234
sieve.increment()
@@ -179,8 +241,9 @@ final class NBKPrimeSieveTests: XCTestCase {
179241
check(sieve, limit: 131071, count: 12251, last: 131071)
180242
}
181243

182-
func test003KiB() {
244+
func testKiB003() {
183245
let ((sieve)) = T(size: .KiB(003))
246+
XCTAssertEqual(sieve.stride,49152)
184247
check(sieve, limit: 049151, count: 05051, last: 049139)
185248

186249
sieve.increment()
@@ -193,8 +256,9 @@ final class NBKPrimeSieveTests: XCTestCase {
193256
check(sieve, limit: 196607, count: 17704, last: 196597)
194257
}
195258

196-
func test004KiB() {
259+
func testKiB004() {
197260
let ((sieve)) = T(size: .KiB(004))
261+
XCTAssertEqual(sieve.stride,65536)
198262
check(sieve, limit: 065535, count: 06542, last: 065521)
199263

200264
sieve.increment()

0 commit comments

Comments
 (0)