Skip to content

Commit ebf209d

Browse files
committed
Add range-based permutations(ofCount:)
1 parent f454ace commit ebf209d

File tree

1 file changed

+100
-39
lines changed

1 file changed

+100
-39
lines changed

Sources/Algorithms/Permutations.swift

Lines changed: 100 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,56 @@ public struct Permutations<Base: Collection> {
1414
/// The base collection to iterate over for permutations.
1515
public let base: Base
1616

17+
@usableFromInline
1718
internal let baseCount: Int
18-
internal let countToChoose: Int
1919

20+
/// The range of accepted sizes of permutations.
21+
/// - Note: This may be empty if the attempted range entirely exceeds the
22+
/// bounds of the size of the `base` collection.
23+
@usableFromInline
24+
internal let kRange: Range<Int>
25+
26+
/// Initializes a `Permutations` for all permutations of `base` of size `k`.
27+
/// - Parameters:
28+
/// - base: The collection to iterate over for permutations
29+
/// - k: The expected size of each permutation, or `nil` (default) to
30+
/// iterate over all permutations of the same size as the base collection.
31+
@usableFromInline
2032
internal init(_ base: Base, k: Int? = nil) {
33+
let kRange: ClosedRange<Int>?
34+
if let countToChoose = k {
35+
kRange = countToChoose ... countToChoose
36+
} else {
37+
kRange = nil
38+
}
39+
self.init(base, kRange: kRange)
40+
}
41+
42+
/// Initializes a `Permutations` for all combinations of `base` of sizes
43+
/// within a given range.
44+
/// - Parameters:
45+
/// - base: The collection to iterate over for permutations.
46+
/// - kRange: The range of accepted sizes of permutations, or `nil` to
47+
/// iterate over all permutations of the same size as the base collection.
48+
@usableFromInline
49+
internal init<R: RangeExpression>(
50+
_ base: Base, kRange: R?
51+
) where R.Bound == Int {
2152
self.base = base
2253
let baseCount = base.count
2354
self.baseCount = baseCount
24-
self.countToChoose = k ?? baseCount
55+
let upperBound = baseCount + 1
56+
self.kRange = kRange?.relative(to: 0 ..< .max)
57+
.clamped(to: 0 ..< upperBound) ??
58+
baseCount ..< upperBound
2559
}
2660

61+
/// The total number of permutations.
62+
@inlinable
2763
public var count: Int {
28-
return baseCount >= countToChoose
29-
? stride(from: baseCount, to: baseCount - countToChoose, by: -1).reduce(1, *)
30-
: 0
64+
return kRange.map {
65+
stride(from: baseCount, to: baseCount - $0, by: -1).reduce(1, *)
66+
}.reduce(0, +)
3167
}
3268
}
3369

@@ -38,36 +74,30 @@ extension Permutations: Sequence {
3874
internal var base: Base
3975

4076
@usableFromInline
41-
internal var indexes: [Base.Index]
42-
@usableFromInline
43-
internal var hasMorePermutations: Bool
77+
internal let baseCount: Int
78+
79+
/// The current range of accepted sizes of permutations.
80+
/// - Note: The range is contracted until empty while iterating over
81+
/// permutations of different sizes. When the range is empty, iteration is
82+
/// finished.
4483
@usableFromInline
45-
internal var countToChoose: Int = 0
46-
47-
/// `true` if we're generating permutations of the full collection.
84+
internal var kRange: Range<Int>
85+
86+
/// Whether or not iteration is finished (`kRange` is empty)
4887
@usableFromInline
49-
internal var permutesFullCollection: Bool {
50-
countToChoose == indexes.count
88+
internal var isFinished: Bool {
89+
return kRange.isEmpty
5190
}
5291

5392
@usableFromInline
54-
internal init(_ base: Base) {
55-
self.base = base
56-
self.indexes = Array(base.indices)
57-
self.countToChoose = self.indexes.count
58-
self.hasMorePermutations = true
59-
}
93+
internal var indexes: [Base.Index]
6094

6195
@usableFromInline
62-
internal init(_ base: Base, count: Int) {
63-
self.base = base
64-
self.countToChoose = count
65-
66-
// Produce exactly one empty permutation when `count == 0`.
67-
self.indexes = count == 0 ? [] : Array(base.indices)
68-
69-
// Can't produce any permutations when `count > base.count`.
70-
self.hasMorePermutations = count <= indexes.count
96+
internal init(_ permutations: Permutations) {
97+
self.base = permutations.base
98+
self.baseCount = permutations.baseCount
99+
self.kRange = permutations.kRange
100+
self.indexes = Array(permutations.base.indices)
71101
}
72102

73103
/// Advances the `indexes` array such that the first `countToChoose`
@@ -83,6 +113,7 @@ extension Permutations: Sequence {
83113
/// - Complexity: O(*n*), where *n* is the length of the collection.
84114
@usableFromInline
85115
internal mutating func nextState() -> Bool {
116+
let countToChoose = self.kRange.lowerBound
86117
let edge = countToChoose - 1
87118

88119
// Find first index greater than the one at `edge`.
@@ -113,31 +144,53 @@ extension Permutations: Sequence {
113144

114145
@inlinable
115146
public mutating func next() -> [Base.Element]? {
116-
if !hasMorePermutations { return nil }
147+
guard !isFinished else { return nil }
148+
149+
/// Advances `kRange` by incrementing its `lowerBound` until the range is
150+
/// empty, when iteration is finished.
151+
func advanceKRange() {
152+
if kRange.lowerBound < kRange.upperBound {
153+
let advancedLowerBound = kRange.lowerBound + 1
154+
kRange = advancedLowerBound ..< kRange.upperBound
155+
indexes = Array(base.indices)
156+
}
157+
}
158+
159+
let countToChoose = self.kRange.lowerBound
160+
if countToChoose == 0 {
161+
defer {
162+
advanceKRange()
163+
}
164+
return []
165+
}
117166

167+
let permutesFullCollection = (countToChoose == baseCount)
118168
if permutesFullCollection {
119169
// If we're permuting the full collection, each iteration is just a
120170
// call to `nextPermutation` on `indexes`.
121-
defer { hasMorePermutations = indexes.nextPermutation() }
171+
defer {
172+
let hasMorePermutations = indexes.nextPermutation()
173+
if !hasMorePermutations {
174+
advanceKRange()
175+
}
176+
}
122177
return indexes.map { base[$0] }
123178
} else {
124179
// Otherwise, return the items at the first `countToChoose` indices and
125180
// advance the state.
126-
defer { hasMorePermutations = nextState() }
181+
defer {
182+
let hasMorePermutations = nextState()
183+
if !hasMorePermutations {
184+
advanceKRange()
185+
}
186+
}
127187
return indexes.prefix(countToChoose).map { base[$0] }
128188
}
129189
}
130190
}
131191

132-
@usableFromInline
133-
internal var permutesFullCollection: Bool {
134-
baseCount == countToChoose
135-
}
136-
137192
public func makeIterator() -> Iterator {
138-
permutesFullCollection
139-
? Iterator(base)
140-
: Iterator(base, count: countToChoose)
193+
Iterator(self)
141194
}
142195
}
143196

@@ -196,6 +249,13 @@ where Self: BidirectionalCollection, Element: Comparable
196249
//===----------------------------------------------------------------------===//
197250

198251
extension Collection {
252+
@inlinable
253+
public func permutations<R: RangeExpression>(
254+
ofCount kRange: R
255+
) -> Permutations<Self> where R.Bound == Int {
256+
return Permutations(self, kRange: kRange)
257+
}
258+
199259
/// Returns a collection of the permutations of this collection of the
200260
/// specified length.
201261
///
@@ -242,6 +302,7 @@ extension Collection {
242302
/// of this entire collection.
243303
///
244304
/// - Complexity: O(1)
305+
@inlinable
245306
public func permutations(ofCount k: Int? = nil) -> Permutations<Self> {
246307
precondition(
247308
k ?? 0 >= 0,

0 commit comments

Comments
 (0)