@@ -84,9 +84,202 @@ extension SlidingWindows: Collection {
84
84
)
85
85
}
86
86
87
- // TODO: Implement distance(from:to:), index(_:offsetBy:) and
88
- // index(_:offsetBy:limitedBy:)
87
+ public func index( _ i: Index , offsetBy distance: Int ) -> Index {
88
+ guard distance != 0 else { return i }
89
+
90
+ return distance > 0
91
+ ? offsetForward ( i, by: distance)
92
+ : offsetBackward ( i, by: - distance)
93
+ }
94
+
95
+ public func index(
96
+ _ i: Index ,
97
+ offsetBy distance: Int ,
98
+ limitedBy limit: Index
99
+ ) -> Index ? {
100
+ guard distance != 0 else { return i }
101
+ guard limit != i else { return nil }
102
+
103
+ if distance > 0 {
104
+ return limit > i
105
+ ? offsetForward ( i, by: distance, limitedBy: limit)
106
+ : offsetForward ( i, by: distance)
107
+ } else {
108
+ return limit < i
109
+ ? offsetBackward ( i, by: - distance, limitedBy: limit)
110
+ : offsetBackward ( i, by: - distance)
111
+ }
112
+ }
113
+
114
+ private func offsetForward( _ i: Index , by distance: Int ) -> Index {
115
+ guard let index = offsetForward ( i, by: distance, limitedBy: endIndex)
116
+ else { fatalError ( " Index is out of bounds " ) }
117
+ return index
118
+ }
119
+
120
+ private func offsetBackward( _ i: Index , by distance: Int ) -> Index {
121
+ guard let index = offsetBackward ( i, by: distance, limitedBy: startIndex)
122
+ else { fatalError ( " Index is out of bounds " ) }
123
+ return index
124
+ }
125
+
126
+ private func offsetForward(
127
+ _ i: Index , by distance: Int , limitedBy limit: Index
128
+ ) -> Index ? {
129
+ assert ( distance > 0 )
130
+ assert ( limit > i)
131
+
132
+ // `endIndex` and the index before it both have `base.endIndex` as their
133
+ // upper bound, so we first advance to the base index _before_ the upper
134
+ // bound of the output, in order to avoid advancing past the end of `base`
135
+ // when advancing to `endIndex`.
136
+ //
137
+ // Advancing by 4:
138
+ //
139
+ // input: [x|x x x x x|x x x x] [x x|x x x x x|x x x]
140
+ // |> > >|>| or |> > >|
141
+ // output: [x x x x x|x x x x x] [x x x x x x x x x x] (`endIndex`)
142
+
143
+ if distance >= size {
144
+ // Avoid traversing `self[i.lowerBound..<i.upperBound]` when the lower
145
+ // bound of the output is greater than or equal to the upper bound of the
146
+ // input.
147
+
148
+ // input: [x|x x x x|x x x x x x x]
149
+ // |> >|> > >|>|
150
+ // output: [x x x x x x x|x x x x|x]
151
+
152
+ guard limit. lowerBound >= i. upperBound,
153
+ let lowerBound = base. index (
154
+ i. upperBound,
155
+ offsetBy: distance - size,
156
+ limitedBy: limit. lowerBound) ,
157
+ let indexBeforeUpperBound = base. index (
158
+ lowerBound,
159
+ offsetBy: size - 1 ,
160
+ limitedBy: limit. upperBound)
161
+ else { return nil }
162
+
163
+ // If `indexBeforeUpperBound` equals `base.endIndex`, we're advancing to
164
+ // `endIndex`.
165
+ guard indexBeforeUpperBound != base. endIndex else { return endIndex }
166
+
167
+ return Index (
168
+ lowerBound: lowerBound,
169
+ upperBound: base. index ( after: indexBeforeUpperBound) )
170
+ } else {
171
+ // input: [x|x x x x x x|x x x x x]
172
+ // |> > > >| |> > >|>|
173
+ // output: [x x x x x|x x x x x x|x]
174
+
175
+ guard let indexBeforeUpperBound = base. index (
176
+ i. upperBound,
177
+ offsetBy: distance - 1 ,
178
+ limitedBy: limit. upperBound)
179
+ else { return nil }
180
+
181
+ // If `indexBeforeUpperBound` equals the limit, the upper bound itself
182
+ // exceeds it.
183
+ guard indexBeforeUpperBound != limit. upperBound || limit == endIndex
184
+ else { return nil }
185
+
186
+ // If `indexBeforeUpperBound` equals `base.endIndex`, we're advancing to
187
+ // `endIndex`.
188
+ guard indexBeforeUpperBound != base. endIndex else { return endIndex }
189
+
190
+ return Index (
191
+ lowerBound: base. index ( i. lowerBound, offsetBy: distance) ,
192
+ upperBound: base. index ( after: indexBeforeUpperBound) )
193
+ }
194
+ }
195
+
196
+ private func offsetBackward(
197
+ _ i: Index , by distance: Int , limitedBy limit: Index
198
+ ) -> Index ? {
199
+ assert ( distance > 0 )
200
+ assert ( limit < i)
201
+
202
+ if i == endIndex {
203
+ // Advance `base.endIndex` by `distance - 1`, because the index before
204
+ // `endIndex` also has `base.endIndex` as its upper bound.
205
+ //
206
+ // Advancing by 4:
207
+ //
208
+ // input: [x x x x x x x x x x] (`endIndex`)
209
+ // |< < < < <|< < <|
210
+ // output: [x x|x x x x x|x x x]
211
+
212
+ guard let upperBound = base. index (
213
+ base. endIndex,
214
+ offsetBy: - ( distance - 1 ) ,
215
+ limitedBy: limit. upperBound)
216
+ else { return nil }
217
+
218
+ return Index (
219
+ lowerBound: base. index ( upperBound, offsetBy: - size) ,
220
+ upperBound: upperBound)
221
+ } else if distance >= size {
222
+ // Avoid traversing `self[i.lowerBound..<i.upperBound]` when the upper
223
+ // bound of the output is less than or equal to the lower bound of the
224
+ // input.
225
+ //
226
+ // input: [x x x x x x x|x x x x|x]
227
+ // |< < < <|< <|
228
+ // output: [x|x x x x|x x x x x x x]
229
+
230
+ guard limit. upperBound <= i. lowerBound,
231
+ let upperBound = base. index (
232
+ i. lowerBound,
233
+ offsetBy: - ( distance - size) ,
234
+ limitedBy: limit. upperBound)
235
+ else { return nil }
236
+
237
+ return Index (
238
+ lowerBound: base. index ( upperBound, offsetBy: - size) ,
239
+ upperBound: upperBound)
240
+ } else {
241
+ // input: [x x x x x|x x x x x x|x]
242
+ // |< < < <| |< < < <|
243
+ // output: [x|x x x x x x|x x x x x]
244
+
245
+ guard let lowerBound = base. index (
246
+ i. lowerBound,
247
+ offsetBy: - distance,
248
+ limitedBy: limit. lowerBound)
249
+ else { return nil }
250
+
251
+ return Index (
252
+ lowerBound: lowerBound,
253
+ upperBound: base. index ( i. lowerBound, offsetBy: - distance) )
254
+ }
255
+ }
256
+
257
+ public func distance( from start: Index , to end: Index ) -> Int {
258
+ guard start <= end else { return - distance( from: end, to: start) }
259
+ guard start != end else { return 0 }
260
+ guard end < endIndex else {
261
+ // We add 1 here because the index before `endIndex` also has
262
+ // `base.endIndex` as its upper bound.
263
+ return base [ start. upperBound... ] . count + 1
264
+ }
89
265
266
+ if start. upperBound <= end. lowerBound {
267
+ // The distance between `start.lowerBound` and `start.upperBound` is
268
+ // already known.
269
+ //
270
+ // start: [x|x x x x|x x x x x x x]
271
+ // |- - - -|> >|
272
+ // end: [x x x x x x x|x x x x|x]
273
+
274
+ return size + base[ start. upperBound..< end. lowerBound] . count
275
+ } else {
276
+ // start: [x|x x x x x x|x x x x x]
277
+ // |> > > >|
278
+ // end: [x x x x x|x x x x x x|x]
279
+
280
+ return base [ start. lowerBound..< end. lowerBound] . count
281
+ }
282
+ }
90
283
}
91
284
92
285
extension SlidingWindows : BidirectionalCollection where Base: BidirectionalCollection {
0 commit comments