@@ -23,6 +23,10 @@ public struct LazyChunked<Base: Collection, Subject> {
2323 @usableFromInline
2424 internal let belongInSameGroup : ( Subject , Subject ) -> Bool
2525
26+ /// The upper bound of the first chunk.
27+ @usableFromInline
28+ internal var firstUpperBound : Base . Index
29+
2630 @usableFromInline
2731 internal init (
2832 base: Base ,
@@ -32,46 +36,37 @@ public struct LazyChunked<Base: Collection, Subject> {
3236 self . base = base
3337 self . projection = projection
3438 self . belongInSameGroup = belongInSameGroup
39+ self . firstUpperBound = base. startIndex
40+
41+ if !base. isEmpty {
42+ firstUpperBound = endOfChunk ( startingAt: base. startIndex)
43+ }
3544 }
3645}
3746
3847extension LazyChunked : LazyCollectionProtocol {
3948 /// A position in a chunked collection.
4049 public struct Index : Comparable {
41- /// The lower bound of the chunk at this position.
50+ /// The range corresponding to the chunk at this position.
4251 @usableFromInline
43- internal var lowerBound : Base . Index
52+ internal var baseRange : Range < Base . Index >
4453
45- /// The upper bound of the chunk at this position.
46- ///
47- /// `upperBound` is optional so that computing `startIndex` can be an O(1)
48- /// operation. When `upperBound` is `nil`, the actual upper bound is found
49- /// when subscripting or calling `index(after:)`.
5054 @usableFromInline
51- internal var upperBound : Base . Index ?
52-
53- @usableFromInline
54- internal init ( lowerBound: Base . Index , upperBound: Base . Index ? = nil ) {
55- self . lowerBound = lowerBound
56- self . upperBound = upperBound
55+ internal init ( _ baseRange: Range < Base . Index > ) {
56+ self . baseRange = baseRange
5757 }
5858
5959 @inlinable
6060 public static func == ( lhs: Index , rhs: Index ) -> Bool {
61- // Only use the lower bound to test for equality, since sometimes the
62- // `startIndex` will have an upper bound of `nil` and sometimes it won't,
63- // such as when retrieved by:
64- // `c.index(before: c.index(after: c.startIndex))`.
65- //
6661 // Since each index represents the range of a disparate chunk, no two
6762 // unique indices will have the same lower bound.
68- lhs. lowerBound == rhs. lowerBound
63+ lhs. baseRange . lowerBound == rhs. baseRange . lowerBound
6964 }
7065
7166 @inlinable
7267 public static func < ( lhs: Index , rhs: Index ) -> Bool {
7368 // Only use the lower bound to test for ordering, as above.
74- lhs. lowerBound < rhs. lowerBound
69+ lhs. baseRange . lowerBound < rhs. baseRange . lowerBound
7570 }
7671 }
7772
@@ -87,28 +82,27 @@ extension LazyChunked: LazyCollectionProtocol {
8782
8883 @inlinable
8984 public var startIndex : Index {
90- Index ( lowerBound : base. startIndex)
85+ Index ( base. startIndex..< firstUpperBound )
9186 }
9287
9388 @inlinable
9489 public var endIndex : Index {
95- Index ( lowerBound : base. endIndex)
90+ Index ( base . endIndex ..< base. endIndex)
9691 }
9792
9893 @inlinable
9994 public func index( after i: Index ) -> Index {
10095 precondition ( i != endIndex, " Can't advance past endIndex " )
101- let upperBound = i. upperBound ?? endOfChunk ( startingAt : i . lowerBound )
96+ let upperBound = i. baseRange . upperBound
10297 guard upperBound != base. endIndex else { return endIndex }
10398 let end = endOfChunk ( startingAt: upperBound)
104- return Index ( lowerBound : upperBound, upperBound : end)
99+ return Index ( upperBound..< end)
105100 }
106101
107102 @inlinable
108103 public subscript( position: Index ) -> Base . SubSequence {
109- let upperBound = position. upperBound
110- ?? endOfChunk ( startingAt: position. lowerBound)
111- return base [ position. lowerBound..< upperBound]
104+ precondition ( position != endIndex, " Can't subscript using endIndex " )
105+ return base [ position. baseRange]
112106 }
113107}
114108
@@ -144,8 +138,8 @@ extension LazyChunked: BidirectionalCollection
144138 @inlinable
145139 public func index( before i: Index ) -> Index {
146140 precondition ( i != startIndex, " Can't advance before startIndex " )
147- let start = startOfChunk ( endingAt: i. lowerBound)
148- return Index ( lowerBound : start, upperBound : i . lowerBound)
141+ let start = startOfChunk ( endingAt: i. baseRange . lowerBound)
142+ return Index ( start..< i . baseRange . lowerBound)
149143 }
150144}
151145
@@ -157,9 +151,7 @@ extension LazyCollectionProtocol {
157151 /// Returns a lazy collection of subsequences of this collection, chunked by
158152 /// the given predicate.
159153 ///
160- /// - Complexity: O(1). When iterating over the resulting collection,
161- /// accessing each successive chunk has a complexity of O(*m*), where *m*
162- /// is the length of the chunk.
154+ /// - Complexity: O(*n*), because the start index is pre-computed.
163155 @inlinable
164156 public func chunked(
165157 by belongInSameGroup: @escaping ( Element , Element ) -> Bool
@@ -173,9 +165,7 @@ extension LazyCollectionProtocol {
173165 /// Returns a lazy collection of subsequences of this collection, chunked by
174166 /// grouping elements that project to the same value.
175167 ///
176- /// - Complexity: O(1). When iterating over the resulting collection,
177- /// accessing each successive chunk has a complexity of O(*m*), where *m*
178- /// is the length of the chunk.
168+ /// - Complexity: O(*n*), because the start index is pre-computed.
179169 @inlinable
180170 public func chunked< Subject: Equatable > (
181171 on projection: @escaping ( Element ) -> Subject
@@ -276,7 +266,7 @@ public struct ChunkedByCount<Base: Collection> {
276266 /// Creates a view instance that presents the elements of `base`
277267 /// in `SubSequence` chunks of the given count.
278268 ///
279- /// - Complexity: O(n)
269+ /// - Complexity: O(*n*), because the start index is pre-computed.
280270 @inlinable
281271 internal init ( _base: Base , _chunkCount: Int ) {
282272 self . base = _base
@@ -522,7 +512,7 @@ extension Collection {
522512 /// print(c.chunks(ofCount: 3).map(Array.init))
523513 /// // [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]
524514 ///
525- /// - Complexity: O(1)
515+ /// - Complexity: O(*n*), because the start index is pre-computed.
526516 @inlinable
527517 public func chunks( ofCount count: Int ) -> ChunkedByCount < Self > {
528518 precondition ( count > 0 , " Cannot chunk with count <= 0! " )
0 commit comments