From c636b09f3d1913a0a5a91cf5e650caa0e5a6a7b2 Mon Sep 17 00:00:00 2001 From: Pranav Kasetti Date: Sun, 14 Mar 2021 13:27:40 +0000 Subject: [PATCH 01/19] [test] Divide up existing Cycle tests and add FiniteCycle placeholder tests. --- Tests/SwiftAlgorithmsTests/CycleTests.swift | 31 ++++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/Tests/SwiftAlgorithmsTests/CycleTests.swift b/Tests/SwiftAlgorithmsTests/CycleTests.swift index 0d52788b..24b85e62 100644 --- a/Tests/SwiftAlgorithmsTests/CycleTests.swift +++ b/Tests/SwiftAlgorithmsTests/CycleTests.swift @@ -19,28 +19,45 @@ final class CycleTests: XCTestCase { [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4], cycle.prefix(20) ) + } + func testCycleClosedRangePrefix() { let a = Array((0..<17).cycled().prefix(10_000)) XCTAssertEqual(10_000, a.count) - + } + + func testEmptyCycle() { let empty = Array("".cycled()) XCTAssert(empty.isEmpty) } - + + func testCycleLazy() { + XCTAssertLazySequence((1...4).lazy.cycled()) + } + func testRepeated() { let repeats = (1...4).cycled(times: 3) XCTAssertEqualSequences( [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4], repeats) - + } + + func testRepeatedClosedRange() { + let repeats = Array((1..<5).cycled(times: 2500)) + XCTAssertEqual(10_000, repeats.count) + } + + func testRepeatedEmptyCollection() { let empty1 = Array("".cycled(times: 100)) XCTAssert(empty1.isEmpty) - + } + + func testRepeatedZeroTimesCycle() { let empty2 = Array("Hello".cycled(times: 0)) XCTAssert(empty2.isEmpty) } - - func testCycleLazy() { - XCTAssertLazySequence((1...4).lazy.cycled()) + + func testRepeatedLazy() { + XCTAssertLazySequence((1...4).lazy.cycled(times: 3)) } } From b4d06363c0bd0f72518ecea38ccee20dcaaed0e8 Mon Sep 17 00:00:00 2001 From: Pranav Kasetti Date: Sun, 14 Mar 2021 13:15:52 +0000 Subject: [PATCH 02/19] Implement FiniteCycle with Iterator wrapper around Product2 Iterator. --- Sources/Algorithms/Cycle.swift | 41 ++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/Sources/Algorithms/Cycle.swift b/Sources/Algorithms/Cycle.swift index fe3c9661..f743f90a 100644 --- a/Sources/Algorithms/Cycle.swift +++ b/Sources/Algorithms/Cycle.swift @@ -56,6 +56,47 @@ extension Cycle: Sequence { extension Cycle: LazySequenceProtocol where Base: LazySequenceProtocol {} + +/// A collection wrapper that repeats the elements of a base collection for a finite number of times. +public struct FiniteCycle { + /// The collection to repeat. + public let base: Base + + /// The number of times to repeat the collection. + public let times: Int + + @usableFromInline + internal init(base: Base, times: Int) { + self.base = base + self.times = times + } +} + +extension FiniteCycle: Sequence { + /// The iterator for a `FiniteCycle` sequence. + public struct Iterator : IteratorProtocol { + @usableFromInline + var productIterator: Product2, Base>.Iterator + + @usableFromInline + internal init(_ product: Product2, Base>) { + self.productIterator = product.makeIterator() + } + + @inlinable + public mutating func next() -> Base.Element? { + self.productIterator.next()?.1 + } + } + + public func makeIterator() -> Iterator { + let iterations: Range = 0.. Date: Sun, 14 Mar 2021 13:23:26 +0000 Subject: [PATCH 03/19] Change method signature of cycled(times:) to use new FiniteCycle. --- Sources/Algorithms/Cycle.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/Algorithms/Cycle.swift b/Sources/Algorithms/Cycle.swift index f743f90a..996d9acc 100644 --- a/Sources/Algorithms/Cycle.swift +++ b/Sources/Algorithms/Cycle.swift @@ -150,7 +150,7 @@ extension Collection { /// /// - Complexity: O(1) @inlinable - public func cycled(times: Int) -> FlattenSequence> { - repeatElement(self, count: times).joined() + public func cycled(times: Int) -> FiniteCycle { + FiniteCycle(base: self, times: times) } } From 34888687f8e539dfa9b907a4ef2c4d16ff8eda32 Mon Sep 17 00:00:00 2001 From: Pranav Kasetti Date: Sun, 14 Mar 2021 13:43:30 +0000 Subject: [PATCH 04/19] Add tuple labels to Product2 for clarity. --- Sources/Algorithms/Cycle.swift | 2 +- Sources/Algorithms/Product.swift | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Sources/Algorithms/Cycle.swift b/Sources/Algorithms/Cycle.swift index 996d9acc..ab39f2be 100644 --- a/Sources/Algorithms/Cycle.swift +++ b/Sources/Algorithms/Cycle.swift @@ -85,7 +85,7 @@ extension FiniteCycle: Sequence { @inlinable public mutating func next() -> Base.Element? { - self.productIterator.next()?.1 + self.productIterator.next()?.element2 } } diff --git a/Sources/Algorithms/Product.swift b/Sources/Algorithms/Product.swift index 8b353ba8..cdd16f23 100644 --- a/Sources/Algorithms/Product.swift +++ b/Sources/Algorithms/Product.swift @@ -44,7 +44,7 @@ extension Product2: Sequence { } @inlinable - public mutating func next() -> (Base1.Element, Base2.Element)? { + public mutating func next() -> (element1: Base1.Element, element2: Base2.Element)? { // This is the initial state, where i1.next() has never // been called, or the final state, where i1.next() has // already returned nil. @@ -57,7 +57,7 @@ extension Product2: Sequence { // Get the next element from the second sequence, if not // at end. if let element2 = i2.next() { - return (element1!, element2) + return (element1: element1!, element2: element2) } // We've reached the end of the second sequence, so: @@ -70,7 +70,7 @@ extension Product2: Sequence { i2 = base2.makeIterator() if let element2 = i2.next() { - return (element1, element2) + return (element1: element1, element2: element2) } else { return nil } @@ -121,8 +121,8 @@ extension Product2: Collection where Base1: Collection { } @inlinable - public subscript(position: Index) -> (Base1.Element, Base2.Element) { - (base1[position.i1], base2[position.i2]) + public subscript(position: Index) -> (element1: Base1.Element, element2: Base2.Element) { + (element1: base1[position.i1], element2: base2[position.i2]) } /// Forms an index from a pair of base indices, normalizing From a38d07a49a84958fa2290459a82b2a8af7cfb721 Mon Sep 17 00:00:00 2001 From: Pranav Kasetti Date: Sun, 14 Mar 2021 18:14:56 +0000 Subject: [PATCH 05/19] Apply column 80 width consistently. --- Sources/Algorithms/Cycle.swift | 3 ++- Sources/Algorithms/Product.swift | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Sources/Algorithms/Cycle.swift b/Sources/Algorithms/Cycle.swift index ab39f2be..69e86272 100644 --- a/Sources/Algorithms/Cycle.swift +++ b/Sources/Algorithms/Cycle.swift @@ -57,7 +57,8 @@ extension Cycle: Sequence { extension Cycle: LazySequenceProtocol where Base: LazySequenceProtocol {} -/// A collection wrapper that repeats the elements of a base collection for a finite number of times. +/// A collection wrapper that repeats the elements of a base collection for a +/// finite number of times. public struct FiniteCycle { /// The collection to repeat. public let base: Base diff --git a/Sources/Algorithms/Product.swift b/Sources/Algorithms/Product.swift index cdd16f23..1e530e07 100644 --- a/Sources/Algorithms/Product.swift +++ b/Sources/Algorithms/Product.swift @@ -44,7 +44,8 @@ extension Product2: Sequence { } @inlinable - public mutating func next() -> (element1: Base1.Element, element2: Base2.Element)? { + public mutating func next() -> (element1: Base1.Element, + element2: Base2.Element)? { // This is the initial state, where i1.next() has never // been called, or the final state, where i1.next() has // already returned nil. @@ -121,7 +122,8 @@ extension Product2: Collection where Base1: Collection { } @inlinable - public subscript(position: Index) -> (element1: Base1.Element, element2: Base2.Element) { + public subscript(position: Index) -> (element1: Base1.Element, + element2: Base2.Element) { (element1: base1[position.i1], element2: base2[position.i2]) } From fc2cdc1167689c7b92ddc7162c3aa51eddf051df Mon Sep 17 00:00:00 2001 From: Pranav Kasetti Date: Sun, 14 Mar 2021 18:15:23 +0000 Subject: [PATCH 06/19] Make base and times internal to avoid future Equatable issues. --- Sources/Algorithms/Cycle.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/Algorithms/Cycle.swift b/Sources/Algorithms/Cycle.swift index 69e86272..6edb06ff 100644 --- a/Sources/Algorithms/Cycle.swift +++ b/Sources/Algorithms/Cycle.swift @@ -61,10 +61,10 @@ extension Cycle: LazySequenceProtocol where Base: LazySequenceProtocol {} /// finite number of times. public struct FiniteCycle { /// The collection to repeat. - public let base: Base + internal let base: Base /// The number of times to repeat the collection. - public let times: Int + internal let times: Int @usableFromInline internal init(base: Base, times: Int) { From 4c4e36f248bb0e80662a37e4e51770623b0cfdf2 Mon Sep 17 00:00:00 2001 From: Pranav Kasetti Date: Sat, 20 Mar 2021 17:38:45 +0000 Subject: [PATCH 07/19] Refactor times and base to single Product2 type, and refactor Iterator. --- Sources/Algorithms/Cycle.swift | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/Sources/Algorithms/Cycle.swift b/Sources/Algorithms/Cycle.swift index 6edb06ff..ffa2eb57 100644 --- a/Sources/Algorithms/Cycle.swift +++ b/Sources/Algorithms/Cycle.swift @@ -60,16 +60,12 @@ extension Cycle: LazySequenceProtocol where Base: LazySequenceProtocol {} /// A collection wrapper that repeats the elements of a base collection for a /// finite number of times. public struct FiniteCycle { - /// The collection to repeat. - internal let base: Base - - /// The number of times to repeat the collection. - internal let times: Int + /// A Product2 instance for iterating the Base collection. + internal let product: Product2, Base> @usableFromInline internal init(base: Base, times: Int) { - self.base = base - self.times = times + self.product = Product2(0.., Base>.Iterator @usableFromInline - internal init(_ product: Product2, Base>) { + internal init(product: Product2, Base>) { self.productIterator = product.makeIterator() } @@ -91,8 +87,7 @@ extension FiniteCycle: Sequence { } public func makeIterator() -> Iterator { - let iterations: Range = 0.. Date: Sat, 20 Mar 2021 17:46:16 +0000 Subject: [PATCH 08/19] Add Collection, BidrectionalCollection and RandomAccessCollection conformance to FiniteCycle. --- Sources/Algorithms/Cycle.swift | 47 ++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/Sources/Algorithms/Cycle.swift b/Sources/Algorithms/Cycle.swift index ffa2eb57..12915dd2 100644 --- a/Sources/Algorithms/Cycle.swift +++ b/Sources/Algorithms/Cycle.swift @@ -93,6 +93,53 @@ extension FiniteCycle: Sequence { extension FiniteCycle: LazySequenceProtocol where Base: LazySequenceProtocol {} +extension FiniteCycle: Collection { + + public typealias Index = Product2, Base>.Index + + public subscript(_ index: Index) -> Element { + product[index].element2 + } + + public var startIndex: Index { + product.startIndex + } + + public var endIndex: Index { + product.endIndex + } + + public func index(after i: Index) -> Index { + product.index(after: i) + } + + public func index(_ i: Index, offsetBy distance: Int) -> Index { + product.index(i, offsetBy: distance) + } + + public func index( + _ i: Index, + offsetBy distance: Int, + limitedBy limit: Index + ) -> Index? { + product.index(i, offsetBy: distance, limitedBy: limit) + } + + public func distance(from start: Index, to end: Index) -> Int { + product.distance(from: start, to: end) + } +} + +extension FiniteCycle: BidirectionalCollection + where Base: BidirectionalCollection { + public func index(before i: Index) -> Index { + product.index(before: i) + } +} + +extension FiniteCycle: RandomAccessCollection + where Base: RandomAccessCollection { } + //===----------------------------------------------------------------------===// // cycled() //===----------------------------------------------------------------------===// From c257737b2726823f281aa8dea78aa23131113166 Mon Sep 17 00:00:00 2001 From: Pranav Kasetti Date: Sat, 20 Mar 2021 17:58:31 +0000 Subject: [PATCH 09/19] Add tests for index methods on FiniteCycle. --- Tests/SwiftAlgorithmsTests/CycleTests.swift | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Tests/SwiftAlgorithmsTests/CycleTests.swift b/Tests/SwiftAlgorithmsTests/CycleTests.swift index 24b85e62..4dc4b9d7 100644 --- a/Tests/SwiftAlgorithmsTests/CycleTests.swift +++ b/Tests/SwiftAlgorithmsTests/CycleTests.swift @@ -60,4 +60,24 @@ final class CycleTests: XCTestCase { func testRepeatedLazy() { XCTAssertLazySequence((1...4).lazy.cycled(times: 3)) } + + func testRepeatedIndexMethods() { + let cycle = (1..<5).cycled(times: 2) + let startIndex = cycle.startIndex + var nextIndex = cycle.index(after: startIndex) + XCTAssertEqual(cycle.distance(from: startIndex, to: nextIndex), 1) + + nextIndex = cycle.index(nextIndex, offsetBy: 5) + XCTAssertEqual(cycle.distance(from: startIndex, to: nextIndex), 6) + + nextIndex = cycle.index(nextIndex, offsetBy: 2, limitedBy: cycle.endIndex)! + XCTAssertEqual(cycle.distance(from: startIndex, to: nextIndex), 8) + + let outOfBounds = cycle.index(nextIndex, offsetBy: 1, + limitedBy: cycle.endIndex) + XCTAssertNil(outOfBounds) + + let previousIndex = cycle.index(before: nextIndex) + XCTAssertEqual(cycle.distance(from: startIndex, to: previousIndex), 7) + } } From d876f8b1d27e4ec5622a5da649d5f6f94c8ab9d4 Mon Sep 17 00:00:00 2001 From: Pranav Kasetti Date: Sat, 20 Mar 2021 18:07:22 +0000 Subject: [PATCH 10/19] Add new count property to cycle with a test. --- Sources/Algorithms/Cycle.swift | 16 ++++++++++------ Tests/SwiftAlgorithmsTests/CycleTests.swift | 5 +++++ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/Sources/Algorithms/Cycle.swift b/Sources/Algorithms/Cycle.swift index 12915dd2..7fc1cd74 100644 --- a/Sources/Algorithms/Cycle.swift +++ b/Sources/Algorithms/Cycle.swift @@ -97,8 +97,8 @@ extension FiniteCycle: Collection { public typealias Index = Product2, Base>.Index - public subscript(_ index: Index) -> Element { - product[index].element2 + public var count: Int { + product.count } public var startIndex: Index { @@ -109,10 +109,18 @@ extension FiniteCycle: Collection { product.endIndex } + public subscript(_ index: Index) -> Element { + product[index].element2 + } + public func index(after i: Index) -> Index { product.index(after: i) } + public func distance(from start: Index, to end: Index) -> Int { + product.distance(from: start, to: end) + } + public func index(_ i: Index, offsetBy distance: Int) -> Index { product.index(i, offsetBy: distance) } @@ -124,10 +132,6 @@ extension FiniteCycle: Collection { ) -> Index? { product.index(i, offsetBy: distance, limitedBy: limit) } - - public func distance(from start: Index, to end: Index) -> Int { - product.distance(from: start, to: end) - } } extension FiniteCycle: BidirectionalCollection diff --git a/Tests/SwiftAlgorithmsTests/CycleTests.swift b/Tests/SwiftAlgorithmsTests/CycleTests.swift index 4dc4b9d7..6badaff8 100644 --- a/Tests/SwiftAlgorithmsTests/CycleTests.swift +++ b/Tests/SwiftAlgorithmsTests/CycleTests.swift @@ -80,4 +80,9 @@ final class CycleTests: XCTestCase { let previousIndex = cycle.index(before: nextIndex) XCTAssertEqual(cycle.distance(from: startIndex, to: previousIndex), 7) } + + func testRepeatedCount() { + let cycle = (1..<5).cycled(times: 2) + XCTAssertEqual(cycle.count, 8) + } } From 037b09a3432822f52c8221fe7a2e981e6f0860b4 Mon Sep 17 00:00:00 2001 From: Pranav Kasetti Date: Wed, 24 Mar 2021 11:18:40 +0000 Subject: [PATCH 11/19] Add @inlinable annotations. --- Sources/Algorithms/Cycle.swift | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Sources/Algorithms/Cycle.swift b/Sources/Algorithms/Cycle.swift index 7fc1cd74..737c3d5c 100644 --- a/Sources/Algorithms/Cycle.swift +++ b/Sources/Algorithms/Cycle.swift @@ -61,7 +61,7 @@ extension Cycle: LazySequenceProtocol where Base: LazySequenceProtocol {} /// finite number of times. public struct FiniteCycle { /// A Product2 instance for iterating the Base collection. - internal let product: Product2, Base> + public let product: Product2, Base> @usableFromInline internal init(base: Base, times: Int) { @@ -86,6 +86,7 @@ extension FiniteCycle: Sequence { } } + @inlinable public func makeIterator() -> Iterator { return Iterator(product: product) } @@ -97,34 +98,42 @@ extension FiniteCycle: Collection { public typealias Index = Product2, Base>.Index + @inlinable public var count: Int { product.count } + @inlinable public var startIndex: Index { product.startIndex } + @inlinable public var endIndex: Index { product.endIndex } + @inlinable public subscript(_ index: Index) -> Element { product[index].element2 } + @inlinable public func index(after i: Index) -> Index { product.index(after: i) } + @inlinable public func distance(from start: Index, to end: Index) -> Int { product.distance(from: start, to: end) } + @inlinable public func index(_ i: Index, offsetBy distance: Int) -> Index { product.index(i, offsetBy: distance) } + @inlinable public func index( _ i: Index, offsetBy distance: Int, @@ -136,6 +145,7 @@ extension FiniteCycle: Collection { extension FiniteCycle: BidirectionalCollection where Base: BidirectionalCollection { + @inlinable public func index(before i: Index) -> Index { product.index(before: i) } From 154f738e0cfe9419332ceaaa022c0260fbdb1a63 Mon Sep 17 00:00:00 2001 From: Pranav Kasetti Date: Wed, 24 Mar 2021 11:06:24 +0000 Subject: [PATCH 12/19] Remove Sequence conformance for FiniteCycle and add LazyCollectionProtocol conformance. --- Sources/Algorithms/Cycle.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Sources/Algorithms/Cycle.swift b/Sources/Algorithms/Cycle.swift index 737c3d5c..cc285876 100644 --- a/Sources/Algorithms/Cycle.swift +++ b/Sources/Algorithms/Cycle.swift @@ -69,7 +69,7 @@ public struct FiniteCycle { } } -extension FiniteCycle: Sequence { +extension FiniteCycle { /// The iterator for a `FiniteCycle` sequence. public struct Iterator : IteratorProtocol { @usableFromInline @@ -92,7 +92,8 @@ extension FiniteCycle: Sequence { } } -extension FiniteCycle: LazySequenceProtocol where Base: LazySequenceProtocol {} +extension FiniteCycle: LazySequenceProtocol, LazyCollectionProtocol + where Base: LazyCollectionProtocol { } extension FiniteCycle: Collection { From 952afb03ec25db2875d2ed10b29e2ebceee9fd0f Mon Sep 17 00:00:00 2001 From: Pranav Kasetti Date: Wed, 24 Mar 2021 11:16:58 +0000 Subject: [PATCH 13/19] Update Cycle.md. --- Guides/Cycle.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Guides/Cycle.md b/Guides/Cycle.md index 727c6034..fb0ad811 100644 --- a/Guides/Cycle.md +++ b/Guides/Cycle.md @@ -28,7 +28,7 @@ Two new methods are added to collections: extension Collection { func cycled() -> Cycle - func cycled(times: Int) -> FlattenSequence> + func cycled(times: Int) -> FiniteCycle } ``` @@ -36,8 +36,8 @@ The new `Cycle` type is a sequence only, given that the `Collection` protocol design makes infinitely large types impossible/impractical. `Cycle` also conforms to `LazySequenceProtocol` when the base type conforms. -Note that despite its name, the returned `FlattenSequence` will always have -`Collection` conformance, and will have `BidirectionalCollection` conformance +Note that the returned `FiniteCycle` will always have `Collection` +conformance, and will have `BidirectionalCollection` conformance when called on a bidirectional collection. ### Complexity From 62abcb4c0629fe54fd0c5217848c02159e08c4bf Mon Sep 17 00:00:00 2001 From: Pranav Kasetti Date: Wed, 24 Mar 2021 11:17:25 +0000 Subject: [PATCH 14/19] Add conditional conformances for Equatable and Hashable. --- Sources/Algorithms/Cycle.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Sources/Algorithms/Cycle.swift b/Sources/Algorithms/Cycle.swift index cc285876..26244baf 100644 --- a/Sources/Algorithms/Cycle.swift +++ b/Sources/Algorithms/Cycle.swift @@ -153,7 +153,10 @@ extension FiniteCycle: BidirectionalCollection } extension FiniteCycle: RandomAccessCollection - where Base: RandomAccessCollection { } + where Base: RandomAccessCollection {} + +extension FiniteCycle: Equatable where Base: Equatable {} +extension FiniteCycle: Hashable where Base: Hashable {} //===----------------------------------------------------------------------===// // cycled() From 60731934079e50444f36f03a4b030bafceec7235 Mon Sep 17 00:00:00 2001 From: Pranav Kasetti Date: Wed, 24 Mar 2021 11:32:37 +0000 Subject: [PATCH 15/19] Make more @inlinable. --- Sources/Algorithms/Cycle.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/Algorithms/Cycle.swift b/Sources/Algorithms/Cycle.swift index 26244baf..49195db2 100644 --- a/Sources/Algorithms/Cycle.swift +++ b/Sources/Algorithms/Cycle.swift @@ -63,7 +63,7 @@ public struct FiniteCycle { /// A Product2 instance for iterating the Base collection. public let product: Product2, Base> - @usableFromInline + @inlinable internal init(base: Base, times: Int) { self.product = Product2(0.., Base>.Iterator - @usableFromInline + @inlinable internal init(product: Product2, Base>) { self.productIterator = product.makeIterator() } From f65f13069a17c3693ef377dc6d7de73857d81cd8 Mon Sep 17 00:00:00 2001 From: Pranav Kasetti Date: Mon, 5 Apr 2021 15:26:18 +0100 Subject: [PATCH 16/19] Refactor typealias Index into struct, and remove Iterator. --- Sources/Algorithms/Cycle.swift | 72 ++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 33 deletions(-) diff --git a/Sources/Algorithms/Cycle.swift b/Sources/Algorithms/Cycle.swift index 49195db2..306ca4ab 100644 --- a/Sources/Algorithms/Cycle.swift +++ b/Sources/Algorithms/Cycle.swift @@ -69,69 +69,64 @@ public struct FiniteCycle { } } -extension FiniteCycle { - /// The iterator for a `FiniteCycle` sequence. - public struct Iterator : IteratorProtocol { +extension FiniteCycle: LazySequenceProtocol, LazyCollectionProtocol + where Base: LazyCollectionProtocol { } + +extension FiniteCycle: Collection { + + public typealias Element = Base.Element + + public struct Index: Comparable { + /// The index corresponding to the Product2 index at this position. @usableFromInline - var productIterator: Product2, Base>.Iterator + internal let productIndex: Product2, Base>.Index @inlinable - internal init(product: Product2, Base>) { - self.productIterator = product.makeIterator() + internal init(_ productIndex: Product2, Base>.Index) { + self.productIndex = productIndex } @inlinable - public mutating func next() -> Base.Element? { - self.productIterator.next()?.element2 + public static func == (lhs: Index, rhs: Index) -> Bool { + lhs.productIndex == rhs.productIndex } - } - - @inlinable - public func makeIterator() -> Iterator { - return Iterator(product: product) - } -} - -extension FiniteCycle: LazySequenceProtocol, LazyCollectionProtocol - where Base: LazyCollectionProtocol { } - -extension FiniteCycle: Collection { - - public typealias Index = Product2, Base>.Index - @inlinable - public var count: Int { - product.count + @inlinable + public static func < (lhs: Index, rhs: Index) -> Bool { + lhs.productIndex < rhs.productIndex + } } @inlinable public var startIndex: Index { - product.startIndex + Index(product.startIndex) } @inlinable public var endIndex: Index { - product.endIndex + Index(product.endIndex) } @inlinable public subscript(_ index: Index) -> Element { - product[index].element2 + product[index.productIndex].element2 } @inlinable public func index(after i: Index) -> Index { - product.index(after: i) + let productIndex = product.index(after: i.productIndex) + return Index(productIndex) } @inlinable public func distance(from start: Index, to end: Index) -> Int { - product.distance(from: start, to: end) + product.distance(from: start.productIndex, to: end.productIndex) } @inlinable public func index(_ i: Index, offsetBy distance: Int) -> Index { - product.index(i, offsetBy: distance) + let productIndex = product.index(i.productIndex, offsetBy: distance) + return Index(productIndex) } @inlinable @@ -140,7 +135,17 @@ extension FiniteCycle: Collection { offsetBy distance: Int, limitedBy limit: Index ) -> Index? { - product.index(i, offsetBy: distance, limitedBy: limit) + guard let productIndex = product.index(i.productIndex, + offsetBy: distance, + limitedBy: limit.productIndex) else { + return nil + } + return Index(productIndex) + } + + @inlinable + public var count: Int { + product.count } } @@ -148,7 +153,8 @@ extension FiniteCycle: BidirectionalCollection where Base: BidirectionalCollection { @inlinable public func index(before i: Index) -> Index { - product.index(before: i) + let productIndex = product.index(before: i.productIndex) + return Index(productIndex) } } From 5527e1df727d88d7f934dd86e1f2128afb7feb90 Mon Sep 17 00:00:00 2001 From: Pranav Kasetti Date: Mon, 5 Apr 2021 15:26:51 +0100 Subject: [PATCH 17/19] Make let product internal with @usableFromInline. --- Sources/Algorithms/Cycle.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sources/Algorithms/Cycle.swift b/Sources/Algorithms/Cycle.swift index 306ca4ab..275a6338 100644 --- a/Sources/Algorithms/Cycle.swift +++ b/Sources/Algorithms/Cycle.swift @@ -61,7 +61,8 @@ extension Cycle: LazySequenceProtocol where Base: LazySequenceProtocol {} /// finite number of times. public struct FiniteCycle { /// A Product2 instance for iterating the Base collection. - public let product: Product2, Base> + @usableFromInline + internal let product: Product2, Base> @inlinable internal init(base: Base, times: Int) { From 66b6affb35e11f67b0d5ada3fcf32f13f6ab0b70 Mon Sep 17 00:00:00 2001 From: Pranav Kasetti Date: Tue, 6 Apr 2021 22:59:18 +0100 Subject: [PATCH 18/19] Revert tuple labels. --- Sources/Algorithms/Cycle.swift | 2 +- Sources/Algorithms/Product.swift | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Sources/Algorithms/Cycle.swift b/Sources/Algorithms/Cycle.swift index 275a6338..c791f8a9 100644 --- a/Sources/Algorithms/Cycle.swift +++ b/Sources/Algorithms/Cycle.swift @@ -110,7 +110,7 @@ extension FiniteCycle: Collection { @inlinable public subscript(_ index: Index) -> Element { - product[index.productIndex].element2 + product[index.productIndex].1 } @inlinable diff --git a/Sources/Algorithms/Product.swift b/Sources/Algorithms/Product.swift index 1e530e07..82e52308 100644 --- a/Sources/Algorithms/Product.swift +++ b/Sources/Algorithms/Product.swift @@ -44,8 +44,8 @@ extension Product2: Sequence { } @inlinable - public mutating func next() -> (element1: Base1.Element, - element2: Base2.Element)? { + public mutating func next() -> (Base1.Element, + Base2.Element)? { // This is the initial state, where i1.next() has never // been called, or the final state, where i1.next() has // already returned nil. @@ -58,7 +58,7 @@ extension Product2: Sequence { // Get the next element from the second sequence, if not // at end. if let element2 = i2.next() { - return (element1: element1!, element2: element2) + return (element1!, element2) } // We've reached the end of the second sequence, so: @@ -71,7 +71,7 @@ extension Product2: Sequence { i2 = base2.makeIterator() if let element2 = i2.next() { - return (element1: element1, element2: element2) + return (element1, element2) } else { return nil } @@ -122,9 +122,9 @@ extension Product2: Collection where Base1: Collection { } @inlinable - public subscript(position: Index) -> (element1: Base1.Element, - element2: Base2.Element) { - (element1: base1[position.i1], element2: base2[position.i2]) + public subscript(position: Index) -> (Base1.Element, + Base2.Element) { + (base1[position.i1], base2[position.i2]) } /// Forms an index from a pair of base indices, normalizing From e583de90410852d8c199fcbc442ae9e248d69e38 Mon Sep 17 00:00:00 2001 From: Pranav Kasetti Date: Wed, 7 Apr 2021 11:45:47 +0100 Subject: [PATCH 19/19] Update documentation descriptions. --- Guides/Cycle.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Guides/Cycle.md b/Guides/Cycle.md index fb0ad811..b137b25d 100644 --- a/Guides/Cycle.md +++ b/Guides/Cycle.md @@ -16,8 +16,7 @@ for x in (1...3).cycled(times: 3) { // Prints 1 through 3 three times ``` -`cycled(times:)` combines two other existing standard library functions -(`repeatElement` and `joined`) to provide a more expressive way of repeating a +`cycled(times:)` provides a more expressive way of repeating a collection's elements a limited number of times. ## Detailed Design @@ -38,7 +37,8 @@ conforms to `LazySequenceProtocol` when the base type conforms. Note that the returned `FiniteCycle` will always have `Collection` conformance, and will have `BidirectionalCollection` conformance -when called on a bidirectional collection. +when called on a bidirectional collection. `FiniteCycle` also +conforms to `LazyCollectionProtocol` when the base type conforms. ### Complexity