Skip to content

Commit 6231cca

Browse files
austinzhengmoiseev
authored andcommitted
[stdlib] Implementing O(1) methods on CountableClosedRange (#2993)
* [stdlib] Implementing O(1) methods on `CountableClosedRange` Changes: - Implemented O(1) `index(_:offsetBy:)` and `distance(from:to:)` methods on `CountableClosedRange` - Fixed some unit tests - Added many more unit tests for Countable*Range index APIs * Clarify test intent
1 parent 4b0005d commit 6231cca

File tree

3 files changed

+362
-29
lines changed

3 files changed

+362
-29
lines changed

stdlib/public/core/ClosedRange.swift

Lines changed: 55 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public struct ClosedRangeIndex<Bound> : Comparable
3232
// CountableClosedRange is not interchangeable with CountableRange in all
3333
// contexts.
3434
Bound : protocol<_Strideable, Comparable>,
35-
Bound.Stride : Integer {
35+
Bound.Stride : SignedInteger {
3636
/// Creates the "past the end" position.
3737
internal init() { _value = .pastEnd }
3838

@@ -47,26 +47,64 @@ public struct ClosedRangeIndex<Bound> : Comparable
4747
}
4848
}
4949

50-
internal func _predecessor(upperBound limit: Bound) -> ClosedRangeIndex {
50+
internal func _predecessor(
51+
lowerBound: Bound, upperBound limit: Bound
52+
) -> ClosedRangeIndex {
5153
switch _value {
52-
case .inRange(let x): return ClosedRangeIndex(x.advanced(by: -1))
53-
case .pastEnd: return ClosedRangeIndex(limit)
54+
case .inRange(let x):
55+
_precondition(x > lowerBound, "Incrementing past start index")
56+
return ClosedRangeIndex(x.advanced(by: -1))
57+
case .pastEnd:
58+
_precondition(limit >= lowerBound, "Incrementing past start index")
59+
return ClosedRangeIndex(limit)
5460
}
5561
}
5662

5763
internal func _advanced(
58-
by n: Bound.Stride, upperBound limit: Bound
64+
by n: Bound.Stride, lowerBound: Bound, upperBound limit: Bound
5965
) -> ClosedRangeIndex {
6066
switch _value {
6167
case .inRange(let x):
6268
let d = x.distance(to: limit)
63-
if n <= d { return ClosedRangeIndex(x.advanced(by: n)) }
69+
if n <= d {
70+
let newPosition = x.advanced(by: n)
71+
_precondition(newPosition >= lowerBound,
72+
"Advancing past start index")
73+
return ClosedRangeIndex(newPosition)
74+
}
6475
if d - -1 == n { return ClosedRangeIndex() }
6576
_preconditionFailure("Advancing past end index")
6677
case .pastEnd:
67-
return n == 0
68-
? self :
69-
ClosedRangeIndex(limit)._advanced(by: n, upperBound: limit)
78+
if n == 0 {
79+
return self
80+
} else if n > 0 {
81+
_preconditionFailure("Advancing past end index")
82+
} else {
83+
return ClosedRangeIndex(limit)._advanced(
84+
by: (n + 1),
85+
lowerBound: lowerBound,
86+
upperBound: limit
87+
)
88+
}
89+
}
90+
}
91+
92+
internal func _distance(
93+
to: ClosedRangeIndex<Bound>, upperBound limit: Bound
94+
) -> Bound.Stride {
95+
switch (_value, to._value) {
96+
case let (.inRange(left), .inRange(right)):
97+
// in range <--> in range
98+
return left.distance(to: right)
99+
case let (.inRange(left), .pastEnd):
100+
// in range --> end
101+
return 1 + left.distance(to: limit)
102+
case let (.pastEnd, .inRange(right)):
103+
// in range <-- end
104+
return limit.distance(to: right) - 1
105+
case (.pastEnd, .pastEnd):
106+
// end <--> end
107+
return 0
70108
}
71109
}
72110

@@ -220,17 +258,20 @@ public struct CountableClosedRange<Bound> : RandomAccessCollection
220258
}
221259

222260
public func index(after i: Index) -> Index {
223-
// FIXME: swift-3-indexing-model: range checks and tests.
224261
return i._successor(upperBound: upperBound)
225262
}
226263

227264
public func index(before i: Index) -> Index {
228-
// FIXME: swift-3-indexing-model: range checks and tests.
229-
return i._predecessor(upperBound: upperBound)
265+
return i._predecessor(lowerBound: lowerBound, upperBound: upperBound)
230266
}
231267

232-
// FIXME: swift-3-indexing-model: implement O(1) `index(_:offsetBy:)`
233-
// and `distance(from:to:)`, and write tests for them.
268+
public func index(_ i: Index, offsetBy n: IndexDistance) -> Index {
269+
return i._advanced(by: n, lowerBound: lowerBound, upperBound: upperBound)
270+
}
271+
272+
public func distance(from start: Index, to end: Index) -> IndexDistance {
273+
return start._distance(to: end, upperBound: upperBound)
274+
}
234275

235276
/// Accesses the element at specified position.
236277
///

stdlib/public/core/Range.swift.gyb

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -115,26 +115,26 @@ public struct CountableRange<Bound> : RandomAccessCollection
115115
}
116116

117117
public func index(after i: Index) -> Index {
118-
// FIXME: swift-3-indexing-model: tests.
119118
_failEarlyRangeCheck(i, bounds: startIndex..<endIndex)
120119

121120
return i.advanced(by: 1)
122121
}
123122

124123
public func index(before i: Index) -> Index {
125-
// FIXME: swift-3-indexing-model: range check i: should allow `endIndex`.
126-
//_failEarlyRangeCheck(i, bounds: startIndex..<endIndex)
124+
_precondition(i > lowerBound)
125+
_precondition(i <= upperBound)
127126

128127
return i.advanced(by: -1)
129128
}
130129

131130
public func index(_ i: Index, offsetBy n: IndexDistance) -> Index {
132-
// FIXME: swift-3-indexing-model: tests.
133-
return i.advanced(by: n)
131+
let r = i.advanced(by: n)
132+
_precondition(r >= lowerBound)
133+
_precondition(r <= upperBound)
134+
return r
134135
}
135136

136137
public func distance(from start: Index, to end: Index) -> IndexDistance {
137-
// FIXME: swift-3-indexing-model: tests.
138138
return start.distance(to: end)
139139
}
140140

0 commit comments

Comments
 (0)