Skip to content

Commit 74a25e8

Browse files
committed
Refactor to index(_:offsetBy:limitedBy:) and utilise the new test utilities for validateIndexTraversals
1 parent 08f5dd7 commit 74a25e8

File tree

2 files changed

+51
-74
lines changed

2 files changed

+51
-74
lines changed

Sources/Algorithms/Stride.swift

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ extension Stride: Collection {
6060
self.base = base
6161
}
6262

63+
init?(_ base: Base.Index?) {
64+
guard let base = base else { return nil }
65+
self.base = base
66+
}
67+
6368
public static func < (lhs: Index, rhs: Index) -> Bool {
6469
lhs.base < rhs.base
6570
}
@@ -79,17 +84,34 @@ extension Stride: Collection {
7984

8085
public func index(after i: Index) -> Index {
8186
precondition(i.base < base.endIndex, "Advancing past end index")
82-
return base.index(i.base, offsetBy: stride, limitedBy: base.endIndex)
83-
.map(Index.init) ?? endIndex
87+
return index(i, offsetBy: 1, limitedBy: endIndex) ?? endIndex
8488
}
85-
89+
8690
public func index(
8791
_ i: Index,
88-
offsetBy distance: Int,
92+
offsetBy n: Int,
8993
limitedBy limit: Index
9094
) -> Index? {
91-
base.index(i.base, offsetBy: distance * stride, limitedBy: limit.base)
92-
.map(Index.init)
95+
guard n != 0 else { return i }
96+
guard limit != i else { return nil }
97+
switch (i, n) {
98+
case (endIndex, ..<0):
99+
let baseEnd = base.index(base.endIndex, offsetBy: -((base.count - 1) % stride + 1))
100+
return Index(base.index(baseEnd, offsetBy: (n - n.signum()) * stride, limitedBy: limit.base))
101+
case (_, 1...):
102+
let max = limit < i ? endIndex.base : limit.base
103+
let idx = base.index(i.base, offsetBy: n * stride, limitedBy: max)
104+
if let idx = idx {
105+
return idx > max ? endIndex : Index(idx)
106+
}
107+
guard i >= limit || limit == endIndex else {
108+
return nil
109+
}
110+
let isToEnd = distance(from: i, to: endIndex) == n
111+
return isToEnd ? endIndex : nil
112+
case _:
113+
return Index(base.index(i.base, offsetBy: n * stride, limitedBy: limit.base))
114+
}
93115
}
94116

95117
public var count: Int {
@@ -99,37 +121,25 @@ extension Stride: Collection {
99121

100122
public func distance(from start: Index, to end: Index) -> Int {
101123
let distance = base.distance(from: start.base, to: end.base)
102-
return distance / stride + (distance % stride > 0 ? 1 : 0)
124+
return distance / stride + (abs(distance % stride) > 0 ? distance.signum() : 0)
103125
}
104126

105127
public func index(_ i: Index, offsetBy distance: Int) -> Index {
106128
precondition(distance <= 0 || i.base < base.endIndex, "Advancing past end index")
107129
precondition(distance >= 0 || i.base > base.startIndex, "Incrementing past start index")
108-
return Index(base.index(i.base, offsetBy: distance * stride))
130+
let limit = distance > 0 ? endIndex : startIndex
131+
let idx = index(i, offsetBy: distance, limitedBy: limit)
132+
precondition(idx != nil, "The distance \(distance) is not valid for this collection")
133+
return idx!
109134
}
110135
}
111136

112137
extension Stride: BidirectionalCollection
113138
where Base: RandomAccessCollection {
114-
139+
115140
public func index(before i: Index) -> Index {
116141
precondition(i.base > base.startIndex, "Incrementing past start index")
117-
if i == endIndex {
118-
let count = base.count
119-
precondition(count > 0, "Can't move before the starting index")
120-
return Index(
121-
base.index(base.endIndex, offsetBy: -((count - 1) % stride + 1))
122-
)
123-
} else {
124-
guard let step = base.index(
125-
i.base,
126-
offsetBy: -stride,
127-
limitedBy: startIndex.base
128-
) else {
129-
fatalError("Incrementing past start index")
130-
}
131-
return Index(step)
132-
}
142+
return index(i, offsetBy: -1)
133143
}
134144
}
135145

Tests/SwiftAlgorithmsTests/StrideTests.swift

Lines changed: 16 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -89,59 +89,26 @@ final class StridingTests: XCTestCase {
8989
}
9090

9191
func testIndexTraversals() {
92+
let zero_to_one_hundered_range = 0...100
9293
validateIndexTraversals(
93-
(0...100).striding(by: 10)
94-
// (0...100).striding(by: 11)
95-
// (0...100).striding(by: 101)
94+
zero_to_one_hundered_range.striding(by: 10),
95+
zero_to_one_hundered_range.striding(by: 11),
96+
zero_to_one_hundered_range.striding(by: 101)
97+
)
98+
let zero_to_one_hundered_array = zero_to_one_hundered_range.map{ $0 }
99+
validateIndexTraversals(
100+
zero_to_one_hundered_array.striding(by: 10),
101+
zero_to_one_hundered_array.striding(by: 11),
102+
zero_to_one_hundered_array.striding(by: 101)
103+
)
104+
let string = "swift rocks".map(String.init)
105+
validateIndexTraversals(
106+
string.striding(by: 1),
107+
string.striding(by: 2),
108+
string.striding(by: 10)
96109
)
97110
}
98-
99-
func testDistance() {
100111

101-
do {
102-
let a = (0...100).striding(by: 11)
103-
XCTAssertEqual(a.distance(from: a.startIndex, to: a.endIndex), a.count)
104-
for (i, index) in a.indices.enumerated() {
105-
XCTAssertEqual(a.distance(from: a.startIndex, to: index), i)
106-
}
107-
108-
var i = a.startIndex
109-
a.formIndex(&i, offsetBy: 3)
110-
XCTAssertEqual(a.distance(from: a.startIndex, to: i), 3)
111-
XCTAssertEqual(a[i], 33)
112-
}
113-
114-
do {
115-
116-
let a = (0...100).striding(by: 10)
117-
XCTAssertEqual(a.distance(from: a.startIndex, to: a.endIndex), a.count)
118-
119-
for (i, index) in a.indices.enumerated() {
120-
XCTAssertEqual(a.distance(from: a.startIndex, to: index), i)
121-
}
122-
123-
var i = a.startIndex
124-
a.formIndex(&i, offsetBy: 3)
125-
XCTAssertEqual(a.distance(from: a.startIndex, to: i), 3)
126-
XCTAssertEqual(a[i], 30)
127-
}
128-
129-
do {
130-
131-
let a = (0...100).striding(by: 101)
132-
XCTAssertEqual(a.distance(from: a.startIndex, to: a.endIndex), a.count)
133-
134-
for (i, index) in a.indices.enumerated() {
135-
XCTAssertEqual(a.distance(from: a.startIndex, to: index), i)
136-
}
137-
138-
var i = a.startIndex
139-
a.formIndex(&i, offsetBy: 1)
140-
XCTAssertEqual(a.distance(from: a.startIndex, to: i), a.count)
141-
XCTAssertEqual(i, a.endIndex)
142-
// a[i] // == Fatal error: Index out of range
143-
}
144-
}
145112

146113
func testOffsetBy() {
147114
let a = (0...100).striding(by: 22)

0 commit comments

Comments
 (0)