Skip to content

Commit a0b7954

Browse files
Use Swift Numerics for elementary functions instead of Darwin/Glibc/etc (#28)
Also lets us use root instead of exp(log(x)/k), which is more accurate in some cases (but mainly is just nicer to read). The downside is that this introduces a dependency for Algorithms, where previously it had none. I think that Numerics is an OK thing to depend on (especially for these functions, which are available from 0.0.1), but it _is_ a _massive increase_ in the number of dependencies that the package has, and we should give that some thought.
1 parent 73c4a21 commit a0b7954

File tree

2 files changed

+12
-19
lines changed

2 files changed

+12
-19
lines changed

Package.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,14 @@ let package = Package(
2020
targets: ["Algorithms"]),
2121
],
2222
dependencies: [
23+
.package(url: "https://github.com/apple/swift-numerics", from: "0.0.1"),
2324
],
2425
targets: [
2526
.target(
2627
name: "Algorithms",
27-
dependencies: []),
28+
dependencies: [
29+
.product(name: "RealModule", package: "swift-numerics"),
30+
]),
2831
.testTarget(
2932
name: "SwiftAlgorithmsTests",
3033
dependencies: ["Algorithms"]),

Sources/Algorithms/RandomSample.swift

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,9 @@
99
//
1010
//===----------------------------------------------------------------------===//
1111

12-
// For log(_:) and exp(_:)
13-
#if canImport(Glibc)
12+
// For log(_:) and root(_:_:)
1413
@_implementationOnly
15-
import Glibc
16-
#elseif canImport(ucrt)
17-
@_implementationOnly
18-
import ucrt
19-
#elseif canImport(Darwin)
20-
@_implementationOnly
21-
import Darwin
22-
#endif
14+
import RealModule
2315

2416
//===----------------------------------------------------------------------===//
2517
// randomStableSample(count:)
@@ -90,15 +82,15 @@ extension Collection {
9082
// https://dl.acm.org/doi/pdf/10.1145/198429.198435
9183

9284
fileprivate func nextW<G: RandomNumberGenerator>(
93-
k: Double, using rng: inout G
85+
k: Int, using rng: inout G
9486
) -> Double {
95-
exp(log(Double.random(in: 0..<1, using: &rng)) / k)
87+
Double.root(.random(in: 0..<1, using: &rng), k)
9688
}
9789

9890
fileprivate func nextOffset<G: RandomNumberGenerator>(
9991
w: Double, using rng: inout G
10092
) -> Int {
101-
Int(log(Double.random(in: 0..<1, using: &rng)) / log(1 - w))
93+
Int(Double.log(.random(in: 0..<1, using: &rng)) / .log(1 - w))
10294
}
10395

10496
extension Collection {
@@ -129,11 +121,10 @@ extension Collection {
129121
result.append(self[i])
130122
formIndex(after: &i)
131123
}
132-
133-
let dk = Double(k)
124+
134125
while i < endIndex {
135126
// Calculate the next value of w.
136-
w *= nextW(k: dk, using: &rng)
127+
w *= nextW(k: k, using: &rng)
137128

138129
// Find index of the next element to swap into the reservoir.
139130
let offset = nextOffset(w: w, using: &rng)
@@ -198,10 +189,9 @@ extension Sequence {
198189
result.append(el)
199190
}
200191

201-
let dk = Double(k)
202192
while true {
203193
// Calculate the next value of w.
204-
w *= nextW(k: dk, using: &rng)
194+
w *= nextW(k: k, using: &rng)
205195

206196
// Find the offset of the next element to swap into the reservoir.
207197
var offset = nextOffset(w: w, using: &rng) + 1

0 commit comments

Comments
 (0)