@@ -305,13 +305,15 @@ extension ChunkedByCount: Collection {
305305 }
306306 }
307307
308- /// - Complexity: O(n)
308+ /// - Complexity: O(1)
309+ @inlinable
309310 public var startIndex : Index { computedStartIndex }
311+ @inlinable
310312 public var endIndex : Index {
311313 Index ( _baseRange: base. endIndex..< base. endIndex)
312314 }
313315
314- /// - Complexity: O(n )
316+ /// - Complexity: O(1 )
315317 public subscript( i: Index ) -> Element {
316318 precondition ( i < endIndex, " Index out of range " )
317319 return base [ i. baseRange]
@@ -358,7 +360,81 @@ where Base: RandomAccessCollection {
358360 return Index ( _baseRange: baseIdx..< i. baseRange. lowerBound)
359361 }
360362
361- // TODO: index(_:offsetBy:) and index(_:offsetBy:limitedBy:)
363+ @inlinable
364+ public func index(
365+ _ i: Index , offsetBy offset: Int , limitedBy limit: Index
366+ ) -> Index ? {
367+ guard offset != 0 else { return i }
368+ guard limit != i else { return nil }
369+
370+ if offset > 0 {
371+ guard limit <= i || distance ( from: i, to: limit) >= offset else {
372+ return nil
373+ }
374+ return offsetForward ( i, offsetBy: offset)
375+ } else {
376+ guard limit >= i || distance ( from: i, to: limit) <= offset else {
377+ return nil
378+ }
379+ return offsetBackward ( i, offsetBy: offset)
380+ }
381+ }
382+
383+ @inlinable
384+ public func index( _ i: Index , offsetBy distance: Int ) -> Index {
385+ guard distance != 0 else { return i }
386+
387+ return distance > 0
388+ ? offsetForward ( i, offsetBy: distance)
389+ : offsetBackward ( i, offsetBy: distance)
390+ }
391+
392+ @usableFromInline
393+ internal func offsetForward( _ i: Index , offsetBy distance: Int ) -> Index {
394+ return makeOffsetIndex (
395+ from: i, baseBound: base. endIndex, distance: distance
396+ )
397+ }
398+
399+ @usableFromInline
400+ internal func offsetBackward( _ i: Index , offsetBy n: Int ) -> Index {
401+ var idx = i
402+ var distance = n
403+ // If we know that the last chunk is the only one that can possible
404+ // have a variadic count. So in order to simplify and avoid another
405+ // calculation of offsets(that is already done at `index(before:)`)
406+ // we just move one position already so the index can be calculated
407+ // assuming all chunks have the same size.
408+ if i. baseRange. lowerBound == base. endIndex {
409+ formIndex ( before: & idx)
410+ distance += 1
411+ // If the offset was simply one, we are done.
412+ guard distance != 0 else {
413+ return idx
414+ }
415+ }
416+
417+ return makeOffsetIndex (
418+ from: idx, baseBound: base. startIndex, distance: distance
419+ )
420+ }
421+
422+ // Helper to compute index(offsetBy:) index.
423+ internal func makeOffsetIndex(
424+ from i: Index , baseBound: Base . Index , distance: Int
425+ ) -> Index {
426+ let baseStartIdx = base. index (
427+ i. baseRange. lowerBound, offsetBy: distance * chunkCount,
428+ limitedBy: baseBound
429+ ) ?? baseBound
430+
431+ let baseEndIdx = base. index (
432+ i. baseRange. lowerBound, offsetBy: ( distance + 1 ) * chunkCount,
433+ limitedBy: base. endIndex
434+ ) ?? base. endIndex
435+
436+ return Index ( _baseRange: baseStartIdx..< baseEndIdx)
437+ }
362438}
363439
364440extension ChunkedByCount {
@@ -399,7 +475,7 @@ extension Collection {
399475 /// - Complexity: O(1)
400476 @inlinable
401477 public func chunks( ofCount count: Int ) -> ChunkedByCount < Self > {
402- precondition ( count > 0 , " Cannot chunk with count <= 0!" )
478+ precondition ( count > 0 , " Cannot chunk with count <= 0! " )
403479 return ChunkedByCount ( _base: self , _chunkCount: count)
404480 }
405481}
0 commit comments