Skip to content

Commit afac704

Browse files
[Chunked] Implemeting of ChunkedByCount index(offsetBy:)
1 parent 79aeb24 commit afac704

File tree

1 file changed

+80
-4
lines changed

1 file changed

+80
-4
lines changed

Sources/Algorithms/Chunked.swift

Lines changed: 80 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -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

364440
extension 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

Comments
 (0)