Skip to content

Commit

Permalink
Added words(_:) page size to NBKPrimeSieve. Cleanup (#114).
Browse files Browse the repository at this point in the history
  • Loading branch information
oscbyspro committed Dec 2, 2023
1 parent d62aa1f commit f721b61
Show file tree
Hide file tree
Showing 2 changed files with 161 additions and 72 deletions.
161 changes: 93 additions & 68 deletions Sources/NBKCoreKit/Models/NBKPrimeSieve.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,11 @@ public final class NBKPrimeSieve: CustomStringConvertible {
///
@usableFromInline var cache: Cache

/// A cyclical pattern used to skip small prime multiples.
/// A cyclical pattern used to skip multiples of small primes.
@usableFromInline let wheel: Wheel

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

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

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

/// The number of bits in one number page.
@inlinable public var size: UInt {
self.cache.count
}

//=------------------------------------------------------------------------=
// MARK: Utilities
//=------------------------------------------------------------------------=
Expand Down Expand Up @@ -135,7 +130,7 @@ public final class NBKPrimeSieve: CustomStringConvertible {
///
/// It represents at most `Int.max` bits so the odd number stride fits.
///
/// - Requires: `words * UInt.bitWidth <= Int.max`
/// - Requires: `2 * UInt.bitWidth * words <= UInt.max`
///
@usableFromInline let words: Int

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

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

/// The size per page measured in KiB (i.e. 1024 B).
@inlinable public static func KiB(_ count: Int) -> Self {
Self(words: 8 * 1024 / UInt.bitWidth * count)
Self(words: count * (8 * 1024 / UInt.bitWidth))
}

/// The size per page measured in words.
@inlinable public static func words(_ count: Int) -> Self {
Self(words: count)
}
}
}
Expand All @@ -182,7 +182,7 @@ extension NBKPrimeSieve {
// mark composites not hit by the wheel
//=--------------------------------------=
precull: do {
var pattern = NBK.CyclicIterator(self.small)!
var pattern = NBK.CyclicIterator(self.small.pattern)!
pattern.set(iteration: 1 &+ NBK.PBI.quotient(dividing: NBK.ZeroOrMore(self.limit &>> 1), by: NBK.PowerOf2(bitWidth: UInt.self)))
self.cache.sieve(pattern: pattern)
}
Expand Down Expand Up @@ -236,7 +236,7 @@ extension NBKPrimeSieve {
}
}

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

/// ### Development
///
/// The possibility of size overflow is preconditioned by the size model.
/// Size overflow is prevented by the size model's preconditions.
///
@frozen @usableFromInline struct Cache {

Expand All @@ -325,19 +325,8 @@ extension NBKPrimeSieve {
// MARK: Initializers
//=--------------------------------------------------------------------=

/// Rounds the given `size` down to at least one machine word.
///
/// ### On a 64-bit machine
///
/// ```
/// bits: 000 ..< 064 -> 064
/// bits: 064 ..< 128 -> 064
/// bits: 128 ..< 192 -> 128
/// bits: 192 ..< 256 -> 192
/// ```
///
@inlinable init(size: Size) {
self.base = Array(repeating: UInt.max, count: Int(size.words))
self.base = Array(repeating: UInt.max, count: size.words)
}

//=--------------------------------------------------------------------=
Expand Down Expand Up @@ -403,7 +392,7 @@ extension NBKPrimeSieve {
// MARK: State
//=--------------------------------------------------------------------=

/// The primes used to create this wheel.
/// The primes used to create this instance.
@usableFromInline let primes: [UInt]

/// Returns the next `value` from the `index`.
Expand Down Expand Up @@ -484,54 +473,90 @@ extension NBKPrimeSieve {
}

//=----------------------------------------------------------------------------=
// MARK: + Miscellaneous
// MARK: + Details x Small
//=----------------------------------------------------------------------------=

extension NBKPrimeSieve {

//=------------------------------------------------------------------------=
// MARK: Utilities
//=------------------------------------------------------------------------=
//*========================================================================*
// MARK: * Small
//*========================================================================*

@usableFromInline static func pattern(primes: [UInt]) -> [UInt] {
var pattern = [UInt](repeating: UInt.max, count: Int(primes.reduce(1, *)))
var next:(prime: UInt, product: UInt); next.prime = primes.first!; next.product = next.prime
var primeIndex = primes.startIndex; while primeIndex < primes.endIndex {
//=----------------------------------=
let current: (prime: UInt, product: UInt) = next
var patternIndex = NBK.PBI.dividing(NBK.ZeroOrMore(current.prime &>> 1), by: NBK.PowerOf2(bitWidth: UInt.self))
while patternIndex.quotient < current.prime {
var chunk = UInt.zero

while patternIndex.remainder < UInt.bitWidth {
chunk |= 1 &<< patternIndex.remainder
patternIndex.remainder &+= current.prime
}; chunk.formOnesComplement()

var destination = patternIndex.quotient; while destination < current.product {
pattern[Int(bitPattern: destination)] &= chunk
destination &+= current.prime
/// A cyclical pattern used to cull multiples of small primes.
///
/// ### Development
///
/// Consider multiple patterns formed by chunking:
///
/// [03, 67], [05, 61], [07, 59],
/// [11, 53], [13, 47], [17, 43],
/// [19, 41], [23, 37], [29, 31],
///
@frozen @usableFromInline struct Small {

//=--------------------------------------------------------------------=
// MARK: State
//=--------------------------------------------------------------------=

/// The primes used to create this instance.
@usableFromInline var primes: [UInt]

/// A cyclical pattern used to cull multiples of small primes.
@usableFromInline var pattern: [UInt]

//=--------------------------------------------------------------------=
// MARK: Initializers
//=--------------------------------------------------------------------=

@inlinable init(primes: [UInt]) {
self.primes = primes
self.pattern = Self.pattern(primes: primes)
}

//=--------------------------------------------------------------------=
// MARK: Utilities
//=--------------------------------------------------------------------=

@usableFromInline static func pattern(primes: [UInt]) -> [UInt] {
var pattern = [UInt](repeating: UInt.max, count: Int(primes.reduce(1, *)))
var next:(prime: UInt, product: UInt); next.prime = primes.first!; next.product = next.prime
var primeIndex = primes.startIndex; while primeIndex < primes.endIndex {
//=----------------------------------=
let current: (prime: UInt, product: UInt) = next
var patternIndex = NBK.PBI.dividing(NBK.ZeroOrMore(current.prime &>> 1), by: NBK.PowerOf2(bitWidth: UInt.self))
while patternIndex.quotient < current.prime {
var chunk = UInt.zero

while patternIndex.remainder < UInt.bitWidth {
chunk |= 1 &<< patternIndex.remainder
patternIndex.remainder &+= current.prime
}; chunk.formOnesComplement()

var destination = patternIndex.quotient; while destination < current.product {
pattern[Int(bitPattern: destination)] &= chunk
destination &+= current.prime
}

patternIndex.quotient &+= NBK.PBI.quotient (dividing: NBK.ZeroOrMore(patternIndex.remainder), by: NBK.PowerOf2(bitWidth: UInt.self))
patternIndex.remainder = NBK.PBI.remainder(dividing: NBK.ZeroOrMore(patternIndex.remainder), by: NBK.PowerOf2(bitWidth: UInt.self))
}
//=----------------------------------=
primes.formIndex(after: &primeIndex)
//=----------------------------------=
if primeIndex < primes.endIndex {
next.prime = primes[primeIndex]
next.product &*= next.prime
}

patternIndex.quotient &+= NBK.PBI.quotient (dividing: NBK.ZeroOrMore(patternIndex.remainder), by: NBK.PowerOf2(bitWidth: UInt.self))
patternIndex.remainder = NBK.PBI.remainder(dividing: NBK.ZeroOrMore(patternIndex.remainder), by: NBK.PowerOf2(bitWidth: UInt.self))
}
//=----------------------------------=
primes.formIndex(after: &primeIndex)
//=----------------------------------=
if primeIndex < primes.endIndex {
next.prime = primes[primeIndex]
next.product &*= next.prime
}

var destination = current.product; while destination < next.product {
for source in 0 as UInt ..< current.product {
pattern[Int(bitPattern: destination)] &= pattern[Int(bitPattern: source)]
destination += 1 as UInt
var destination = current.product; while destination < next.product {
for source in 0 as UInt ..< current.product {
pattern[Int(bitPattern: destination)] &= pattern[Int(bitPattern: source)]
destination += 1 as UInt
}
}
}

return pattern as [UInt] as [UInt] as [UInt]
}

return pattern as [UInt] as [UInt] as [UInt]
}
}
72 changes: 68 additions & 4 deletions Tests/NBKCoreKitTests/Models/NBKPrimeSieve.swift
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,69 @@ final class NBKPrimeSieveTests: XCTestCase {
// MARK: Tests
//=------------------------------------------------------------------------=

func test001KiB() {
func testBit064() {
let ((sieve)) = T(size: .words( 0064 / UInt.bitWidth))
check(sieve, limit: 0127, count: 0031, last: 127)

sieve.increment()
check(sieve, limit: 0255, count: 0054, last: 251)

sieve.increment()
check(sieve, limit: 0383, count: 0076, last: 383)

sieve.increment()
check(sieve, limit: 0511, count: 0097, last: 509)
}

func testBit128() {
let ((sieve)) = T(size: .words( 0128 / UInt.bitWidth))
check(sieve, limit: 0255, count: 0054, last: 0251)

sieve.increment()
check(sieve, limit: 0511, count: 0097, last: 0509)

sieve.increment()
check(sieve, limit: 0767, count: 0135, last: 0761)

sieve.increment()
check(sieve, limit: 1023, count: 0172, last: 1021)
}

func testBit192() {
let ((sieve)) = T(size: .words( 0192 / UInt.bitWidth))
check(sieve, limit: 0383, count: 0076, last: 0383)

sieve.increment()
check(sieve, limit: 0767, count: 0135, last: 0761)

sieve.increment()
check(sieve, limit: 1151, count: 0190, last: 1151)

sieve.increment()
check(sieve, limit: 1535, count: 0242, last: 1531)
}

func testBit256() {
let ((sieve)) = T(size: .words( 0256 / UInt.bitWidth))
check(sieve, limit: 0511, count: 0097, last: 0509)

sieve.increment()
check(sieve, limit: 1023, count: 0172, last: 1021)

sieve.increment()
check(sieve, limit: 1535, count: 0242, last: 1531)

sieve.increment()
check(sieve, limit: 2047, count: 0309, last: 2039)
}

//=------------------------------------------------------------------------=
// MARK: Tests x KiB
//=------------------------------------------------------------------------=

func testKiB001() {
let ((sieve)) = T(size: .KiB(001))
XCTAssertEqual(sieve.stride,16384)
check(sieve, limit: 016383, count: 01900, last: 016381)

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

func test002KiB() {
func testKiB002() {
let ((sieve)) = T(size: .KiB(002))
XCTAssertEqual(sieve.stride,32768)
check(sieve, limit: 032767, count: 03512, last: 032749)

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

func test003KiB() {
func testKiB003() {
let ((sieve)) = T(size: .KiB(003))
XCTAssertEqual(sieve.stride,49152)
check(sieve, limit: 049151, count: 05051, last: 049139)

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

func test004KiB() {
func testKiB004() {
let ((sieve)) = T(size: .KiB(004))
XCTAssertEqual(sieve.stride,65536)
check(sieve, limit: 065535, count: 06542, last: 065521)

sieve.increment()
Expand Down

0 comments on commit f721b61

Please sign in to comment.