Skip to content

Commit 7be7d17

Browse files
committed
Update AdjacentPairs implementations and tests
1 parent 0f27d99 commit 7be7d17

File tree

2 files changed

+165
-152
lines changed

2 files changed

+165
-152
lines changed

Sources/Algorithms/AdjacentPairs.swift

+128-92
Original file line numberDiff line numberDiff line change
@@ -18,35 +18,35 @@ extension Sequence {
1818
/// The following example uses the `adjacentPairs()` method to iterate over
1919
/// adjacent pairs of integers:
2020
///
21-
/// for pair in (1...5).adjacentPairs() {
22-
/// print(pair)
23-
/// }
24-
/// // Prints "(1, 2)"
25-
/// // Prints "(2, 3)"
26-
/// // Prints "(3, 4)"
27-
/// // Prints "(4, 5)"
21+
/// for pair in (1...).prefix(5).adjacentPairs() {
22+
/// print(pair)
23+
/// }
24+
/// // Prints "(1, 2)"
25+
/// // Prints "(2, 3)"
26+
/// // Prints "(3, 4)"
27+
/// // Prints "(4, 5)"
2828
@inlinable
2929
public func adjacentPairs() -> AdjacentPairsSequence<Self> {
3030
AdjacentPairsSequence(base: self)
3131
}
3232
}
3333

3434
extension Collection {
35-
/// A collection of adjacent pairs of elements built from an underlying collection.
35+
/// A collection of adjacent pairs of elements built from an underlying
36+
/// collection.
3637
///
37-
/// In an `AdjacentPairsCollection`, the elements of the *i*th pair are the *i*th
38-
/// and *(i+1)*th elements of the underlying sequence. The following example
39-
/// uses the `adjacentPairs()` method to iterate over adjacent pairs of
40-
/// integers:
41-
/// ```
42-
/// for pair in (1...5).adjacentPairs() {
43-
/// print(pair)
44-
/// }
45-
/// // Prints "(1, 2)"
46-
/// // Prints "(2, 3)"
47-
/// // Prints "(3, 4)"
48-
/// // Prints "(4, 5)"
49-
/// ```
38+
/// In an `AdjacentPairsCollection`, the elements of the *i*th pair are the
39+
/// *i*th and *(i+1)*th elements of the underlying sequence. The following
40+
/// example uses the `adjacentPairs()` method to iterate over adjacent pairs
41+
/// of integers:
42+
///
43+
/// for pair in (1...5).adjacentPairs() {
44+
/// print(pair)
45+
/// }
46+
/// // Prints "(1, 2)"
47+
/// // Prints "(2, 3)"
48+
/// // Prints "(3, 4)"
49+
/// // Prints "(4, 5)"
5050
@inlinable
5151
public func adjacentPairs() -> AdjacentPairsCollection<Self> {
5252
AdjacentPairsCollection(base: self)
@@ -55,19 +55,8 @@ extension Collection {
5555

5656
/// A sequence of adjacent pairs of elements built from an underlying sequence.
5757
///
58-
/// In an `AdjacentPairsSequence`, the elements of the *i*th pair are the *i*th
59-
/// and *(i+1)*th elements of the underlying sequence. The following example
60-
/// uses the `adjacentPairs()` method to iterate over adjacent pairs of
61-
/// integers:
62-
/// ```
63-
/// for pair in (1...5).adjacentPairs() {
64-
/// print(pair)
65-
/// }
66-
/// // Prints "(1, 2)"
67-
/// // Prints "(2, 3)"
68-
/// // Prints "(3, 4)"
69-
/// // Prints "(4, 5)"
70-
/// ```
58+
/// Use the `adjacentPairs()` method on a sequence to create an
59+
/// `AdjacentPairsSequence` instance.
7160
public struct AdjacentPairsSequence<Base: Sequence> {
7261
@usableFromInline
7362
internal let base: Base
@@ -124,21 +113,11 @@ extension AdjacentPairsSequence: Sequence {
124113
}
125114
}
126115

127-
/// A collection of adjacent pairs of elements built from an underlying collection.
116+
/// A collection of adjacent pairs of elements built from an underlying
117+
/// collection.
128118
///
129-
/// In an `AdjacentPairsCollection`, the elements of the *i*th pair are the *i*th
130-
/// and *(i+1)*th elements of the underlying sequence. The following example
131-
/// uses the `adjacentPairs()` method to iterate over adjacent pairs of
132-
/// integers:
133-
/// ```
134-
/// for pair in (1...5).adjacentPairs() {
135-
/// print(pair)
136-
/// }
137-
/// // Prints "(1, 2)"
138-
/// // Prints "(2, 3)"
139-
/// // Prints "(3, 4)"
140-
/// // Prints "(4, 5)"
141-
/// ```
119+
/// Use the `adjacentPairs()` method on a collection to create an
120+
/// `AdjacentPairsCollection` instance.
142121
public struct AdjacentPairsCollection<Base: Collection> {
143122
@usableFromInline
144123
internal let base: Base
@@ -148,13 +127,25 @@ public struct AdjacentPairsCollection<Base: Collection> {
148127
@inlinable
149128
internal init(base: Base) {
150129
self.base = base
130+
131+
// Lazily build the end index, since we can't use the instance
132+
// property pre-initialization
133+
var endIndex: Index {
134+
Index(first: base.endIndex, second: base.endIndex)
135+
}
151136

152-
// Precompute `startIndex` to ensure O(1) behavior,
153-
// avoiding indexing past `endIndex`
154-
let start = base.startIndex
155-
let end = base.endIndex
156-
let second = start == end ? start : base.index(after: start)
157-
self.startIndex = Index(first: start, second: second)
137+
// Precompute `startIndex` to ensure O(1) behavior.
138+
guard !base.isEmpty else {
139+
self.startIndex = endIndex
140+
return
141+
}
142+
143+
// If there's only one element (i.e. the second index of base == endIndex)
144+
// then this collection should be empty.
145+
let secondIndex = base.index(after: base.startIndex)
146+
self.startIndex = secondIndex == base.endIndex
147+
? endIndex
148+
: Index(first: base.startIndex, second: secondIndex)
158149
}
159150
}
160151

@@ -181,22 +172,22 @@ extension AdjacentPairsCollection {
181172
self.second = second
182173
}
183174

175+
@inlinable
176+
public static func ==(lhs: Index, rhs: Index) -> Bool {
177+
lhs.first == rhs.first
178+
}
179+
184180
@inlinable
185181
public static func < (lhs: Index, rhs: Index) -> Bool {
186-
(lhs.first, lhs.second) < (rhs.first, rhs.second)
182+
lhs.first < rhs.first
187183
}
188184
}
189185
}
190186

191187
extension AdjacentPairsCollection: Collection {
192188
@inlinable
193189
public var endIndex: Index {
194-
switch base.endIndex {
195-
case startIndex.first, startIndex.second:
196-
return startIndex
197-
case let end:
198-
return Index(first: end, second: end)
199-
}
190+
Index(first: base.endIndex, second: base.endIndex)
200191
}
201192

202193
@inlinable
@@ -206,6 +197,7 @@ extension AdjacentPairsCollection: Collection {
206197

207198
@inlinable
208199
public func index(after i: Index) -> Index {
200+
precondition(i != endIndex, "Can't advance beyond endIndex")
209201
let next = base.index(after: i.second)
210202
return next == base.endIndex
211203
? endIndex
@@ -214,38 +206,74 @@ extension AdjacentPairsCollection: Collection {
214206

215207
@inlinable
216208
public func index(_ i: Index, offsetBy distance: Int) -> Index {
217-
if distance == 0 {
218-
return i
219-
} else if distance > 0 {
220-
let firstOffsetIndex = base.index(i.first, offsetBy: distance)
221-
let secondOffsetIndex = base.index(after: firstOffsetIndex)
222-
return secondOffsetIndex == base.endIndex
223-
? endIndex
224-
: Index(first: firstOffsetIndex, second: secondOffsetIndex)
209+
guard distance != 0 else { return i }
210+
211+
guard let result = distance > 0
212+
? offsetForward(i, by: distance, limitedBy: endIndex)
213+
: offsetBackward(i, by: -distance, limitedBy: startIndex)
214+
else { fatalError("Index out of bounds") }
215+
return result
216+
}
217+
218+
@inlinable
219+
public func index(
220+
_ i: Index, offsetBy distance: Int, limitedBy limit: Index
221+
) -> Index? {
222+
guard distance != 0 else { return i }
223+
guard limit != i else { return nil }
224+
225+
if distance > 0 {
226+
let limit = limit > i ? limit : endIndex
227+
return offsetForward(i, by: distance, limitedBy: limit)
225228
} else {
226-
return i == endIndex
227-
? Index(first: base.index(i.first, offsetBy: distance - 1),
228-
second: base.index(i.first, offsetBy: distance))
229-
: Index(first: base.index(i.first, offsetBy: distance),
230-
second: i.first)
229+
let limit = limit < i ? limit : startIndex
230+
return offsetBackward(i, by: -distance, limitedBy: limit)
231231
}
232232
}
233+
234+
@inlinable
235+
internal func offsetForward(
236+
_ i: Index, by distance: Int, limitedBy limit: Index
237+
) -> Index? {
238+
assert(distance > 0)
239+
assert(limit > i)
240+
241+
guard let newFirst = base.index(i.second, offsetBy: distance - 1, limitedBy: limit.first),
242+
newFirst != base.endIndex
243+
else { return nil }
244+
let newSecond = base.index(after: newFirst)
245+
246+
precondition(newSecond <= base.endIndex, "Can't advance beyond endIndex")
247+
return newSecond == base.endIndex
248+
? endIndex
249+
: Index(first: newFirst, second: newSecond)
250+
}
251+
252+
@inlinable
253+
internal func offsetBackward(
254+
_ i: Index, by distance: Int, limitedBy limit: Index
255+
) -> Index? {
256+
assert(distance > 0)
257+
assert(limit < i)
258+
259+
let offset = i == endIndex ? 0 : 1
260+
guard let newSecond = base.index(
261+
i.first,
262+
offsetBy: -(distance - offset),
263+
limitedBy: limit.second)
264+
else { return nil }
265+
let newFirst = base.index(newSecond, offsetBy: -1)
266+
precondition(newSecond >= base.startIndex, "Can't move before startIndex")
267+
return Index(first: newFirst, second: newSecond)
268+
}
233269

234270
@inlinable
235271
public func distance(from start: Index, to end: Index) -> Int {
236-
let offset: Int
237-
switch (start.first, end.first) {
238-
case (base.endIndex, base.endIndex):
239-
return 0
240-
case (base.endIndex, _):
241-
offset = +1
242-
case (_, base.endIndex):
243-
offset = -1
244-
default:
245-
offset = 0
246-
}
247-
248-
return base.distance(from: start.first, to: end.first) + offset
272+
// While there's a 2-step gap between the `first` base index values in
273+
// `endIndex` and the penultimate index of this collection, the `second`
274+
// base index values are consistently one step apart throughout the
275+
// entire collection.
276+
base.distance(from: start.second, to: end.second)
249277
}
250278

251279
@inlinable
@@ -259,13 +287,21 @@ extension AdjacentPairsCollection: BidirectionalCollection
259287
{
260288
@inlinable
261289
public func index(before i: Index) -> Index {
262-
i == endIndex
263-
? Index(first: base.index(i.first, offsetBy: -2),
264-
second: base.index(before: i.first))
265-
: Index(first: base.index(before: i.first),
266-
second: i.first)
290+
precondition(i != startIndex, "Can't offset before startIndex")
291+
let second = i == endIndex
292+
? base.index(before: base.endIndex)
293+
: i.first
294+
let first = base.index(before: second)
295+
return Index(first: first, second: second)
267296
}
268297
}
269298

270299
extension AdjacentPairsCollection: RandomAccessCollection
271300
where Base: RandomAccessCollection {}
301+
302+
extension AdjacentPairsCollection.Index: Hashable where Base.Index: Hashable {
303+
@inlinable
304+
public func hash(into hasher: inout Hasher) {
305+
hasher.combine(first)
306+
}
307+
}

0 commit comments

Comments
 (0)