Skip to content

Commit 551b4af

Browse files
author
Tim Vermeulen
authored
Simplify Chain's index offsetting (#116)
* Use the offsetting structure from `Product2` * Rename `convertIndex` to `normalizeIndex`
1 parent dca2544 commit 551b4af

File tree

1 file changed

+60
-53
lines changed

1 file changed

+60
-53
lines changed

Sources/Algorithms/Chain.swift

Lines changed: 60 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -102,15 +102,15 @@ extension Chain2: Collection where Base1: Collection, Base2: Collection {
102102
/// Converts an index of `Base1` to the corresponding `Index` by mapping
103103
/// `base1.endIndex` to `base2.startIndex`.
104104
@inlinable
105-
internal func convertIndex(_ i: Base1.Index) -> Index {
105+
internal func normalizeIndex(_ i: Base1.Index) -> Index {
106106
i == base1.endIndex ? Index(second: base2.startIndex) : Index(first: i)
107107
}
108108

109109
@inlinable
110110
public var startIndex: Index {
111111
// if `base1` is empty, this will return `base2.startIndex` - if `base2` is
112112
// also empty, this will correctly equal `base2.endIndex`
113-
convertIndex(base1.startIndex)
113+
normalizeIndex(base1.startIndex)
114114
}
115115

116116
@inlinable
@@ -133,109 +133,116 @@ extension Chain2: Collection where Base1: Collection, Base2: Collection {
133133
switch i.position {
134134
case let .first(i):
135135
assert(i != base1.endIndex)
136-
return convertIndex(base1.index(after: i))
136+
return normalizeIndex(base1.index(after: i))
137137
case let .second(i):
138138
return Index(second: base2.index(after: i))
139139
}
140140
}
141141

142142
@inlinable
143-
public func index(_ i: Index, offsetBy n: Int) -> Index {
144-
if n == 0 { return i }
145-
return n > 0
146-
? offsetForward(i, by: n, limitedBy: endIndex)!
147-
: offsetBackward(i, by: -n, limitedBy: startIndex)!
143+
public func index(_ i: Index, offsetBy distance: Int) -> Index {
144+
guard distance != 0 else { return i }
145+
146+
return distance > 0
147+
? offsetForward(i, by: distance)
148+
: offsetBackward(i, by: -distance)
148149
}
149-
150+
150151
@inlinable
151152
public func index(
152153
_ i: Index,
153-
offsetBy n: Int,
154+
offsetBy distance: Int,
154155
limitedBy limit: Index
155156
) -> Index? {
156-
if n == 0 { return i }
157-
return n > 0
158-
? offsetForward(i, by: n, limitedBy: limit)
159-
: offsetBackward(i, by: -n, limitedBy: limit)
157+
if distance >= 0 {
158+
return limit >= i
159+
? offsetForward(i, by: distance, limitedBy: limit)
160+
: offsetForward(i, by: distance)
161+
} else {
162+
return limit <= i
163+
? offsetBackward(i, by: -distance, limitedBy: limit)
164+
: offsetBackward(i, by: -distance)
165+
}
166+
}
167+
168+
@inlinable
169+
internal func offsetForward(_ i: Index, by distance: Int) -> Index {
170+
guard let index = offsetForward(i, by: distance, limitedBy: endIndex)
171+
else { fatalError("Index is out of bounds") }
172+
return index
173+
}
174+
175+
@inlinable
176+
internal func offsetBackward(_ i: Index, by distance: Int) -> Index {
177+
guard let index = offsetBackward(i, by: distance, limitedBy: startIndex)
178+
else { fatalError("Index is out of bounds") }
179+
return index
160180
}
161181

162182
@inlinable
163183
internal func offsetForward(
164-
_ i: Index, by n: Int, limitedBy limit: Index
184+
_ i: Index, by distance: Int, limitedBy limit: Index
165185
) -> Index? {
186+
assert(distance >= 0)
187+
assert(limit >= i)
188+
166189
switch (i.position, limit.position) {
167190
case let (.first(i), .first(limit)):
168-
if limit >= i {
169-
// `limit` is relevant, so `base2` cannot be reached
170-
return base1.index(i, offsetBy: n, limitedBy: limit)
171-
.map(Index.init(first:))
172-
} else if let j = base1.index(i, offsetBy: n, limitedBy: base1.endIndex) {
173-
// the offset stays within the bounds of `base1`
174-
return convertIndex(j)
175-
} else {
176-
// the offset overflows the bounds of `base1` by `n - d`
177-
let d = base1.distance(from: i, to: base1.endIndex)
178-
return Index(second: base2.index(base2.startIndex, offsetBy: n - d))
179-
}
191+
return base1.index(i, offsetBy: distance, limitedBy: limit)
192+
.map(Index.init(first:))
180193

181194
case let (.first(i), .second(limit)):
182-
if let j = base1.index(i, offsetBy: n, limitedBy: base1.endIndex) {
195+
if let j = base1.index(i, offsetBy: distance, limitedBy: base1.endIndex) {
183196
// the offset stays within the bounds of `base1`
184-
return convertIndex(j)
197+
return normalizeIndex(j)
185198
} else {
186199
// the offset overflows the bounds of `base1` by `n - d`
187200
let d = base1.distance(from: i, to: base1.endIndex)
188-
return base2.index(base2.startIndex, offsetBy: n - d, limitedBy: limit)
201+
return base2.index(base2.startIndex, offsetBy: distance - d, limitedBy: limit)
189202
.map(Index.init(second:))
190203
}
191204

192-
case let (.second(i), .first):
193-
// `limit` has no effect here
194-
return Index(second: base2.index(i, offsetBy: n))
205+
case (.second, .first):
206+
// impossible because `limit >= i`
207+
fatalError()
195208

196209
case let (.second(i), .second(limit)):
197-
return base2.index(i, offsetBy: n, limitedBy: limit)
210+
return base2.index(i, offsetBy: distance, limitedBy: limit)
198211
.map(Index.init(second:))
199212
}
200213
}
201214

202215
@inlinable
203216
internal func offsetBackward(
204-
_ i: Index, by n: Int, limitedBy limit: Index
217+
_ i: Index, by distance: Int, limitedBy limit: Index
205218
) -> Index? {
219+
assert(distance >= 0)
220+
assert(limit <= i)
221+
206222
switch (i.position, limit.position) {
207223
case let (.first(i), .first(limit)):
208-
return base1.index(i, offsetBy: -n, limitedBy: limit)
224+
return base1.index(i, offsetBy: -distance, limitedBy: limit)
209225
.map(Index.init(first:))
210226

211-
case let (.first(i), .second):
212-
// `limit` has no effect here
213-
return Index(first: base1.index(i, offsetBy: -n))
227+
case (.first, .second):
228+
// impossible because `limit <= i`
229+
fatalError()
214230

215231
case let (.second(i), .first(limit)):
216-
if let j = base2.index(i, offsetBy: -n, limitedBy: base2.startIndex) {
232+
if let j = base2.index(i, offsetBy: -distance, limitedBy: base2.startIndex) {
217233
// the offset stays within the bounds of `base2`
218234
return Index(second: j)
219235
} else {
220236
// the offset overflows the bounds of `base2` by `n - d`
221237
let d = base2.distance(from: base2.startIndex, to: i)
222-
return base1.index(base1.endIndex, offsetBy: -(n - d), limitedBy: limit)
238+
return base1.index(base1.endIndex, offsetBy: -(distance - d), limitedBy: limit)
223239
.map(Index.init(first:))
224240
}
225241

226242
case let (.second(i), .second(limit)):
227-
if limit <= i {
228-
// `limit` is relevant, so `base1` cannot be reached
229-
return base2.index(i, offsetBy: -n, limitedBy: limit)
230-
.map(Index.init(second:))
231-
} else if let j = base2.index(i, offsetBy: -n, limitedBy: base2.startIndex) {
232-
// the offset stays within the bounds of `base2`
233-
return Index(second: j)
234-
} else {
235-
// the offset overflows the bounds of `base2` by `n - d`
236-
let d = base2.distance(from: base2.startIndex, to: i)
237-
return Index(first: base1.index(base1.endIndex, offsetBy: -(n - d)))
238-
}
243+
// `limit` is relevant, so `base1` cannot be reached
244+
return base2.index(i, offsetBy: -distance, limitedBy: limit)
245+
.map(Index.init(second:))
239246
}
240247
}
241248

0 commit comments

Comments
 (0)