@@ -84,9 +84,202 @@ extension SlidingWindows: Collection {
8484 )
8585 }
8686
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+ }
89265
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+ }
90283}
91284
92285extension SlidingWindows : BidirectionalCollection where Base: BidirectionalCollection {
0 commit comments