diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index c0e95b81..c18bed92 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -5,9 +5,23 @@ on: types: [opened, reopened, synchronize] jobs: + validate_format_config: + name: Validate Format Config + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + - name: Install apt dependencies + run: sudo apt-get -qq update && sudo apt-get -qq -y install curl + - name: Compare against swift-mmio swift-format config + run: | + curl -sL https://raw.githubusercontent.com/apple/swift-mmio/refs/heads/main/.swift-format -o .swift-format-mmio + diff .swift-format .swift-format-mmio + tests: name: Test uses: swiftlang/github-workflows/.github/workflows/swift_package_test.yml@main + soundness: name: Soundness uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main @@ -16,5 +30,3 @@ jobs: license_header_check_enabled: false # https://github.com/apple/swift-algorithms/issues/251 docs_check_enabled: false - # https://github.com/apple/swift-algorithms/issues/252 - format_check_enabled: false diff --git a/.swift-format b/.swift-format new file mode 100644 index 00000000..1efd0615 --- /dev/null +++ b/.swift-format @@ -0,0 +1,71 @@ +{ + "fileScopedDeclarationPrivacy" : { + "accessLevel" : "private" + }, + "indentation" : { + "spaces" : 2 + }, + "indentConditionalCompilationBlocks" : false, + "indentSwitchCaseLabels" : false, + "lineBreakAroundMultilineExpressionChainComponents" : false, + "lineBreakBeforeControlFlowKeywords" : false, + "lineBreakBeforeEachArgument" : false, + "lineBreakBeforeEachGenericRequirement" : false, + "lineLength" : 80, + "maximumBlankLines" : 1, + "multiElementCollectionTrailingCommas" : true, + "noAssignmentInExpressions" : { + "allowedFunctions" : [ + "XCTAssertNoThrow" + ] + }, + "prioritizeKeepingFunctionOutputTogether" : false, + "respectsExistingLineBreaks" : true, + "rules" : { + "AllPublicDeclarationsHaveDocumentation" : false, + "AlwaysUseLiteralForEmptyCollectionInit" : true, + "AlwaysUseLowerCamelCase" : true, + "AmbiguousTrailingClosureOverload" : false, + "BeginDocumentationCommentWithOneLineSummary" : true, + "DoNotUseSemicolons" : true, + "DontRepeatTypeInStaticProperties" : true, + "FileScopedDeclarationPrivacy" : true, + "FullyIndirectEnum" : true, + "GroupNumericLiterals" : true, + "IdentifiersMustBeASCII" : true, + "NeverForceUnwrap" : true, + "NeverUseForceTry" : true, + "NeverUseImplicitlyUnwrappedOptionals" : true, + "NoAccessLevelOnExtensionDeclaration" : true, + "NoAssignmentInExpressions" : true, + "NoBlockComments" : true, + "NoCasesWithOnlyFallthrough" : true, + "NoEmptyTrailingClosureParentheses" : true, + "NoLabelsInCasePatterns" : true, + "NoLeadingUnderscores" : false, + "NoParensAroundConditions" : true, + "NoPlaygroundLiterals" : true, + "NoVoidReturnOnFunctionSignature" : true, + "OmitExplicitReturns" : true, + "OneCasePerLine" : true, + "OneVariableDeclarationPerLine" : true, + "OnlyOneTrailingClosureArgument" : true, + "OrderedImports" : true, + "ReplaceForEachWithForLoop" : true, + "ReturnVoidInsteadOfEmptyTuple" : true, + "TypeNamesShouldBeCapitalized" : true, + "UseEarlyExits" : false, + "UseExplicitNilCheckInConditions" : true, + "UseLetInEveryBoundCaseVariable" : true, + "UseShorthandTypeNames" : true, + "UseSingleLinePropertyGetter" : true, + "UseSynthesizedInitializer" : true, + "UseTripleSlashForDocumentationComments" : true, + "UseWhereClausesInForLoops" : false, + "ValidateDocumentationComments" : true + }, + "spacesBeforeEndOfLineComments": 2, + "spacesAroundRangeFormationOperators" : false, + "tabWidth" : 2, + "version" : 1 +} diff --git a/Package.swift b/Package.swift index 8dcb18cc..68a4c3df 100644 --- a/Package.swift +++ b/Package.swift @@ -13,23 +13,23 @@ import PackageDescription let package = Package( - name: "swift-algorithms", - products: [ - .library( - name: "Algorithms", - targets: ["Algorithms"]), - ], - dependencies: [ - .package(url: "https://github.com/apple/swift-numerics.git", from: "1.0.0"), - ], - targets: [ - .target( - name: "Algorithms", - dependencies: [ - .product(name: "RealModule", package: "swift-numerics"), - ]), - .testTarget( - name: "SwiftAlgorithmsTests", - dependencies: ["Algorithms"]), - ] + name: "swift-algorithms", + products: [ + .library( + name: "Algorithms", + targets: ["Algorithms"]) + ], + dependencies: [ + .package(url: "https://github.com/apple/swift-numerics.git", from: "1.0.0") + ], + targets: [ + .target( + name: "Algorithms", + dependencies: [ + .product(name: "RealModule", package: "swift-numerics") + ]), + .testTarget( + name: "SwiftAlgorithmsTests", + dependencies: ["Algorithms"]), + ] ) diff --git a/Sources/Algorithms/AdjacentPairs.swift b/Sources/Algorithms/AdjacentPairs.swift index dd8473dc..5b81f8cf 100644 --- a/Sources/Algorithms/AdjacentPairs.swift +++ b/Sources/Algorithms/AdjacentPairs.swift @@ -126,7 +126,7 @@ extension AdjacentPairsSequence: Sequence { } extension AdjacentPairsSequence: LazySequenceProtocol - where Base: LazySequenceProtocol {} +where Base: LazySequenceProtocol {} /// A collection of adjacent pairs of elements built from an underlying /// collection. @@ -143,7 +143,8 @@ public struct AdjacentPairsCollection { @inlinable internal init(base: Base) { self.base = base - self.secondBaseIndex = base.isEmpty + self.secondBaseIndex = + base.isEmpty ? base.endIndex : base.index(after: base.startIndex) } @@ -154,7 +155,7 @@ extension AdjacentPairsCollection { public struct Index: Comparable { @usableFromInline internal var first: Base.Index - + @usableFromInline internal var second: Base.Index @@ -168,7 +169,7 @@ extension AdjacentPairsCollection { public static func == (lhs: Index, rhs: Index) -> Bool { lhs.first == rhs.first } - + @inlinable public static func < (lhs: Index, rhs: Index) -> Bool { lhs.first < rhs.first @@ -183,7 +184,7 @@ extension AdjacentPairsCollection: Collection { first: secondBaseIndex == base.endIndex ? base.endIndex : base.startIndex, second: secondBaseIndex) } - + @inlinable public var endIndex: Index { Index(first: base.endIndex, second: base.endIndex) @@ -207,20 +208,23 @@ extension AdjacentPairsCollection: Collection { public func index(_ i: Index, offsetBy distance: Int) -> Index { guard distance != 0 else { return i } - guard let result = distance > 0 - ? offsetForward(i, by: distance, limitedBy: endIndex) - : offsetBackward(i, by: -distance, limitedBy: startIndex) + guard + let result = distance > 0 + ? offsetForward(i, by: distance, limitedBy: endIndex) + : offsetBackward(i, by: -distance, limitedBy: startIndex) else { fatalError("Index out of bounds") } return result } @inlinable public func index( - _ i: Index, offsetBy distance: Int, limitedBy limit: Index + _ i: Index, + offsetBy distance: Int, + limitedBy limit: Index ) -> Index? { guard distance != 0 else { return i } guard limit != i else { return nil } - + if distance > 0 { let limit = limit > i ? limit : endIndex return offsetForward(i, by: distance, limitedBy: limit) @@ -229,38 +233,48 @@ extension AdjacentPairsCollection: Collection { return offsetBackward(i, by: -distance, limitedBy: limit) } } - + @inlinable internal func offsetForward( - _ i: Index, by distance: Int, limitedBy limit: Index + _ i: Index, + by distance: Int, + limitedBy limit: Index ) -> Index? { assert(distance > 0) assert(limit > i) - - guard let newFirst = base.index(i.second, offsetBy: distance - 1, limitedBy: limit.first), - newFirst != base.endIndex + + let newFirst = base.index( + i.second, + offsetBy: distance - 1, + limitedBy: limit.first) + guard let newFirst, newFirst != base.endIndex else { return nil } + let newSecond = base.index(after: newFirst) - + precondition(newSecond <= base.endIndex, "Can't advance beyond endIndex") return newSecond == base.endIndex ? endIndex : Index(first: newFirst, second: newSecond) } - + @inlinable internal func offsetBackward( - _ i: Index, by distance: Int, limitedBy limit: Index + _ i: Index, + by distance: Int, + limitedBy limit: Index ) -> Index? { assert(distance > 0) assert(limit < i) - + let offset = i == endIndex ? 0 : 1 - guard let newSecond = base.index( + let newSecond = base.index( i.first, offsetBy: -(distance - offset), limitedBy: limit.second) + guard let newSecond else { return nil } + let newFirst = base.index(newSecond, offsetBy: -1) precondition(newFirst >= base.startIndex, "Can't move before startIndex") return Index(first: newFirst, second: newSecond) @@ -282,12 +296,12 @@ extension AdjacentPairsCollection: Collection { } extension AdjacentPairsCollection: BidirectionalCollection - where Base: BidirectionalCollection -{ +where Base: BidirectionalCollection { @inlinable public func index(before i: Index) -> Index { precondition(i != startIndex, "Can't offset before startIndex") - let second = i == endIndex + let second = + i == endIndex ? base.index(before: base.endIndex) : i.first let first = base.index(before: second) @@ -296,10 +310,10 @@ extension AdjacentPairsCollection: BidirectionalCollection } extension AdjacentPairsCollection: RandomAccessCollection - where Base: RandomAccessCollection {} +where Base: RandomAccessCollection {} extension AdjacentPairsCollection: LazySequenceProtocol, LazyCollectionProtocol - where Base: LazySequenceProtocol {} +where Base: LazySequenceProtocol {} extension AdjacentPairsCollection.Index: Hashable where Base.Index: Hashable { @inlinable diff --git a/Sources/Algorithms/Chain.swift b/Sources/Algorithms/Chain.swift index 572050ec..2cda16f1 100644 --- a/Sources/Algorithms/Chain.swift +++ b/Sources/Algorithms/Chain.swift @@ -11,12 +11,11 @@ /// A concatenation of two sequences with the same element type. public struct Chain2Sequence - where Base1.Element == Base2.Element -{ +where Base1.Element == Base2.Element { /// The first sequence in this chain. @usableFromInline internal let base1: Base1 - + /// The second sequence in this chain. @usableFromInline internal let base2: Base2 @@ -33,29 +32,30 @@ extension Chain2Sequence: Sequence { public struct Iterator: IteratorProtocol { @usableFromInline internal var iterator1: Base1.Iterator - + @usableFromInline internal var iterator2: Base2.Iterator - + @inlinable internal init(_ concatenation: Chain2Sequence) { iterator1 = concatenation.base1.makeIterator() iterator2 = concatenation.base2.makeIterator() } - + @inlinable public mutating func next() -> Base1.Element? { - return iterator1.next() ?? iterator2.next() + iterator1.next() ?? iterator2.next() } } - + @inlinable public func makeIterator() -> Iterator { Iterator(self) } } -extension Chain2Sequence: Collection where Base1: Collection, Base2: Collection { +extension Chain2Sequence: Collection +where Base1: Collection, Base2: Collection { /// A position in a `Chain2Sequence` instance. public struct Index: Comparable { // The internal index representation, which can either be an index of the @@ -91,14 +91,14 @@ extension Chain2Sequence: Collection where Base1: Collection, Base2: Collection return true case (.second, .first): return false - case let (.first(l), .first(r)): + case (.first(let l), .first(let r)): return l < r - case let (.second(l), .second(r)): + case (.second(let l), .second(let r)): return l < r } } } - + /// Converts an index of `Base1` to the corresponding `Index` by mapping /// `base1.endIndex` to `base2.startIndex`. @inlinable @@ -121,9 +121,9 @@ extension Chain2Sequence: Collection where Base1: Collection, Base2: Collection @inlinable public subscript(i: Index) -> Base1.Element { switch i.position { - case let .first(i): + case .first(let i): return base1[i] - case let .second(i): + case .second(let i): return base2[i] } } @@ -131,23 +131,23 @@ extension Chain2Sequence: Collection where Base1: Collection, Base2: Collection @inlinable public func index(after i: Index) -> Index { switch i.position { - case let .first(i): + case .first(let i): assert(i != base1.endIndex) return normalizeIndex(base1.index(after: i)) - case let .second(i): + case .second(let i): return Index(second: base2.index(after: i)) } } - + @inlinable public func index(_ i: Index, offsetBy distance: Int) -> Index { guard distance != 0 else { return i } - + return distance > 0 ? offsetForward(i, by: distance) : offsetBackward(i, by: -distance) } - + @inlinable public func index( _ i: Index, @@ -168,45 +168,50 @@ extension Chain2Sequence: Collection where Base1: Collection, Base2: Collection @inlinable internal func offsetForward(_ i: Index, by distance: Int) -> Index { guard let index = offsetForward(i, by: distance, limitedBy: endIndex) - else { fatalError("Index is out of bounds") } + else { fatalError("Index is out of bounds") } return index } - + @inlinable internal func offsetBackward(_ i: Index, by distance: Int) -> Index { guard let index = offsetBackward(i, by: distance, limitedBy: startIndex) - else { fatalError("Index is out of bounds") } + else { fatalError("Index is out of bounds") } return index } @inlinable internal func offsetForward( - _ i: Index, by distance: Int, limitedBy limit: Index + _ i: Index, + by distance: Int, + limitedBy limit: Index ) -> Index? { assert(distance >= 0) assert(limit >= i) - + switch (i.position, limit.position) { - case let (.first(i), .first(limit)): + case (.first(let i), .first(let limit)): return base1.index(i, offsetBy: distance, limitedBy: limit) .map(Index.init(first:)) - - case let (.first(i), .second(limit)): - if let j = base1.index(i, offsetBy: distance, limitedBy: base1.endIndex) { - // the offset stays within the bounds of `base1` - return normalizeIndex(j) - } else { + + case (.first(let i), .second(let limit)): + guard + let j = base1.index(i, offsetBy: distance, limitedBy: base1.endIndex) + else { // the offset overflows the bounds of `base1` by `n - d` let d = base1.distance(from: i, to: base1.endIndex) - return base2.index(base2.startIndex, offsetBy: distance - d, limitedBy: limit) + return + base2 + .index(base2.startIndex, offsetBy: distance - d, limitedBy: limit) .map(Index.init(second:)) } - + // the offset stays within the bounds of `base1` + return normalizeIndex(j) + case (.second, .first): // impossible because `limit >= i` fatalError() - - case let (.second(i), .second(limit)): + + case (.second(let i), .second(let limit)): return base2.index(i, offsetBy: distance, limitedBy: limit) .map(Index.init(second:)) } @@ -214,49 +219,54 @@ extension Chain2Sequence: Collection where Base1: Collection, Base2: Collection @inlinable internal func offsetBackward( - _ i: Index, by distance: Int, limitedBy limit: Index + _ i: Index, + by distance: Int, + limitedBy limit: Index ) -> Index? { assert(distance >= 0) assert(limit <= i) - + switch (i.position, limit.position) { - case let (.first(i), .first(limit)): + case (.first(let i), .first(let limit)): return base1.index(i, offsetBy: -distance, limitedBy: limit) .map(Index.init(first:)) - + case (.first, .second): // impossible because `limit <= i` fatalError() - - case let (.second(i), .first(limit)): - if let j = base2.index(i, offsetBy: -distance, limitedBy: base2.startIndex) { - // the offset stays within the bounds of `base2` - return Index(second: j) - } else { + + case (.second(let i), .first(let limit)): + guard + let j = base2.index(i, offsetBy: -distance, limitedBy: base2.startIndex) + else { // the offset overflows the bounds of `base2` by `n - d` let d = base2.distance(from: base2.startIndex, to: i) - return base1.index(base1.endIndex, offsetBy: -(distance - d), limitedBy: limit) + return + base1 + .index(base1.endIndex, offsetBy: -(distance - d), limitedBy: limit) .map(Index.init(first:)) } + // the offset stays within the bounds of `base2` + return Index(second: j) - case let (.second(i), .second(limit)): + case (.second(let i), .second(let limit)): // `limit` is relevant, so `base1` cannot be reached return base2.index(i, offsetBy: -distance, limitedBy: limit) .map(Index.init(second:)) } } - + @inlinable public func distance(from start: Index, to end: Index) -> Int { switch (start.position, end.position) { - case let (.first(i), .first(j)): + case (.first(let i), .first(let j)): return base1.distance(from: i, to: j) - case let (.second(i), .second(j)): + case (.second(let i), .second(let j)): return base2.distance(from: i, to: j) - case let (.first(i), .second(j)): + case (.first(let i), .second(let j)): return base1.distance(from: i, to: base1.endIndex) + base2.distance(from: base2.startIndex, to: j) - case let (.second(i), .first(j)): + case (.second(let i), .first(let j)): return base2.distance(from: i, to: base2.startIndex) + base1.distance(from: base1.endIndex, to: j) } @@ -264,15 +274,14 @@ extension Chain2Sequence: Collection where Base1: Collection, Base2: Collection } extension Chain2Sequence: BidirectionalCollection - where Base1: BidirectionalCollection, Base2: BidirectionalCollection -{ +where Base1: BidirectionalCollection, Base2: BidirectionalCollection { @inlinable public func index(before i: Index) -> Index { assert(i != startIndex, "Can't advance before startIndex") switch i.position { - case let .first(i): + case .first(let i): return Index(first: base1.index(before: i)) - case let .second(i): + case .second(let i): return i == base2.startIndex ? Index(first: base1.index(before: base1.endIndex)) : Index(second: base2.index(before: i)) @@ -281,7 +290,7 @@ extension Chain2Sequence: BidirectionalCollection } extension Chain2Sequence: RandomAccessCollection - where Base1: RandomAccessCollection, Base2: RandomAccessCollection {} +where Base1: RandomAccessCollection, Base2: RandomAccessCollection {} //===----------------------------------------------------------------------===// // chain(_:_:) diff --git a/Sources/Algorithms/Chunked.swift b/Sources/Algorithms/Chunked.swift index 74f774ba..42e7d6f0 100644 --- a/Sources/Algorithms/Chunked.swift +++ b/Sources/Algorithms/Chunked.swift @@ -17,19 +17,19 @@ public struct ChunkedByCollection { /// The collection that this instance provides a view onto. @usableFromInline internal let base: Base - + /// The projection function. @usableFromInline internal let projection: (Base.Element) -> Subject - + /// The predicate. @usableFromInline internal let belongInSameGroup: (Subject, Subject) -> Bool - + /// The end index of the first chunk. @usableFromInline internal var endOfFirstChunk: Base.Index - + @inlinable internal init( base: Base, @@ -40,7 +40,7 @@ public struct ChunkedByCollection { self.projection = projection self.belongInSameGroup = belongInSameGroup self.endOfFirstChunk = base.startIndex - + if !base.isEmpty { endOfFirstChunk = endOfChunk(startingAt: base.startIndex) } @@ -53,19 +53,19 @@ extension ChunkedByCollection: Collection { /// The range corresponding to the chunk at this position. @usableFromInline internal var baseRange: Range - + @inlinable internal init(_ baseRange: Range) { self.baseRange = baseRange } - + @inlinable public static func == (lhs: Index, rhs: Index) -> Bool { // Since each index represents the range of a disparate chunk, no two // unique indices will have the same lower bound. lhs.baseRange.lowerBound == rhs.baseRange.lowerBound } - + @inlinable public static func < (lhs: Index, rhs: Index) -> Bool { // Only use the lower bound to test for ordering, as above. @@ -78,24 +78,24 @@ extension ChunkedByCollection: Collection { @inlinable internal func endOfChunk(startingAt start: Base.Index) -> Base.Index { var subject = projection(base[start]) - + return base[base.index(after: start)...].endOfPrefix(while: { element in let nextSubject = projection(element) defer { subject = nextSubject } return belongInSameGroup(subject, nextSubject) }) } - + @inlinable public var startIndex: Index { Index(base.startIndex.. Index { precondition(i != endIndex, "Can't advance past endIndex") @@ -104,7 +104,7 @@ extension ChunkedByCollection: Collection { let end = endOfChunk(startingAt: upperBound) return Index(upperBound.. Base.SubSequence { precondition(position != endIndex, "Can't subscript using endIndex") @@ -115,15 +115,14 @@ extension ChunkedByCollection: Collection { extension ChunkedByCollection.Index: Hashable where Base.Index: Hashable {} extension ChunkedByCollection: BidirectionalCollection - where Base: BidirectionalCollection -{ +where Base: BidirectionalCollection { /// Returns the index in the base collection of the start of the chunk ending /// at the given index. @inlinable internal func startOfChunk(endingAt end: Base.Index) -> Base.Index { let indexBeforeEnd = base.index(before: end) var subject = projection(base[indexBeforeEnd]) - + return base[.. { @usableFromInline internal var chunked: ChunkedByCollection - + @inlinable internal init( base: Base, @@ -163,24 +162,26 @@ public struct ChunkedOnCollection { extension ChunkedOnCollection: Collection { public typealias Index = ChunkedByCollection.Index - + @inlinable public var startIndex: Index { chunked.startIndex } - + @inlinable public var endIndex: Index { chunked.endIndex } - + @inlinable public subscript(position: Index) -> (Subject, Base.SubSequence) { let subsequence = chunked[position] + // swift-format-ignore: NeverForceUnwrap + // Chunks are never empty, so `.first!` is safe. let subject = chunked.projection(subsequence.first!) return (subject, subsequence) } - + @inlinable public func index(after i: Index) -> Index { chunked.index(after: i) @@ -188,8 +189,7 @@ extension ChunkedOnCollection: Collection { } extension ChunkedOnCollection: BidirectionalCollection - where Base: BidirectionalCollection -{ +where Base: BidirectionalCollection { @inlinable public func index(before i: Index) -> Index { chunked.index(before: i) @@ -204,26 +204,26 @@ public struct EvenlyChunkedCollection { /// The base collection. @usableFromInline internal let base: Base - + /// The number of equal chunks the base collection is divided into. @usableFromInline internal let numberOfChunks: Int - + /// The count of the base collection. @usableFromInline internal let baseCount: Int - + /// The upper bound of the first chunk. @usableFromInline internal var firstUpperBound: Base.Index - + @inlinable internal init(base: Base, numberOfChunks: Int) { self.base = base self.numberOfChunks = numberOfChunks self.baseCount = base.count self.firstUpperBound = base.startIndex - + if numberOfChunks > 0 { firstUpperBound = endOfChunk(startingAt: base.startIndex, offset: 0) } @@ -237,37 +237,42 @@ extension EvenlyChunkedCollection { internal var numberOfLargeChunks: Int { baseCount % numberOfChunks } - + /// Returns the size of a chunk at a given offset. @inlinable internal func sizeOfChunk(offset: Int) -> Int { let isLargeChunk = offset < numberOfLargeChunks return baseCount / numberOfChunks + (isLargeChunk ? 1 : 0) } - + /// Returns the index in the base collection of the end of the chunk starting /// at the given index. @inlinable - internal func endOfChunk(startingAt start: Base.Index, offset: Int) -> Base.Index { + internal func endOfChunk(startingAt start: Base.Index, offset: Int) + -> Base.Index + { base.index(start, offsetBy: sizeOfChunk(offset: offset)) } - + /// Returns the index in the base collection of the start of the chunk ending /// at the given index. @inlinable - internal func startOfChunk(endingAt end: Base.Index, offset: Int) -> Base.Index { + internal func startOfChunk(endingAt end: Base.Index, offset: Int) + -> Base.Index + { base.index(end, offsetBy: -sizeOfChunk(offset: offset)) } - + /// Returns the index that corresponds to the chunk that starts at the given /// base index. @inlinable - internal func indexOfChunk(startingAt start: Base.Index, offset: Int) -> Index { + internal func indexOfChunk(startingAt start: Base.Index, offset: Int) -> Index + { guard offset != numberOfChunks else { return endIndex } let end = endOfChunk(startingAt: start, offset: offset) return Index(start.. - + /// The offset corresponding to the chunk at this position. The first chunk /// has offset `0` and all other chunks have an offset `1` greater than the /// previous. @usableFromInline internal var offset: Int - + @inlinable internal init(_ baseRange: Range, offset: Int) { self.baseRange = baseRange self.offset = offset } - + @inlinable public static func == (lhs: Self, rhs: Self) -> Bool { lhs.offset == rhs.offset } - + @inlinable public static func < (lhs: Self, rhs: Self) -> Bool { lhs.offset < rhs.offset } } - + public typealias Element = Base.SubSequence @inlinable public var startIndex: Index { Index(base.startIndex.. Index { precondition(i != endIndex, "Can't advance past endIndex") let start = i.baseRange.upperBound return indexOfChunk(startingAt: start, offset: i.offset + 1) } - + @inlinable public subscript(position: Index) -> Element { precondition(position != endIndex) return base[position.baseRange] } - + @inlinable public func index(_ i: Index, offsetBy distance: Int) -> Index { /// Returns the base distance between two `EvenChunksCollection` indices @@ -338,14 +343,14 @@ extension EvenlyChunkedCollection: Collection { func baseDistance(from offsetA: Int, to offsetB: Int) -> Int { let smallChunkSize = baseCount / numberOfChunks let numberOfChunks = (offsetB - offsetA) - 1 - + let largeChunksEnd = Swift.min(self.numberOfLargeChunks, offsetB) let largeChunksStart = Swift.min(self.numberOfLargeChunks, offsetA + 1) let numberOfLargeChunks = largeChunksEnd - largeChunksStart - + return smallChunkSize * numberOfChunks + numberOfLargeChunks } - + if distance == 0 { return i } else if distance > 0 { @@ -360,9 +365,13 @@ extension EvenlyChunkedCollection: Collection { return indexOfChunk(endingAt: end, offset: offset) } } - + @inlinable - public func index(_ i: Index, offsetBy distance: Int, limitedBy limit: Index) -> Index? { + public func index( + _ i: Index, + offsetBy distance: Int, + limitedBy limit: Index + ) -> Index? { if distance >= 0 { if (0.. Int { end.offset - start.offset @@ -384,8 +393,7 @@ extension EvenlyChunkedCollection: Collection { extension EvenlyChunkedCollection.Index: Hashable where Base.Index: Hashable {} extension EvenlyChunkedCollection: BidirectionalCollection - where Base: BidirectionalCollection -{ +where Base: BidirectionalCollection { @inlinable public func index(before i: Index) -> Index { precondition(i != startIndex, "Can't advance before startIndex") @@ -394,13 +402,13 @@ extension EvenlyChunkedCollection: BidirectionalCollection } extension EvenlyChunkedCollection: RandomAccessCollection - where Base: RandomAccessCollection {} +where Base: RandomAccessCollection {} extension EvenlyChunkedCollection: LazySequenceProtocol - where Base: LazySequenceProtocol {} +where Base: LazySequenceProtocol {} extension EvenlyChunkedCollection: LazyCollectionProtocol - where Base: LazyCollectionProtocol {} +where Base: LazyCollectionProtocol {} //===----------------------------------------------------------------------===// // lazy.chunked(by:) / lazy.chunked(on:) @@ -411,7 +419,10 @@ extension LazySequenceProtocol where Self: Collection, Elements: Collection { /// the given predicate. /// /// - Parameter belongInSameGroup: A closure that takes two adjacent elements - /// of the sequence and returns whether or not they belong in the same group. + /// of the sequence and returns whether or not they belong in the same + /// group. + /// - Returns: A collection of subsequences, with every pairwise group of + /// elements in each chunk returning `true` for `belongInSameGroup`. /// /// - Complexity: O(*n*), because the start index is pre-computed. @inlinable @@ -423,13 +434,15 @@ extension LazySequenceProtocol where Self: Collection, Elements: Collection { projection: { $0 }, belongInSameGroup: belongInSameGroup) } - + /// Returns a lazy collection of subsequences of this collection, chunked by /// grouping elements that project to equal values. /// /// - Parameter projection: A closure that takes an element in the sequence - /// and returns an `Equatable` value that can be used to determine if adjacent - /// elements belong in the same group. + /// and returns an `Equatable` value that can be used to determine if + /// adjacent elements belong in the same group. + /// - Returns: A collection of subsequences, with every element in each chunk + /// returning the same value for `projection`. /// /// - Complexity: O(*n*), because the start index is pre-computed. @inlinable @@ -438,7 +451,8 @@ extension LazySequenceProtocol where Self: Collection, Elements: Collection { ) -> ChunkedOnCollection { ChunkedOnCollection( base: elements, - projection: projection) + projection: projection + ) } } @@ -451,8 +465,10 @@ extension Collection { /// given predicate. /// /// - Parameter belongInSameGroup: A closure that takes two adjacent elements - /// of the collection and returns whether or not they belong in the same - /// group. + /// of the collection and returns whether or not they belong in the same + /// group. + /// - Returns: A collection of subsequences, with every pairwise group of + /// elements in each chunk returning `true` for `belongInSameGroup`. /// /// - Complexity: O(*n*), where *n* is the length of this collection. @inlinable @@ -461,10 +477,10 @@ extension Collection { ) rethrows -> [SubSequence] { guard !isEmpty else { return [] } var result: [SubSequence] = [] - + var start = startIndex var current = self[start] - + for (index, element) in indexed().dropFirst() { if try !belongInSameGroup(current, element) { result.append(self[start.. [(Subject, SubSequence)] { guard !isEmpty else { return [] } var result: [(Subject, SubSequence)] = [] - + var start = startIndex var subject = try projection(self[start]) - + for (index, element) in indexed().dropFirst() { let nextSubject = try projection(element) if subject != nextSubject { @@ -506,11 +524,11 @@ extension Collection { subject = nextSubject } } - + if start != endIndex { result.append((subject, self[start...])) } - + return result } } @@ -532,13 +550,13 @@ extension Collection { /// `LazyMapCollection` public struct ChunksOfCountCollection { public typealias Element = Base.SubSequence - + @usableFromInline internal let base: Base - + @usableFromInline internal let chunkCount: Int - + @usableFromInline internal var endOfFirstChunk: Base.Index @@ -550,13 +568,15 @@ public struct ChunksOfCountCollection { internal init(_base: Base, _chunkCount: Int) { self.base = _base self.chunkCount = _chunkCount - + // Compute the start index upfront in order to make start index a O(1) // lookup. - self.endOfFirstChunk = _base.index( - _base.startIndex, offsetBy: _chunkCount, - limitedBy: _base.endIndex - ) ?? _base.endIndex + self.endOfFirstChunk = + _base.index( + _base.startIndex, + offsetBy: _chunkCount, + limitedBy: _base.endIndex + ) ?? _base.endIndex } } @@ -564,7 +584,7 @@ extension ChunksOfCountCollection: Collection { public struct Index { @usableFromInline internal let baseRange: Range - + @inlinable internal init(_baseRange: Range) { self.baseRange = _baseRange @@ -576,40 +596,46 @@ extension ChunksOfCountCollection: Collection { public var startIndex: Index { Index(_baseRange: base.startIndex.. Element { precondition(i != endIndex, "Index out of range") return base[i.baseRange] } - + @inlinable public func index(after i: Index) -> Index { precondition(i != endIndex, "Advancing past end index") - let baseIdx = base.index( - i.baseRange.upperBound, offsetBy: chunkCount, - limitedBy: base.endIndex - ) ?? base.endIndex + let baseIdx = + base.index( + i.baseRange.upperBound, + offsetBy: chunkCount, + limitedBy: base.endIndex + ) ?? base.endIndex return Index(_baseRange: i.baseRange.upperBound.. Bool { + public static func == ( + lhs: ChunksOfCountCollection.Index, + rhs: ChunksOfCountCollection.Index + ) -> Bool { lhs.baseRange.lowerBound == rhs.baseRange.lowerBound } - + @inlinable - public static func < (lhs: ChunksOfCountCollection.Index, - rhs: ChunksOfCountCollection.Index) -> Bool { + public static func < ( + lhs: ChunksOfCountCollection.Index, + rhs: ChunksOfCountCollection.Index + ) -> Bool { lhs.baseRange.lowerBound < rhs.baseRange.lowerBound } } @@ -620,7 +646,7 @@ where Base: RandomAccessCollection { @inlinable public func index(before i: Index) -> Index { precondition(i != startIndex, "Advancing past start index") - + var offset = chunkCount if i.baseRange.lowerBound == base.endIndex { let remainder = base.count % chunkCount @@ -628,11 +654,13 @@ where Base: RandomAccessCollection { offset = remainder } } - - let baseIdx = base.index( - i.baseRange.lowerBound, offsetBy: -offset, - limitedBy: base.startIndex - ) ?? base.startIndex + + let baseIdx = + base.index( + i.baseRange.lowerBound, + offsetBy: -offset, + limitedBy: base.startIndex + ) ?? base.startIndex return Index(_baseRange: baseIdx.. Int { let distance = - base.distance(from: start.baseRange.lowerBound, - to: end.baseRange.lowerBound) + base + .distance(from: start.baseRange.lowerBound, to: end.baseRange.lowerBound) let (quotient, remainder) = - distance.quotientAndRemainder(dividingBy: chunkCount) + distance + .quotientAndRemainder(dividingBy: chunkCount) return quotient + remainder.signum() } @@ -654,14 +683,16 @@ extension ChunksOfCountCollection { base.count.quotientAndRemainder(dividingBy: chunkCount) return quotient + remainder.signum() } - + @inlinable public func index( - _ i: Index, offsetBy offset: Int, limitedBy limit: Index + _ i: Index, + offsetBy offset: Int, + limitedBy limit: Index ) -> Index? { guard offset != 0 else { return i } guard limit != i else { return nil } - + if offset > 0 { return limit > i ? offsetForward(i, offsetBy: offset, limit: limit) @@ -676,33 +707,40 @@ extension ChunksOfCountCollection { @inlinable public func index(_ i: Index, offsetBy distance: Int) -> Index { guard distance != 0 else { return i } - - let idx = distance > 0 - ? offsetForward(i, offsetBy: distance) - : offsetBackward(i, offsetBy: distance) + + let idx = + distance > 0 + ? offsetForward(i, offsetBy: distance) + : offsetBackward(i, offsetBy: distance) guard let index = idx else { fatalError("Out of bounds") } return index } - + @inlinable internal func offsetForward( - _ i: Index, offsetBy distance: Int, limit: Index? = nil + _ i: Index, + offsetBy distance: Int, + limit: Index? = nil ) -> Index? { assert(distance > 0) return makeOffsetIndex( - from: i, baseBound: base.endIndex, - distance: distance, baseDistance: distance * chunkCount, - limit: limit, by: > + from: i, + baseBound: base.endIndex, + distance: distance, + baseDistance: distance * chunkCount, + limit: limit, + by: > ) } - + // Convenience to compute offset backward base distance. @inlinable internal func computeOffsetBackwardBaseDistance( - _ i: Index, _ distance: Int + _ i: Index, + _ distance: Int ) -> Int { if i == endIndex { let remainder = base.count % chunkCount @@ -716,32 +754,41 @@ extension ChunksOfCountCollection { } return distance * chunkCount } - + @inlinable internal func offsetBackward( - _ i: Index, offsetBy distance: Int, limit: Index? = nil + _ i: Index, + offsetBy distance: Int, + limit: Index? = nil ) -> Index? { assert(distance < 0) - let baseDistance = - computeOffsetBackwardBaseDistance(i, distance) + let baseDistance = computeOffsetBackwardBaseDistance(i, distance) return makeOffsetIndex( - from: i, baseBound: base.startIndex, - distance: distance, baseDistance: baseDistance, - limit: limit, by: < + from: i, + baseBound: base.startIndex, + distance: distance, + baseDistance: baseDistance, + limit: limit, + by: < ) } - + // Helper to compute `index(offsetBy:)` index. @inlinable internal func makeOffsetIndex( - from i: Index, baseBound: Base.Index, distance: Int, baseDistance: Int, - limit: Index?, by limitFn: (Base.Index, Base.Index) -> Bool + from i: Index, + baseBound: Base.Index, + distance: Int, + baseDistance: Int, + limit: Index?, + by limitFn: (Base.Index, Base.Index) -> Bool ) -> Index? { let baseIdx = base.index( - i.baseRange.lowerBound, offsetBy: baseDistance, + i.baseRange.lowerBound, + offsetBy: baseDistance, limitedBy: baseBound ) - + if let limit = limit { if baseIdx == nil { // If we passed the bounds while advancing forward, and the limit is the @@ -766,12 +813,16 @@ extension ChunksOfCountCollection { return nil } } - + let baseStartIdx = baseIdx ?? baseBound - let baseEndIdx = base.index( - baseStartIdx, offsetBy: chunkCount, limitedBy: base.endIndex - ) ?? base.endIndex - + let baseEndIdx = + base.index( + baseStartIdx, + offsetBy: chunkCount, + limitedBy: base.endIndex + ) + ?? base.endIndex + return Index(_baseRange: baseStartIdx.. EvenlyChunkedCollection { precondition(count >= 0, "Can't divide into a negative number of chunks") - precondition(count > 0 || isEmpty, "Can't divide a non-empty collection into 0 chunks") + precondition( + count > 0 || isEmpty, + "Can't divide a non-empty collection into 0 chunks" + ) return EvenlyChunkedCollection(base: self, numberOfChunks: count) } } diff --git a/Sources/Algorithms/Combinations.swift b/Sources/Algorithms/Combinations.swift index 0bfcbff3..2276fd17 100644 --- a/Sources/Algorithms/Combinations.swift +++ b/Sources/Algorithms/Combinations.swift @@ -14,17 +14,17 @@ public struct CombinationsSequence { /// The collection to iterate over for combinations. @usableFromInline internal let base: Base - + @usableFromInline internal let baseCount: Int - + /// The range of accepted sizes of combinations. /// /// - Note: This may be `nil` if the attempted range entirely exceeds the /// upper bounds of the size of the `base` collection. @usableFromInline internal let kRange: Range? - + /// Initializes a `CombinationsSequence` for all combinations of `base` of /// size `k`. /// @@ -35,7 +35,7 @@ public struct CombinationsSequence { internal init(_ base: Base, k: Int) { self.init(base, kRange: k...k) } - + /// Initializes a `CombinationsSequence` for all combinations of `base` of /// sizes within a given range. /// @@ -44,27 +44,29 @@ public struct CombinationsSequence { /// - kRange: The range of accepted sizes of combinations. @inlinable internal init( - _ base: Base, kRange: R + _ base: Base, + kRange: R ) where R.Bound == Int { let range = kRange.relative(to: 0 ..< .max) self.base = base let baseCount = base.count self.baseCount = baseCount let upperBound = baseCount + 1 - self.kRange = range.lowerBound < upperBound - ? range.clamped(to: 0 ..< upperBound) + self.kRange = + range.lowerBound < upperBound + ? range.clamped(to: 0.. Int { switch k { case n, 0: return 1 @@ -73,7 +75,7 @@ public struct CombinationsSequence { default: return n * binomial(n: n - 1, k: k - 1) / k } } - + return k.map { binomial(n: n, k: $0) }.reduce(0, +) @@ -89,7 +91,7 @@ extension CombinationsSequence: Sequence { public struct Iterator: IteratorProtocol { @usableFromInline internal let base: Base - + /// The current range of accepted sizes of combinations. /// /// - Note: The range is contracted until empty while iterating over @@ -97,23 +99,23 @@ extension CombinationsSequence: Sequence { /// finished. @usableFromInline internal var kRange: Range - + /// Whether or not iteration is finished (`kRange` is empty) @inlinable internal var isFinished: Bool { - return kRange.isEmpty + kRange.isEmpty } - + @usableFromInline internal var indexes: [Base.Index] - + @inlinable internal init(_ combinations: CombinationsSequence) { self.base = combinations.base self.kRange = combinations.kRange ?? 0..<0 self.indexes = Array(combinations.base.indices.prefix(kRange.lowerBound)) } - + /// Advances the current indices to the next set of combinations. If /// `indexes.count == 3` and `base.count == 5`, the indices advance like /// this: @@ -139,12 +141,12 @@ extension CombinationsSequence: Sequence { func advanceKRange() { if kRange.lowerBound < kRange.upperBound { let advancedLowerBound = kRange.lowerBound + 1 - kRange = advancedLowerBound ..< kRange.upperBound + kRange = advancedLowerBound.. [Base.Element]? { guard !isFinished else { return nil } @@ -183,7 +185,7 @@ extension CombinationsSequence: Sequence { return indexes.map { i in base[i] } } } - + @inlinable public func makeIterator() -> Iterator { Iterator(self) @@ -191,7 +193,7 @@ extension CombinationsSequence: Sequence { } extension CombinationsSequence: LazySequenceProtocol - where Base: LazySequenceProtocol {} +where Base: LazySequenceProtocol {} //===----------------------------------------------------------------------===// // combinations(ofCount:) @@ -255,6 +257,8 @@ extension Collection { /// /// - Parameter kRange: The range of numbers of elements to include in each /// combination. + /// - Returns: A sequence of the combinations of this collection's + /// elements, for all sizes in `kRange`, from smallest to largest. /// /// - Complexity: O(1) for random-access base collections. O(*n*) where *n* /// is the number of elements in the base collection, since @@ -265,7 +269,7 @@ extension Collection { ) -> CombinationsSequence where R.Bound == Int { CombinationsSequence(self, kRange: kRange) } - + /// Returns a collection of combinations of this collection's elements, with /// each combination having the specified number of elements. /// @@ -291,13 +295,17 @@ extension Collection { /// the resulting sequence has no elements. /// /// - Parameter k: The number of elements to include in each combination. + /// - Returns: A sequence of the combinations of this collection's + /// elements. /// /// - Complexity: O(1) for random-access base collections. O(*n*) where *n* /// is the number of elements in the base collection, since /// `CombinationsSequence` accesses the `count` of the base collection. @inlinable public func combinations(ofCount k: Int) -> CombinationsSequence { - precondition(k >= 0, "Can't have combinations with a negative number of elements.") + precondition( + k >= 0, + "Can't have combinations with a negative number of elements.") return CombinationsSequence(self, k: k) } } diff --git a/Sources/Algorithms/Compacted.swift b/Sources/Algorithms/Compacted.swift index 1e7ba489..36612fc4 100644 --- a/Sources/Algorithms/Compacted.swift +++ b/Sources/Algorithms/Compacted.swift @@ -12,11 +12,11 @@ /// A `Sequence` that iterates over every non-nil element from the original /// `Sequence`. public struct CompactedSequence: Sequence - where Base.Element == Element? { +where Base.Element == Element? { @usableFromInline let base: Base - + @inlinable init(base: Base) { self.base = base @@ -25,12 +25,12 @@ public struct CompactedSequence: Sequence public struct Iterator: IteratorProtocol { @usableFromInline var base: Base.Iterator - + @inlinable init(base: Base.Iterator) { self.base = base } - + @inlinable public mutating func next() -> Element? { while let wrapped = base.next() { @@ -68,7 +68,7 @@ extension Sequence { /// Complexity: O(1) @inlinable public func compacted() -> CompactedSequence - where Element == Unwrapped? { + where Element == Unwrapped? { CompactedSequence(base: self) } } @@ -76,74 +76,79 @@ extension Sequence { /// A `Collection` that iterates over every non-nil element from the original /// `Collection`. public struct CompactedCollection: Collection - where Base.Element == Element? { +where Base.Element == Element? { @usableFromInline let base: Base - + @inlinable init(base: Base) { self.base = base let idx = base.firstIndex(where: { $0 != nil }) ?? base.endIndex self.startIndex = Index(base: idx) } - + public struct Index { @usableFromInline let base: Base.Index - + @inlinable init(base: Base.Index) { self.base = base } } - + public var startIndex: Index - + @inlinable public var endIndex: Index { Index(base: base.endIndex) } - + @inlinable public subscript(position: Index) -> Element { + // swift-format-ignore: NeverForceUnwrap + // All indices are only for non-`nil` elements. base[position.base]! } - + @inlinable public func index(after i: Index) -> Index { precondition(i != endIndex, "Index out of bounds") - + let baseIdx = base.index(after: i.base) guard let idx = base[baseIdx...].firstIndex(where: { $0 != nil }) - else { return endIndex } + else { return endIndex } return Index(base: idx) } } extension CompactedCollection: BidirectionalCollection - where Base: BidirectionalCollection { +where Base: BidirectionalCollection { @inlinable public func index(before i: Index) -> Index { precondition(i != startIndex, "Index out of bounds") - - guard let idx = - base[startIndex.base.. Bool { + public static func < ( + lhs: CompactedCollection.Index, + rhs: CompactedCollection.Index + ) -> Bool { lhs.base < rhs.base } } extension CompactedCollection.Index: Hashable - where Base.Index: Hashable {} +where Base.Index: Hashable {} extension Collection { /// Returns a new `Collection` that iterates over every non-nil element from @@ -167,8 +172,7 @@ extension Collection { /// original `Collection`. @inlinable public func compacted() -> CompactedCollection - where Element == Unwrapped? - { + where Element == Unwrapped? { CompactedCollection(base: self) } } @@ -178,7 +182,7 @@ extension Collection { //===----------------------------------------------------------------------===// extension CompactedSequence: LazySequenceProtocol - where Base: LazySequenceProtocol {} +where Base: LazySequenceProtocol {} extension CompactedCollection: LazySequenceProtocol, LazyCollectionProtocol - where Base: LazySequenceProtocol {} +where Base: LazySequenceProtocol {} diff --git a/Sources/Algorithms/Cycle.swift b/Sources/Algorithms/Cycle.swift index 23d14164..5c88b4ec 100644 --- a/Sources/Algorithms/Cycle.swift +++ b/Sources/Algorithms/Cycle.swift @@ -14,7 +14,7 @@ public struct CycledSequence { /// The collection to repeat. @usableFromInline internal let base: Base - + @inlinable internal init(base: Base) { self.base = base @@ -26,29 +26,29 @@ extension CycledSequence: Sequence { public struct Iterator: IteratorProtocol { @usableFromInline internal let base: Base - + @usableFromInline internal var current: Base.Index - + @inlinable internal init(base: Base) { self.base = base self.current = base.startIndex } - + @inlinable public mutating func next() -> Base.Element? { guard !base.isEmpty else { return nil } - + if current == base.endIndex { current = base.startIndex } - + defer { base.formIndex(after: ¤t) } return base[current] } } - + @inlinable public func makeIterator() -> Iterator { Iterator(base: base) @@ -56,7 +56,7 @@ extension CycledSequence: Sequence { } extension CycledSequence: LazySequenceProtocol - where Base: LazySequenceProtocol {} +where Base: LazySequenceProtocol {} /// A collection wrapper that repeats the elements of a base collection for a /// finite number of times. @@ -134,10 +134,11 @@ extension CycledTimesCollection: Collection { offsetBy distance: Int, limitedBy limit: Index ) -> Index? { - guard let productIndex = product.index( - i.productIndex, - offsetBy: distance, - limitedBy: limit.productIndex) + guard + let productIndex = product.index( + i.productIndex, + offsetBy: distance, + limitedBy: limit.productIndex) else { return nil } return Index(productIndex) } @@ -149,7 +150,7 @@ extension CycledTimesCollection: Collection { } extension CycledTimesCollection: BidirectionalCollection - where Base: BidirectionalCollection { +where Base: BidirectionalCollection { @inlinable public func index(before i: Index) -> Index { let productIndex = product.index(before: i.productIndex) @@ -158,10 +159,10 @@ extension CycledTimesCollection: BidirectionalCollection } extension CycledTimesCollection: RandomAccessCollection - where Base: RandomAccessCollection {} +where Base: RandomAccessCollection {} extension CycledTimesCollection: LazySequenceProtocol, LazyCollectionProtocol - where Base: LazySequenceProtocol {} +where Base: LazySequenceProtocol {} //===----------------------------------------------------------------------===// // cycled() @@ -197,7 +198,7 @@ extension Collection { public func cycled() -> CycledSequence { CycledSequence(base: self) } - + /// Returns a sequence that repeats the elements of this collection the /// specified number of times. /// diff --git a/Sources/Algorithms/EitherSequence.swift b/Sources/Algorithms/EitherSequence.swift index d28c4a10..974045c2 100644 --- a/Sources/Algorithms/EitherSequence.swift +++ b/Sources/Algorithms/EitherSequence.swift @@ -24,9 +24,9 @@ extension Either: Equatable where Left: Equatable, Right: Equatable { @usableFromInline internal static func == (lhs: Self, rhs: Self) -> Bool { switch (lhs, rhs) { - case let (.left(lhs), .left(rhs)): + case (.left(let lhs), .left(let rhs)): return lhs == rhs - case let (.right(lhs), .right(rhs)): + case (.right(let lhs), .right(let rhs)): return lhs == rhs case (.left, .right), (.right, .left): return false @@ -38,9 +38,9 @@ extension Either: Comparable where Left: Comparable, Right: Comparable { @usableFromInline internal static func < (lhs: Self, rhs: Self) -> Bool { switch (lhs, rhs) { - case let (.left(lhs), .left(rhs)): + case (.left(let lhs), .left(let rhs)): return lhs < rhs - case let (.right(lhs), .right(rhs)): + case (.right(let lhs), .right(let rhs)): return lhs < rhs case (.left, .right): return true @@ -57,8 +57,7 @@ extension Either: Comparable where Left: Comparable, Right: Comparable { /// A sequence that has one of the two specified types. @usableFromInline internal enum EitherSequence - where Left.Element == Right.Element -{ +where Left.Element == Right.Element { case left(Left) case right(Right) } @@ -68,16 +67,16 @@ extension EitherSequence: Sequence { internal struct Iterator: IteratorProtocol { @usableFromInline internal var left: Left.Iterator? - + @usableFromInline internal var right: Right.Iterator? - + @inlinable internal mutating func next() -> Left.Element? { left?.next() ?? right?.next() } } - + @usableFromInline internal func makeIterator() -> Iterator { switch self { @@ -90,8 +89,7 @@ extension EitherSequence: Sequence { } extension EitherSequence: Collection - where Left: Collection, Right: Collection, Left.Element == Right.Element -{ +where Left: Collection, Right: Collection, Left.Element == Right.Element { @usableFromInline internal typealias Index = Either @@ -118,9 +116,9 @@ extension EitherSequence: Collection @inlinable internal subscript(position: Index) -> Element { switch (self, position) { - case let (.left(s), .left(i)): + case (.left(let s), .left(let i)): return s[i] - case let (.right(s), .right(i)): + case (.right(let s), .right(let i)): return s[i] default: fatalError() @@ -129,10 +127,10 @@ extension EitherSequence: Collection @inlinable internal func index(after i: Index) -> Index { - switch (self,i) { - case let (.left(s), .left(i)): + switch (self, i) { + case (.left(let s), .left(let i)): return .left(s.index(after: i)) - case let (.right(s), .right(i)): + case (.right(let s), .right(let i)): return .right(s.index(after: i)) default: fatalError() @@ -146,9 +144,9 @@ extension EitherSequence: Collection limitedBy limit: Index ) -> Index? { switch (self, i, limit) { - case let (.left(s), .left(i), .left(limit)): + case (.left(let s), .left(let i), .left(let limit)): return s.index(i, offsetBy: distance, limitedBy: limit).map { .left($0) } - case let (.right(s), .right(i), .right(limit)): + case (.right(let s), .right(let i), .right(let limit)): return s.index(i, offsetBy: distance, limitedBy: limit).map { .right($0) } default: fatalError() @@ -158,9 +156,9 @@ extension EitherSequence: Collection @inlinable internal func index(_ i: Index, offsetBy distance: Int) -> Index { switch (self, i) { - case let (.left(s), .left(i)): + case (.left(let s), .left(let i)): return .left(s.index(i, offsetBy: distance)) - case let (.right(s), .right(i)): + case (.right(let s), .right(let i)): return .right(s.index(i, offsetBy: distance)) default: fatalError() @@ -170,9 +168,9 @@ extension EitherSequence: Collection @inlinable internal func distance(from start: Index, to end: Index) -> Int { switch (self, start, end) { - case let (.left(s), .left(i), .left(j)): + case (.left(let s), .left(let i), .left(let j)): return s.distance(from: i, to: j) - case let (.right(s), .right(i), .right(j)): + case (.right(let s), .right(let i), .right(let j)): return s.distance(from: i, to: j) default: fatalError() @@ -181,14 +179,13 @@ extension EitherSequence: Collection } extension EitherSequence: BidirectionalCollection - where Left: BidirectionalCollection, Right: BidirectionalCollection -{ +where Left: BidirectionalCollection, Right: BidirectionalCollection { @inlinable internal func index(before i: Index) -> Index { switch (self, i) { - case let (.left(s), .left(i)): + case (.left(let s), .left(let i)): return .left(s.index(before: i)) - case let (.right(s), .right(i)): + case (.right(let s), .right(let i)): return .right(s.index(before: i)) default: fatalError() @@ -197,4 +194,4 @@ extension EitherSequence: BidirectionalCollection } extension EitherSequence: RandomAccessCollection - where Left: RandomAccessCollection, Right: RandomAccessCollection {} +where Left: RandomAccessCollection, Right: RandomAccessCollection {} diff --git a/Sources/Algorithms/EndsWith.swift b/Sources/Algorithms/EndsWith.swift index 2109bc02..bf8088e5 100644 --- a/Sources/Algorithms/EndsWith.swift +++ b/Sources/Algorithms/EndsWith.swift @@ -14,8 +14,6 @@ //===----------------------------------------------------------------------===// extension BidirectionalCollection where Element: Equatable { - - /// Returns a Boolean value indicating whether the final elements of the /// collection are the same as the elements in another collection. /// @@ -45,7 +43,7 @@ extension BidirectionalCollection where Element: Equatable { public func ends( with possibleSuffix: PossibleSuffix ) -> Bool where PossibleSuffix.Element == Element { - return self.ends(with: possibleSuffix, by: ==) + self.ends(with: possibleSuffix, by: ==) } } @@ -78,7 +76,8 @@ extension BidirectionalCollection { with possibleSuffix: PossibleSuffix, by areEquivalent: (Element, PossibleSuffix.Element) throws -> Bool ) rethrows -> Bool { - try self.reversed().starts(with: possibleSuffix.reversed(), by: areEquivalent) + try self + .reversed() + .starts(with: possibleSuffix.reversed(), by: areEquivalent) } } - diff --git a/Sources/Algorithms/FlattenCollection.swift b/Sources/Algorithms/FlattenCollection.swift index 23b8a87f..ad9b8c73 100644 --- a/Sources/Algorithms/FlattenCollection.swift +++ b/Sources/Algorithms/FlattenCollection.swift @@ -13,14 +13,13 @@ /// collections. @usableFromInline internal struct FlattenCollection - where Base.Element: Collection -{ +where Base.Element: Collection { @usableFromInline internal let base: Base - + @usableFromInline internal let indexOfFirstNonEmptyElement: Base.Index - + @inlinable internal init(base: Base) { self.base = base @@ -33,35 +32,39 @@ extension FlattenCollection: Collection { internal struct Index: Comparable { @usableFromInline internal let outer: Base.Index - + @usableFromInline internal let inner: Base.Element.Index? - + @inlinable init(outer: Base.Index, inner: Base.Element.Index?) { self.outer = outer self.inner = inner } - + @inlinable internal static func < (lhs: Self, rhs: Self) -> Bool { guard lhs.outer == rhs.outer else { return lhs.outer < rhs.outer } + // swift-format-ignore: NeverForceUnwrap + // `inner` is nil iff `outer` is `base.endIndex`, and the + // previous line established that the two outer indices are equal. + // Thus, either both inner indices are nil or neither are. return lhs.inner == nil ? false : lhs.inner! < rhs.inner! } } - + @inlinable internal var startIndex: Index { let outer = indexOfFirstNonEmptyElement let inner = outer == base.endIndex ? nil : base[outer].startIndex return Index(outer: outer, inner: inner) } - + @inlinable internal var endIndex: Index { Index(outer: base.endIndex, inner: nil) } - + /// Forms an index from a pair of base indices, normalizing /// `(i, base2.endIndex)` to `(base1.index(after: i), base2.startIndex)` if /// necessary. @@ -70,56 +73,64 @@ extension FlattenCollection: Collection { outer: Base.Index, inner: Base.Element.Index ) -> Index { - if inner == base[outer].endIndex { - let outer = base[base.index(after: outer)...] - .endOfPrefix(while: { $0.isEmpty }) - let inner = outer == base.endIndex ? nil : base[outer].startIndex - return Index(outer: outer, inner: inner) - } else { + guard inner == base[outer].endIndex else { return Index(outer: outer, inner: inner) } + let outer = base[base.index(after: outer)...] + .endOfPrefix(while: { $0.isEmpty }) + let inner = outer == base.endIndex ? nil : base[outer].startIndex + return Index(outer: outer, inner: inner) } - + @inlinable internal func index(after index: Index) -> Index { let element = base[index.outer] + // swift-format-ignore: NeverForceUnwrap + // This unwrap, like the unchecked subscript above, enforces the + // precondition that you can't advance past `endIndex`. let nextInner = element.index(after: index.inner!) return normalizeIndex(outer: index.outer, inner: nextInner) } - + @inlinable internal subscript(position: Index) -> Base.Element.Element { + // swift-format-ignore: NeverForceUnwrap + // This unwrap, along with the unchecked subscript, enforces the + // precondition that you can't subscript at `endIndex`. base[position.outer][position.inner!] } - + @inlinable internal func distance(from start: Index, to end: Index) -> Int { guard start.outer <= end.outer - else { return -distance(from: end, to: start) } + else { return -distance(from: end, to: start) } guard let startInner = start.inner - else { return 0 } + else { return 0 } guard start.outer != end.outer - else { - return base[start.outer].distance(from: startInner, to: end.inner!) - } - + else { + // swift-format-ignore: NeverForceUnwrap + // Established via guards that outer indices are equal and `start.inner` + // is non-nil, so `end.inner` must also be non-`nil`. + return base[start.outer].distance(from: startInner, to: end.inner!) + } + let firstPart = base[start.outer][startInner...].count let middlePart = base[start.outer.. Index { guard distance != 0 else { return index } - + return distance > 0 ? offsetForward(index, by: distance) : offsetBackward(index, by: -distance) } - + @inlinable internal func index( _ index: Index, @@ -127,7 +138,7 @@ extension FlattenCollection: Collection { limitedBy limit: Index ) -> Index? { guard distance != 0 else { return index } - + if distance > 0 { return limit >= index ? offsetForward(index, by: distance, limitedBy: limit) @@ -138,133 +149,139 @@ extension FlattenCollection: Collection { : offsetBackward(index, by: -distance) } } - + @inlinable internal func offsetForward(_ i: Index, by distance: Int) -> Index { guard let index = offsetForward(i, by: distance, limitedBy: endIndex) - else { fatalError("Index is out of bounds") } + else { fatalError("Index is out of bounds") } return index } - + @inlinable internal func offsetBackward(_ i: Index, by distance: Int) -> Index { guard let index = offsetBackward(i, by: distance, limitedBy: startIndex) - else { fatalError("Index is out of bounds") } + else { fatalError("Index is out of bounds") } return index } - + @inlinable internal func offsetForward( - _ index: Index, by distance: Int, limitedBy limit: Index + _ index: Index, + by distance: Int, + limitedBy limit: Index ) -> Index? { assert(distance > 0) assert(limit >= index) - + if index.outer == limit.outer { - if let indexInner = index.inner, let limitInner = limit.inner { - return base[index.outer] - .index(indexInner, offsetBy: distance, limitedBy: limitInner) - .map { inner in Index(outer: index.outer, inner: inner) } - } else { + guard let indexInner = index.inner, let limitInner = limit.inner else { // `index` and `limit` are both `endIndex` return nil } + return base[index.outer] + .index(indexInner, offsetBy: distance, limitedBy: limitInner) + .map { inner in Index(outer: index.outer, inner: inner) } } - + + // swift-format-ignore: NeverForceUnwrap // `index <= limit` and `index.outer != limit.outer`, so `index != endIndex` let indexInner = index.inner! let element = base[index.outer] - + if let inner = element.index( - indexInner, - offsetBy: distance, - limitedBy: element.endIndex + indexInner, + offsetBy: distance, + limitedBy: element.endIndex ) { return normalizeIndex(outer: index.outer, inner: inner) } - + var remainder = distance - element[indexInner...].count var outer = base.index(after: index.outer) - + while outer != limit.outer { let element = base[outer] - + if let inner = element.index( - element.startIndex, - offsetBy: remainder, - limitedBy: element.endIndex + element.startIndex, + offsetBy: remainder, + limitedBy: element.endIndex ) { return normalizeIndex(outer: outer, inner: inner) } - + remainder -= element.count base.formIndex(after: &outer) } - + if let limitInner = limit.inner { let element = base[outer] return element.index( element.startIndex, offsetBy: remainder, - limitedBy: limitInner) - .map { inner in Index(outer: outer, inner: inner) } - } else { - return nil + limitedBy: limitInner + ) + .map { inner in Index(outer: outer, inner: inner) } } + return nil } - + @inlinable internal func offsetBackward( - _ index: Index, by distance: Int, limitedBy limit: Index + _ index: Index, + by distance: Int, + limitedBy limit: Index ) -> Index? { assert(distance > 0) assert(limit <= index) - + if index.outer == limit.outer { - if let indexInner = index.inner, let limitInner = limit.inner { - return base[index.outer] - .index(indexInner, offsetBy: -distance, limitedBy: limitInner) - .map { inner in Index(outer: index.outer, inner: inner) } - } else { + guard let indexInner = index.inner, let limitInner = limit.inner else { // `index` and `limit` are both `endIndex` return nil } + return base[index.outer] + .index(indexInner, offsetBy: -distance, limitedBy: limitInner) + .map { inner in Index(outer: index.outer, inner: inner) } } - + var remainder = distance - + if let indexInner = index.inner { let element = base[index.outer] - + if let inner = element.index( - indexInner, - offsetBy: -remainder, - limitedBy: element.startIndex + indexInner, + offsetBy: -remainder, + limitedBy: element.startIndex ) { return Index(outer: index.outer, inner: inner) } - + remainder -= element[.. Index { if let inner = index.inner { let element = base[index.outer] - + if inner != element.startIndex { let previousInner = element.index(before: inner) return Index(outer: index.outer, inner: previousInner) } } - - let previousOuter = base[..(by keyForValue: (Element) throws -> GroupKey) rethrows -> [GroupKey: [Element]] { + public func grouped( + by keyForValue: (Element) throws -> GroupKey + ) rethrows -> [GroupKey: [Element]] { try Dictionary(grouping: self, by: keyForValue) } } diff --git a/Sources/Algorithms/Indexed.swift b/Sources/Algorithms/Indexed.swift index 6298b6d6..5e5ca2f3 100644 --- a/Sources/Algorithms/Indexed.swift +++ b/Sources/Algorithms/Indexed.swift @@ -15,7 +15,7 @@ public struct IndexedCollection { /// The base collection. @usableFromInline internal let base: Base - + @inlinable internal init(base: Base) { self.base = base @@ -25,32 +25,32 @@ public struct IndexedCollection { extension IndexedCollection: Collection { /// The element type for an `IndexedCollection` collection. public typealias Element = (index: Base.Index, element: Base.Element) - + @inlinable public var startIndex: Base.Index { base.startIndex } - + @inlinable public var endIndex: Base.Index { base.endIndex } - + @inlinable public subscript(position: Base.Index) -> Element { (index: position, element: base[position]) } - + @inlinable public func index(after i: Base.Index) -> Base.Index { base.index(after: i) } - + @inlinable public func index(_ i: Base.Index, offsetBy distance: Int) -> Base.Index { base.index(i, offsetBy: distance) } - + @inlinable public func index( _ i: Base.Index, @@ -59,12 +59,12 @@ extension IndexedCollection: Collection { ) -> Base.Index? { base.index(i, offsetBy: distance, limitedBy: limit) } - + @inlinable public func distance(from start: Base.Index, to end: Base.Index) -> Int { base.distance(from: start, to: end) } - + @inlinable public var indices: Base.Indices { base.indices @@ -72,8 +72,7 @@ extension IndexedCollection: Collection { } extension IndexedCollection: BidirectionalCollection - where Base: BidirectionalCollection -{ +where Base: BidirectionalCollection { @inlinable public func index(before i: Base.Index) -> Base.Index { base.index(before: i) @@ -81,10 +80,10 @@ extension IndexedCollection: BidirectionalCollection } extension IndexedCollection: RandomAccessCollection - where Base: RandomAccessCollection {} +where Base: RandomAccessCollection {} extension IndexedCollection: LazySequenceProtocol, LazyCollectionProtocol - where Base: LazySequenceProtocol {} +where Base: LazySequenceProtocol {} //===----------------------------------------------------------------------===// // indexed() diff --git a/Sources/Algorithms/Intersperse.swift b/Sources/Algorithms/Intersperse.swift index 706c4ea2..837f7a03 100644 --- a/Sources/Algorithms/Intersperse.swift +++ b/Sources/Algorithms/Intersperse.swift @@ -18,10 +18,10 @@ public struct InterspersedSequence { @usableFromInline internal let base: Base - + @usableFromInline internal let separator: Base.Element - + @inlinable internal init(base: Base, separator: Base.Element) { self.base = base @@ -38,16 +38,16 @@ extension InterspersedSequence: Sequence { case element(Base.Element) case separator } - + @usableFromInline internal var iterator: Base.Iterator - + @usableFromInline internal let separator: Base.Element - + @usableFromInline internal var state = State.start - + @inlinable internal init(iterator: Base.Iterator, separator: Base.Element) { self.iterator = iterator @@ -89,7 +89,7 @@ extension InterspersedSequence: Collection where Base: Collection { case element(Base.Index) case separator(next: Base.Index) } - + @usableFromInline internal let representation: Representation @@ -101,15 +101,15 @@ extension InterspersedSequence: Collection where Base: Collection { @inlinable public static func < (lhs: Index, rhs: Index) -> Bool { switch (lhs.representation, rhs.representation) { - case let (.element(li), .element(ri)), - let (.separator(next: li), .separator(next: ri)), - let (.element(li), .separator(next: ri)): + case (.element(let li), .element(let ri)), + (.separator(next: let li), .separator(next: let ri)), + (.element(let li), .separator(next: let ri)): return li < ri - case let (.separator(next: li), .element(ri)): + case (.separator(next: let li), .element(let ri)): return li <= ri } } - + @inlinable internal static func element(_ index: Base.Index) -> Self { Self(representation: .element(index)) @@ -135,9 +135,9 @@ extension InterspersedSequence: Collection where Base: Collection { public func index(after i: Index) -> Index { precondition(i != endIndex, "Can't advance past endIndex") switch i.representation { - case let .element(index): + case .element(let index): return .separator(next: base.index(after: index)) - case let .separator(next): + case .separator(let next): return .element(next) } } @@ -149,20 +149,20 @@ extension InterspersedSequence: Collection where Base: Collection { case .separator: return separator } } - + @inlinable public func distance(from start: Index, to end: Index) -> Int { switch (start.representation, end.representation) { - case let (.element(element), .separator(next: separator)): + case (.element(let element), .separator(next: let separator)): return 2 * base.distance(from: element, to: separator) - 1 - case let (.separator(next: separator), .element(element)): + case (.separator(next: let separator), .element(let element)): return 2 * base.distance(from: separator, to: element) + 1 - case let (.element(start), .element(end)), - let (.separator(start), .separator(end)): + case (.element(let start), .element(let end)), + (.separator(let start), .separator(let end)): return 2 * base.distance(from: start, to: end) } } - + @inlinable public func index(_ index: Index, offsetBy distance: Int) -> Index { distance >= 0 @@ -172,84 +172,94 @@ extension InterspersedSequence: Collection where Base: Collection { @inlinable public func index( - _ index: Index, - offsetBy distance: Int, - limitedBy limit: Index - ) -> Index? { - if distance >= 0 { - return limit >= index - ? offsetForward(index, by: distance, limitedBy: limit) - : offsetForward(index, by: distance) - } else { - return limit <= index - ? offsetBackward(index, by: -distance, limitedBy: limit) - : offsetBackward(index, by: -distance) - } + _ index: Index, + offsetBy distance: Int, + limitedBy limit: Index + ) -> Index? { + if distance >= 0 { + return limit >= index + ? offsetForward(index, by: distance, limitedBy: limit) + : offsetForward(index, by: distance) + } else { + return limit <= index + ? offsetBackward(index, by: -distance, limitedBy: limit) + : offsetBackward(index, by: -distance) } - + } + @inlinable internal func offsetForward(_ i: Index, by distance: Int) -> Index { guard let index = offsetForward(i, by: distance, limitedBy: endIndex) - else { fatalError("Index is out of bounds") } + else { fatalError("Index is out of bounds") } return index } - + @inlinable internal func offsetBackward(_ i: Index, by distance: Int) -> Index { guard let index = offsetBackward(i, by: distance, limitedBy: startIndex) - else { fatalError("Index is out of bounds") } + else { fatalError("Index is out of bounds") } return index } - + @inlinable internal func offsetForward( - _ index: Index, by distance: Int, limitedBy limit: Index + _ index: Index, + by distance: Int, + limitedBy limit: Index ) -> Index? { assert(distance >= 0) assert(limit >= index) - - switch (index.representation, limit.representation, distance.isMultiple(of: 2)) { - case let (.element(index), .element(limit), true), - let (.separator(next: index), .element(limit), false): + + switch ( + index.representation, limit.representation, distance.isMultiple(of: 2) + ) { + case (.element(let index), .element(let limit), true), + (.separator(next: let index), .element(let limit), false): return base.index(index, offsetBy: distance / 2, limitedBy: limit) .map { .element($0) } - - case let (.element(index), .element(limit), false), - let (.element(index), .separator(next: limit), false), - let (.separator(next: index), .element(limit), true), - let (.separator(next: index), .separator(next: limit), true): + + case (.element(let index), .element(let limit), false), + (.element(let index), .separator(next: let limit), false), + (.separator(next: let index), .element(let limit), true), + (.separator(next: let index), .separator(next: let limit), true): return base.index(index, offsetBy: (distance + 1) / 2, limitedBy: limit) .map { .separator(next: $0) } - - case let (.element(index), .separator(next: limit), true), - let (.separator(next: index), .separator(next: limit), false): + + case (.element(let index), .separator(next: let limit), true), + (.separator(next: let index), .separator(next: let limit), false): return base.index(index, offsetBy: distance / 2, limitedBy: limit) .flatMap { $0 == limit ? nil : .element($0) } } } - + @inlinable internal func offsetBackward( - _ index: Index, by distance: Int, limitedBy limit: Index + _ index: Index, + by distance: Int, + limitedBy limit: Index ) -> Index? { assert(distance >= 0) assert(limit <= index) - - switch (index.representation, limit.representation, distance.isMultiple(of: 2)) { - case let (.element(index), .element(limit), true), - let (.element(index), .separator(next: limit), true), - let (.separator(next: index), .element(limit), false), - let (.separator(next: index), .separator(next: limit), false): - return base.index(index, offsetBy: -((distance + 1) / 2), limitedBy: limit) - .map { .element($0) } - - case let (.element(index), .separator(next: limit), false), - let (.separator(next: index), .separator(next: limit), true): + + switch ( + index.representation, limit.representation, distance.isMultiple(of: 2) + ) { + case (.element(let index), .element(let limit), true), + (.element(let index), .separator(next: let limit), true), + (.separator(next: let index), .element(let limit), false), + (.separator(next: let index), .separator(next: let limit), false): + return base.index( + index, offsetBy: -((distance + 1) / 2), limitedBy: limit + ) + .map { .element($0) } + + case (.element(let index), .separator(next: let limit), false), + (.separator(next: let index), .separator(next: let limit), true): return base.index(index, offsetBy: -(distance / 2), limitedBy: limit) .map { .separator(next: $0) } - - case let (.element(index), .element(limit), false), - let (.separator(next: index), .element(limit), true): + + case (.element(let index), .element(let limit), false), + (.separator(next: let index), .element(let limit), true): return base.index(index, offsetBy: -(distance / 2), limitedBy: limit) .flatMap { $0 == limit ? nil : .separator(next: $0) } } @@ -257,34 +267,32 @@ extension InterspersedSequence: Collection where Base: Collection { } extension InterspersedSequence: BidirectionalCollection - where Base: BidirectionalCollection -{ +where Base: BidirectionalCollection { @inlinable public func index(before i: Index) -> Index { precondition(i != startIndex, "Can't move before startIndex") switch i.representation { - case let .element(index): + case .element(let index): return .separator(next: index) - case let .separator(next): + case .separator(let next): return .element(base.index(before: next)) } } } extension InterspersedSequence: RandomAccessCollection - where Base: RandomAccessCollection {} +where Base: RandomAccessCollection {} extension InterspersedSequence: LazySequenceProtocol - where Base: LazySequenceProtocol {} +where Base: LazySequenceProtocol {} extension InterspersedSequence: LazyCollectionProtocol - where Base: LazySequenceProtocol & Collection {} +where Base: LazySequenceProtocol & Collection {} //===----------------------------------------------------------------------===// // InterspersedMap //===----------------------------------------------------------------------===// - /// A sequence over the results of applying a closure to the sequence's /// elements, with a separator that separates each pair of adjacent transformed /// values. @@ -292,10 +300,10 @@ extension InterspersedSequence: LazyCollectionProtocol internal struct InterspersedMapSequence { @usableFromInline internal let base: Base - + @usableFromInline internal let transform: (Base.Element) -> Result - + @usableFromInline internal let separator: (Base.Element, Base.Element) -> Result } @@ -309,19 +317,19 @@ extension InterspersedMapSequence: Sequence { case element(Base.Element) case separator(previous: Base.Element) } - + @usableFromInline internal var base: Base.Iterator - + @usableFromInline internal let transform: (Base.Element) -> Result - + @usableFromInline internal let separator: (Base.Element, Base.Element) -> Result - + @usableFromInline internal var state = State.start - + @inlinable internal init( base: Base.Iterator, @@ -368,61 +376,64 @@ extension InterspersedMapSequence: Collection where Base: Collection { case element(Base.Index) case separator(previous: Base.Index, next: Base.Index) } - + @usableFromInline - internal let representation: Representation - + internal let base: Representation + @inlinable internal init(representation: Representation) { - self.representation = representation + self.base = representation } - + @inlinable internal static func element(_ index: Base.Index) -> Self { Self(representation: .element(index)) } @inlinable - internal static func separator(previous: Base.Index, next: Base.Index) -> Self { + internal static func separator( + previous: Base.Index, + next: Base.Index + ) -> Self { Self(representation: .separator(previous: previous, next: next)) } - + @inlinable internal static func == (lhs: Self, rhs: Self) -> Bool { - switch (lhs.representation, rhs.representation) { - case let (.element(lhs), .element(rhs)), - let (.separator(_, next: lhs), .separator(_, next: rhs)): + switch (lhs.base, rhs.base) { + case (.element(let lhs), .element(let rhs)), + (.separator(_, next: let lhs), .separator(_, next: let rhs)): return lhs == rhs case (.element, .separator), (.separator, .element): return false } } - + @inlinable internal static func < (lhs: Self, rhs: Self) -> Bool { - switch (lhs.representation, rhs.representation) { - case let (.element(lhs), .element(rhs)), - let (.separator(_, next: lhs), .separator(_, next: rhs)), - let (.element(lhs), .separator(_, next: rhs)), - let (.separator(previous: lhs, _), .element(rhs)): + switch (lhs.base, rhs.base) { + case (.element(let lhs), .element(let rhs)), + (.separator(_, next: let lhs), .separator(_, next: let rhs)), + (.element(let lhs), .separator(_, next: let rhs)), + (.separator(previous: let lhs, _), .element(let rhs)): return lhs < rhs } } } - + @inlinable internal var startIndex: Index { base.isEmpty ? endIndex : .element(base.startIndex) } - + @inlinable internal var endIndex: Index { .separator(previous: base.endIndex, next: base.endIndex) } - + @inlinable internal func index(after index: Index) -> Index { - switch index.representation { + switch index.base { case .element(let index): let next = base.index(after: index) return .separator(previous: index, next: next) @@ -430,39 +441,39 @@ extension InterspersedMapSequence: Collection where Base: Collection { return .element(next) } } - + @inlinable internal subscript(position: Index) -> Result { - switch position.representation { + switch position.base { case .element(let index): return transform(base[index]) - case let .separator(previous, next): + case .separator(let previous, let next): return separator(base[previous], base[next]) } } - + @inlinable internal func distance(from start: Index, to end: Index) -> Int { - switch (start.representation, end.representation) { - case let (.element(lhs), .element(rhs)), - let (.separator(_, next: lhs), .separator(_, next: rhs)): + switch (start.base, end.base) { + case (.element(let lhs), .element(let rhs)), + (.separator(_, next: let lhs), .separator(_, next: let rhs)): return 2 * base.distance(from: lhs, to: rhs) - case let (.element(lhs), .separator(_, next: rhs)): + case (.element(let lhs), .separator(_, next: let rhs)): return 2 * base.distance(from: lhs, to: rhs) - 1 - case let (.separator(_, next: lhs), .element(rhs)): + case (.separator(_, next: let lhs), .element(let rhs)): return 2 * base.distance(from: lhs, to: rhs) + 1 } } - + @inlinable internal func index(_ index: Index, offsetBy distance: Int) -> Index { guard distance != 0 else { return index } - + return distance > 0 ? offsetForward(index, by: distance) : offsetBackward(index, by: -distance) } - + @inlinable internal func index( _ index: Index, @@ -470,7 +481,7 @@ extension InterspersedMapSequence: Collection where Base: Collection { limitedBy limit: Index ) -> Index? { guard distance != 0 else { return index } - + if distance > 0 { return limit >= index ? offsetForward(index, by: distance, limitedBy: limit) @@ -481,38 +492,40 @@ extension InterspersedMapSequence: Collection where Base: Collection { : offsetBackward(index, by: -distance) } } - + @inlinable internal func offsetForward(_ i: Index, by distance: Int) -> Index { guard let index = offsetForward(i, by: distance, limitedBy: endIndex) else { fatalError("Index is out of bounds") } return index } - + @inlinable internal func offsetBackward(_ i: Index, by distance: Int) -> Index { guard let index = offsetBackward(i, by: distance, limitedBy: startIndex) else { fatalError("Index is out of bounds") } return index } - + @inlinable internal func offsetForward( - _ index: Index, by distance: Int, limitedBy limit: Index + _ index: Index, + by distance: Int, + limitedBy limit: Index ) -> Index? { assert(distance > 0) assert(limit >= index) - - switch (index.representation, limit.representation, distance.isMultiple(of: 2)) { - case let (.element(index), .element(limit), true), - let (.separator(_, next: index), .element(limit), false): + + switch (index.base, limit.base, distance.isMultiple(of: 2)) { + case (.element(let index), .element(let limit), true), + (.separator(_, next: let index), .element(let limit), false): return base.index(index, offsetBy: distance / 2, limitedBy: limit) .map { .element($0) } - - case let (.element(index), .element(limit), false), - let (.element(index), .separator(_, next: limit), false), - let (.separator(_, next: index), .element(limit), true), - let (.separator(_, next: index), .separator(_, next: limit), true): + + case (.element(let index), .element(let limit), false), + (.element(let index), .separator(_, next: let limit), false), + (.separator(_, next: let index), .element(let limit), true), + (.separator(_, next: let index), .separator(_, next: let limit), true): return base.index(index, offsetBy: (distance - 1) / 2, limitedBy: limit) .flatMap { guard $0 != limit else { return nil } @@ -521,36 +534,40 @@ extension InterspersedMapSequence: Collection where Base: Collection { ? endIndex : .separator(previous: $0, next: next) } - - case let (.element(index), .separator(_, next: limit), true), - let (.separator(_, next: index), .separator(_, next: limit), false): + + case (.element(let index), .separator(_, next: let limit), true), + (.separator(_, next: let index), .separator(_, next: let limit), false): return base.index(index, offsetBy: distance / 2, limitedBy: limit) .flatMap { $0 == limit ? nil : .element($0) } } } - + @inlinable internal func offsetBackward( - _ index: Index, by distance: Int, limitedBy limit: Index + _ index: Index, + by distance: Int, + limitedBy limit: Index ) -> Index? { assert(distance > 0) assert(limit <= index) - - switch (index.representation, limit.representation, distance.isMultiple(of: 2)) { - case let (.element(index), .element(limit), true), - let (.element(index), .separator(_, next: limit), true), - let (.separator(_, next: index), .element(limit), false), - let (.separator(_, next: index), .separator(_, next: limit), false): - return base.index(index, offsetBy: -((distance + 1) / 2), limitedBy: limit) + + switch (index.base, limit.base, distance.isMultiple(of: 2)) { + case (.element(let index), .element(let limit), true), + (.element(let index), .separator(_, next: let limit), true), + (.separator(_, next: let index), .element(let limit), false), + (.separator(_, next: let index), .separator(_, next: let limit), false): + return + base + .index(index, offsetBy: -((distance + 1) / 2), limitedBy: limit) .map { .element($0) } - - case let (.element(index), .separator(_, next: limit), false), - let (.separator(_, next: index), .separator(_, next: limit), true): + + case (.element(let index), .separator(_, next: let limit), false), + (.separator(_, next: let index), .separator(_, next: let limit), true): return base.index(index, offsetBy: -(distance / 2), limitedBy: limit) .map { .separator(previous: base.index($0, offsetBy: -1), next: $0) } - - case let (.element(index), .element(limit), false), - let (.separator(_, next: index), .element(limit), true): + + case (.element(let index), .element(let limit), false), + (.separator(_, next: let index), .element(let limit), true): return base.index(index, offsetBy: -(distance / 2), limitedBy: limit) .flatMap { $0 == limit @@ -562,15 +579,14 @@ extension InterspersedMapSequence: Collection where Base: Collection { } extension InterspersedMapSequence: BidirectionalCollection - where Base: BidirectionalCollection -{ +where Base: BidirectionalCollection { @inlinable internal func index(before index: Index) -> Index { - switch index.representation { + switch index.base { case .element(let index): let previous = base.index(before: index) return .separator(previous: previous, next: index) - case let .separator(previous, next): + case .separator(let previous, let next): let index = next == base.endIndex ? base.index(before: next) : previous return .element(index) } @@ -578,11 +594,10 @@ extension InterspersedMapSequence: BidirectionalCollection } extension InterspersedMapSequence.Index: Hashable - where Base.Index: Hashable -{ +where Base.Index: Hashable { @inlinable internal func hash(into hasher: inout Hasher) { - switch representation { + switch base { case .element(let base): hasher.combine(false) hasher.combine(base) @@ -595,7 +610,7 @@ extension InterspersedMapSequence.Index: Hashable extension InterspersedMapSequence: LazySequenceProtocol {} extension InterspersedMapSequence: LazyCollectionProtocol - where Base: Collection {} +where Base: Collection {} //===----------------------------------------------------------------------===// // interspersed(with:) diff --git a/Sources/Algorithms/Joined.swift b/Sources/Algorithms/Joined.swift index 13f2f69b..5a87bdd8 100644 --- a/Sources/Algorithms/Joined.swift +++ b/Sources/Algorithms/Joined.swift @@ -16,15 +16,17 @@ /// A sequence that presents the elements of a base sequence of sequences /// concatenated using a given separator. public struct JoinedBySequence - where Base.Element: Sequence, Base.Element.Element == Separator.Element -{ +where Base.Element: Sequence, Base.Element.Element == Separator.Element { @usableFromInline - internal typealias Inner = FlattenSequence>>> - + internal typealias Inner = FlattenSequence< + InterspersedSequence< + LazyMapSequence> + > + > + @usableFromInline internal let inner: Inner - + @inlinable internal init(base: Base, separator: Separator) { self.inner = base.lazy @@ -38,18 +40,18 @@ extension JoinedBySequence: Sequence { public struct Iterator: IteratorProtocol { @usableFromInline internal var inner: Inner.Iterator - + @inlinable internal init(inner: Inner.Iterator) { self.inner = inner } - + @inlinable public mutating func next() -> Base.Element.Element? { inner.next() } } - + @inlinable public func makeIterator() -> Iterator { Iterator(inner: inner.makeIterator()) @@ -57,7 +59,7 @@ extension JoinedBySequence: Sequence { } extension JoinedBySequence: LazySequenceProtocol - where Base: LazySequenceProtocol {} +where Base: LazySequenceProtocol {} //===----------------------------------------------------------------------===// // JoinedByClosureSequence @@ -67,15 +69,17 @@ extension JoinedBySequence: LazySequenceProtocol /// concatenated using a given separator that depends on the sequences right /// before and after it. public struct JoinedByClosureSequence - where Base.Element: Sequence, Base.Element.Element == Separator.Element -{ +where Base.Element: Sequence, Base.Element.Element == Separator.Element { @usableFromInline - internal typealias Inner = FlattenSequence>> - + internal typealias Inner = FlattenSequence< + InterspersedMapSequence< + Base, EitherSequence + > + > + @usableFromInline internal let inner: Inner - + @inlinable internal init( base: Base, @@ -84,7 +88,8 @@ public struct JoinedByClosureSequence self.inner = base.lazy .interspersedMap( EitherSequence.left, - with: { EitherSequence.right(separator($0, $1)) }) + with: { EitherSequence.right(separator($0, $1)) } + ) .joined() } } @@ -93,18 +98,18 @@ extension JoinedByClosureSequence: Sequence { public struct Iterator: IteratorProtocol { @usableFromInline internal var inner: Inner.Iterator - + @inlinable internal init(inner: Inner.Iterator) { self.inner = inner } - + @inlinable public mutating func next() -> Base.Element.Element? { inner.next() } } - + @inlinable public func makeIterator() -> Iterator { Iterator(inner: inner.makeIterator()) @@ -120,15 +125,17 @@ extension JoinedByClosureSequence: LazySequenceProtocol {} /// A collection that presents the elements of a base collection of collections /// concatenated using a given separator. public struct JoinedByCollection - where Base.Element: Collection, Base.Element.Element == Separator.Element -{ +where Base.Element: Collection, Base.Element.Element == Separator.Element { @usableFromInline - internal typealias Inner = FlattenCollection>>> - + internal typealias Inner = FlattenCollection< + InterspersedSequence< + LazyMapSequence> + > + > + @usableFromInline internal let inner: Inner - + @inlinable internal init(base: Base, separator: Separator) { self.inner = base.lazy @@ -142,48 +149,48 @@ extension JoinedByCollection: Collection { public struct Index: Comparable { @usableFromInline internal let inner: Inner.Index - + @inlinable internal init(_ inner: Inner.Index) { self.inner = inner } - + @inlinable public static func == (lhs: Self, rhs: Self) -> Bool { lhs.inner == rhs.inner } - + @inlinable public static func < (lhs: Self, rhs: Self) -> Bool { lhs.inner < rhs.inner } } - + @inlinable public var startIndex: Index { Index(inner.startIndex) } - + @inlinable public var endIndex: Index { Index(inner.endIndex) } - + @inlinable public func index(after index: Index) -> Index { Index(inner.index(after: index.inner)) } - + @inlinable public subscript(position: Index) -> Base.Element.Element { inner[position.inner] } - + @inlinable public func index(_ index: Index, offsetBy distance: Int) -> Index { Index(inner.index(index.inner, offsetBy: distance)) } - + @inlinable public func index( _ index: Index, @@ -193,7 +200,7 @@ extension JoinedByCollection: Collection { inner.index(index.inner, offsetBy: distance, limitedBy: limit.inner) .map(Index.init) } - + @inlinable public func distance(from start: Index, to end: Index) -> Int { inner.distance(from: start.inner, to: end.inner) @@ -201,9 +208,10 @@ extension JoinedByCollection: Collection { } extension JoinedByCollection: BidirectionalCollection - where Base: BidirectionalCollection, - Base.Element: BidirectionalCollection, - Separator: BidirectionalCollection +where + Base: BidirectionalCollection, + Base.Element: BidirectionalCollection, + Separator: BidirectionalCollection { @inlinable public func index(before index: Index) -> Index { @@ -212,7 +220,7 @@ extension JoinedByCollection: BidirectionalCollection } extension JoinedByCollection: LazySequenceProtocol, LazyCollectionProtocol - where Base: LazySequenceProtocol {} +where Base: LazySequenceProtocol {} //===----------------------------------------------------------------------===// // JoinedByClosureCollection @@ -222,15 +230,17 @@ extension JoinedByCollection: LazySequenceProtocol, LazyCollectionProtocol /// concatenated using a given separator that depends on the collections right /// before and after it. public struct JoinedByClosureCollection - where Base.Element: Collection, Base.Element.Element == Separator.Element -{ +where Base.Element: Collection, Base.Element.Element == Separator.Element { @usableFromInline - internal typealias Inner = FlattenCollection>> - + internal typealias Inner = FlattenCollection< + InterspersedMapSequence< + Base, EitherSequence + > + > + @usableFromInline internal let inner: Inner - + @inlinable internal init( base: Base, @@ -239,7 +249,8 @@ public struct JoinedByClosureCollection self.inner = base.lazy .interspersedMap( EitherSequence.left, - with: { EitherSequence.right(separator($0, $1)) }) + with: { EitherSequence.right(separator($0, $1)) } + ) .joined() } } @@ -248,48 +259,48 @@ extension JoinedByClosureCollection: Collection { public struct Index: Comparable { @usableFromInline internal let inner: Inner.Index - + @inlinable internal init(_ inner: Inner.Index) { self.inner = inner } - + @inlinable public static func == (lhs: Self, rhs: Self) -> Bool { lhs.inner == rhs.inner } - + @inlinable public static func < (lhs: Self, rhs: Self) -> Bool { lhs.inner < rhs.inner } } - + @inlinable public var startIndex: Index { Index(inner.startIndex) } - + @inlinable public var endIndex: Index { Index(inner.endIndex) } - + @inlinable public func index(after index: Index) -> Index { Index(inner.index(after: index.inner)) } - + @inlinable public subscript(position: Index) -> Base.Element.Element { inner[position.inner] } - + @inlinable public func index(_ index: Index, offsetBy distance: Int) -> Index { Index(inner.index(index.inner, offsetBy: distance)) } - + @inlinable public func index( _ index: Index, @@ -299,7 +310,7 @@ extension JoinedByClosureCollection: Collection { inner.index(index.inner, offsetBy: distance, limitedBy: limit.inner) .map(Index.init) } - + @inlinable public func distance(from start: Index, to end: Index) -> Int { inner.distance(from: start.inner, to: end.inner) @@ -307,9 +318,10 @@ extension JoinedByClosureCollection: Collection { } extension JoinedByClosureCollection: BidirectionalCollection - where Base: BidirectionalCollection, - Base.Element: BidirectionalCollection, - Separator: BidirectionalCollection +where + Base: BidirectionalCollection, + Base.Element: BidirectionalCollection, + Separator: BidirectionalCollection { @inlinable public func index(before index: Index) -> Index { @@ -337,7 +349,7 @@ extension Sequence where Element: Sequence { { joined(by: CollectionOfOne(separator)) } - + /// Returns the concatenation of the elements in this sequence of sequences, /// inserting the given separator between each sequence. /// @@ -349,30 +361,29 @@ extension Sequence where Element: Sequence { public func joined( by separator: Separator ) -> JoinedBySequence - where Separator: Collection, Separator.Element == Element.Element - { + where Separator: Collection, Separator.Element == Element.Element { JoinedBySequence(base: self, separator: separator) } - + @inlinable internal func _joined( by update: (inout [Element.Element], Element, Element) throws -> Void ) rethrows -> [Element.Element] { var iterator = makeIterator() guard let first = iterator.next() else { return [] } - + var result = Array(first) var previous = first - + while let next = iterator.next() { try update(&result, previous, next) result.append(contentsOf: next) previous = next } - + return result } - + /// Returns the concatenation of the elements in this sequence of sequences, /// inserting the separator produced by the closure between each sequence. /// @@ -386,7 +397,7 @@ extension Sequence where Element: Sequence { ) rethrows -> [Element.Element] { try _joined(by: { $0.append(try separator($1, $2)) }) } - + /// Returns the concatenation of the elements in this sequence of sequences, /// inserting the separator produced by the closure between each sequence. /// @@ -398,8 +409,7 @@ extension Sequence where Element: Sequence { public func joined( by separator: (Element, Element) throws -> Separator ) rethrows -> [Element.Element] - where Separator: Sequence, Separator.Element == Element.Element - { + where Separator: Sequence, Separator.Element == Element.Element { try _joined(by: { $0.append(contentsOf: try separator($1, $2)) }) } } @@ -417,7 +427,7 @@ extension LazySequenceProtocol where Element: Sequence { ) -> JoinedByClosureSequence> { joined(by: { CollectionOfOne(separator($0, $1)) }) } - + /// Returns the concatenation of the elements in this sequence of sequences, /// inserting the separator produced by the closure between each sequence. @inlinable @@ -446,7 +456,7 @@ extension Collection where Element: Collection { { joined(by: CollectionOfOne(separator)) } - + /// Returns the concatenation of the elements in this collection of /// collections, inserting the given separator between each collection. /// @@ -476,7 +486,7 @@ extension LazySequenceProtocol where Elements: Collection, Element: Collection { ) -> JoinedByClosureCollection> { joined(by: { CollectionOfOne(separator($0, $1)) }) } - + /// Returns the concatenation of the elements in this collection of /// collections, inserting the separator produced by the closure between each /// sequence. diff --git a/Sources/Algorithms/Keyed.swift b/Sources/Algorithms/Keyed.swift index 0446ef69..099cd92f 100755 --- a/Sources/Algorithms/Keyed.swift +++ b/Sources/Algorithms/Keyed.swift @@ -10,40 +10,51 @@ //===----------------------------------------------------------------------===// extension Sequence { - /// Creates a new Dictionary from the elements of `self`, keyed by the - /// results returned by the given `keyForValue` closure. + /// Creates a new dictionary keyed by the result of the given closure, using + /// the latest element for any duplicate keys. /// - /// If the key derived for a new element collides with an existing key from a previous element, - /// the latest value will be kept. + /// If the key derived for a new element collides with an existing key from a + /// previous element, the latest value will be kept. /// - /// - Parameters: - /// - keyForValue: A closure that returns a key for each element in `self`. + /// - Parameter keyForValue: A closure that returns a key for each element in + /// `self`. + /// - Returns: A dictionary with key-value pairs generated by calling + /// `keyForValue` for each element in this sequence. If `keyForValue` + /// returns the same key for two or more elements, the returned dictionary + /// uses the last value for that key. @inlinable public func keyed( by keyForValue: (Element) throws -> Key ) rethrows -> [Key: Element] { - return try self.keyed(by: keyForValue, resolvingConflictsWith: { _, old, new in new }) + try self.reduce(into: [:]) { result, element in + try result[keyForValue(element)] = element + } } - /// Creates a new Dictionary from the elements of `self`, keyed by the - /// results returned by the given `keyForValue` closure. As the dictionary is - /// built, the initializer calls the `resolve` closure with the current and - /// new values for any duplicate keys. Pass a closure as `resolve` that - /// returns the value to use in the resulting dictionary: The closure can - /// choose between the two values, combine them to produce a new value, or - /// even throw an error. + /// Creates a new dictionary keyed by the result of the first closure, using + /// the second closure to resolve the ambiguity for any duplicate keys. + /// + /// As the dictionary is built, the initializer calls the `resolve` closure + /// with the current and new values for any duplicate keys. Pass a closure as + /// `resolve` that returns the value to use for that key in the resulting + /// dictionary. The closure can choose between the two values, combine them to + /// produce a new value, or even throw an error. /// /// - Parameters: /// - keyForValue: A closure that returns a key for each element in `self`. /// - resolve: A closure that is called with the values for any duplicate /// keys that are encountered. The closure returns the desired value for /// the final dictionary. + /// - Returns: A dictionary with key-value pairs generated by calling + /// `keyForValue` for each element in this sequence. If `keyForValue` + /// returns the same key for two or more elements, the result of the + /// `resolve` closure is used as the value for that key. @inlinable public func keyed( by keyForValue: (Element) throws -> Key, resolvingConflictsWith resolve: (Key, Element, Element) throws -> Element ) rethrows -> [Key: Element] { - var result = [Key: Element]() + var result: [Key: Element] = [:] for element in self { let key = try keyForValue(element) diff --git a/Sources/Algorithms/MinMax.swift b/Sources/Algorithms/MinMax.swift index ea6b5a7a..5d35e13c 100644 --- a/Sources/Algorithms/MinMax.swift +++ b/Sources/Algorithms/MinMax.swift @@ -10,24 +10,24 @@ //===----------------------------------------------------------------------===// extension Sequence { - /// Implementation for min(count:areInIncreasingOrder:) + // Implementation for min(count:areInIncreasingOrder:) @inlinable internal func _minImplementation( count: Int, sortedBy areInIncreasingOrder: (Element, Element) throws -> Bool ) rethrows -> [Element] { var iterator = makeIterator() - + var result: [Element] = [] result.reserveCapacity(count) while result.count < count, let e = iterator.next() { result.append(e) } try result.sort(by: areInIncreasingOrder) - + while let e = iterator.next() { - // To be part of `result`, `e` must be strictly less than `result.last`. - guard try areInIncreasingOrder(e, result.last!) else { continue } + // To be part of `result`, `e` must be strictly less than `result[count - 1]`. + guard try areInIncreasingOrder(e, result[count - 1]) else { continue } result.removeLast() let insertionIndex = try result.partitioningIndex { try areInIncreasingOrder(e, $0) } @@ -36,25 +36,25 @@ extension Sequence { return result } - - /// Implementation for max(count:areInIncreasingOrder:) + + // Implementation for max(count:areInIncreasingOrder:) @inlinable internal func _maxImplementation( count: Int, sortedBy areInIncreasingOrder: (Element, Element) throws -> Bool ) rethrows -> [Element] { var iterator = makeIterator() - + var result: [Element] = [] result.reserveCapacity(count) while result.count < count, let e = iterator.next() { result.append(e) } try result.sort(by: areInIncreasingOrder) - + while let e = iterator.next() { - // To be part of `result`, `e` must be greater/equal to `result.first`. - guard try !areInIncreasingOrder(e, result.first!) else { continue } + // To be part of `result`, `e` must be greater/equal to `result[0]`. + guard try !areInIncreasingOrder(e, result[0]) else { continue } let insertionIndex = try result.partitioningIndex { try areInIncreasingOrder(e, $0) } @@ -69,10 +69,10 @@ extension Sequence { } result[insertionIndex - 1] = e } - + return result } - + /// Returns the smallest elements of this sequence, as sorted by the given /// predicate. /// @@ -105,7 +105,9 @@ extension Sequence { count: Int, sortedBy areInIncreasingOrder: (Element, Element) throws -> Bool ) rethrows -> [Element] { - precondition(count >= 0, """ + precondition( + count >= 0, + """ Cannot find a minimum with a negative count of elements! """ ) @@ -150,7 +152,9 @@ extension Sequence { count: Int, sortedBy areInIncreasingOrder: (Element, Element) throws -> Bool ) rethrows -> [Element] { - precondition(count >= 0, """ + precondition( + count >= 0, + """ Cannot find a maximum with a negative count of elements! """ ) @@ -251,14 +255,16 @@ extension Collection { count: Int, sortedBy areInIncreasingOrder: (Element, Element) throws -> Bool ) rethrows -> [Element] { - precondition(count >= 0, """ + precondition( + count >= 0, + """ Cannot find a minimum with a negative count of elements! """ ) // Make sure we are within bounds. let prefixCount = Swift.min(count, self.count) - + // Do nothing if we're prefixing nothing. guard prefixCount > 0 else { return [] @@ -269,7 +275,7 @@ extension Collection { guard prefixCount < (self.count / 10) else { return Array(try sorted(by: areInIncreasingOrder).prefix(prefixCount)) } - + return try _minImplementation(count: count, sortedBy: areInIncreasingOrder) } @@ -305,14 +311,16 @@ extension Collection { count: Int, sortedBy areInIncreasingOrder: (Element, Element) throws -> Bool ) rethrows -> [Element] { - precondition(count >= 0, """ + precondition( + count >= 0, + """ Cannot find a maximum with a negative count of elements! """ ) // Make sure we are within bounds. let suffixCount = Swift.min(count, self.count) - + // Do nothing if we're suffixing nothing. guard suffixCount > 0 else { return [] diff --git a/Sources/Algorithms/Partition.swift b/Sources/Algorithms/Partition.swift index 181547ed..56fb8412 100644 --- a/Sources/Algorithms/Partition.swift +++ b/Sources/Algorithms/Partition.swift @@ -33,8 +33,9 @@ extension MutableCollection { ? subrange.lowerBound : subrange.upperBound } - - let h = n / 2, i = index(subrange.lowerBound, offsetBy: h) + + let h = n / 2 + let i = index(subrange.lowerBound, offsetBy: h) let j = try stablePartition( count: h, subrange: subrange.lowerBound.., - by belongsInSecondPartition: (Element) throws-> Bool + by belongsInSecondPartition: (Element) throws -> Bool ) rethrows -> Index { try stablePartition( count: distance(from: subrange.lowerBound, to: subrange.upperBound), subrange: subrange, by: belongsInSecondPartition) } - + /// Moves all elements satisfying the given predicate into a suffix of this /// collection, preserving the relative order of the elements in both /// partitions, and returns the start of the resulting suffix. @@ -75,11 +80,15 @@ extension MutableCollection { /// - Parameter belongsInSecondPartition: A predicate used to partition the /// collection. All elements satisfying this predicate are ordered after /// all elements not satisfying it. + /// - Returns: The pivot index `p`, which is the index of the first element + /// in the reordered collection that matches `belongsInSecondPartition`. + /// If no elements in the collection match `belongsInSecondPartition`, the + /// returned index is equal to the collection's `endIndex`. /// /// - Complexity: O(*n* log *n*), where *n* is the length of this collection. @inlinable public mutating func stablePartition( - by belongsInSecondPartition: (Element) throws-> Bool + by belongsInSecondPartition: (Element) throws -> Bool ) rethrows -> Index { try stablePartition( subrange: startIndex.. Index { var n = count var l = startIndex - + while n > 0 { let half = n / 2 let mid = index(l, offsetBy: half) @@ -224,24 +265,23 @@ extension Sequence { /// // Prints "["Kim", "Karl"]" /// /// - Parameter predicate: A closure that takes an element of the sequence as - /// its argument and returns a Boolean value indicating whether the element - /// should be included in the second returned array. Otherwise, the element - /// will appear in the first returned array. - /// + /// its argument and returns a Boolean value indicating whether the element + /// should be included in the second returned array. Otherwise, the element + /// will appear in the first returned array. /// - Returns: Two arrays with all of the elements of the receiver. The - /// first array contains all the elements that `predicate` didn’t allow, and - /// the second array contains all the elements that `predicate` allowed. The - /// order of the elements in the arrays matches the order of the elements in - /// the original sequence. + /// first array contains all the elements that `predicate` didn’t allow, and + /// the second array contains all the elements that `predicate` allowed. The + /// order of the elements in the arrays matches the order of the elements in + /// the original sequence. /// /// - Complexity: O(*n*), where *n* is the length of the sequence. @inlinable public func partitioned( by predicate: (Element) throws -> Bool ) rethrows -> (falseElements: [Element], trueElements: [Element]) { - var lhs = [Element]() - var rhs = [Element]() - + var lhs: [Element] = [] + var rhs: [Element] = [] + for element in self { if try predicate(element) { rhs.append(element) @@ -249,7 +289,7 @@ extension Sequence { lhs.append(element) } } - + return (lhs, rhs) } } @@ -269,15 +309,14 @@ extension Collection { /// // Prints "["Kim", "Karl"]" /// /// - Parameter predicate: A closure that takes an element of the collection - /// as its argument and returns a Boolean value indicating whether the element - /// should be included in the second returned array. Otherwise, the element - /// will appear in the first returned array. - /// + /// as its argument and returns a Boolean value indicating whether the + /// element should be included in the second returned array. Otherwise, the + /// element will appear in the first returned array. /// - Returns: Two arrays with all of the elements of the receiver. The - /// first array contains all the elements that `predicate` didn’t allow, and - /// the second array contains all the elements that `predicate` allowed. The - /// order of the elements in the arrays matches the order of the elements in - /// the original collection. + /// first array contains all the elements that `predicate` didn’t allow, and + /// the second array contains all the elements that `predicate` allowed. The + /// order of the elements in the arrays matches the order of the elements in + /// the original collection. /// /// - Complexity: O(*n*), where *n* is the length of the collection. @inlinable @@ -287,23 +326,26 @@ extension Collection { guard !self.isEmpty else { return ([], []) } - + // Since collections have known sizes, we can allocate one array of size // `self.count`, then insert items at the beginning or end of that contiguous // block. This way, we don’t have to do any dynamic array resizing. Since we // insert the right elements on the right side in reverse order, we need to // reverse them back to the original order at the end. - + let count = self.count - + // Inside of the `initializer` closure, we set what the actual mid-point is. // We will use this to partition the single array into two. var midPoint: Int = 0 - + let elements = try [Element]( unsafeUninitializedCapacity: count, initializingWith: { buffer, initializedCount in - var lhs = buffer.baseAddress! + // swift-format-ignore: NeverForceUnwrap + // Non-empty check above. + let bufferStart = buffer.baseAddress! + var lhs = bufferStart var rhs = lhs + buffer.count do { for element in self { @@ -315,26 +357,28 @@ extension Collection { lhs += 1 } } - - precondition(lhs == rhs, """ + + precondition( + lhs == rhs, + """ Collection's `count` differed from the number of elements iterated. """ ) - - let rhsIndex = rhs - buffer.baseAddress! + + let rhsIndex = rhs - bufferStart buffer[rhsIndex...].reverse() initializedCount = buffer.count - + midPoint = rhsIndex } catch { - let lhsCount = lhs - buffer.baseAddress! - let rhsCount = (buffer.baseAddress! + buffer.count) - rhs - buffer.baseAddress!.deinitialize(count: lhsCount) + let lhsCount = lhs - bufferStart + let rhsCount = (bufferStart + buffer.count) - rhs + bufferStart.deinitialize(count: lhsCount) rhs.deinitialize(count: rhsCount) throw error } }) - + let lhs = elements[.. { /// The base collection to iterate over for permutations. @usableFromInline internal let base: Base - + @usableFromInline internal let baseCount: Int - + /// The range of accepted sizes of permutations. /// /// - Note: This may be empty if the attempted range entirely exceeds the /// bounds of the size of the `base` collection. @usableFromInline internal let kRange: Range - + /// Initializes a `PermutationsSequence` for all permutations of `base` of /// size `k`. /// @@ -102,13 +102,13 @@ public struct PermutationsSequence { internal init(_ base: Base, k: Int? = nil) { let kRange: ClosedRange? if let countToChoose = k { - kRange = countToChoose ... countToChoose + kRange = countToChoose...countToChoose } else { kRange = nil } self.init(base, kRange: kRange) } - + /// Initializes a `PermutationsSequence` for all combinations of `base` of /// sizes within a given range. /// @@ -118,17 +118,18 @@ public struct PermutationsSequence { /// iterate over all permutations of the same size as the base collection. @inlinable internal init( - _ base: Base, kRange: R? + _ base: Base, + kRange: R? ) where R.Bound == Int { self.base = base let baseCount = base.count self.baseCount = baseCount let upperBound = baseCount + 1 - self.kRange = kRange?.relative(to: 0 ..< .max) - .clamped(to: 0 ..< upperBound) ?? - baseCount ..< upperBound + self.kRange = + kRange?.relative(to: 0 ..< .max) + .clamped(to: 0.. - + /// Whether or not iteration is finished (`kRange` is empty) @inlinable internal var isFinished: Bool { - return kRange.isEmpty + kRange.isEmpty } - + @usableFromInline internal var indexes: [Base.Index] - + @inlinable internal init(_ permutations: PermutationsSequence) { self.base = permutations.base @@ -170,7 +171,7 @@ extension PermutationsSequence: Sequence { self.kRange = permutations.kRange self.indexes = Array(permutations.base.indices) } - + /// Advances the `indexes` array such that the first `countToChoose` /// elements contain the next lexicographic ordering of elements. /// @@ -186,44 +187,49 @@ extension PermutationsSequence: Sequence { internal mutating func nextState() -> Bool { let countToChoose = self.kRange.lowerBound let edge = countToChoose - 1 - + // Find first index greater than the one at `edge`. - if let i = indexes[countToChoose...].firstIndex(where: { indexes[edge] < $0 }) { + if let i = indexes[countToChoose...] + .firstIndex(where: { indexes[edge] < $0 }) + { indexes.swapAt(edge, i) } else { - indexes.reverse(subrange: countToChoose ..< indexes.endIndex) - + indexes.reverse(subrange: countToChoose..= 0 && indexes[lastAscent] >= indexes[lastAscent + 1]) { + while lastAscent >= 0 && indexes[lastAscent] >= indexes[lastAscent + 1] + { lastAscent -= 1 } if lastAscent < 0 { return false } - + // Find rightmost index less than that at `lastAscent`. - if let i = indexes[lastAscent...].lastIndex(where: { indexes[lastAscent] < $0 }) { + if let i = indexes[lastAscent...] + .lastIndex(where: { indexes[lastAscent] < $0 }) + { indexes.swapAt(lastAscent, i) } - indexes.reverse(subrange: (lastAscent + 1) ..< indexes.endIndex) + indexes.reverse(subrange: (lastAscent + 1).. [Base.Element]? { guard !isFinished else { return nil } - + /// Advances `kRange` by incrementing its `lowerBound` until the range is /// empty, when iteration is finished. func advanceKRange() { kRange.removeFirst() indexes = Array(base.indices) } - + let countToChoose = self.kRange.lowerBound if countToChoose == 0 { defer { @@ -231,7 +237,7 @@ extension PermutationsSequence: Sequence { } return [] } - + let permutesFullCollection = (countToChoose == baseCount) if permutesFullCollection { // If we're permuting the full collection, each iteration is just a @@ -256,7 +262,7 @@ extension PermutationsSequence: Sequence { } } } - + @inlinable public func makeIterator() -> Iterator { Iterator(self) @@ -264,7 +270,7 @@ extension PermutationsSequence: Sequence { } extension PermutationsSequence: LazySequenceProtocol - where Base: LazySequenceProtocol {} +where Base: LazySequenceProtocol {} //===----------------------------------------------------------------------===// // permutations(ofCount:) @@ -323,6 +329,8 @@ extension Collection { /// clamped to the number of elements in this collection. Passing a range /// covering sizes greater than the number of elements in this collection /// results in an empty sequence. + /// - Returns: A sequence of the permutations of this collection's elements, + /// for all sizes in `kRange`, from smallest to largest. /// /// - Complexity: O(1) for random-access base collections. O(*n*) where *n* /// is the number of elements in the base collection, since @@ -333,7 +341,7 @@ extension Collection { ) -> PermutationsSequence where R.Bound == Int { PermutationsSequence(self, kRange: kRange) } - + /// Returns a collection of the permutations of this collection of the /// specified length. /// @@ -379,12 +387,15 @@ extension Collection { /// If `k` is `nil`, the resulting sequence represents permutations of this /// entire collection. If `k` is greater than the number of elements in /// this collection, the resulting sequence is empty. + /// - Returns: A sequence of the permutations of this collection's elements. /// /// - Complexity: O(1) for random-access base collections. O(*n*) where *n* /// is the number of elements in the base collection, since /// `PermutationsSequence` accesses the `count` of the base collection. @inlinable - public func permutations(ofCount k: Int? = nil) -> PermutationsSequence { + public func permutations( + ofCount k: Int? = nil + ) -> PermutationsSequence { precondition( k ?? 0 >= 0, "Can't have permutations with a negative number of elements.") @@ -405,10 +416,10 @@ public struct UniquePermutationsSequence { /// The base collection to iterate over for permutations. @usableFromInline internal let base: Base - + @usableFromInline internal var indexes: [Base.Index] - + @usableFromInline internal let kRange: Range } @@ -418,30 +429,31 @@ extension UniquePermutationsSequence where Base.Element: Hashable { internal static func _indexes(_ base: Base) -> [Base.Index] { let firstIndexesAndCountsByElement = Dictionary( base.indices.lazy.map { (base[$0], ($0, 1)) }, - uniquingKeysWith: { indexAndCount, _ in (indexAndCount.0, indexAndCount.1 + 1) }) - + uniquingKeysWith: { indexAndCount, _ in + (indexAndCount.0, indexAndCount.1 + 1) + }) + return firstIndexesAndCountsByElement .values.sorted(by: { $0.0 < $1.0 }) .flatMap { index, count in repeatElement(index, count: count) } } - + @inlinable internal init(_ elements: Base) { self.indexes = Self._indexes(elements) self.base = elements - self.kRange = self.indexes.count ..< (self.indexes.count + 1) + self.kRange = self.indexes.count..<(self.indexes.count + 1) } @inlinable internal init(_ base: Base, _ range: R) - where R.Bound == Int - { + where R.Bound == Int { self.indexes = Self._indexes(base) self.base = base - + let upperBound = self.indexes.count + 1 self.kRange = range.relative(to: 0 ..< .max) - .clamped(to: 0 ..< upperBound) + .clamped(to: 0.. @@ -461,19 +473,20 @@ extension UniquePermutationsSequence: Sequence { internal var initial = true @inlinable - internal init(_ elements: Base, indexes: [Base.Index], lengths: Range) { + internal init(_ elements: Base, indexes: [Base.Index], lengths: Range) + { self.base = elements self.indexes = indexes self.lengths = lengths } - + @inlinable public mutating func next() -> [Base.Element]? { // In the end case, `lengths` is an empty range. if lengths.isEmpty { return nil } - + // The first iteration must produce the original sorted array, before any // permutations. We skip the permutation the first time so that we can // always mutate the array _before_ returning a slice, which avoids @@ -490,11 +503,11 @@ extension UniquePermutationsSequence: Sequence { return nil } } - + return indexes[.. Iterator { Iterator(base, indexes: indexes, lengths: kRange) @@ -502,7 +515,7 @@ extension UniquePermutationsSequence: Sequence { } extension UniquePermutationsSequence: LazySequenceProtocol - where Base: LazySequenceProtocol {} +where Base: LazySequenceProtocol {} extension Collection where Element: Hashable { /// Returns a sequence of the unique permutations of this sequence of the @@ -540,6 +553,8 @@ extension Collection where Element: Hashable { /// If `k` is `nil`, the resulting sequence represents permutations of this /// entire collection. If `k` is greater than the number of elements in /// this collection, the resulting sequence is empty. + /// - Returns: A sequence of the unique permutations of this collection's + /// elements. /// /// - Complexity: O(*n*), where *n* is the number of elements in this /// collection. @@ -547,11 +562,10 @@ extension Collection where Element: Hashable { public func uniquePermutations(ofCount k: Int? = nil) -> UniquePermutationsSequence { - if let k = k { - return UniquePermutationsSequence(self, k ..< (k + 1)) - } else { + guard let k = k else { return UniquePermutationsSequence(self) } + return UniquePermutationsSequence(self, k..<(k + 1)) } /// Returns a collection of the unique permutations of this sequence with @@ -580,6 +594,8 @@ extension Collection where Element: Hashable { /// clamped to the number of elements in this collection. Passing a range /// covering sizes greater than the number of elements in this collection /// results in an empty sequence. + /// - Returns: A sequence of the unique permutations of this collection's + /// elements, for all sizes in `kRange`, from smallest to largest. /// /// - Complexity: O(*n*), where *n* is the number of elements in this /// collection. diff --git a/Sources/Algorithms/Product.swift b/Sources/Algorithms/Product.swift index d5611e99..5ab05091 100644 --- a/Sources/Algorithms/Product.swift +++ b/Sources/Algorithms/Product.swift @@ -14,11 +14,11 @@ public struct Product2Sequence { /// The outer sequence in the product. @usableFromInline internal let base1: Base1 - + /// The inner sequence in the product. @usableFromInline internal let base2: Base2 - + @inlinable internal init(_ base1: Base1, _ base2: Base2) { self.base1 = base1 @@ -28,7 +28,7 @@ public struct Product2Sequence { extension Product2Sequence: Sequence { public typealias Element = (Base1.Element, Base2.Element) - + /// The iterator for a `Product2Sequence` sequence. public struct Iterator: IteratorProtocol { @usableFromInline @@ -47,10 +47,12 @@ extension Product2Sequence: Sequence { self.i2 = c.base2.makeIterator() self.element1 = nil } - + @inlinable - public mutating func next() -> (Base1.Element, - 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. @@ -59,27 +61,29 @@ extension Product2Sequence: Sequence { // once Base1 is exhausted, return `nil` forever if element1 == nil { return nil } } - + // Get the next element from the second sequence, if not // at end. if let element2 = i2.next() { + // swift-format-ignore: NeverForceUnwrap + // `element1` has a nil check above. return (element1!, element2) } - + // We've reached the end of the second sequence, so: // 1) Get the next element of the first sequence, if exists // 2) Restart iteration of the second sequence // 3) Get the first element of the second sequence, if exists element1 = i1.next() - guard let element1 = element1 - else { return nil } - + guard let element1 else { + return nil + } + i2 = base2.makeIterator() - if let element2 = i2.next() { - return (element1, element2) - } else { + guard let element2 = i2.next() else { return nil } + return (element1, element2) } } @@ -96,43 +100,45 @@ extension Product2Sequence: Collection where Base1: Collection { internal var i1: Base1.Index @usableFromInline internal var i2: Base2.Index - + @inlinable internal init(i1: Base1.Index, i2: Base2.Index) { self.i1 = i1 self.i2 = i2 } - + @inlinable public static func < (lhs: Index, rhs: Index) -> Bool { (lhs.i1, lhs.i2) < (rhs.i1, rhs.i2) } } - + @inlinable public var count: Int { base1.count * base2.count } - + @inlinable public var startIndex: Index { Index( i1: base2.isEmpty ? base1.endIndex : base1.startIndex, i2: base2.startIndex) } - + @inlinable public var endIndex: Index { // `base2.startIndex` simplifies index calculations. Index(i1: base1.endIndex, i2: base2.startIndex) } - + @inlinable - public subscript(position: Index) -> (Base1.Element, - Base2.Element) { + public subscript(position: Index) -> ( + Base1.Element, + Base2.Element + ) { (base1[position.i1], base2[position.i2]) } - + /// Forms an index from a pair of base indices, normalizing /// `(i, base2.endIndex)` to `(base1.index(after: i), base2.startIndex)` if /// necessary. @@ -142,24 +148,24 @@ extension Product2Sequence: Collection where Base1: Collection { ? Index(i1: base1.index(after: i1), i2: base2.startIndex) : Index(i1: i1, i2: i2) } - + @inlinable public func index(after i: Index) -> Index { precondition(i.i1 != base1.endIndex, "Can't advance past endIndex") return normalizeIndex(i.i1, base2.index(after: i.i2)) } - + @inlinable public func distance(from start: Index, to end: Index) -> Int { guard start.i1 <= end.i1 - else { return -distance(from: end, to: start) } + else { return -distance(from: end, to: start) } guard start.i1 != end.i1 - else { return base2.distance(from: start.i2, to: end.i2) } - + else { return base2.distance(from: start.i2, to: end.i2) } + // The number of full cycles through `base2` between `start` and `end`, // excluding the cycles that `start` and `end` are on. let fullBase2Cycles = base1[start.i1.. [l l l c c c c c c|r r r] // ^ // end.i2 - + let left = base2[.. [l l l|c c c c c c r r r] // ^ // end.i2 - + let left = base2[.. 0 else { return right + left } - + let center = base2[end.i2.. Index { guard distance != 0 else { return i } - + return distance > 0 ? offsetForward(i, by: distance) : offsetBackward(i, by: -distance) } - + @inlinable public func index( _ i: Index, @@ -232,51 +238,55 @@ extension Product2Sequence: Collection where Base1: Collection { @inlinable internal func offsetForward(_ i: Index, by distance: Int) -> Index { guard let index = offsetForward(i, by: distance, limitedBy: endIndex) - else { fatalError("Index is out of bounds") } + else { fatalError("Index is out of bounds") } return index } - + @inlinable internal func offsetBackward(_ i: Index, by distance: Int) -> Index { guard let index = offsetBackward(i, by: distance, limitedBy: startIndex) - else { fatalError("Index is out of bounds") } + else { fatalError("Index is out of bounds") } return index } - + @inlinable internal func offsetForward( - _ i: Index, by distance: Int, limitedBy limit: Index + _ i: Index, + by distance: Int, + limitedBy limit: Index ) -> Index? { assert(distance >= 0) assert(limit >= i) - + if limit.i1 == i.i1 { // Delegate to `base2` if the offset is limited to `i.i1`. // // i.i2 limit.i2 // v v // i.i1 > [x x x|x x x x x x|x x x] - + return base2.index(i.i2, offsetBy: distance, limitedBy: limit.i2) .map { i2 in Index(i1: i.i1, i2: i2) } } - - - if let i2 = base2.index(i.i2, offsetBy: distance, limitedBy: base2.endIndex) { + + if let i2 = + base2 + .index(i.i2, offsetBy: distance, limitedBy: base2.endIndex) + { // `distance` does not overflow `base2[i.i2...]`. // // i.i2 i2 // v v // i.i1 > [x x x|x x x x x x|x x x] // [ |> > > > > >| ] (`distance`) - + return normalizeIndex(i.i1, i2) } - + let suffixCount = base2[i.i2...].count let remaining = distance - suffixCount let nextI1 = base1.index(after: i.i1) - + if limit.i1 == nextI1 { // Delegate to `base2` if the offset is limited to `nextI1`. // @@ -286,12 +296,17 @@ extension Product2Sequence: Collection where Base1: Collection { // nextI1 > [x x x x x x x x x|x x x] // ^ // limit.i2 - - return base2.index(base2.startIndex, offsetBy: remaining, limitedBy: limit.i2) + + return + base2 + .index(base2.startIndex, offsetBy: remaining, limitedBy: limit.i2) .map { i2 in Index(i1: nextI1, i2: i2) } } - - if let i2 = base2.index(base2.startIndex, offsetBy: remaining, limitedBy: i.i2) { + + if let i2 = + base2 + .index(base2.startIndex, offsetBy: remaining, limitedBy: i.i2) + { // `remaining` does not overflow `base2[.. [x x x|x x x x x x x x x] // ^ // i2 - + return Index(i1: nextI1, i2: i2) } - + let prefixCount = base2[.. Index? { assert(distance >= 0) assert(limit <= i) - + if limit.i1 == i.i1 { // Delegate to `base2` if the offset is limited to `i.i1`. // // limit.i2 i.i2 // v v // i.i1 > [x x x|x x x x x x|x x x] - + return base2.index(i.i2, offsetBy: -distance, limitedBy: limit.i2) .map { i2 in Index(i1: i.i1, i2: i2) } } - - if let i2 = base2.index(i.i2, offsetBy: -distance, limitedBy: base2.startIndex) { + + if let i2 = + base2 + .index(i.i2, offsetBy: -distance, limitedBy: base2.startIndex) + { // `distance` does not underflow `base2[.. [x x x|x x x x x x|x x x] // [ |< < < < < <| ] (`distance`) - + return Index(i1: i.i1, i2: i2) } - + let prefixCount = base2[.. [x x x x x x x x x|x x x] // ^ // i.i2 - - return base2.index(base2.endIndex, offsetBy: -remaining, limitedBy: limit.i2) + + return + base2 + .index(base2.endIndex, offsetBy: -remaining, limitedBy: limit.i2) .map { i2 in Index(i1: previousI1, i2: i2) } } - - if let i2 = base2.index(base2.endIndex, offsetBy: -remaining, limitedBy: i.i2) { + + if let i2 = + base2 + .index(base2.endIndex, offsetBy: -remaining, limitedBy: i.i2) + { // `remaining` does not underflow `base2[i.i2...]`. // // i2 @@ -379,17 +407,17 @@ extension Product2Sequence: Collection where Base1: Collection { // i.i1 > [x x x|x x x x x x x x x] // ^ // i.i2 - + return Index(i1: previousI1, i2: i2) } - + let suffixCount = base2[i.i2...].count let base2Count = prefixCount + suffixCount let base1Distance = remaining / base2Count - + // The distance from `base2.endIndex` to the target. let base2Distance = remaining % base2Count - + if base2Distance == 0 { // We end up exactly between two cycles, so `base1Distance` would // overshoot the target by 1. @@ -402,46 +430,54 @@ extension Product2Sequence: Collection where Base1: Collection { // i.i1 > [x x x|x x x x x x x x x] // ^ // i.i2 - - if let i1 = base1.index(previousI1, offsetBy: -(base1Distance - 1), limitedBy: limit.i1) { - let index = Index(i1: i1, i2: base2.startIndex) - return index < limit ? nil : index - } else { + + guard + let i1 = base1.index( + previousI1, + offsetBy: -(base1Distance - 1), + limitedBy: limit.i1) + else { return nil } + let index = Index(i1: i1, i2: base2.startIndex) + return index < limit ? nil : index } - - guard let i1 = base1.index(previousI1, offsetBy: -base1Distance, limitedBy: limit.i1) - else { return nil } - + + guard + let i1 = + base1 + .index(previousI1, offsetBy: -base1Distance, limitedBy: limit.i1) + else { return nil } + let base2Limit = limit.i1 == i1 ? limit.i2 : base2.startIndex - return base2.index(base2.endIndex, offsetBy: -base2Distance, limitedBy: base2Limit) + return + base2 + .index(base2.endIndex, offsetBy: -base2Distance, limitedBy: base2Limit) .map { i2 in Index(i1: i1, i2: i2) } } } extension Product2Sequence: BidirectionalCollection - where Base1: BidirectionalCollection, Base2: BidirectionalCollection -{ +where Base1: BidirectionalCollection, Base2: BidirectionalCollection { @inlinable public func index(before i: Index) -> Index { - precondition(i != startIndex, - "Can't move before startIndex") - if i.i2 == base2.startIndex { - return Index( - i1: base1.index(before: i.i1), - i2: base2.index(before: base2.endIndex)) - } else { + precondition( + i != startIndex, + "Can't move before startIndex") + guard i.i2 == base2.startIndex else { return Index(i1: i.i1, i2: base2.index(before: i.i2)) } + return Index( + i1: base1.index(before: i.i1), + i2: base2.index(before: base2.endIndex)) } } extension Product2Sequence: RandomAccessCollection - where Base1: RandomAccessCollection, Base2: RandomAccessCollection {} +where Base1: RandomAccessCollection, Base2: RandomAccessCollection {} extension Product2Sequence.Index: Hashable - where Base1.Index: Hashable, Base2.Index: Hashable {} +where Base1.Index: Hashable, Base2.Index: Hashable {} //===----------------------------------------------------------------------===// // product(_:_:) @@ -454,7 +490,6 @@ extension Product2Sequence.Index: Hashable /// element of the tuple is from the first collection and the second element is /// from the second collection. /// -/// /// let numbers = 1...3 /// let colors = ["cerise", "puce", "heliotrope"] /// for (number, color) in product(numbers, colors) { @@ -478,11 +513,14 @@ extension Product2Sequence.Index: Hashable /// - Parameters: /// - s1: The first sequence to iterate over. /// - s2: The second sequence to iterate over. +/// - Returns: A sequence of tuples, with every combination of the elements +/// of `s1` and `s2`, in order. /// /// - Complexity: O(1) @inlinable public func product( - _ s1: Base1, _ s2: Base2 + _ s1: Base1, + _ s2: Base2 ) -> Product2Sequence { Product2Sequence(s1, s2) } diff --git a/Sources/Algorithms/RandomSample.swift b/Sources/Algorithms/RandomSample.swift index 4ec863d6..21bb31bd 100644 --- a/Sources/Algorithms/RandomSample.swift +++ b/Sources/Algorithms/RandomSample.swift @@ -18,7 +18,7 @@ extension Double { internal static func root(_ x: Double, _ n: Int) -> Double { guard x >= 0 || n % 2 != 0 else { return .nan } if n == 3 { return cbrt(x) } - return Double(signOf: x, magnitudeOf: pow(x.magnitude, 1/Double(n))) + return Double(signOf: x, magnitudeOf: pow(x.magnitude, 1 / Double(n))) } @_transparent @@ -36,8 +36,7 @@ internal import RealModule #elseif swift(>=5.10) import RealModule #else -@_implementationOnly -import RealModule +@_implementationOnly import RealModule #endif //===----------------------------------------------------------------------===// @@ -57,16 +56,17 @@ extension Collection { /// - Complexity: O(*n*), where *n* is the length of the collection. @inlinable public func randomStableSample( - count k: Int, using rng: inout G + count k: Int, + using rng: inout G ) -> [Element] { guard k > 0 else { return [] } - + var remainingCount = count guard k < remainingCount else { return Array(self) } - + var result: [Element] = [] result.reserveCapacity(k) - + var i = startIndex var countToSelect = k while countToSelect > 0 { @@ -79,10 +79,10 @@ extension Collection { formIndex(after: &i) remainingCount -= 1 } - + return result } - + /// Randomly selects the specified number of elements from this collection, /// maintaining their relative order. /// @@ -111,14 +111,16 @@ extension Collection { @usableFromInline internal func nextW( - k: Int, using rng: inout G + k: Int, + using rng: inout G ) -> Double { Double.root(.random(in: 0..<1, using: &rng), k) } @usableFromInline internal func nextOffset( - w: Double, using rng: inout G + w: Double, + using rng: inout G ) -> Int { let offset = Double.log(.random(in: 0..<1, using: &rng)) / .log(onePlus: -w) return offset < Double(Int.max) ? Int(offset) : Int.max @@ -139,29 +141,30 @@ extension Collection { /// where *n* is the length of the collection. @inlinable public func randomSample( - count k: Int, using rng: inout G + count k: Int, + using rng: inout G ) -> [Element] { guard k > 0 else { return [] } var w = 1.0 var result: [Element] = [] result.reserveCapacity(k) - + // Fill the reservoir with the first `k` elements. var i = startIndex while i != endIndex, result.count < k { result.append(self[i]) formIndex(after: &i) } - + while i != endIndex { // Calculate the next value of w. w *= nextW(k: k, using: &rng) - + // Find index of the next element to swap into the reservoir. let offset = nextOffset(w: w, using: &rng) i = index(i, offsetBy: offset, limitedBy: endIndex) ?? endIndex - + if i != endIndex { // Swap selected element with a randomly chosen one in the reservoir. let j = Int.random(in: 0..( - count k: Int, using rng: inout G + count k: Int, + using rng: inout G ) -> [Element] { guard k > 0 else { return [] } - + var w = 1.0 var result: [Element] = [] result.reserveCapacity(k) - + // Fill the reservoir with the first `k` elements. var iterator = makeIterator() while result.count < k, let el = iterator.next() { @@ -225,26 +229,26 @@ extension Sequence { while true { // Calculate the next value of w. w *= nextW(k: k, using: &rng) - + // Find the offset of the next element to swap into the reservoir. var offset = nextOffset(w: w, using: &rng) - + // Skip over `offset` elements to find the selected element. - while offset > 0, let _ = iterator.next() { + while offset > 0, iterator.next() != nil { offset -= 1 } guard let nextElement = iterator.next() else { break } - + // Swap selected element with a randomly chosen one in the reservoir. let j = Int.random(in: 0.. Void ) rethrows -> [Result] { - var output = [Result]() + var output: [Result] = [] output.reserveCapacity(underestimatedCount + 1) output.append(initial) @@ -249,7 +249,7 @@ extension ExclusiveReductionsSequence: Collection where Base: Collection { case base(Base.Index, Result) case end } - + @usableFromInline internal let representation: Representation @@ -257,7 +257,7 @@ extension ExclusiveReductionsSequence: Collection where Base: Collection { internal init(_ representation: Representation) { self.representation = representation } - + @inlinable public static func == (lhs: Self, rhs: Self) -> Bool { switch (lhs.representation, rhs.representation) { @@ -269,7 +269,7 @@ extension ExclusiveReductionsSequence: Collection where Base: Collection { return false } } - + @inlinable public static func < (lhs: Self, rhs: Self) -> Bool { switch (lhs.representation, rhs.representation) { @@ -319,11 +319,11 @@ extension ExclusiveReductionsSequence: Collection where Base: Collection { @inlinable public func distance(from start: Index, to end: Index) -> Int { switch (start.representation, end.representation) { - case let (.base(start, _), .base(end, _)): + case (.base(let start, _), .base(let end, _)): return base.distance(from: start, to: end) - case let (.base(index, _), .end): + case (.base(let index, _), .end): return base.distance(from: index, to: base.endIndex) + 1 - case let (.end, .base(index, _)): + case (.end, .base(let index, _)): return base.distance(from: base.endIndex, to: index) - 1 case (.end, .end): return 0 @@ -334,11 +334,10 @@ extension ExclusiveReductionsSequence: Collection where Base: Collection { extension ExclusiveReductionsSequence: LazySequenceProtocol {} extension ExclusiveReductionsSequence: LazyCollectionProtocol - where Base: Collection {} +where Base: Collection {} extension ExclusiveReductionsSequence.Index: Hashable - where Base.Index: Hashable -{ +where Base.Index: Hashable { @inlinable public func hash(into hasher: inout Hasher) { switch representation { @@ -483,7 +482,7 @@ extension InclusiveReductionsSequence: Collection where Base: Collection { public struct Index: Comparable { @usableFromInline internal let base: Base.Index - + @usableFromInline internal let result: Element? @@ -497,7 +496,7 @@ extension InclusiveReductionsSequence: Collection where Base: Collection { public static func < (lhs: Self, rhs: Self) -> Bool { lhs.base < rhs.base } - + @inlinable public static func == (lhs: Self, rhs: Self) -> Bool { lhs.base == rhs.base @@ -519,7 +518,7 @@ extension InclusiveReductionsSequence: Collection where Base: Collection { guard let result = index.result else { fatalError("Can't subscript using endIndex") } - + return result } @@ -528,12 +527,13 @@ extension InclusiveReductionsSequence: Collection where Base: Collection { guard let result = index.result else { fatalError("Can't advance past endIndex") } - + let index = base.index(after: index.base) - let nextResult = index == base.endIndex + let nextResult = + index == base.endIndex ? nil : transform(result, base[index]) - + return Index(base: index, result: nextResult) } @@ -546,11 +546,10 @@ extension InclusiveReductionsSequence: Collection where Base: Collection { extension InclusiveReductionsSequence: LazySequenceProtocol {} extension InclusiveReductionsSequence: LazyCollectionProtocol - where Base: Collection {} +where Base: Collection {} extension InclusiveReductionsSequence.Index: Hashable - where Base.Index: Hashable -{ +where Base.Index: Hashable { @inlinable public func hash(into hasher: inout Hasher) { hasher.combine(base) diff --git a/Sources/Algorithms/Rotate.swift b/Sources/Algorithms/Rotate.swift index 4d0fa0c0..ef991e5e 100644 --- a/Sources/Algorithms/Rotate.swift +++ b/Sources/Algorithms/Rotate.swift @@ -15,8 +15,10 @@ extension MutableCollection where Self: BidirectionalCollection { /// Reverses the elements of the collection, moving from each end until - /// `limit` is reached from either direction. The returned indices are the - /// start and end of the range of unreversed elements. + /// `limit` is reached from either direction. + /// + /// The returned indices are the start and end of the range of unreversed + /// elements. /// /// Input: /// [a b c d e f g h i j k l m n o p] @@ -28,11 +30,12 @@ extension MutableCollection where Self: BidirectionalCollection { /// lower upper /// /// - Postcondition: For returned indices `(lower, upper)`: - /// `lower == limit || upper == limit` + /// `lower == limit || upper == limit`. @inlinable @discardableResult internal mutating func _reverse( - subrange: Range, until limit: Index + subrange: Range, + until limit: Index ) -> (Index, Index) { var lower = subrange.lowerBound var upper = subrange.upperBound @@ -43,7 +46,7 @@ extension MutableCollection where Self: BidirectionalCollection { } return (lower, upper) } - + /// Reverses the elements within the given subrange. /// /// This example reverses the numbers within the subrange at the start of the @@ -75,8 +78,10 @@ extension MutableCollection where Self: BidirectionalCollection { extension MutableCollection { /// Swaps the elements of the two given subranges, up to the upper bound of - /// the smaller subrange. The returned indices are the ends of the two ranges - /// that were actually swapped. + /// the smaller subrange. + /// + /// The returned indices are the ends of the two ranges that were actually + /// swapped. /// /// Input: /// [a b c d e f g h i j k l m n o p] @@ -96,19 +101,19 @@ extension MutableCollection { /// - p == lhs.upperBound || q == rhs.upperBound @inlinable internal mutating func _swapNonemptySubrangePrefixes( - _ lhs: Range, _ rhs: Range + _ lhs: Range, + _ rhs: Range ) -> (Index, Index) { assert(!lhs.isEmpty) assert(!rhs.isEmpty) - + var p = lhs.lowerBound var q = rhs.lowerBound repeat { swapAt(p, q) formIndex(after: &p) formIndex(after: &q) - } - while p != lhs.upperBound && q != rhs.upperBound + } while p != lhs.upperBound && q != rhs.upperBound return (p, q) } @@ -139,13 +144,14 @@ extension MutableCollection { subrange: Range, toStartAt newStart: Index ) -> Index { - var m = newStart, s = subrange.lowerBound + var m = newStart + var s = subrange.lowerBound let e = subrange.upperBound - + // Handle the trivial cases if s == m { return e } if m == e { return s } - + // We have two regions of possibly-unequal length that need to be exchanged. // The return value of this method is going to be the position following // that of the element that is currently last (element j). @@ -154,7 +160,7 @@ extension MutableCollection { // ^ ^ ^ ^ ^ ^ // s m e s m e // - var ret = e // start with a known incorrect result. + var ret = e // start with a known incorrect result. while true { // Exchange the leading elements of each region (up to the length of the // shorter region). @@ -166,7 +172,7 @@ extension MutableCollection { // s s1 m m1/e s s1/m m1 e // let (s1, m1) = _swapNonemptySubrangePrefixes(s.. Iterator { Iterator( @@ -101,25 +101,25 @@ extension SplitSequence: Sequence { extension SplitSequence.Iterator: IteratorProtocol { @inlinable public mutating func next() -> Element? { - var currentElement = base.next() + var nextElement = base.next() var subsequence: Element = [] // Add the next elements of the base sequence to this subsequence, until we // reach a separator, unless we've already split the maximum number of // times. In all cases, stop at the end of the base sequence. - while currentElement != nil { - if splitCount < maxSplits && isSeparator(currentElement!) { + while let currentElement = nextElement { + if splitCount < maxSplits && isSeparator(currentElement) { if omittingEmptySubsequences && subsequence.isEmpty { // Keep going if we don't want to return an empty subsequence. - currentElement = base.next() + nextElement = base.next() continue } else { splitCount += 1 break } } else { - subsequence.append(currentElement!) - currentElement = base.next() + subsequence.append(currentElement) + nextElement = base.next() } } @@ -127,7 +127,7 @@ extension SplitSequence.Iterator: IteratorProtocol { // and we've either returned the maximum number of subsequences (one more // than the number of separators), or the only subsequence left to return is // empty and we're omitting those. - if currentElement == nil + if nextElement == nil && (sequenceLength == splitCount + 1 || omittingEmptySubsequences && subsequence.isEmpty) { @@ -219,7 +219,7 @@ extension LazySequenceProtocol { /// satisfying the `isSeparator` predicate and for each element at the /// start or end of the sequence satisfying the `isSeparator` /// predicate. The default value is `true`. - /// - whereSeparator: A closure that takes an element as an argument and + /// - isSeparator: A closure that takes an element as an argument and /// returns a Boolean value indicating whether the sequence should be /// split at that element. /// - Returns: A lazy sequence of subsequences, split from this sequence's @@ -634,7 +634,7 @@ extension LazySequenceProtocol where Self: Collection, Elements: Collection { /// satisfying the `isSeparator` predicate and for each element at the /// start or end of the collection satisfying the `isSeparator` /// predicate. The default value is `true`. - /// - whereSeparator: A closure that takes an element as an argument and + /// - isSeparator: A closure that takes an element as an argument and /// returns a Boolean value indicating whether the collection should be /// split at that element. /// - Returns: A lazy collection of subsequences, split from this collection's @@ -659,8 +659,7 @@ extension LazySequenceProtocol where Self: Collection, Elements: Collection { } extension LazySequenceProtocol - where Self: Collection, Elements: Collection, Element: Equatable -{ +where Self: Collection, Elements: Collection, Element: Equatable { /// Lazily returns the longest possible subsequences of the collection, in /// order, around elements equal to the given element. /// diff --git a/Sources/Algorithms/Stride.swift b/Sources/Algorithms/Stride.swift index bf91612b..fc521ba6 100644 --- a/Sources/Algorithms/Stride.swift +++ b/Sources/Algorithms/Stride.swift @@ -15,7 +15,7 @@ extension Sequence { /// Returns a sequence stepping through the elements every `step` starting at - /// the first value. Any remainders of the stride will be trimmed. + /// the first value. /// /// (0...10).striding(by: 2) // == [0, 2, 4, 6, 8, 10] /// (0...10).striding(by: 3) // == [0, 3, 6, 9] @@ -24,8 +24,9 @@ extension Sequence { /// striding `step`. /// /// - Parameter step: The amount to step with each iteration. - /// - Returns: Returns a sequence for stepping through the elements by the - /// specified amount. + /// - Returns: A sequence for stepping through the elements by the specified + /// amount. Any remaining elements after the last multiple of the stride + /// are omitted. @inlinable public func striding(by step: Int) -> StridingSequence { StridingSequence(base: self, stride: step) @@ -34,18 +35,18 @@ extension Sequence { extension Collection { /// Returns a sequence stepping through the elements every `step` starting at - /// the first value. Any remainders of the stride will be trimmed. + /// the first value. /// /// (0...10).striding(by: 2) // == [0, 2, 4, 6, 8, 10] /// (0...10).striding(by: 3) // == [0, 3, 6, 9] /// - /// - Complexity: O(1). Access to successive values is O(1) if the collection - /// conforms to `RandomAccessCollection`; otherwise, O(_k_), where _k_ is - /// the striding `step`. + /// - Complexity: O(1). Access to successive values is O(k) where _k_ is the + /// striding `step`. /// /// - Parameter step: The amount to step with each iteration. - /// - Returns: Returns a collection for stepping through the elements by the - /// specified amount. + /// - Returns: A sequence for stepping through the elements by the specified + /// amount. Any remaining elements after the last multiple of the stride + /// are omitted. @inlinable public func striding(by step: Int) -> StridingCollection { StridingCollection(base: self, stride: step) @@ -56,10 +57,10 @@ extension Collection { public struct StridingSequence { @usableFromInline internal let base: Base - + @usableFromInline internal let stride: Int - + @inlinable internal init(base: Base, stride: Int) { precondition(stride > 0, "Stride must be greater than zero") @@ -80,19 +81,19 @@ extension StridingSequence: Sequence { public struct Iterator: IteratorProtocol { @usableFromInline internal var iterator: Base.Iterator - + @usableFromInline internal let stride: Int - + @usableFromInline internal var striding: Bool = false - + @inlinable internal init(iterator: Base.Iterator, stride: Int) { self.iterator = iterator self.stride = stride } - + @inlinable public mutating func next() -> Base.Element? { guard striding else { @@ -105,7 +106,7 @@ extension StridingSequence: Sequence { return iterator.next() } } - + @inlinable public func makeIterator() -> Iterator { Iterator(iterator: base.makeIterator(), stride: stride) @@ -113,16 +114,16 @@ extension StridingSequence: Sequence { } extension StridingSequence: LazySequenceProtocol - where Base: LazySequenceProtocol {} +where Base: LazySequenceProtocol {} /// A wrapper that strides over a base collection. public struct StridingCollection { @usableFromInline internal let base: Base - + @usableFromInline internal let stride: Int - + @inlinable internal init(base: Base, stride: Int) { precondition(stride > 0, "striding must be greater than zero") @@ -143,39 +144,39 @@ extension StridingCollection: Collection { public struct Index: Comparable { @usableFromInline internal let base: Base.Index - + @usableFromInline internal init(_ base: Base.Index) { self.base = base } - + @inlinable public static func < (lhs: Index, rhs: Index) -> Bool { lhs.base < rhs.base } } - + @inlinable public var startIndex: Index { Index(base.startIndex) } - + @inlinable public var endIndex: Index { Index(base.endIndex) } - + @inlinable public subscript(i: Index) -> Base.Element { base[i.base] } - + @inlinable public func index(after i: Index) -> Index { precondition(i.base != base.endIndex, "Advancing past end index") return index(i, offsetBy: 1) } - + @inlinable public func index( _ i: Index, @@ -184,12 +185,12 @@ extension StridingCollection: Collection { ) -> Index? { guard n != 0 else { return i } guard limit != i else { return nil } - + return n > 0 ? offsetForward(i, offsetBy: n, limitedBy: limit) : offsetBackward(i, offsetBy: -n, limitedBy: limit) } - + @inlinable internal func offsetForward( _ i: Index, @@ -197,16 +198,17 @@ extension StridingCollection: Collection { limitedBy limit: Index ) -> Index? { if limit < i { - if let idx = base.index( - i.base, - offsetBy: n * stride, - limitedBy: base.endIndex - ) { - return Index(idx) - } else { + guard + let idx = base.index( + i.base, + offsetBy: n * stride, + limitedBy: base.endIndex + ) + else { assert(distance(from: i, to: endIndex) == n, "Advancing past end index") return endIndex } + return Index(idx) } else if let idx = base.index( i.base, offsetBy: n * stride, @@ -219,7 +221,7 @@ extension StridingCollection: Collection { : nil } } - + @inlinable internal func offsetBackward( _ i: Index, @@ -236,37 +238,41 @@ extension StridingCollection: Collection { distance = n * -stride } return base.index( - i.base, - offsetBy: distance, - limitedBy: limit.base + i.base, + offsetBy: distance, + limitedBy: limit.base ).map(Index.init) } - + @inlinable public var count: Int { base.isEmpty ? 0 : (base.count - 1) / stride + 1 } - + @inlinable public func distance(from start: Index, to end: Index) -> Int { let distance = base.distance(from: start.base, to: end.base) return distance / stride + (distance % stride).signum() } - + @inlinable public func index(_ i: Index, offsetBy distance: Int) -> Index { - precondition(distance <= 0 || i.base != base.endIndex, "Advancing past end index") - precondition(distance >= 0 || i.base != base.startIndex, "Incrementing past start index") + precondition( + distance <= 0 || i.base != base.endIndex, "Advancing past end index") + precondition( + distance >= 0 || i.base != base.startIndex, + "Incrementing past start index") let limit = distance > 0 ? endIndex : startIndex - let idx = index(i, offsetBy: distance, limitedBy: limit) - precondition(idx != nil, "The distance \(distance) is not valid for this collection") - return idx! + guard let idx = index(i, offsetBy: distance, limitedBy: limit) else { + preconditionFailure( + "The distance \(distance) is not valid for this collection") + } + return idx } } extension StridingCollection: BidirectionalCollection - where Base: RandomAccessCollection { - +where Base: RandomAccessCollection { @inlinable public func index(before i: Index) -> Index { precondition(i.base != base.startIndex, "Incrementing past start index") @@ -275,9 +281,9 @@ extension StridingCollection: BidirectionalCollection } extension StridingCollection: RandomAccessCollection - where Base: RandomAccessCollection {} +where Base: RandomAccessCollection {} extension StridingCollection: LazySequenceProtocol, LazyCollectionProtocol - where Base: LazySequenceProtocol {} +where Base: LazySequenceProtocol {} extension StridingCollection.Index: Hashable where Base.Index: Hashable {} diff --git a/Sources/Algorithms/Suffix.swift b/Sources/Algorithms/Suffix.swift index c942adf4..8c140276 100644 --- a/Sources/Algorithms/Suffix.swift +++ b/Sources/Algorithms/Suffix.swift @@ -14,13 +14,16 @@ //===----------------------------------------------------------------------===// extension BidirectionalCollection { - /// Returns a subsequence containing the elements from the end until - /// `predicate` returns `false` and skipping the remaining elements. + /// Returns a subsequence containing the suffix of this collection where + /// all elements pass the given closure. /// /// - Parameter predicate: A closure that takes an element of the sequence as /// its argument and returns `true` if the element should be included or /// `false` if it should be excluded. Once the predicate returns `false` it /// will not be called again. + /// - Returns: A subsequence that is a suffix of the collection, where + /// `predicate` returns `true` for all the elements of the returned + /// subsequence. /// /// - Complexity: O(*n*), where *n* is the length of the collection. @inlinable @@ -43,6 +46,8 @@ extension Collection { /// as its argument and returns `true` if the element is part of the prefix /// or `false` if it is not. Once the predicate returns `false` it will not /// be called again. + /// - Returns: The index of the first element for which `predicate` does not + /// succeed, or `endIndex` if all the collection's elements pass. /// /// - Complexity: O(*n*), where *n* is the length of the collection. @inlinable @@ -69,6 +74,8 @@ extension BidirectionalCollection { /// as its argument and returns `true` if the element is part of the suffix /// or `false` if it is not. Once the predicate returns `false` it will not /// be called again. + /// - Returns: The index of the last element for which `predicate` does not + /// succeed, or `startIndex` if all the collection's elements pass. /// /// - Complexity: O(*n*), where *n* is the length of the collection. @inlinable diff --git a/Sources/Algorithms/Trim.swift b/Sources/Algorithms/Trim.swift index c3318c38..134cb703 100644 --- a/Sources/Algorithms/Trim.swift +++ b/Sources/Algorithms/Trim.swift @@ -14,19 +14,21 @@ extension Collection { /// Returns a `SubSequence` formed by discarding all elements at the start of - /// the collection which satisfy the given predicate. + /// the collection that satisfy the given predicate. /// /// This example uses `trimmingPrefix(while:)` to get a substring without the /// white space at the beginning of the string: /// /// let myString = " hello, world " - /// print(myString.trimmingPrefix(while: \.isWhitespace)) // "hello, world " + /// print(myString.trimmingPrefix(while: \.isWhitespace)) + /// // Prints "hello, world " /// - /// - Parameter predicate: A closure which determines if the element should be - /// omitted from the resulting slice. + /// - Parameter predicate: A closure that determines if the element should be + /// omitted from the result. + /// - Returns: A subsequence of this collection, starting at the first element + /// for which `predicate` returns `false`. /// /// - Complexity: O(*n*), where *n* is the length of this collection. - /// @inlinable public func trimmingPrefix( while predicate: (Element) throws -> Bool @@ -41,7 +43,7 @@ extension Collection { //===----------------------------------------------------------------------===// extension Collection where Self: RangeReplaceableCollection { - /// Mutates a `Collection` by discarding all elements at the start of it which + /// Mutates the collection by discarding all elements at the start that /// satisfy the given predicate. /// /// This example uses `trimPrefix(while:)` to remove the white space at the @@ -49,13 +51,13 @@ extension Collection where Self: RangeReplaceableCollection { /// /// let myString = " hello, world " /// myString.trimPrefix(while: \.isWhitespace) - /// print(myString) // "hello, world " + /// print(myString) + /// // Prints "hello, world " /// - /// - Parameter predicate: A closure which determines if the element should be - /// removed from the string. + /// - Parameter predicate: A closure that determines if the element should be + /// removed from the result. /// /// - Complexity: O(*n*), where *n* is the length of this collection. - /// @inlinable @_disfavoredOverload public mutating func trimPrefix( @@ -67,7 +69,7 @@ extension Collection where Self: RangeReplaceableCollection { } extension Collection where Self == Self.SubSequence { - /// Mutates a `Collection` by discarding all elements at the start of it which + /// Mutates the collection by discarding all elements at the start that /// satisfy the given predicate. /// /// This example uses `trimPrefix(while:)` to remove the white space at the @@ -77,11 +79,10 @@ extension Collection where Self == Self.SubSequence { /// myString.trimPrefix(while: \.isWhitespace) /// print(myString) // "hello, world " /// - /// - Parameters predicate: A closure which determines if the element should + /// - Parameters predicate: A closure that determines if the element should /// be removed from the string. /// /// - Complexity: O(*n*), where *n* is the length of this collection. - /// @inlinable public mutating func trimPrefix( while predicate: (Element) throws -> Bool @@ -96,28 +97,31 @@ extension Collection where Self == Self.SubSequence { extension BidirectionalCollection { /// Returns a `SubSequence` formed by discarding all elements at the start and - /// end of the collection which satisfy the given predicate. + /// end of the collection that satisfy the given predicate. /// /// This example uses `trimming(while:)` to get a substring without the white /// space at the beginning and end of the string: /// /// let myString = " hello, world " - /// print(myString.trimming(while: \.isWhitespace)) // "hello, world" + /// print(myString.trimming(while: \.isWhitespace)) + /// // Prints "hello, world" /// - /// - Parameter predicate: A closure which determines if the element should be + /// - Parameter predicate: A closure that determines if the element should be /// omitted from the resulting slice. + /// - Returns: A subsequence of this collection, starting at the first element + /// for which `predicate` returns `false` and ending at the last element for + /// which `predicate` returns `true`. /// /// - Complexity: O(*n*), where *n* is the length of this collection. - /// @inlinable public func trimming( while predicate: (Element) throws -> Bool ) rethrows -> SubSequence { try trimmingPrefix(while: predicate).trimmingSuffix(while: predicate) } - + /// Returns a `SubSequence` formed by discarding all elements at the end of - /// the collection which satisfy the given predicate. + /// the collection that satisfy the given predicate. /// /// This example uses `trimmingSuffix(while:)` to get a substring without the /// white space at the end of the string: @@ -125,11 +129,12 @@ extension BidirectionalCollection { /// let myString = " hello, world " /// print(myString.trimmingSuffix(while: \.isWhitespace)) // " hello, world" /// - /// - Parameter predicate: A closure which determines if the element should be + /// - Parameter predicate: A closure that determines if the element should be /// omitted from the resulting slice. + /// - Returns: A subsequence of this collection, ending at the last element + /// for which `predicate` returns `true`. /// /// - Complexity: O(*n*), where *n* is the length of this collection. - /// @inlinable public func trimmingSuffix( while predicate: (Element) throws -> Bool @@ -145,7 +150,7 @@ extension BidirectionalCollection { extension BidirectionalCollection where Self: RangeReplaceableCollection { /// Mutates a `BidirectionalCollection` by discarding all elements at the - /// start and at the end of it which satisfy the given predicate. + /// start and at the end of it that satisfy the given predicate. /// /// This example uses `trim(while:)` to remove the white space at the /// beginning of the string: @@ -154,11 +159,10 @@ extension BidirectionalCollection where Self: RangeReplaceableCollection { /// myString.trim(while: \.isWhitespace) /// print(myString) // "hello, world" /// - /// - Parameter predicate: A closure which determines if the element should be + /// - Parameter predicate: A closure that determines if the element should be /// removed from the string. /// /// - Complexity: O(*n*), where *n* is the length of this collection. - /// @inlinable @_disfavoredOverload public mutating func trim( @@ -167,9 +171,9 @@ extension BidirectionalCollection where Self: RangeReplaceableCollection { try trimSuffix(while: predicate) try trimPrefix(while: predicate) } - + /// Mutates a `BidirectionalCollection` by discarding all elements at the end - /// of it which satisfy the given predicate. + /// of it that satisfy the given predicate. /// /// This example uses `trimSuffix(while:)` to remove the white space at the /// beginning of the string: @@ -178,11 +182,10 @@ extension BidirectionalCollection where Self: RangeReplaceableCollection { /// myString.trimSuffix(while: \.isWhitespace) /// print(myString) // " hello, world" /// - /// - Parameter predicate: A closure which determines if the element should be + /// - Parameter predicate: A closure that determines if the element should be /// removed from the string. /// /// - Complexity: O(*n*), where *n* is the length of this collection. - /// @inlinable @_disfavoredOverload public mutating func trimSuffix( @@ -195,7 +198,7 @@ extension BidirectionalCollection where Self: RangeReplaceableCollection { extension BidirectionalCollection where Self == Self.SubSequence { /// Mutates a `BidirectionalCollection` by discarding all elements at the - /// start and at the end of it which satisfy the given predicate. + /// start and at the end of it that satisfy the given predicate. /// /// This example uses `trim(while:)` to remove the white space at the /// beginning of the string: @@ -204,11 +207,10 @@ extension BidirectionalCollection where Self == Self.SubSequence { /// myString.trim(while: \.isWhitespace) /// print(myString) // "hello, world" /// - /// - Parameter predicate: A closure which determines if the element should be + /// - Parameter predicate: A closure that determines if the element should be /// removed from the string. /// /// - Complexity: O(*n*), where *n* is the length of this collection. - /// @inlinable public mutating func trim( while predicate: (Element) throws -> Bool @@ -217,7 +219,7 @@ extension BidirectionalCollection where Self == Self.SubSequence { } /// Mutates a `BidirectionalCollection` by discarding all elements at the end - /// of it which satisfy the given predicate. + /// of it that satisfy the given predicate. /// /// This example uses `trimSuffix(while:)` to remove the white space at the /// beginning of the string: @@ -226,11 +228,10 @@ extension BidirectionalCollection where Self == Self.SubSequence { /// myString.trimSuffix(while: \.isWhitespace) /// print(myString) // " hello, world" /// - /// - Parameter predicate: A closure which determines if the element should be + /// - Parameter predicate: A closure that determines if the element should be /// removed from the string. /// /// - Complexity: O(*n*), where *n* is the length of this collection. - /// @inlinable public mutating func trimSuffix( while predicate: (Element) throws -> Bool diff --git a/Sources/Algorithms/Unique.swift b/Sources/Algorithms/Unique.swift index 074f8d05..5ac6ef40 100644 --- a/Sources/Algorithms/Unique.swift +++ b/Sources/Algorithms/Unique.swift @@ -14,11 +14,11 @@ public struct UniquedSequence { /// The base collection. @usableFromInline internal let base: Base - + /// The projection function. @usableFromInline internal let projection: (Base.Element) -> Subject - + @usableFromInline internal init(base: Base, projection: @escaping (Base.Element) -> Subject) { self.base = base @@ -31,13 +31,13 @@ extension UniquedSequence: Sequence { public struct Iterator: IteratorProtocol { @usableFromInline internal var base: Base.Iterator - + @usableFromInline internal let projection: (Base.Element) -> Subject - + @usableFromInline internal var seen: Set = [] - + @usableFromInline internal init( base: Base.Iterator, @@ -46,7 +46,7 @@ extension UniquedSequence: Sequence { self.base = base self.projection = projection } - + @inlinable public mutating func next() -> Base.Element? { while let element = base.next() { @@ -57,7 +57,7 @@ extension UniquedSequence: Sequence { return nil } } - + @inlinable public func makeIterator() -> Iterator { Iterator(base: base.makeIterator(), projection: projection) @@ -65,7 +65,7 @@ extension UniquedSequence: Sequence { } extension UniquedSequence: LazySequenceProtocol - where Base: LazySequenceProtocol {} +where Base: LazySequenceProtocol {} //===----------------------------------------------------------------------===// // uniqued() diff --git a/Sources/Algorithms/Windows.swift b/Sources/Algorithms/Windows.swift index bee7b864..a0aff403 100644 --- a/Sources/Algorithms/Windows.swift +++ b/Sources/Algorithms/Windows.swift @@ -49,10 +49,10 @@ extension Collection { public struct WindowsOfCountCollection { @usableFromInline internal let base: Base - + @usableFromInline internal let windowSize: Int - + @usableFromInline internal var endOfFirstWindow: Base.Index? @@ -62,7 +62,8 @@ public struct WindowsOfCountCollection { self.base = base self.windowSize = windowSize self.endOfFirstWindow = - base.index(base.startIndex, offsetBy: windowSize, limitedBy: base.endIndex) + base + .index(base.startIndex, offsetBy: windowSize, limitedBy: base.endIndex) } } @@ -71,41 +72,40 @@ extension WindowsOfCountCollection: Collection { public struct Index: Comparable { @usableFromInline internal var lowerBound: Base.Index - + @usableFromInline internal var upperBound: Base.Index - + @inlinable internal init(lowerBound: Base.Index, upperBound: Base.Index) { self.lowerBound = lowerBound self.upperBound = upperBound } - + @inlinable public static func == (lhs: Index, rhs: Index) -> Bool { lhs.lowerBound == rhs.lowerBound } - + @inlinable public static func < (lhs: Index, rhs: Index) -> Bool { lhs.lowerBound < rhs.lowerBound } } - + @inlinable public var startIndex: Index { - if let upperBound = endOfFirstWindow { - return Index(lowerBound: base.startIndex, upperBound: upperBound) - } else { + guard let upperBound = endOfFirstWindow else { return endIndex } + return Index(lowerBound: base.startIndex, upperBound: upperBound) } - + @inlinable public var endIndex: Index { Index(lowerBound: base.endIndex, upperBound: base.endIndex) } - + @inlinable public subscript(index: Index) -> Base.SubSequence { precondition( @@ -113,28 +113,29 @@ extension WindowsOfCountCollection: Collection { "Windows index is out of range") return base[index.lowerBound.. Index { precondition(index != endIndex, "Advancing past end index") guard index.upperBound < base.endIndex else { return endIndex } - - let lowerBound = windowSize == 1 + + let lowerBound = + windowSize == 1 ? index.upperBound : base.index(after: index.lowerBound) let upperBound = base.index(after: index.upperBound) return Index(lowerBound: lowerBound, upperBound: upperBound) } - + @inlinable public func index(_ i: Index, offsetBy distance: Int) -> Index { guard distance != 0 else { return i } - + return distance > 0 ? offsetForward(i, by: distance) : offsetBackward(i, by: -distance) } - + @inlinable public func index( _ i: Index, @@ -143,7 +144,7 @@ extension WindowsOfCountCollection: Collection { ) -> Index? { guard distance != 0 else { return i } guard limit != i else { return nil } - + if distance > 0 { return limit > i ? offsetForward(i, by: distance, limitedBy: limit) @@ -154,28 +155,30 @@ extension WindowsOfCountCollection: Collection { : offsetBackward(i, by: -distance) } } - + @inlinable internal func offsetForward(_ i: Index, by distance: Int) -> Index { guard let index = offsetForward(i, by: distance, limitedBy: endIndex) - else { fatalError("Index is out of bounds") } + else { fatalError("Index is out of bounds") } return index } - + @inlinable internal func offsetBackward(_ i: Index, by distance: Int) -> Index { guard let index = offsetBackward(i, by: distance, limitedBy: startIndex) - else { fatalError("Index is out of bounds") } + else { fatalError("Index is out of bounds") } return index } - + @inlinable internal func offsetForward( - _ i: Index, by distance: Int, limitedBy limit: Index + _ i: Index, + by distance: Int, + limitedBy limit: Index ) -> Index? { assert(distance > 0) assert(limit > i) - + // `endIndex` and the index before it both have `base.endIndex` as their // upper bound, so we first advance to the base index _before_ the upper // bound of the output, in order to avoid advancing past the end of `base` @@ -186,31 +189,31 @@ extension WindowsOfCountCollection: Collection { // input: [x|x x x x x|x x x x] [x x|x x x x x|x x x] // |> > >|>| or |> > >| // output: [x x x x x|x x x x x] [x x x x x x x x x x] (`endIndex`) - + if distance >= windowSize { // Avoid traversing `self[i.lowerBound.. >|> > >|>| // output: [x x x x x x x|x x x x|x] - + guard limit.lowerBound >= i.upperBound, - let lowerBound = base.index( - i.upperBound, - offsetBy: distance - windowSize, - limitedBy: limit.lowerBound), - let indexBeforeUpperBound = base.index( - lowerBound, - offsetBy: windowSize - 1, - limitedBy: limit.upperBound) + let lowerBound = base.index( + i.upperBound, + offsetBy: distance - windowSize, + limitedBy: limit.lowerBound), + let indexBeforeUpperBound = base.index( + lowerBound, + offsetBy: windowSize - 1, + limitedBy: limit.upperBound) else { return nil } - + // If `indexBeforeUpperBound` equals `base.endIndex`, we're advancing to // `endIndex`. guard indexBeforeUpperBound != base.endIndex else { return endIndex } - + return Index( lowerBound: lowerBound, upperBound: base.index(after: indexBeforeUpperBound)) @@ -218,35 +221,38 @@ extension WindowsOfCountCollection: Collection { // input: [x|x x x x x x|x x x x x] // |> > > >| |> > >|>| // output: [x x x x x|x x x x x x|x] - - guard let indexBeforeUpperBound = base.index( - i.upperBound, - offsetBy: distance - 1, - limitedBy: limit.upperBound) + + guard + let indexBeforeUpperBound = base.index( + i.upperBound, + offsetBy: distance - 1, + limitedBy: limit.upperBound) else { return nil } - + // If `indexBeforeUpperBound` equals the limit, the upper bound itself // exceeds it. guard indexBeforeUpperBound != limit.upperBound || limit == endIndex - else { return nil } - + else { return nil } + // If `indexBeforeUpperBound` equals `base.endIndex`, we're advancing to // `endIndex`. guard indexBeforeUpperBound != base.endIndex else { return endIndex } - + return Index( lowerBound: base.index(i.lowerBound, offsetBy: distance), upperBound: base.index(after: indexBeforeUpperBound)) } } - + @inlinable internal func offsetBackward( - _ i: Index, by distance: Int, limitedBy limit: Index - ) -> Index? { + _ i: Index, + by distance: Int, + limitedBy limit: Index + ) -> Index? { assert(distance > 0) assert(limit < i) - + if i == endIndex { // Advance `base.endIndex` by `distance - 1`, because the index before // `endIndex` also has `base.endIndex` as its upper bound. @@ -256,13 +262,14 @@ extension WindowsOfCountCollection: Collection { // input: [x x x x x x x x x x] (`endIndex`) // |< < < < <|< < <| // output: [x x|x x x x x|x x x] - - guard let upperBound = base.index( - base.endIndex, - offsetBy: -(distance - 1), - limitedBy: limit.upperBound) + + guard + let upperBound = base.index( + base.endIndex, + offsetBy: -(distance - 1), + limitedBy: limit.upperBound) else { return nil } - + return Index( lowerBound: base.index(upperBound, offsetBy: -windowSize), upperBound: upperBound) @@ -274,14 +281,14 @@ extension WindowsOfCountCollection: Collection { // input: [x x x x x x x|x x x x|x] // |< < < <|< <| // output: [x|x x x x|x x x x x x x] - + guard limit.upperBound <= i.lowerBound, - let upperBound = base.index( - i.lowerBound, - offsetBy: -(distance - windowSize), - limitedBy: limit.upperBound) + let upperBound = base.index( + i.lowerBound, + offsetBy: -(distance - windowSize), + limitedBy: limit.upperBound) else { return nil } - + return Index( lowerBound: base.index(upperBound, offsetBy: -windowSize), upperBound: upperBound) @@ -289,19 +296,20 @@ extension WindowsOfCountCollection: Collection { // input: [x x x x x|x x x x x x|x] // |< < < <| |< < < <| // output: [x|x x x x x x|x x x x x] - - guard let lowerBound = base.index( - i.lowerBound, - offsetBy: -distance, - limitedBy: limit.lowerBound) + + guard + let lowerBound = base.index( + i.lowerBound, + offsetBy: -distance, + limitedBy: limit.lowerBound) else { return nil } - + return Index( lowerBound: lowerBound, upperBound: base.index(i.lowerBound, offsetBy: -distance)) } } - + @inlinable public func distance(from start: Index, to end: Index) -> Int { guard start <= end else { return -distance(from: end, to: start) } @@ -319,42 +327,39 @@ extension WindowsOfCountCollection: Collection { // start: [x|x x x x|x x x x x x x] // |- - - -|> >| // end: [x x x x x x x|x x x x|x] - + return windowSize + base[start.upperBound.. > > >| // end: [x x x x x|x x x x x x|x] - + return base[start.lowerBound.. Index { - precondition(index != startIndex, "Incrementing past start index") + precondition(index != startIndex, "Decrementing past start index") if index == endIndex { return Index( lowerBound: base.index(index.lowerBound, offsetBy: -windowSize), - upperBound: index.upperBound - ) + upperBound: index.upperBound) } else { return Index( lowerBound: base.index(before: index.lowerBound), - upperBound: base.index(before: index.upperBound) - ) + upperBound: base.index(before: index.upperBound)) } } } extension WindowsOfCountCollection: RandomAccessCollection - where Base: RandomAccessCollection {} +where Base: RandomAccessCollection {} extension WindowsOfCountCollection: LazySequenceProtocol, LazyCollectionProtocol - where Base: LazySequenceProtocol {} +where Base: LazySequenceProtocol {} extension WindowsOfCountCollection.Index: Hashable where Base.Index: Hashable {} diff --git a/Tests/SwiftAlgorithmsTests/AdjacentPairsTests.swift b/Tests/SwiftAlgorithmsTests/AdjacentPairsTests.swift index f07fe52a..9188260a 100644 --- a/Tests/SwiftAlgorithmsTests/AdjacentPairsTests.swift +++ b/Tests/SwiftAlgorithmsTests/AdjacentPairsTests.swift @@ -9,63 +9,63 @@ // //===----------------------------------------------------------------------===// -import XCTest import Algorithms +import XCTest final class AdjacentPairsTests: XCTestCase { func testEmptySequence() { let pairs = (0...).prefix(0).adjacentPairs() - XCTAssertEqualSequences(pairs, [], by: ==) + expectEqualSequences(pairs, [], by: ==) } - + func testOneElementSequence() { let pairs = (0...).prefix(1).adjacentPairs() - XCTAssertEqualSequences(pairs, [], by: ==) + expectEqualSequences(pairs, [], by: ==) } - + func testTwoElementSequence() { let pairs = (0...).prefix(2).adjacentPairs() - XCTAssertEqualSequences(pairs, [(0, 1)], by: ==) + expectEqualSequences(pairs, [(0, 1)], by: ==) } - + func testThreeElementSequence() { let pairs = (0...).prefix(3).adjacentPairs() - XCTAssertEqualSequences(pairs, [(0, 1), (1, 2)], by: ==) + expectEqualSequences(pairs, [(0, 1), (1, 2)], by: ==) } - + func testManySequences() { for n in 4...100 { let pairs = (0...).prefix(n).adjacentPairs() - XCTAssertEqualSequences(pairs, zip(0..., 1...).prefix(n - 1), by: ==) + expectEqualSequences(pairs, zip(0..., 1...).prefix(n - 1), by: ==) } } - + func testZeroElements() { let pairs = (0..<0).adjacentPairs() XCTAssertEqual(pairs.startIndex, pairs.endIndex) - XCTAssertEqualSequences(pairs, [], by: ==) + expectEqualSequences(pairs, [], by: ==) } func testOneElement() { let pairs = (0..<1).adjacentPairs() XCTAssertEqual(pairs.startIndex, pairs.endIndex) - XCTAssertEqualSequences(pairs, [], by: ==) + expectEqualSequences(pairs, [], by: ==) } func testTwoElements() { let pairs = (0..<2).adjacentPairs() - XCTAssertEqualSequences(pairs, [(0, 1)], by: ==) + expectEqualSequences(pairs, [(0, 1)], by: ==) } func testThreeElements() { let pairs = (0..<3).adjacentPairs() - XCTAssertEqualSequences(pairs, [(0, 1), (1, 2)], by: ==) + expectEqualSequences(pairs, [(0, 1), (1, 2)], by: ==) } func testManyElements() { for n in 4...100 { let pairs = (0..>( indicesIncludingEnd: { chain in @@ -42,27 +43,27 @@ final class ChainTests: XCTestCase { + chain.base2.indices.map { .init(second: $0) } + [.init(second: chain.base2.endIndex)] }) - + validator.validate(chain("abcd", "XYZ"), expectedCount: 4 + 3) validator.validate(chain("abcd", ""), expectedCount: 4 + 0) validator.validate(chain("", "XYZ"), expectedCount: 0 + 3) validator.validate(chain("", ""), expectedCount: 0 + 0) } - + func testChainIndexOffsetAcrossBoundary() { let c = chain("abc", "XYZ") - + do { let i = c.index(c.startIndex, offsetBy: 3, limitedBy: c.startIndex) XCTAssertNil(i) } - + do { let i = c.index(c.startIndex, offsetBy: 4) let j = c.index(i, offsetBy: -2) XCTAssertEqual(c[j], "c") } - + do { let i = c.index(c.startIndex, offsetBy: 3) let j = c.index(i, offsetBy: -1, limitedBy: i) diff --git a/Tests/SwiftAlgorithmsTests/ChunkedTests.swift b/Tests/SwiftAlgorithmsTests/ChunkedTests.swift index b8567ea9..f02ee16f 100644 --- a/Tests/SwiftAlgorithmsTests/ChunkedTests.swift +++ b/Tests/SwiftAlgorithmsTests/ChunkedTests.swift @@ -9,23 +9,22 @@ // //===----------------------------------------------------------------------===// -import XCTest import Algorithms +import XCTest final class ChunkedTests: XCTestCase { let fruits = [ - "Apple", "Apricot", "Avocado", "Banana", - "Bilberry", "Blackberry", "Blackcurrant", "Blueberry", - "Currant", "Cherry", "Cherimoya", "Clementine", - "Date", "Damson", "Dragonfruit", "Durian", - "Eggplant", "Elderberry", "Feijoa", - "Grape", "Grapefruit", "Guava", + "Apple", "Apricot", "Avocado", "Banana", + "Bilberry", "Blackberry", "Blackcurrant", "Blueberry", + "Currant", "Cherry", "Cherimoya", "Clementine", + "Date", "Damson", "Dragonfruit", "Durian", + "Eggplant", "Elderberry", "Feijoa", + "Grape", "Grapefruit", "Guava", ] func validateFruitChunks(_ fruitChunks: C) - where C.Element == ArraySlice - { - let expectedChunks: Array> = [ + where C.Element == ArraySlice { + let expectedChunks: [ArraySlice] = [ fruits[0..<3], fruits[3..<8], fruits[8..<12], @@ -34,16 +33,17 @@ final class ChunkedTests: XCTestCase { fruits[18..<19], fruits[19..<22], ] - XCTAssertEqualSequences(expectedChunks, fruitChunks, by: ==) - + expectEqualSequences(expectedChunks, fruitChunks, by: ==) + XCTAssertEqual(fruits[19..<22], fruitChunks.last) - - XCTAssertEqual("Currant", fruitChunks.first(where: { $0.count == 4 })?.first) + + XCTAssertEqual( + "Currant", fruitChunks.first(where: { $0.count == 4 })?.first) XCTAssertEqual("Date", fruitChunks.last(where: { $0.count == 4 })?.first) XCTAssertNil(fruitChunks.first(where: { $0.count == 0 })) XCTAssertNil(fruitChunks.last(where: { $0.count == 0 })) } - + func testSimple() { // Example let names = ["David", "Kyle", "Karoy", "Nate"] @@ -51,9 +51,10 @@ final class ChunkedTests: XCTestCase { let expected: [(Character, ArraySlice)] = [ ("D", ["David"]), ("K", ["Kyle", "Karoy"]), - ("N", ["Nate"])] - XCTAssertEqualSequences(expected, chunks, by: ==) - + ("N", ["Nate"]), + ] + expectEqualSequences(expected, chunks, by: ==) + // Empty sequence XCTAssertEqual(0, names.prefix(0).chunked(on: { $0.first }).count) @@ -61,10 +62,10 @@ final class ChunkedTests: XCTestCase { let namesStartingWithD = ["David", "Don", "Darren"] XCTAssertEqual(1, namesStartingWithD.chunked(on: { $0.first }).count) } - + func testChunkedOn() { validateFruitChunks(fruits.chunked(on: { $0.first }).map { $1 }) - + let lazyChunks = fruits.lazy.chunked(on: { $0.first }) validateFruitChunks(lazyChunks.map { $1 }) IndexValidator().validate(lazyChunks) @@ -72,73 +73,83 @@ final class ChunkedTests: XCTestCase { func testChunkedBy() { validateFruitChunks(fruits.chunked(by: { $0.first == $1.first })) - + let lazyChunks = fruits.lazy.chunked(by: { $0.first == $1.first }) validateFruitChunks(lazyChunks) IndexValidator().validate(lazyChunks) } - + func testChunkedByComparesConsecutiveElements() { - XCTAssertEqualSequences( + expectEqualSequences( [1, 2, 3, 4, 6, 7, 8, 9].chunked(by: { $1 - $0 == 1 }), [[1, 2, 3, 4], [6, 7, 8, 9]]) - - XCTAssertEqualSequences( + + expectEqualSequences( [1, 2, 3, 4, 6, 7, 8, 9].lazy.chunked(by: { $1 - $0 == 1 }), [[1, 2, 3, 4], [6, 7, 8, 9]]) - - XCTAssertEqualSequences( + + expectEqualSequences( [1, 2, 3, 4, 6, 7, 8, 9].lazy.chunked(by: { $1 - $0 == 1 }).reversed(), [[6, 7, 8, 9], [1, 2, 3, 4]]) - + IndexValidator().validate([1, 2, 3].lazy.chunked(by: { $1 - $0 == 1 })) } - + func testChunkedLazy() { - XCTAssertLazySequence(fruits.lazy.chunked(by: { $0.first == $1.first })) - XCTAssertLazySequence(fruits.lazy.chunked(on: { $0.first })) + requireLazySequence(fruits.lazy.chunked(by: { $0.first == $1.first })) + requireLazySequence(fruits.lazy.chunked(on: { $0.first })) } - + //===----------------------------------------------------------------------===// // Tests for `chunks(ofCount:)` //===----------------------------------------------------------------------===// - + func testChunksOfCount() { - XCTAssertEqualSequences([Int]().chunks(ofCount: 1), []) - XCTAssertEqualSequences([Int]().chunks(ofCount: 5), []) + expectEqualSequences([Int]().chunks(ofCount: 1), []) + expectEqualSequences([Int]().chunks(ofCount: 5), []) let collection1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - XCTAssertEqualSequences(collection1.chunks(ofCount: 1), - [[1], [2], [3], [4], [5], [6], [7], [8], [9], [10]]) - XCTAssertEqualSequences(collection1.chunks(ofCount: 3), - [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]) - XCTAssertEqualSequences(collection1.chunks(ofCount: 5), - [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]) - XCTAssertEqualSequences(collection1.chunks(ofCount: 11), - [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]) - + expectEqualSequences( + collection1.chunks(ofCount: 1), + [[1], [2], [3], [4], [5], [6], [7], [8], [9], [10]]) + expectEqualSequences( + collection1.chunks(ofCount: 3), + [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10]]) + expectEqualSequences( + collection1.chunks(ofCount: 5), + [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]]) + expectEqualSequences( + collection1.chunks(ofCount: 11), + [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]) + let collection2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] - XCTAssertEqualSequences(collection2.chunks(ofCount: 3), - [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11]]) + expectEqualSequences( + collection2.chunks(ofCount: 3), + [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11]]) } - + func testChunksOfCountBidirectional() { let collection1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - XCTAssertEqualSequences(collection1.chunks(ofCount: 1).reversed(), - [[10], [9], [8], [7], [6], [5], [4], [3], [2], [1]]) - XCTAssertEqualSequences(collection1.chunks(ofCount: 3).reversed(), - [[10], [7, 8, 9], [4, 5, 6], [1, 2, 3]]) - XCTAssertEqualSequences(collection1.chunks(ofCount: 5).reversed(), - [[6, 7, 8, 9, 10], [1, 2, 3, 4, 5]]) - XCTAssertEqualSequences(collection1.chunks(ofCount: 11).reversed(), - [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]) - + expectEqualSequences( + collection1.chunks(ofCount: 1).reversed(), + [[10], [9], [8], [7], [6], [5], [4], [3], [2], [1]]) + expectEqualSequences( + collection1.chunks(ofCount: 3).reversed(), + [[10], [7, 8, 9], [4, 5, 6], [1, 2, 3]]) + expectEqualSequences( + collection1.chunks(ofCount: 5).reversed(), + [[6, 7, 8, 9, 10], [1, 2, 3, 4, 5]]) + expectEqualSequences( + collection1.chunks(ofCount: 11).reversed(), + [[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]) + let collection2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] - XCTAssertEqualSequences(collection2.chunks(ofCount: 3).reversed(), - [[10, 11], [7, 8, 9], [4, 5, 6], [1, 2, 3]]) + expectEqualSequences( + collection2.chunks(ofCount: 3).reversed(), + [[10, 11], [7, 8, 9], [4, 5, 6], [1, 2, 3]]) } - + func testChunksOfCountCount() { XCTAssertEqual([Int]().chunks(ofCount: 1).count, 0) XCTAssertEqual([Int]().chunks(ofCount: 5).count, 0) @@ -148,20 +159,20 @@ final class ChunkedTests: XCTestCase { XCTAssertEqual(collection1.chunks(ofCount: 3).count, 4) XCTAssertEqual(collection1.chunks(ofCount: 5).count, 2) XCTAssertEqual(collection1.chunks(ofCount: 11).count, 1) - + let collection2 = (1...50).map { $0 } XCTAssertEqual(collection2.chunks(ofCount: 9).count, 6) } func testEmptyChunksOfCountTraversal() { let emptyChunks = [Int]().chunks(ofCount: 1) - + IndexValidator().validate(emptyChunks, expectedCount: 0) } - + func testChunksOfCountTraversal() { let validator = IndexValidator>>() - + for i in 1...10 { let range = 1...50 let chunks = range.chunks(ofCount: i) @@ -170,29 +181,29 @@ final class ChunkedTests: XCTestCase { expectedCount: range.count / i + (range.count % i).signum()) } } - + func testEvenChunks() { - XCTAssertEqualSequences( + expectEqualSequences( (0..<10).evenlyChunked(in: 4), [0..<3, 3..<6, 6..<8, 8..<10]) - - XCTAssertEqualSequences( + + expectEqualSequences( (0..<3).evenlyChunked(in: 5), [0..<1, 1..<2, 2..<3, 3..<3, 3..<3]) - - XCTAssertEqualSequences( + + expectEqualSequences( "".evenlyChunked(in: 0), []) - - XCTAssertEqualSequences( + + expectEqualSequences( "".evenlyChunked(in: 1), [""]) } - + func testEvenChunksIndexTraversals() { let validator = IndexValidator>>() - [ + for chunks in [ (0..<10).evenlyChunked(in: 1), (0..<10).evenlyChunked(in: 2), (0..<10).evenlyChunked(in: 3), @@ -201,6 +212,8 @@ final class ChunkedTests: XCTestCase { (0..<0).evenlyChunked(in: 0), (0..<0).evenlyChunked(in: 1), (0..<0).evenlyChunked(in: 10), - ].forEach { validator.validate($0) } + ] { + validator.validate(chunks) + } } } diff --git a/Tests/SwiftAlgorithmsTests/CombinationsTests.swift b/Tests/SwiftAlgorithmsTests/CombinationsTests.swift index 45fe5874..67d10093 100644 --- a/Tests/SwiftAlgorithmsTests/CombinationsTests.swift +++ b/Tests/SwiftAlgorithmsTests/CombinationsTests.swift @@ -9,8 +9,8 @@ // //===----------------------------------------------------------------------===// -import XCTest import Algorithms +import XCTest final class CombinationsTests: XCTestCase { func testCount() { @@ -20,8 +20,8 @@ final class CombinationsTests: XCTestCase { /// the given `file` and `line`. func check( _ x: CombinationsSequence, countsAre l: Int, - file: StaticString, line: UInt) - { + file: StaticString, line: UInt + ) { XCTAssertEqual(x.count, l, "unexpected count", file: file, line: line) XCTAssertEqual( x.underestimatedCount, l, "unexpected underestimatedCount", @@ -34,8 +34,8 @@ final class CombinationsTests: XCTestCase { func check( cHas n: Int, combinationsOfLength l: Int, - file: StaticString = #filePath, line: UInt = #line) - { + file: StaticString = #filePath, line: UInt = #line + ) { check(c.combinations(ofCount: l), countsAre: n, file: file, line: line) } @@ -45,8 +45,8 @@ final class CombinationsTests: XCTestCase { func check( cHas n: Int, combinationsOfLengths l: R, - file: StaticString = #filePath, line: UInt = #line) where R.Bound == Int - { + file: StaticString = #filePath, line: UInt = #line + ) where R.Bound == Int { check(c.combinations(ofCount: l), countsAre: n, file: file, line: line) } @@ -63,70 +63,92 @@ final class CombinationsTests: XCTestCase { // `k` greater than element count results in same number of combinations check(cHas: 5, combinationsOfLengths: 3...10) - + // `k` greater than element count results in same number of combinations check(cHas: 1, combinationsOfLengths: 4...10) - + // `k` entirely greater than element count results in no combinations check(cHas: 0, combinationsOfLengths: 5...10) - + check(cHas: 16, combinationsOfLengths: 0...) check(cHas: 15, combinationsOfLengths: ...3) } - + func testCombinations() { let c = "ABCD" - + let c1 = c.combinations(ofCount: 1) XCTAssertEqual(["A", "B", "C", "D"], c1.map { String($0) }) - + let c2 = c.combinations(ofCount: 2) XCTAssertEqual(["AB", "AC", "AD", "BC", "BD", "CD"], c2.map { String($0) }) - + let c3 = c.combinations(ofCount: 3) XCTAssertEqual(["ABC", "ABD", "ACD", "BCD"], c3.map { String($0) }) - + let c4 = c.combinations(ofCount: 4) XCTAssertEqual(["ABCD"], c4.map { String($0) }) - + let c5 = c.combinations(ofCount: 2...4) - XCTAssertEqual(["AB", "AC", "AD", "BC", "BD", "CD", "ABC", "ABD", "ACD", "BCD", "ABCD"], c5.map { String($0) }) - + XCTAssertEqual( + ["AB", "AC", "AD", "BC", "BD", "CD", "ABC", "ABD", "ACD", "BCD", "ABCD"], + c5.map { String($0) }) + let c6 = c.combinations(ofCount: 0...4) - XCTAssertEqual(["", "A", "B", "C", "D", "AB", "AC", "AD", "BC", "BD", "CD", "ABC", "ABD", "ACD", "BCD", "ABCD"], c6.map { String($0) }) - + XCTAssertEqual( + [ + "", "A", "B", "C", "D", "AB", "AC", "AD", "BC", "BD", "CD", "ABC", + "ABD", "ACD", "BCD", "ABCD", + ], c6.map { String($0) }) + let c7 = c.combinations(ofCount: 0...) - XCTAssertEqual(["", "A", "B", "C", "D", "AB", "AC", "AD", "BC", "BD", "CD", "ABC", "ABD", "ACD", "BCD", "ABCD"], c7.map { String($0) }) - + XCTAssertEqual( + [ + "", "A", "B", "C", "D", "AB", "AC", "AD", "BC", "BD", "CD", "ABC", + "ABD", "ACD", "BCD", "ABCD", + ], c7.map { String($0) }) + let c8 = c.combinations(ofCount: ...4) - XCTAssertEqual(["", "A", "B", "C", "D", "AB", "AC", "AD", "BC", "BD", "CD", "ABC", "ABD", "ACD", "BCD", "ABCD"], c8.map { String($0) }) - + XCTAssertEqual( + [ + "", "A", "B", "C", "D", "AB", "AC", "AD", "BC", "BD", "CD", "ABC", + "ABD", "ACD", "BCD", "ABCD", + ], c8.map { String($0) }) + let c9 = c.combinations(ofCount: ...3) - XCTAssertEqual(["", "A", "B", "C", "D", "AB", "AC", "AD", "BC", "BD", "CD", "ABC", "ABD", "ACD", "BCD"], c9.map { String($0) }) - + XCTAssertEqual( + [ + "", "A", "B", "C", "D", "AB", "AC", "AD", "BC", "BD", "CD", "ABC", + "ABD", "ACD", "BCD", + ], c9.map { String($0) }) + let c10 = c.combinations(ofCount: 1...) - XCTAssertEqual(["A", "B", "C", "D", "AB", "AC", "AD", "BC", "BD", "CD", "ABC", "ABD", "ACD", "BCD", "ABCD"], c10.map { String($0) }) + XCTAssertEqual( + [ + "A", "B", "C", "D", "AB", "AC", "AD", "BC", "BD", "CD", "ABC", "ABD", + "ACD", "BCD", "ABCD", + ], c10.map { String($0) }) } - + func testEmpty() { // `k == 0` results in one zero-length combination - XCTAssertEqualSequences([[]], "".combinations(ofCount: 0)) - XCTAssertEqualSequences([[]], "".combinations(ofCount: 0...0)) - XCTAssertEqualSequences([[]], "ABCD".combinations(ofCount: 0)) - XCTAssertEqualSequences([[]], "ABCD".combinations(ofCount: 0...0)) - + expectEqualSequences([[]], "".combinations(ofCount: 0)) + expectEqualSequences([[]], "".combinations(ofCount: 0...0)) + expectEqualSequences([[]], "ABCD".combinations(ofCount: 0)) + expectEqualSequences([[]], "ABCD".combinations(ofCount: 0...0)) + // `k` greater than element count results in zero combinations - XCTAssertEqualSequences([], "".combinations(ofCount: 5)) - XCTAssertEqualSequences([], "".combinations(ofCount: 5...10)) - XCTAssertEqualSequences([], "ABCD".combinations(ofCount: 5)) - XCTAssertEqualSequences([], "ABCD".combinations(ofCount: 5...10)) + expectEqualSequences([], "".combinations(ofCount: 5)) + expectEqualSequences([], "".combinations(ofCount: 5...10)) + expectEqualSequences([], "ABCD".combinations(ofCount: 5)) + expectEqualSequences([], "ABCD".combinations(ofCount: 5...10)) } - + func testCombinationsLazy() { - XCTAssertLazySequence("ABC".lazy.combinations(ofCount: 1)) - XCTAssertLazySequence("ABC".lazy.combinations(ofCount: 1...3)) - XCTAssertLazySequence("ABC".lazy.combinations(ofCount: 1...)) - XCTAssertLazySequence("ABC".lazy.combinations(ofCount: ...3)) - XCTAssertLazySequence("ABC".lazy.combinations(ofCount: 0...)) + requireLazySequence("ABC".lazy.combinations(ofCount: 1)) + requireLazySequence("ABC".lazy.combinations(ofCount: 1...3)) + requireLazySequence("ABC".lazy.combinations(ofCount: 1...)) + requireLazySequence("ABC".lazy.combinations(ofCount: ...3)) + requireLazySequence("ABC".lazy.combinations(ofCount: 0...)) } } diff --git a/Tests/SwiftAlgorithmsTests/CompactedTests.swift b/Tests/SwiftAlgorithmsTests/CompactedTests.swift index a23d0150..d4db4f10 100644 --- a/Tests/SwiftAlgorithmsTests/CompactedTests.swift +++ b/Tests/SwiftAlgorithmsTests/CompactedTests.swift @@ -9,33 +9,34 @@ // //===----------------------------------------------------------------------===// -import XCTest import Algorithms +import XCTest final class CompactedTests: XCTestCase { let tests: [[Int?]] = [nil, nil, nil, 0, 1, 2] - .uniquePermutations(ofCount: 0...) - .map(Array.init) - + .uniquePermutations(ofCount: 0...) + .map(Array.init) + func testCompactedCompacted() { for collection in self.tests { let seq = AnySequence(collection) - XCTAssertEqualSequences( + expectEqualSequences( seq.compactMap({ $0 }), seq.compacted()) - XCTAssertEqualSequences( + expectEqualSequences( collection.compactMap({ $0 }), collection.compacted()) } } func testCompactedBidirectionalCollection() { for array in self.tests { - XCTAssertEqualSequences(array.compactMap({ $0 }).reversed(), - array.compacted().reversed()) + expectEqualSequences( + array.compactMap({ $0 }).reversed(), + array.compacted().reversed()) } } - + func testCollectionTraversals() { let validator = IndexValidator>() for array in self.tests { diff --git a/Tests/SwiftAlgorithmsTests/CycleTests.swift b/Tests/SwiftAlgorithmsTests/CycleTests.swift index 6badaff8..fafa5125 100644 --- a/Tests/SwiftAlgorithmsTests/CycleTests.swift +++ b/Tests/SwiftAlgorithmsTests/CycleTests.swift @@ -9,13 +9,13 @@ // //===----------------------------------------------------------------------===// -import XCTest import Algorithms +import XCTest final class CycleTests: XCTestCase { func testCycle() { let cycle = (1...4).cycled() - XCTAssertEqualSequences( + expectEqualSequences( [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4], cycle.prefix(20) ) @@ -32,12 +32,12 @@ final class CycleTests: XCTestCase { } func testCycleLazy() { - XCTAssertLazySequence((1...4).lazy.cycled()) + requireLazySequence((1...4).lazy.cycled()) } func testRepeated() { let repeats = (1...4).cycled(times: 3) - XCTAssertEqualSequences( + expectEqualSequences( [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4], repeats) } @@ -58,7 +58,7 @@ final class CycleTests: XCTestCase { } func testRepeatedLazy() { - XCTAssertLazySequence((1...4).lazy.cycled(times: 3)) + requireLazySequence((1...4).lazy.cycled(times: 3)) } func testRepeatedIndexMethods() { @@ -73,8 +73,9 @@ final class CycleTests: XCTestCase { 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) + let outOfBounds = cycle.index( + nextIndex, offsetBy: 1, + limitedBy: cycle.endIndex) XCTAssertNil(outOfBounds) let previousIndex = cycle.index(before: nextIndex) diff --git a/Tests/SwiftAlgorithmsTests/EndsWithTests.swift b/Tests/SwiftAlgorithmsTests/EndsWithTests.swift index 26e21c12..0f033c6d 100644 --- a/Tests/SwiftAlgorithmsTests/EndsWithTests.swift +++ b/Tests/SwiftAlgorithmsTests/EndsWithTests.swift @@ -9,17 +9,17 @@ // //===----------------------------------------------------------------------===// -import XCTest import Algorithms +import XCTest final class EndsWithTests: XCTestCase { func testEndsWithCorrectSuffix() { let a = 8...10 let b = 1...10 - + XCTAssertTrue(b.ends(with: a)) } - + func testDoesntEndWithWrongSuffix() { let a = 8...9 let b = 1...10 @@ -33,12 +33,12 @@ final class EndsWithTests: XCTestCase { func testEndsWithEmpty() { let a = 8...10 - let empty = [Int]() + let empty: [Int] = [] XCTAssertTrue(a.ends(with: empty)) } - + func testEmptyEndsWithEmpty() { - let empty = [Int]() + let empty: [Int] = [] XCTAssertTrue(empty.ends(with: empty)) } @@ -68,24 +68,26 @@ final class EndsWithNonEquatableTests: XCTestCase { func testEndsWithEmpty() { let a = nonEq(8...10) - let empty = [NotEquatable]() + let empty: [NotEquatable] = [] XCTAssertTrue(a.ends(with: empty, by: areEquivalent)) } func testEmptyEndsWithEmpty() { - let empty = [NotEquatable]() + let empty: [NotEquatable] = [] XCTAssertTrue(empty.ends(with: empty, by: areEquivalent)) } func testEmptyDoesNotEndWithNonempty() { XCTAssertFalse([].ends(with: nonEq(1...10), by: areEquivalent)) } - - private func nonEq(_ range: ClosedRange) -> Array> { + + private func nonEq(_ range: ClosedRange) -> [NotEquatable] { range.map(NotEquatable.init) } - private func areEquivalent(lhs: NotEquatable, rhs: NotEquatable) -> Bool { + private func areEquivalent( + lhs: NotEquatable, rhs: NotEquatable + ) -> Bool { lhs.value == rhs.value } @@ -93,4 +95,3 @@ final class EndsWithNonEquatableTests: XCTestCase { let value: T } } - diff --git a/Tests/SwiftAlgorithmsTests/FirstNonNilTests.swift b/Tests/SwiftAlgorithmsTests/FirstNonNilTests.swift index bc07a39c..cc6d0269 100644 --- a/Tests/SwiftAlgorithmsTests/FirstNonNilTests.swift +++ b/Tests/SwiftAlgorithmsTests/FirstNonNilTests.swift @@ -9,8 +9,8 @@ // //===----------------------------------------------------------------------===// -import XCTest import Algorithms +import XCTest final class FirstNonNilTests: XCTestCase { func testFirstNonNil() { diff --git a/Tests/SwiftAlgorithmsTests/GroupedTests.swift b/Tests/SwiftAlgorithmsTests/GroupedTests.swift index 83b24916..f90024ba 100644 --- a/Tests/SwiftAlgorithmsTests/GroupedTests.swift +++ b/Tests/SwiftAlgorithmsTests/GroupedTests.swift @@ -9,8 +9,8 @@ // //===----------------------------------------------------------------------===// -import XCTest import Algorithms +import XCTest final class GroupedTests: XCTestCase { private final class SampleError: Error {} diff --git a/Tests/SwiftAlgorithmsTests/IndexedTests.swift b/Tests/SwiftAlgorithmsTests/IndexedTests.swift index c1939d80..b44ea8f7 100644 --- a/Tests/SwiftAlgorithmsTests/IndexedTests.swift +++ b/Tests/SwiftAlgorithmsTests/IndexedTests.swift @@ -9,8 +9,8 @@ // //===----------------------------------------------------------------------===// -import XCTest import Algorithms +import XCTest final class IndexedTests: XCTestCase { func testIndexed() { @@ -27,8 +27,8 @@ final class IndexedTests: XCTestCase { let indexOfI = si.last(where: { $0.element == "I" })!.index XCTAssertEqual("I", s[indexOfI]) } - + func testIndexedLazy() { - XCTAssertLazyCollection("ABCD".lazy.indexed()) + requireLazyCollection("ABCD".lazy.indexed()) } } diff --git a/Tests/SwiftAlgorithmsTests/IntersperseTests.swift b/Tests/SwiftAlgorithmsTests/IntersperseTests.swift index 49c13762..d4ae1b67 100644 --- a/Tests/SwiftAlgorithmsTests/IntersperseTests.swift +++ b/Tests/SwiftAlgorithmsTests/IntersperseTests.swift @@ -10,49 +10,50 @@ //===----------------------------------------------------------------------===// import XCTest + @testable import Algorithms final class IntersperseTests: XCTestCase { func testSequence() { let interspersed = (1...).prefix(5).interspersed(with: 0) - XCTAssertEqualSequences(interspersed, [1,0,2,0,3,0,4,0,5]) + expectEqualSequences(interspersed, [1, 0, 2, 0, 3, 0, 4, 0, 5]) } func testSequenceEmpty() { let interspersed = (1...).prefix(0).interspersed(with: 0) - XCTAssertEqualSequences(interspersed, []) + expectEqualSequences(interspersed, []) } func testString() { let interspersed = "ABCDE".interspersed(with: "-") - XCTAssertEqualSequences(interspersed, "A-B-C-D-E") + expectEqualSequences(interspersed, "A-B-C-D-E") } func testStringEmpty() { let interspersed = "".interspersed(with: "-") - XCTAssertEqualSequences(interspersed, "") + expectEqualSequences(interspersed, "") } func testArray() { - let interspersed = [1,2,3,4].interspersed(with: 0) - XCTAssertEqualSequences(interspersed, [1,0,2,0,3,0,4]) + let interspersed = [1, 2, 3, 4].interspersed(with: 0) + expectEqualSequences(interspersed, [1, 0, 2, 0, 3, 0, 4]) } func testArrayEmpty() { let interspersed = [].interspersed(with: 0) - XCTAssertEqualSequences(interspersed, []) + expectEqualSequences(interspersed, []) } func testCollection() { - let interspersed = ["A","B","C","D"].interspersed(with: "-") + let interspersed = ["A", "B", "C", "D"].interspersed(with: "-") XCTAssertEqual(interspersed.count, 7) } func testBidirectionalCollection() { let reversed = "ABCDE".interspersed(with: "-").reversed() - XCTAssertEqualSequences(reversed, "E-D-C-B-A") + expectEqualSequences(reversed, "E-D-C-B-A") } - + func testIndexTraversals() { let validator = IndexValidator>() validator.validate("".interspersed(with: "-"), expectedCount: 0) @@ -62,42 +63,44 @@ final class IntersperseTests: XCTestCase { } func testIntersperseLazy() { - XCTAssertLazySequence((1...).prefix(0).lazy.interspersed(with: 0)) - XCTAssertLazyCollection("ABCDE".lazy.interspersed(with: "-")) + requireLazySequence((1...).prefix(0).lazy.interspersed(with: 0)) + requireLazyCollection("ABCDE".lazy.interspersed(with: "-")) } - + func testInterspersedMap() { - XCTAssertEqualSequences( + expectEqualSequences( (0..<0).lazy.interspersedMap({ $0 }, with: { _, _ in 100 }), []) - - XCTAssertEqualSequences( + + expectEqualSequences( (0..<1).lazy.interspersedMap({ $0 }, with: { _, _ in 100 }), [0]) - - XCTAssertEqualSequences( + + expectEqualSequences( (0..<5).lazy.interspersedMap({ $0 }, with: { $0 + $1 + 100 }), [0, 101, 1, 103, 2, 105, 3, 107, 4]) } - + func testInterspersedMapLazy() { - XCTAssertLazySequence(AnySequence([]).lazy.interspersedMap({ $0 }, with: { _, _ in 100 })) - XCTAssertLazyCollection((0..<0).lazy.interspersedMap({ $0 }, with: { _, _ in 100 })) + requireLazySequence( + AnySequence([]).lazy.interspersedMap({ $0 }, with: { _, _ in 100 })) + requireLazyCollection( + (0..<0).lazy.interspersedMap({ $0 }, with: { _, _ in 100 })) } - + func testInterspersedMapIndexTraversals() { let validator = IndexValidator, Int>>() validator.validate( - (0..<0).lazy.interspersedMap({ $0 }, with: {_, _ in 100 }), + (0..<0).lazy.interspersedMap({ $0 }, with: { _, _ in 100 }), expectedCount: 0) validator.validate( - (0..<1).lazy.interspersedMap({ $0 }, with: {_, _ in 100 }), + (0..<1).lazy.interspersedMap({ $0 }, with: { _, _ in 100 }), expectedCount: 1) validator.validate( - (0..<2).lazy.interspersedMap({ $0 }, with: {_, _ in 100 }), + (0..<2).lazy.interspersedMap({ $0 }, with: { _, _ in 100 }), expectedCount: 3) validator.validate( - (0..<5).lazy.interspersedMap({ $0 }, with: {_, _ in 100 }), + (0..<5).lazy.interspersedMap({ $0 }, with: { _, _ in 100 }), expectedCount: 9) } } diff --git a/Tests/SwiftAlgorithmsTests/JoinedTests.swift b/Tests/SwiftAlgorithmsTests/JoinedTests.swift index 690659e5..f9805f33 100644 --- a/Tests/SwiftAlgorithmsTests/JoinedTests.swift +++ b/Tests/SwiftAlgorithmsTests/JoinedTests.swift @@ -10,6 +10,7 @@ //===----------------------------------------------------------------------===// import XCTest + @testable import Algorithms final class JoinedTests: XCTestCase { @@ -21,110 +22,124 @@ final class JoinedTests: XCTestCase { ["foo", "bar"], ["", "", "foo", "", "bar", "baz", ""], ] - + func testJoined() { let expected = ["", "", "", "foo", "foobar", "foobarbaz"] - + for (strings, expected) in zip(stringArrays, expected) { // regular sequence - XCTAssertEqualSequences(AnySequence(strings).joined(), expected) - + expectEqualSequences(AnySequence(strings).joined(), expected) + // lazy sequence - XCTAssertEqualSequences(AnySequence(strings).lazy.joined(), expected) - + expectEqualSequences(AnySequence(strings).lazy.joined(), expected) + // regular collection - XCTAssertEqualSequences(strings.joined(), expected) - + expectEqualSequences(strings.joined(), expected) + // lazy collection - XCTAssertEqualSequences(strings.lazy.joined() as FlattenCollection, expected) + expectEqualSequences( + strings.lazy.joined() as FlattenCollection, expected) } } - + func testJoinedByElement() { let separator: Character = " " let expected = ["", "", " ", "foo", "foo bar", " foo bar baz "] - + for (strings, expected) in zip(stringArrays, expected) { - XCTAssertEqualSequences(AnySequence(strings).joined(by: separator), expected) - XCTAssertEqualSequences(AnySequence(strings).lazy.joined(by: separator), expected) - XCTAssertEqualSequences(strings.joined(by: separator), expected) - XCTAssertEqualSequences(strings.lazy.joined(by: separator), expected) + expectEqualSequences( + AnySequence(strings).joined(by: separator), expected) + expectEqualSequences( + AnySequence(strings).lazy.joined(by: separator), expected) + expectEqualSequences(strings.joined(by: separator), expected) + expectEqualSequences(strings.lazy.joined(by: separator), expected) } } - + func testJoinedBySequence() { let separator = ", " let expected = ["", "", ", ", "foo", "foo, bar", ", , foo, , bar, baz, "] - + for (strings, expected) in zip(stringArrays, expected) { - XCTAssertEqualSequences(AnySequence(strings).joined(by: separator), expected) - XCTAssertEqualSequences(AnySequence(strings).lazy.joined(by: separator), expected) - XCTAssertEqualSequences(strings.joined(by: separator), expected) - XCTAssertEqualSequences(strings.lazy.joined(by: separator), expected) + expectEqualSequences( + AnySequence(strings).joined(by: separator), expected) + expectEqualSequences( + AnySequence(strings).lazy.joined(by: separator), expected) + expectEqualSequences(strings.joined(by: separator), expected) + expectEqualSequences(strings.lazy.joined(by: separator), expected) } } - + func testJoinedByElementClosure() { let separator = { (left: String, right: String) -> Character in left.isEmpty || right.isEmpty ? " " : "-" } - + let expected = ["", "", " ", "foo", "foo-bar", " foo bar-baz "] - + for (strings, expected) in zip(stringArrays, expected) { - XCTAssertEqualSequences(AnySequence(strings).joined(by: separator), expected) - XCTAssertEqualSequences(AnySequence(strings).lazy.joined(by: separator), expected) - XCTAssertEqualSequences(strings.joined(by: separator), expected) - XCTAssertEqualSequences(strings.lazy.joined(by: separator), expected) + expectEqualSequences( + AnySequence(strings).joined(by: separator), expected) + expectEqualSequences( + AnySequence(strings).lazy.joined(by: separator), expected) + expectEqualSequences(strings.joined(by: separator), expected) + expectEqualSequences(strings.lazy.joined(by: separator), expected) } } - + func testJoinedBySequenceClosure() { let separator = { (left: String, right: String) in "(\(left.count), \(right.count))" } - + let expected = [ "", "", "(0, 0)", "foo", "foo(3, 3)bar", - "(0, 0)(0, 3)foo(3, 0)(0, 3)bar(3, 3)baz(3, 0)" + "(0, 0)(0, 3)foo(3, 0)(0, 3)bar(3, 3)baz(3, 0)", ] - + for (strings, expected) in zip(stringArrays, expected) { - XCTAssertEqualSequences(AnySequence(strings).joined(by: separator), expected) - XCTAssertEqualSequences(AnySequence(strings).lazy.joined(by: separator), expected) - XCTAssertEqualSequences(strings.joined(by: separator), expected) - XCTAssertEqualSequences(strings.lazy.joined(by: separator), expected) + expectEqualSequences( + AnySequence(strings).joined(by: separator), expected) + expectEqualSequences( + AnySequence(strings).lazy.joined(by: separator), expected) + expectEqualSequences(strings.joined(by: separator), expected) + expectEqualSequences(strings.lazy.joined(by: separator), expected) } } - + func testJoinedLazy() { - XCTAssertLazySequence(AnySequence([[1], [2]]).lazy.joined()) - XCTAssertLazySequence(AnySequence([[1], [2]]).lazy.joined(by: 1)) - XCTAssertLazySequence(AnySequence([[1], [2]]).lazy.joined(by: { _, _ in 1 })) - XCTAssertLazyCollection([[1], [2]].lazy.joined()) - XCTAssertLazyCollection([[1], [2]].lazy.joined(by: 1)) - XCTAssertLazyCollection([[1], [2]].lazy.joined(by: { _, _ in 1 })) + requireLazySequence(AnySequence([[1], [2]]).lazy.joined()) + requireLazySequence(AnySequence([[1], [2]]).lazy.joined(by: 1)) + requireLazySequence( + AnySequence([[1], [2]]).lazy.joined(by: { _, _ in 1 })) + requireLazyCollection([[1], [2]].lazy.joined()) + requireLazyCollection([[1], [2]].lazy.joined(by: 1)) + requireLazyCollection([[1], [2]].lazy.joined(by: { _, _ in 1 })) } - + func testJoinedIndexTraversals() { let validator = IndexValidator>() - + // the last test case takes too long to run for strings in stringArrays.dropLast() { validator.validate(strings.joined() as FlattenCollection) } } - + func testJoinedByIndexTraversals() { let validator1 = IndexValidator>() - let validator2 = IndexValidator>() - + let validator2 = IndexValidator< + JoinedByClosureCollection<[String], String> + >() + // the last test case takes too long to run - for (strings, separator) in product(stringArrays.dropLast(), ["", " ", ", "]) { + for (strings, separator) in product( + stringArrays.dropLast(), ["", " ", ", "]) + { validator1.validate(strings.joined(by: separator)) validator2.validate(strings.lazy.joined(by: { _, _ in separator })) } diff --git a/Tests/SwiftAlgorithmsTests/KeyedTests.swift b/Tests/SwiftAlgorithmsTests/KeyedTests.swift index 282b2862..fdec3d72 100644 --- a/Tests/SwiftAlgorithmsTests/KeyedTests.swift +++ b/Tests/SwiftAlgorithmsTests/KeyedTests.swift @@ -9,8 +9,8 @@ // //===----------------------------------------------------------------------===// -import XCTest import Algorithms +import XCTest final class KeyedTests: XCTestCase { private final class SampleError: Error {} @@ -32,13 +32,16 @@ final class KeyedTests: XCTestCase { func testNonUniqueKeys() throws { let d = ["Apple", "Avocado", "Banana", "Cherry"].keyed(by: { $0.first! }) XCTAssertEqual(d.count, 3) - XCTAssertEqual(d["A"]!, "Avocado", "On a key-collision, keyed(by:) should take the latest value.") + XCTAssertEqual( + d["A"]!, "Avocado", + "On a key-collision, keyed(by:) should take the latest value.") XCTAssertEqual(d["B"]!, "Banana") XCTAssertEqual(d["C"]!, "Cherry") } func testNonUniqueKeysWithMergeFunction() { - var resolveCallHistory = [(key: Character, current: String, new: String)]() + var resolveCallHistory: [(key: Character, current: String, new: String)] = + [] let expectedCallHistory = [ (key: "A", current: "Apple", new: "Avocado"), (key: "C", current: "Cherry", new: "Coconut"), @@ -58,8 +61,9 @@ final class KeyedTests: XCTestCase { XCTAssertEqual(d["C"]!, "Cherry-Coconut") XCTAssertNil(d["D"]) + // Quick/dirty workaround: tuples aren't Equatable XCTAssertEqual( - resolveCallHistory.map(String.init(describing:)), // quick/dirty workaround: tuples aren't Equatable + resolveCallHistory.map(String.init(describing:)), expectedCallHistory.map(String.init(describing:)) ) } @@ -80,7 +84,8 @@ final class KeyedTests: XCTestCase { let error = SampleError() XCTAssertThrowsError( - try input.keyed(by: { $0.first! }, resolvingConflictsWith: { _, _, _ in throw error }) + try input.keyed( + by: { $0.first! }, resolvingConflictsWith: { _, _, _ in throw error }) ) { thrownError in XCTAssertIdentical(error, thrownError as? SampleError) } diff --git a/Tests/SwiftAlgorithmsTests/MinMaxTests.swift b/Tests/SwiftAlgorithmsTests/MinMaxTests.swift index 6ff99e05..63d13acd 100644 --- a/Tests/SwiftAlgorithmsTests/MinMaxTests.swift +++ b/Tests/SwiftAlgorithmsTests/MinMaxTests.swift @@ -9,41 +9,41 @@ // //===----------------------------------------------------------------------===// -import XCTest import Algorithms +import XCTest final class SortedPrefixTests: XCTestCase { func testMinCount() { // Replacement at start and end let input = [10, 11, 12, 1, 13, 2, 14, 3, 4, 5, 6, 0, 7, 8, 9] let min = input.min(count: 5) - XCTAssertEqualSequences(min, 0..<5) + expectEqualSequences(min, 0..<5) // Stability with all equal values let maxZeroes = Array(repeating: 0, count: 100) .enumerated() .max(count: 5, sortedBy: { $0.element < $1.element }) - XCTAssertEqualSequences(maxZeroes.map { $0.offset }, 95..<100) + expectEqualSequences(maxZeroes.map { $0.offset }, 95..<100) } - + func testMaxCount() { // Replacement at start and end let input = [0, 11, 12, 1, 13, 2, 14, 3, 4, 5, 6, 7, 8, 9, 10] let max = input.max(count: 5) - XCTAssertEqualSequences(max, 10..<15) + expectEqualSequences(max, 10..<15) // Stability with all equal values let maxZeroes = Array(repeating: 0, count: 100) .enumerated() .max(count: 5, sortedBy: { $0.element < $1.element }) - XCTAssertEqualSequences(maxZeroes.map { $0.offset }, 95..<100) + expectEqualSequences(maxZeroes.map { $0.offset }, 95..<100) } - + func testEmpty() { - let array = [Int]() + let array: [Int] = [] XCTAssertEqual(array.min(count: 0), []) XCTAssertEqual(array.min(count: 100), []) - + XCTAssertEqual(array.max(count: 0), []) XCTAssertEqual(array.max(count: 100), []) } @@ -51,23 +51,23 @@ final class SortedPrefixTests: XCTestCase { func testMinMaxSequences() { let input = (1...100).shuffled().interspersed(with: 50) let seq = AnySequence(input) - - XCTAssertEqualSequences(seq.min(count: 0), []) - XCTAssertEqualSequences(seq.min(count: 5), 1...5) - - XCTAssertEqualSequences(seq.max(count: 0), []) - XCTAssertEqualSequences(seq.max(count: 5), 96...100) + + expectEqualSequences(seq.min(count: 0), []) + expectEqualSequences(seq.min(count: 5), 1...5) + + expectEqualSequences(seq.max(count: 0), []) + expectEqualSequences(seq.max(count: 5), 96...100) } - + func testSortedPrefixComparable() { let array: [Int] = [20, 1, 4, 70, 100, 2, 3, 7, 90] XCTAssertEqual(array.min(count: 0), []) XCTAssertEqual(array.min(count: 1), [1]) - XCTAssertEqualSequences(array.min(count: 5), array.sorted().prefix(5)) + expectEqualSequences(array.min(count: 5), array.sorted().prefix(5)) XCTAssertEqual(array.min(count: 10), array.sorted()) } - + func testMinMaxWithHugeCount() { XCTAssertEqual([4, 2, 1, 3].min(count: .max), [1, 2, 3, 4]) XCTAssertEqual([4, 2, 1, 3].max(count: .max), [1, 2, 3, 4]) @@ -83,20 +83,20 @@ final class SortedPrefixTests: XCTestCase { XCTAssertEqual(input.min(count: 1, sortedBy: <), [range.lowerBound]) XCTAssertEqual(input.min(count: 1, sortedBy: >), [range.upperBound]) - XCTAssertEqualSequences(input.min(count: 5, sortedBy: <), range.prefix(5)) - XCTAssertEqualSequences( + expectEqualSequences(input.min(count: 5, sortedBy: <), range.prefix(5)) + expectEqualSequences( input.min(count: 5, sortedBy: >), range.suffix(5).reversed()) - XCTAssertEqualSequences( + expectEqualSequences( input.min(count: 500, sortedBy: <), range.prefix(500)) - XCTAssertEqualSequences( + expectEqualSequences( input.min(count: 500, sortedBy: >), range.suffix(500).reversed()) - XCTAssertEqualSequences(input.min(count: 1000, sortedBy: <), range) - XCTAssertEqualSequences( + expectEqualSequences(input.min(count: 1000, sortedBy: <), range) + expectEqualSequences( input.min(count: 1000, sortedBy: >), range.reversed()) } @@ -111,52 +111,60 @@ final class SortedPrefixTests: XCTestCase { XCTAssertEqual(input.max(count: 1, sortedBy: <), [range.upperBound]) XCTAssertEqual(input.max(count: 1, sortedBy: >), [range.lowerBound]) - XCTAssertEqualSequences(input.max(count: 5, sortedBy: <), range.suffix(5)) - XCTAssertEqualSequences( + expectEqualSequences(input.max(count: 5, sortedBy: <), range.suffix(5)) + expectEqualSequences( input.max(count: 5, sortedBy: >), range.prefix(5).reversed()) - XCTAssertEqualSequences( + expectEqualSequences( input.max(count: 500, sortedBy: <), range.suffix(500)) - XCTAssertEqualSequences( + expectEqualSequences( input.max(count: 500, sortedBy: >), range.prefix(500).reversed()) - XCTAssertEqualSequences(input.max(count: 1000, sortedBy: <), range) - XCTAssertEqualSequences( + expectEqualSequences(input.max(count: 1000, sortedBy: <), range) + expectEqualSequences( input.max(count: 1000, sortedBy: >), range.reversed()) } func testStability() { - assertStability([1,1,1,2,5,7,3,6,2,5,7,3,6], withCount: 3) - assertStability([1,1,1,2,5,7,3,6,2,5,7,3,6], withCount: 6) - assertStability([1,1,1,2,5,7,3,6,2,5,7,3,6], withCount: 20) - assertStability([1,1,1,2,5,7,3,6,2,5,7,3,6], withCount: 1000) + assertStability([1, 1, 1, 2, 5, 7, 3, 6, 2, 5, 7, 3, 6], withCount: 3) + assertStability([1, 1, 1, 2, 5, 7, 3, 6, 2, 5, 7, 3, 6], withCount: 6) + assertStability([1, 1, 1, 2, 5, 7, 3, 6, 2, 5, 7, 3, 6], withCount: 20) + assertStability([1, 1, 1, 2, 5, 7, 3, 6, 2, 5, 7, 3, 6], withCount: 1000) assertStability(Array(repeating: 0, count: 100), withCount: 0) assertStability(Array(repeating: 0, count: 100), withCount: 1) assertStability(Array(repeating: 0, count: 100), withCount: 2) assertStability(Array(repeating: 0, count: 100), withCount: 5) assertStability(Array(repeating: 0, count: 100), withCount: 20) assertStability(Array(repeating: 0, count: 100), withCount: 100) - assertStability(Array(repeating: 1, count: 50) + Array(repeating: 0, count: 50), withCount: 2) - assertStability(Array(repeating: 1, count: 50) + Array(repeating: 0, count: 50), withCount: 5) - assertStability(Array(repeating: 1, count: 50) + Array(repeating: 0, count: 50), withCount: 20) - assertStability(Array(repeating: 1, count: 50) + Array(repeating: 0, count: 50), withCount: 50) - assertStability([0,0], withCount: 1) - assertStability([0,0], withCount: 2) - assertStability([0,1,0,1,0,1], withCount: 2) - assertStability([0,1,0,1,0,1], withCount: 6) - assertStability([0,0,0,1,1,1], withCount: 1) - assertStability([0,0,0,1,1,1], withCount: 3) - assertStability([0,0,0,1,1,1], withCount: 4) - assertStability([0,0,0,1,1,1], withCount: 6) - assertStability([1,1,1,0,0,0], withCount: 1) - assertStability([1,1,1,0,0,0], withCount: 3) - assertStability([1,1,1,0,0,0], withCount: 4) - assertStability([1,1,1,0,0,0], withCount: 6) - assertStability([1,1,1,0,0,0], withCount: 5) + assertStability( + Array(repeating: 1, count: 50) + Array(repeating: 0, count: 50), + withCount: 2) + assertStability( + Array(repeating: 1, count: 50) + Array(repeating: 0, count: 50), + withCount: 5) + assertStability( + Array(repeating: 1, count: 50) + Array(repeating: 0, count: 50), + withCount: 20) + assertStability( + Array(repeating: 1, count: 50) + Array(repeating: 0, count: 50), + withCount: 50) + assertStability([0, 0], withCount: 1) + assertStability([0, 0], withCount: 2) + assertStability([0, 1, 0, 1, 0, 1], withCount: 2) + assertStability([0, 1, 0, 1, 0, 1], withCount: 6) + assertStability([0, 0, 0, 1, 1, 1], withCount: 1) + assertStability([0, 0, 0, 1, 1, 1], withCount: 3) + assertStability([0, 0, 0, 1, 1, 1], withCount: 4) + assertStability([0, 0, 0, 1, 1, 1], withCount: 6) + assertStability([1, 1, 1, 0, 0, 0], withCount: 1) + assertStability([1, 1, 1, 0, 0, 0], withCount: 3) + assertStability([1, 1, 1, 0, 0, 0], withCount: 4) + assertStability([1, 1, 1, 0, 0, 0], withCount: 6) + assertStability([1, 1, 1, 0, 0, 0], withCount: 5) } func assertStability( @@ -165,19 +173,19 @@ final class SortedPrefixTests: XCTestCase { ) { func stableOrder( a: (offset: Int, element: Int), - b: (offset: Int, element: Int)) -> Bool - { + b: (offset: Int, element: Int) + ) -> Bool { a.element == b.element ? a.offset < b.offset : a.element < b.element } - + do { let sorted = actual.enumerated() .min(count: count) { $0.element < $1.element } XCTAssert(sorted.isSorted(by: stableOrder)) } - + do { let sorted = actual.enumerated() .max(count: count) { $0.element < $1.element } diff --git a/Tests/SwiftAlgorithmsTests/PartitionTests.swift b/Tests/SwiftAlgorithmsTests/PartitionTests.swift index 6dd4a8ff..142077d6 100644 --- a/Tests/SwiftAlgorithmsTests/PartitionTests.swift +++ b/Tests/SwiftAlgorithmsTests/PartitionTests.swift @@ -9,8 +9,8 @@ // //===----------------------------------------------------------------------===// -import XCTest import Algorithms +import XCTest final class PartitionTests: XCTestCase { func testStablePartition() { @@ -30,17 +30,17 @@ final class PartitionTests: XCTestCase { var b = a var r = b[p.. = (0 ..< 10) - + let input: Range = (0..<10) + let a = input.partitioningIndex(where: { $0 > 10 }) XCTAssertEqual(a, input.endIndex) - + let b = input.partitioningIndex(where: { $0 >= 0 }) XCTAssertEqual(b, input.startIndex) } - + func testPartitionWithSubrangeBidirectionalCollection() { for length in 10...20 { let a = Array(0..(count: R) where R.Bound == Int { let p = count.relative(to: 0 ..< .max) - .clamped(to: 0 ..< c.count + 1) + .clamped(to: 0..>( indicesIncludingEnd: { product in @@ -47,7 +48,7 @@ final class ProductTests: XCTestCase { } } + [.init(i1: product.base1.endIndex, i2: product.base2.startIndex)] }) - + validator.validate(product([1, 2, 3, 4], "abc"), expectedCount: 4 * 3) validator.validate(product([1, 2, 3, 4], ""), expectedCount: 4 * 0) validator.validate(product([], "abc"), expectedCount: 0 * 3) diff --git a/Tests/SwiftAlgorithmsTests/RandomSampleTests.swift b/Tests/SwiftAlgorithmsTests/RandomSampleTests.swift index ae1243fb..7b0594b9 100644 --- a/Tests/SwiftAlgorithmsTests/RandomSampleTests.swift +++ b/Tests/SwiftAlgorithmsTests/RandomSampleTests.swift @@ -10,6 +10,7 @@ //===----------------------------------------------------------------------===// import XCTest + @testable import Algorithms func validateRandomSamples( @@ -18,8 +19,9 @@ func validateRandomSamples( expectedValue: Int, file: StaticString = (#file), line: UInt = #line ) where S.Element == Int { - let expectedRange = ((expectedValue / 3) * 2) ... ((expectedValue / 3) * 4) - XCTAssertEqualSequences(samples.keys.sorted(), elements, + let expectedRange = ((expectedValue / 3) * 2)...((expectedValue / 3) * 4) + expectEqualSequences( + samples.keys.sorted(), elements, file: file, line: line) for v in samples.values { XCTAssert(expectedRange.contains(v), file: file, line: line) @@ -41,41 +43,44 @@ final class RandomSampleTests: XCTestCase { result[x, default: 0] += 1 } } - validateRandomSamples(samples, elements: c, expectedValue: (k * iterations) / n) + validateRandomSamples( + samples, elements: c, expectedValue: (k * iterations) / n) } - + func testRandomSampleCollection() { let samples: [Int: Int] = (0.. UInt64 { 0 } } var zero = ZeroGenerator() - _ = nextOffset(w: 1, using: &zero) // must not crash + _ = nextOffset(w: 1, using: &zero) // must not crash struct AlmostAllZeroGenerator: RandomNumberGenerator { private var forward: SplitMix64 @@ -119,8 +124,8 @@ final class RandomSampleTests: XCTestCase { } var almostAllZero = AlmostAllZeroGenerator(seed: 0) - _ = s.randomSample(count: k, using: &almostAllZero) // must not crash + _ = s.randomSample(count: k, using: &almostAllZero) // must not crash almostAllZero = AlmostAllZeroGenerator(seed: 0) - _ = c.randomSample(count: k, using: &almostAllZero) // must not crash + _ = c.randomSample(count: k, using: &almostAllZero) // must not crash } } diff --git a/Tests/SwiftAlgorithmsTests/ReductionsTests.swift b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift index 76322d30..831bf291 100644 --- a/Tests/SwiftAlgorithmsTests/ReductionsTests.swift +++ b/Tests/SwiftAlgorithmsTests/ReductionsTests.swift @@ -9,8 +9,8 @@ // //===----------------------------------------------------------------------===// -import XCTest import Algorithms +import XCTest final class ReductionsTests: XCTestCase { struct TestError: Error {} @@ -18,23 +18,26 @@ final class ReductionsTests: XCTestCase { // MARK: - Exclusive Reductions func testExclusiveLazy() { - XCTAssertEqualSequences((1...).prefix(4).lazy.reductions(0, +), [0, 1, 3, 6, 10]) - XCTAssertEqualSequences((1...).prefix(1).lazy.reductions(0, +), [0, 1]) - XCTAssertEqualSequences((1...).prefix(0).lazy.reductions(0, +), [0]) + expectEqualSequences( + (1...).prefix(4).lazy.reductions(0, +), [0, 1, 3, 6, 10]) + expectEqualSequences((1...).prefix(1).lazy.reductions(0, +), [0, 1]) + expectEqualSequences((1...).prefix(0).lazy.reductions(0, +), [0]) - XCTAssertEqualCollections([1, 2, 3, 4].lazy.reductions(0, +), [0, 1, 3, 6, 10]) - XCTAssertEqualCollections([1].lazy.reductions(0, +), [0, 1]) - XCTAssertEqualCollections(EmptyCollection().lazy.reductions(0, +), [0]) + expectEqualCollections( + [1, 2, 3, 4].lazy.reductions(0, +), [0, 1, 3, 6, 10]) + expectEqualCollections([1].lazy.reductions(0, +), [0, 1]) + expectEqualCollections(EmptyCollection().lazy.reductions(0, +), [0]) - XCTAssertEqual([1, 2, 3, 4].lazy.reductions(into: 0, +=), [0, 1, 3, 6, 10]) + XCTAssertEqual( + [1, 2, 3, 4].lazy.reductions(into: 0, +=), [0, 1, 3, 6, 10]) XCTAssertEqual([1].lazy.reductions(into: 0, +=), [0, 1]) XCTAssertEqual(EmptyCollection().lazy.reductions(into: 0, +=), [0]) - XCTAssertLazySequence((1...).prefix(1).lazy.reductions(0, +)) - XCTAssertLazySequence([1].lazy.reductions(0, +)) - XCTAssertLazyCollection([1].lazy.reductions(0, +)) + requireLazySequence((1...).prefix(1).lazy.reductions(0, +)) + requireLazySequence([1].lazy.reductions(0, +)) + requireLazyCollection([1].lazy.reductions(0, +)) } func testExclusiveEager() { @@ -51,9 +54,11 @@ final class ReductionsTests: XCTestCase { XCTAssertNoThrow(try [].reductions(0) { _, _ in throw TestError() }) XCTAssertThrowsError(try [1].reductions(0) { _, _ in throw TestError() }) } - + func testExclusiveIndexTraversals() { - let validator = IndexValidator, Int>>() + let validator = IndexValidator< + ExclusiveReductionsSequence, Int> + >() validator.validate((0..<0).lazy.reductions(0, +), expectedCount: 1) validator.validate((0..<1).lazy.reductions(0, +), expectedCount: 2) validator.validate((0..<4).lazy.reductions(0, +), expectedCount: 5) @@ -62,17 +67,17 @@ final class ReductionsTests: XCTestCase { // MARK: - Inclusive Reductions func testInclusiveLazy() { - XCTAssertEqualSequences((1...).prefix(4).lazy.reductions(+), [1, 3, 6, 10]) - XCTAssertEqualSequences((1...).prefix(1).lazy.reductions(+), [1]) - XCTAssertEqualSequences((1...).prefix(0).lazy.reductions(+), []) + expectEqualSequences((1...).prefix(4).lazy.reductions(+), [1, 3, 6, 10]) + expectEqualSequences((1...).prefix(1).lazy.reductions(+), [1]) + expectEqualSequences((1...).prefix(0).lazy.reductions(+), []) - XCTAssertEqualCollections([1, 2, 3, 4].lazy.reductions(+), [1, 3, 6, 10]) - XCTAssertEqualCollections([1].lazy.reductions(+), [1]) - XCTAssertEqualCollections(EmptyCollection().lazy.reductions(+), []) + expectEqualCollections([1, 2, 3, 4].lazy.reductions(+), [1, 3, 6, 10]) + expectEqualCollections([1].lazy.reductions(+), [1]) + expectEqualCollections(EmptyCollection().lazy.reductions(+), []) - XCTAssertLazySequence((1...).prefix(1).lazy.reductions(+)) - XCTAssertLazySequence([1].lazy.reductions(+)) - XCTAssertLazyCollection([1].lazy.reductions(+)) + requireLazySequence((1...).prefix(1).lazy.reductions(+)) + requireLazySequence([1].lazy.reductions(+)) + requireLazyCollection([1].lazy.reductions(+)) } func testInclusiveEager() { @@ -84,7 +89,7 @@ final class ReductionsTests: XCTestCase { XCTAssertNoThrow(try [1].reductions { _, _ in throw TestError() }) XCTAssertThrowsError(try [1, 1].reductions { _, _ in throw TestError() }) } - + func testInclusiveIndexTraversals() { let validator = IndexValidator>>() validator.validate((0..<0).lazy.reductions(+), expectedCount: 0) diff --git a/Tests/SwiftAlgorithmsTests/RotateTests.swift b/Tests/SwiftAlgorithmsTests/RotateTests.swift index 8d369bbe..1533f091 100644 --- a/Tests/SwiftAlgorithmsTests/RotateTests.swift +++ b/Tests/SwiftAlgorithmsTests/RotateTests.swift @@ -10,27 +10,36 @@ //===----------------------------------------------------------------------===// import XCTest + @testable import Algorithms final class RotateTests: XCTestCase { /// Tests the example given in `_reverse(subrange:until:)`’s documentation func testUnderscoreReverse() { - var input = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p"] + var input = [ + "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", + "p", + ] let limit: Int = 4 - let (lower, upper) = input._reverse(subrange: input.startIndex..>() validator.validate(empty.striding(by: 1)) validator.validate(empty.striding(by: 2)) } - + do { let range = 0...100 let validator = IndexValidator>>() @@ -102,7 +102,7 @@ final class StridingTests: XCTestCase { validator.validate(range.striding(by: 11)) validator.validate(range.striding(by: 101)) } - + do { let array = Array(0...100) let validator = IndexValidator>() @@ -110,7 +110,7 @@ final class StridingTests: XCTestCase { validator.validate(array.striding(by: 11)) validator.validate(array.striding(by: 101)) } - + do { let string = "swift rocks" let validator = IndexValidator>() @@ -119,7 +119,7 @@ final class StridingTests: XCTestCase { validator.validate(string.striding(by: 10)) } } - + func testOffsetBy() { let a = (0...100).striding(by: 22) let b = [0, 22, 44, 66, 88] @@ -127,17 +127,17 @@ final class StridingTests: XCTestCase { XCTAssertEqual(a[a.index(a.startIndex, offsetBy: i)], b[i]) } } - + func testOffsetByEndIndex() { let a = 1...5 - let b = a.striding(by: 3) // [1, 4] + let b = a.striding(by: 3) // [1, 4] let i = b.index(b.startIndex, offsetBy: 2) XCTAssertEqual(i, b.endIndex) } - + func testLazy() { - XCTAssertLazySequence(AnySequence(0..<100).lazy.striding(by: 3)) - XCTAssertLazySequence((0..<100).lazy.striding(by: 3)) - XCTAssertLazyCollection((0..<100).lazy.striding(by: 3)) + requireLazySequence(AnySequence(0..<100).lazy.striding(by: 3)) + requireLazySequence((0..<100).lazy.striding(by: 3)) + requireLazyCollection((0..<100).lazy.striding(by: 3)) } } diff --git a/Tests/SwiftAlgorithmsTests/SuffixTests.swift b/Tests/SwiftAlgorithmsTests/SuffixTests.swift index edfe8a3e..d168565a 100644 --- a/Tests/SwiftAlgorithmsTests/SuffixTests.swift +++ b/Tests/SwiftAlgorithmsTests/SuffixTests.swift @@ -9,40 +9,40 @@ // //===----------------------------------------------------------------------===// -import XCTest import Algorithms +import XCTest final class SuffixTests: XCTestCase { func testSuffix() { let a = 0...10 - XCTAssertEqualSequences(a.suffix(while: { $0 > 5 }), (6...10)) - XCTAssertEqualSequences(a.suffix(while: { $0 > 10 }), []) - XCTAssertEqualSequences(a.suffix(while: { $0 > 9 }), [10]) - XCTAssertEqualSequences(a.suffix(while: { $0 > -1 }), (0...10)) - + expectEqualSequences(a.suffix(while: { $0 > 5 }), (6...10)) + expectEqualSequences(a.suffix(while: { $0 > 10 }), []) + expectEqualSequences(a.suffix(while: { $0 > 9 }), [10]) + expectEqualSequences(a.suffix(while: { $0 > -1 }), (0...10)) + let empty: [Int] = [] - XCTAssertEqualSequences(empty.suffix(while: { $0 > 10 }), []) + expectEqualSequences(empty.suffix(while: { $0 > 10 }), []) } - + func testEndOfPrefix() { let array = Array(0..<10) XCTAssertEqual(array.endOfPrefix(while: { $0 < 3 }), 3) XCTAssertEqual(array.endOfPrefix(while: { _ in false }), array.startIndex) XCTAssertEqual(array.endOfPrefix(while: { _ in true }), array.endIndex) - - let empty = [Int]() + + let empty: [Int] = [] XCTAssertEqual(empty.endOfPrefix(while: { $0 < 3 }), 0) XCTAssertEqual(empty.endOfPrefix(while: { _ in false }), empty.startIndex) XCTAssertEqual(empty.endOfPrefix(while: { _ in true }), empty.endIndex) } - + func testStartOfSuffix() { let array = Array(0..<10) XCTAssertEqual(array.startOfSuffix(while: { $0 >= 3 }), 3) XCTAssertEqual(array.startOfSuffix(while: { _ in false }), array.endIndex) XCTAssertEqual(array.startOfSuffix(while: { _ in true }), array.startIndex) - - let empty = [Int]() + + let empty: [Int] = [] XCTAssertEqual(empty.startOfSuffix(while: { $0 < 3 }), 0) XCTAssertEqual(empty.startOfSuffix(while: { _ in false }), empty.endIndex) XCTAssertEqual(empty.startOfSuffix(while: { _ in true }), empty.startIndex) diff --git a/Tests/SwiftAlgorithmsTests/TestUtilities.swift b/Tests/SwiftAlgorithmsTests/TestUtilities.swift index ec9d8536..d73f808b 100644 --- a/Tests/SwiftAlgorithmsTests/TestUtilities.swift +++ b/Tests/SwiftAlgorithmsTests/TestUtilities.swift @@ -32,16 +32,16 @@ extension Numeric { struct SplitMix64: RandomNumberGenerator { private var state: UInt64 - + init(seed: UInt64) { self.state = seed } - + mutating func next() -> UInt64 { - self.state &+= 0x9e3779b97f4a7c15 + self.state &+= 0x9e37_79b9_7f4a_7c15 var z: UInt64 = self.state - z = (z ^ (z &>> 30)) &* 0xbf58476d1ce4e5b9 - z = (z ^ (z &>> 27)) &* 0x94d049bb133111eb + z = (z ^ (z &>> 30)) &* 0xbf58_476d_1ce4_e5b9 + z = (z ^ (z &>> 27)) &* 0x94d0_49bb_1331_11eb return z ^ (z &>> 31) } } @@ -54,7 +54,7 @@ struct AnyMutableCollection where Base: MutableCollection { extension AnyMutableCollection: MutableCollection { typealias Index = Base.Index typealias Element = Base.Element - + var startIndex: Base.Index { base.startIndex } var endIndex: Base.Index { base.endIndex } @@ -74,18 +74,19 @@ extension MutableCollection { } } -func XCTAssertEqualSequences( +func expectEqualSequences( _ expression1: @autoclosure () throws -> S1, _ expression2: @autoclosure () throws -> S2, _ message: @autoclosure () -> String = "", file: StaticString = (#file), line: UInt = #line ) rethrows where S1.Element: Equatable, S1.Element == S2.Element { - try XCTAssertEqualSequences(expression1(), expression2(), by: ==, + try expectEqualSequences( + expression1(), expression2(), by: ==, message(), file: file, line: line) } // Two sequences contains exactly the same element but not necessarily in the same order. -func XCTAssertUnorderedEqualSequences( +func expectUnorderedEqualSequences( _ expression1: @autoclosure () throws -> S1, _ expression2: @autoclosure () throws -> S2, file: StaticString = (#file), line: UInt = #line @@ -99,9 +100,10 @@ func XCTAssertUnorderedEqualSequences( } s1.remove(at: idx) } - + XCTAssertTrue( - missing.isEmpty, "first sequence missing '\(missing)' elements from second sequence", + missing.isEmpty, + "first sequence missing '\(missing)' elements from second sequence", file: file, line: line ) @@ -111,7 +113,7 @@ func XCTAssertUnorderedEqualSequences( ) } -func XCTAssertEqualSequences( +func expectEqualSequences( _ expression1: @autoclosure () throws -> S1, _ expression2: @autoclosure () throws -> S2, by areEquivalent: (S1.Element, S1.Element) -> Bool, @@ -121,8 +123,9 @@ func XCTAssertEqualSequences( func fail(_ reason: String) { let message = message() - XCTFail(message.isEmpty ? reason : "\(message) - \(reason)", - file: file, line: line) + XCTFail( + message.isEmpty ? reason : "\(message) - \(reason)", + file: file, line: line) } var iter1 = try expression1().makeIterator() @@ -130,11 +133,13 @@ func XCTAssertEqualSequences( var idx = 0 while true { switch (iter1.next(), iter2.next()) { - case let (e1?, e2?) where areEquivalent(e1, e2): + case (let e1?, let e2?) where areEquivalent(e1, e2): idx += 1 continue - case let (e1?, e2?): - fail("element \(e1) on first sequence does not match element \(e2) on second sequence at position \(idx)") + case (let e1?, let e2?): + fail( + "element \(e1) on first sequence does not match element \(e2) on second sequence at position \(idx)" + ) case (_?, nil): fail("second sequence shorter than first") case (nil, _?): @@ -145,11 +150,11 @@ func XCTAssertEqualSequences( } } -func XCTAssertLazySequence(_: S) {} -func XCTAssertLazyCollection(_: S) {} +func requireLazySequence(_: S) {} +func requireLazyCollection(_: S) {} /// Asserts two collections are equal by using their indices to access elements. -func XCTAssertEqualCollections( +func expectEqualCollections( _ expression1: @autoclosure () throws -> C1, _ expression2: @autoclosure () throws -> C2, _ message: @autoclosure () -> String = "", @@ -157,7 +162,8 @@ func XCTAssertEqualCollections( ) rethrows where C1.Element: Equatable, C1.Element == C2.Element { let c1 = try expression1() let c2 = try expression2() - XCTAssertEqual(c1.indices.count, c2.indices.count, message(), file: file, line: line) + XCTAssertEqual( + c1.indices.count, c2.indices.count, message(), file: file, line: line) for index in zip(c1.indices, c2.indices) { XCTAssertEqual(c1[index.0], c2[index.1], message(), file: file, line: line) } @@ -166,7 +172,7 @@ func XCTAssertEqualCollections( struct IndexValidator { let testRandomAccess = true let indicesIncludingEnd: (C) -> [C.Index] - + /// Creates a new instance of an index validator that can verify that indices /// of a collection behave correctly and consistently. /// @@ -188,7 +194,9 @@ struct IndexValidator { /// using the contents of the collection directly. init( testRandomAccess: Bool = true, - indicesIncludingEnd: @escaping (C) -> [C.Index] = { $0.indices + [$0.endIndex] } + indicesIncludingEnd: @escaping (C) -> [C.Index] = { + $0.indices + [$0.endIndex] + } ) { self.indicesIncludingEnd = indicesIncludingEnd } @@ -202,7 +210,7 @@ extension IndexValidator { let file: StaticString let line: UInt } - + /// Verifies that all index traversal methods behave as expected. /// /// Verifies the correctness of the implementations of `startIndex`, @@ -211,10 +219,6 @@ extension IndexValidator { /// `distance(from:to:)` by calling them with just about all possible input /// combinations. /// - /// - Parameters: - /// - collection: The collection to be validated. - /// - expectedCount: - /// /// - Complexity: O(*n*^3) where *n* is the length of the collection if the /// collection conforms to `RandomAccessCollection`, otherwise O(*n*^4). func validate( @@ -224,13 +228,13 @@ extension IndexValidator { ) { let indicesIncludingEnd = self.indicesIncludingEnd(collection) let count = indicesIncludingEnd.count - 1 - + let validator = Validator( collection: collection, count: count, indicesIncludingEnd: indicesIncludingEnd, file: file, line: line) - + validator.validateForward( testRandomAccess: testRandomAccess, expectedCount: expectedCount) @@ -244,13 +248,13 @@ extension IndexValidator where C: BidirectionalCollection { ) { let indicesIncludingEnd = self.indicesIncludingEnd(collection) let count = indicesIncludingEnd.count - 1 - + let validator = Validator( collection: collection, count: count, indicesIncludingEnd: indicesIncludingEnd, file: file, line: line) - + validator.validateForward( testRandomAccess: testRandomAccess, expectedCount: expectedCount) @@ -264,7 +268,7 @@ extension IndexValidator.Validator { XCTAssertEqual( expectedCount, count, "Count mismatch", file: file, line: line) } - + XCTAssertEqual( collection.count, count, "Count mismatch", @@ -281,21 +285,21 @@ extension IndexValidator.Validator { collection.endIndex, indicesIncludingEnd.last, "`endIndex` does not equal the last index", file: file, line: line) - + validateIndexAfter() validateIndices() validateIndexComparisons() - + if testRandomAccess { validateDistanceFromTo() validateIndexOffsetBy() validateIndexOffsetByLimitedBy() } } - + func validateIndexAfter() { var index = collection.startIndex - + for (offset, expected) in indicesIncludingEnd.enumerated().dropFirst() { collection.formIndex(after: &index) XCTAssertEqual( @@ -307,7 +311,7 @@ extension IndexValidator.Validator { file: file, line: line) } } - + func validateIndices() { XCTAssertEqual(collection.indices.count, count) for (offset, index) in collection.indices.enumerated() { @@ -317,7 +321,7 @@ extension IndexValidator.Validator { file: file, line: line) } } - + func validateIndexComparisons() { for (offsetA, a) in indicesIncludingEnd.enumerated() { XCTAssertEqual( @@ -328,7 +332,7 @@ extension IndexValidator.Validator { a < a, "Index at offset \(offsetA) is less than itself", file: file, line: line) - + for (offsetB, b) in indicesIncludingEnd[.., pastLimit: Bool) { for targetOffset in range { let distance = targetOffset - startOffset - let end = collection.index(start, offsetBy: distance, limitedBy: limit) - + let end = collection.index( + start, offsetBy: distance, limitedBy: limit) + if pastLimit { XCTAssertNil( end, @@ -406,7 +415,7 @@ extension IndexValidator.Validator { } } } - + if limit >= start { // the limit has an effect checkTargetRange(startOffset...limitOffset, pastLimit: false) @@ -423,18 +432,20 @@ extension IndexValidator.Validator { extension IndexValidator.Validator where C: BidirectionalCollection { func validateBackward(testRandomAccess: Bool) { validateIndexBefore() - + if testRandomAccess { validateDistanceFromTo() validateIndexOffsetBy() validateIndexOffsetByLimitedBy() } } - + func validateIndexBefore() { var index = collection.endIndex - for (offset, expected) in indicesIncludingEnd.enumerated().dropLast().reversed() { + for (offset, expected) in indicesIncludingEnd.enumerated().dropLast() + .reversed() + { collection.formIndex(before: &index) XCTAssertEqual( index, expected, @@ -445,10 +456,12 @@ extension IndexValidator.Validator where C: BidirectionalCollection { file: file, line: line) } } - + func validateDistanceFromTo() { for (startOffset, start) in indicesIncludingEnd.enumerated() { - for (endOffset, end) in indicesIncludingEnd.enumerated().prefix(startOffset) { + for (endOffset, end) in indicesIncludingEnd.enumerated().prefix( + startOffset) + { let distance = endOffset - startOffset XCTAssertEqual( collection.distance(from: start, to: end), distance, @@ -460,10 +473,12 @@ extension IndexValidator.Validator where C: BidirectionalCollection { } } } - + func validateIndexOffsetBy() { for (startOffset, start) in indicesIncludingEnd.enumerated() { - for (endOffset, end) in indicesIncludingEnd.enumerated().prefix(startOffset) { + for (endOffset, end) in indicesIncludingEnd.enumerated().prefix( + startOffset) + { let distance = endOffset - startOffset XCTAssertEqual( collection.index(start, offsetBy: distance), end, @@ -475,7 +490,7 @@ extension IndexValidator.Validator where C: BidirectionalCollection { } } } - + func validateIndexOffsetByLimitedBy() { for (startOffset, start) in indicesIncludingEnd.enumerated() { for (limitOffset, limit) in indicesIncludingEnd.enumerated() { @@ -486,8 +501,9 @@ extension IndexValidator.Validator where C: BidirectionalCollection { func checkTargetRange(_ range: ClosedRange, pastLimit: Bool) { for targetOffset in range { let distance = targetOffset - startOffset - let end = collection.index(start, offsetBy: distance, limitedBy: limit) - + let end = collection.index( + start, offsetBy: distance, limitedBy: limit) + if pastLimit { XCTAssertNil( end, @@ -508,7 +524,7 @@ extension IndexValidator.Validator where C: BidirectionalCollection { } } } - + if limit <= start { // the limit has an effect checkTargetRange(limitOffset...startOffset, pastLimit: false) diff --git a/Tests/SwiftAlgorithmsTests/TrimTests.swift b/Tests/SwiftAlgorithmsTests/TrimTests.swift index 46d38ce9..c03200ab 100644 --- a/Tests/SwiftAlgorithmsTests/TrimTests.swift +++ b/Tests/SwiftAlgorithmsTests/TrimTests.swift @@ -13,74 +13,78 @@ import Algorithms import XCTest final class TrimTests: XCTestCase { - + func testEmpty() { - let results_empty = ([] as [Int]).trimming { $0.isMultiple(of: 2) } - XCTAssertEqual(results_empty, []) + let resultsEmpty = ([] as [Int]).trimming { $0.isMultiple(of: 2) } + XCTAssertEqual(resultsEmpty, []) } - + func testNoMatch() { // No match (nothing trimmed). - let results_nomatch = [1, 3, 5, 7, 9, 11, 13, 15].trimming { + let resultsNoMatch = [1, 3, 5, 7, 9, 11, 13, 15].trimming { $0.isMultiple(of: 2) } - XCTAssertEqual(results_nomatch, [1, 3, 5, 7, 9, 11, 13, 15]) + XCTAssertEqual(resultsNoMatch, [1, 3, 5, 7, 9, 11, 13, 15]) } - + func testNoTailMatch() { // No tail match (only trim head). - let results_notailmatch = [1, 3, 5, 7, 9, 11, 13, 15].trimming { $0 < 10 } - XCTAssertEqual(results_notailmatch, [11, 13, 15]) + let resultsNoTailMatch = [1, 3, 5, 7, 9, 11, 13, 15].trimming { $0 < 10 } + XCTAssertEqual(resultsNoTailMatch, [11, 13, 15]) } - + func testNoHeadMatch() { // No head match (only trim tail). - let results_noheadmatch = [1, 3, 5, 7, 9, 11, 13, 15].trimming { $0 > 10 } - XCTAssertEqual(results_noheadmatch, [1, 3, 5, 7, 9]) + let resultsNoHeadMatch = [1, 3, 5, 7, 9, 11, 13, 15].trimming { $0 > 10 } + XCTAssertEqual(resultsNoHeadMatch, [1, 3, 5, 7, 9]) } - + func testBothEndsMatch() { // Both ends match, some string of >1 elements do not (return that string). let results = [2, 10, 11, 15, 20, 21, 100].trimming { $0.isMultiple(of: 2) } XCTAssertEqual(results, [11, 15, 20, 21]) } - + func testEverythingMatches() { // Everything matches (trim everything). - let results_allmatch = [1, 3, 5, 7, 9, 11, 13, 15].trimming { _ in true } - XCTAssertEqual(results_allmatch, []) + let resultsAllMatch = [1, 3, 5, 7, 9, 11, 13, 15].trimming { _ in true } + XCTAssertEqual(resultsAllMatch, []) } - + func testEverythingButOneMatches() { // Both ends match, one element does not (trim all except that element). - let results_one = [2, 10, 12, 15, 20, 100].trimming { $0.isMultiple(of: 2) } - XCTAssertEqual(results_one, [15]) + let resultsOne = [2, 10, 12, 15, 20, 100].trimming { $0.isMultiple(of: 2) } + XCTAssertEqual(resultsOne, [15]) } - + func testTrimmingPrefix() { - let results = [2, 10, 12, 15, 20, 100].trimmingPrefix { $0.isMultiple(of: 2) } + let results = [2, 10, 12, 15, 20, 100].trimmingPrefix { + $0.isMultiple(of: 2) + } XCTAssertEqual(results, [15, 20, 100]) } - + func testTrimmingSuffix() { - let results = [2, 10, 12, 15, 20, 100].trimmingSuffix { $0.isMultiple(of: 2) } + let results = [2, 10, 12, 15, 20, 100].trimmingSuffix { + $0.isMultiple(of: 2) + } XCTAssertEqual(results, [2, 10, 12, 15]) } - + // Self == Self.Subsequence func testTrimNoAmbiguity() { var values = [2, 10, 12, 15, 20, 100] as ArraySlice values.trim { $0.isMultiple(of: 2) } XCTAssertEqual(values, [15]) } - + // Self == Self.Subsequence func testTrimPrefixNoAmbiguity() { var values = [2, 10, 12, 15, 20, 100] as ArraySlice values.trimPrefix { $0.isMultiple(of: 2) } XCTAssertEqual(values, [15, 20, 100]) } - + // Self == Self.Subsequence func testTrimSuffixNoAmbiguity() { var values = [2, 10, 12, 15, 20, 100] as ArraySlice diff --git a/Tests/SwiftAlgorithmsTests/UniquePermutationsTests.swift b/Tests/SwiftAlgorithmsTests/UniquePermutationsTests.swift index 39b785e3..fd3b9464 100644 --- a/Tests/SwiftAlgorithmsTests/UniquePermutationsTests.swift +++ b/Tests/SwiftAlgorithmsTests/UniquePermutationsTests.swift @@ -9,12 +9,12 @@ // //===----------------------------------------------------------------------===// -import XCTest import Algorithms +import XCTest final class UniquePermutationsTests: XCTestCase { static let numbers = [1, 1, 1, 2, 3] - + static let numbersPermutations: [[[Int]]] = [ // k = 0 [[]], @@ -23,64 +23,70 @@ final class UniquePermutationsTests: XCTestCase { // 2 [[1, 1], [1, 2], [1, 3], [2, 1], [2, 3], [3, 1], [3, 2]], // 3 - [[1, 1, 1], [1, 1, 2], [1, 1, 3], - [1, 2, 1], [1, 2, 3], [1, 3, 1], [1, 3, 2], - [2, 1, 1], [2, 1, 3], [2, 3, 1], - [3, 1, 1], [3, 1, 2], [3, 2, 1]], + [ + [1, 1, 1], [1, 1, 2], [1, 1, 3], + [1, 2, 1], [1, 2, 3], [1, 3, 1], [1, 3, 2], + [2, 1, 1], [2, 1, 3], [2, 3, 1], + [3, 1, 1], [3, 1, 2], [3, 2, 1], + ], // 4 - [[1, 1, 1, 2], [1, 1, 1, 3], - [1, 1, 2, 1], [1, 1, 2, 3], - [1, 1, 3, 1], [1, 1, 3, 2], - [1, 2, 1, 1], [1, 2, 1, 3], [1, 2, 3, 1], - [1, 3, 1, 1], [1, 3, 1, 2], [1, 3, 2, 1], - [2, 1, 1, 1], [2, 1, 1, 3], [2, 1, 3, 1], [2, 3, 1, 1], - [3, 1, 1, 1], [3, 1, 1, 2], [3, 1, 2, 1], [3, 2, 1, 1]], + [ + [1, 1, 1, 2], [1, 1, 1, 3], + [1, 1, 2, 1], [1, 1, 2, 3], + [1, 1, 3, 1], [1, 1, 3, 2], + [1, 2, 1, 1], [1, 2, 1, 3], [1, 2, 3, 1], + [1, 3, 1, 1], [1, 3, 1, 2], [1, 3, 2, 1], + [2, 1, 1, 1], [2, 1, 1, 3], [2, 1, 3, 1], [2, 3, 1, 1], + [3, 1, 1, 1], [3, 1, 1, 2], [3, 1, 2, 1], [3, 2, 1, 1], + ], // 5 - [[1, 1, 1, 2, 3], [1, 1, 1, 3, 2], - [1, 1, 2, 1, 3], [1, 1, 2, 3, 1], - [1, 1, 3, 1, 2], [1, 1, 3, 2, 1], - [1, 2, 1, 1, 3], [1, 2, 1, 3, 1], [1, 2, 3, 1, 1], - [1, 3, 1, 1, 2], [1, 3, 1, 2, 1], [1, 3, 2, 1, 1], - [2, 1, 1, 1, 3], [2, 1, 1, 3, 1], [2, 1, 3, 1, 1], [2, 3, 1, 1, 1], - [3, 1, 1, 1, 2], [3, 1, 1, 2, 1], [3, 1, 2, 1, 1], [3, 2, 1, 1, 1]] + [ + [1, 1, 1, 2, 3], [1, 1, 1, 3, 2], + [1, 1, 2, 1, 3], [1, 1, 2, 3, 1], + [1, 1, 3, 1, 2], [1, 1, 3, 2, 1], + [1, 2, 1, 1, 3], [1, 2, 1, 3, 1], [1, 2, 3, 1, 1], + [1, 3, 1, 1, 2], [1, 3, 1, 2, 1], [1, 3, 2, 1, 1], + [2, 1, 1, 1, 3], [2, 1, 1, 3, 1], [2, 1, 3, 1, 1], [2, 3, 1, 1, 1], + [3, 1, 1, 1, 2], [3, 1, 1, 2, 1], [3, 1, 2, 1, 1], [3, 2, 1, 1, 1], + ], ] } extension UniquePermutationsTests { func testEmpty() { - XCTAssertEqualSequences(([] as [Int]).uniquePermutations(), [[]]) - XCTAssertEqualSequences(([] as [Int]).uniquePermutations(ofCount: 0), [[]]) - XCTAssertEqualSequences(([] as [Int]).uniquePermutations(ofCount: 1), []) - XCTAssertEqualSequences( + expectEqualSequences(([] as [Int]).uniquePermutations(), [[]]) + expectEqualSequences(([] as [Int]).uniquePermutations(ofCount: 0), [[]]) + expectEqualSequences(([] as [Int]).uniquePermutations(ofCount: 1), []) + expectEqualSequences( ([] as [Int]).uniquePermutations(ofCount: 1...3), []) } - + func testSingleCounts() { for (k, expectation) in Self.numbersPermutations.enumerated() { - XCTAssertEqualSequences( + expectEqualSequences( expectation, Self.numbers.uniquePermutations(ofCount: k)) } - - XCTAssertEqualSequences( + + expectEqualSequences( Self.numbersPermutations[5], Self.numbers.uniquePermutations()) } - + func testRanges() { for lower in Self.numbersPermutations.indices { // upper bounded - XCTAssertEqualSequences( + expectEqualSequences( Self.numbersPermutations[...lower].joined(), Self.numbers.uniquePermutations(ofCount: ...lower)) - + // lower bounded - XCTAssertEqualSequences( + expectEqualSequences( Self.numbersPermutations[lower...].joined(), Self.numbers.uniquePermutations(ofCount: lower...)) for upper in lower.. Bool { lhs.value == rhs.value } @@ -111,12 +117,13 @@ extension UniquePermutationsTests { let numbers = Self.numbers.map(IntBox.init) for k in 0...numbers.count { for p in numbers.uniquePermutations(ofCount: k) { - XCTAssertTrue(p.filter { $0.value == 1 }.allSatisfy { $0 === numbers[0] }) + XCTAssertTrue( + p.filter { $0.value == 1 }.allSatisfy { $0 === numbers[0] }) } } } - + func testLaziness() { - XCTAssertLazySequence("ABCD".lazy.uniquePermutations()) + requireLazySequence("ABCD".lazy.uniquePermutations()) } } diff --git a/Tests/SwiftAlgorithmsTests/UniqueTests.swift b/Tests/SwiftAlgorithmsTests/UniqueTests.swift index 96372af6..0489e00f 100644 --- a/Tests/SwiftAlgorithmsTests/UniqueTests.swift +++ b/Tests/SwiftAlgorithmsTests/UniqueTests.swift @@ -9,8 +9,8 @@ // //===----------------------------------------------------------------------===// -import XCTest import Algorithms +import XCTest final class UniqueTests: XCTestCase { func testUnique() { @@ -18,36 +18,40 @@ final class UniqueTests: XCTestCase { let b = a.uniqued() XCTAssertEqual(b.sorted(), Set(a).sorted()) XCTAssertEqual(10, Array(b).count) - + let c: [Int] = [] - XCTAssertEqualSequences(c.uniqued(), []) - + expectEqualSequences(c.uniqued(), []) + let d = Array(repeating: 1, count: 10) - XCTAssertEqualSequences(d.uniqued(), [1]) + expectEqualSequences(d.uniqued(), [1]) } - + func testUniqueOn() { - let a = ["Albemarle", "Abeforth", "Astrology", "Brandywine", "Beatrice", "Axiom"] + let a = [ + "Albemarle", "Abeforth", "Astrology", "Brandywine", "Beatrice", "Axiom", + ] let b = a.uniqued(on: { $0.first }) XCTAssertEqual(["Albemarle", "Brandywine"], b) - + let c: [Int] = [] XCTAssertEqual(c.uniqued(on: { $0.bitWidth }), []) - + let d = Array(repeating: "Andromeda", count: 10) - XCTAssertEqualSequences(d.uniqued(on: { $0.first }), ["Andromeda"]) + expectEqualSequences(d.uniqued(on: { $0.first }), ["Andromeda"]) } - + func testLazyUniqueOn() { - let a = ["Albemarle", "Abeforth", "Astrology", "Brandywine", "Beatrice", "Axiom"] + let a = [ + "Albemarle", "Abeforth", "Astrology", "Brandywine", "Beatrice", "Axiom", + ] let b = a.lazy.uniqued(on: { $0.first }) - XCTAssertEqualSequences(b, ["Albemarle", "Brandywine"]) - XCTAssertLazySequence(b) + expectEqualSequences(b, ["Albemarle", "Brandywine"]) + requireLazySequence(b) let c: [Int] = [] - XCTAssertEqualSequences(c.lazy.uniqued(on: { $0.bitWidth }), []) - + expectEqualSequences(c.lazy.uniqued(on: { $0.bitWidth }), []) + let d = Array(repeating: "Andromeda", count: 10) - XCTAssertEqualSequences(d.lazy.uniqued(on: { $0.first }), ["Andromeda"]) + expectEqualSequences(d.lazy.uniqued(on: { $0.first }), ["Andromeda"]) } } diff --git a/Tests/SwiftAlgorithmsTests/WindowsTests.swift b/Tests/SwiftAlgorithmsTests/WindowsTests.swift index 92db185b..5b644796 100644 --- a/Tests/SwiftAlgorithmsTests/WindowsTests.swift +++ b/Tests/SwiftAlgorithmsTests/WindowsTests.swift @@ -10,6 +10,7 @@ //===----------------------------------------------------------------------===// import XCTest + @testable import Algorithms final class WindowsTests: XCTestCase { @@ -18,73 +19,73 @@ final class WindowsTests: XCTestCase { let w = s.windows(ofCount: 2) var i = w.startIndex - XCTAssertEqualSequences(w[i], "sw") + expectEqualSequences(w[i], "sw") w.formIndex(after: &i) - XCTAssertEqualSequences(w[i], "wi") + expectEqualSequences(w[i], "wi") w.formIndex(after: &i) - XCTAssertEqualSequences(w[i], "if") + expectEqualSequences(w[i], "if") w.formIndex(after: &i) - XCTAssertEqualSequences(w[i], "ft") + expectEqualSequences(w[i], "ft") -// w.index(after: w.endIndex) // ← Precondition failed: windows index is out of range -// w.index(before: w.startIndex) // ← Precondition failed: windows index is out of range -// w.formIndex(after: &i); w[i] // ← Precondition failed: windows index is out of range + // w.index(after: w.endIndex) // ← Precondition failed: windows index is out of range + // w.index(before: w.startIndex) // ← Precondition failed: windows index is out of range + // w.formIndex(after: &i); w[i] // ← Precondition failed: windows index is out of range } - + func testWindowsOfRange() { let a = 0...100 - + XCTAssertTrue(a.windows(ofCount: 200).isEmpty) - + let w = a.windows(ofCount: 10) - - XCTAssertEqualSequences(w.first!, 0..<10) - XCTAssertEqualSequences(w.last!, 91..<101) + + expectEqualSequences(w.first!, 0..<10) + expectEqualSequences(w.last!, 91..<101) } - + func testWindowsOfInt() { - let a = [ 0, 1, 0, 1 ].windows(ofCount: 2) - + let a = [0, 1, 0, 1].windows(ofCount: 2) + XCTAssertEqual(a.count, 3) XCTAssertEqual(a.map { $0.reduce(0, +) }, [1, 1, 1]) - + let a2 = [0, 1, 2, 3, 4, 5, 6].windows(ofCount: 3).map { $0.reduce(0, +) }.reduce(0, +) - + XCTAssertEqual(a2, 3 + 6 + 9 + 12 + 15) } - + func testWindowsCount() { let a = [0, 1, 2, 3, 4, 5] XCTAssertEqual(a.windows(ofCount: 3).count, 4) - + let a2 = [0, 1, 2, 3, 4] XCTAssertEqual(a2.windows(ofCount: 6).count, 0) - - let a3 = [Int]() + + let a3: [Int] = [] XCTAssertEqual(a3.windows(ofCount: 2).count, 0) } - + func testWindowsSecondAndLast() { let a = [0, 1, 2, 3, 4, 5] let w = a.windows(ofCount: 4) let snd = w[w.index(after: w.startIndex)] - XCTAssertEqualSequences(snd, [1, 2, 3, 4]) - + expectEqualSequences(snd, [1, 2, 3, 4]) + let w2 = a.windows(ofCount: 3) - XCTAssertEqualSequences(w2.last!, [3, 4, 5]) + expectEqualSequences(w2.last!, [3, 4, 5]) } - + func testWindowsIndexAfterAndBefore() { let a = [0, 1, 2, 3, 4, 5].windows(ofCount: 2) var i = a.startIndex a.formIndex(after: &i) a.formIndex(after: &i) a.formIndex(before: &i) - XCTAssertEqualSequences(a[i], [1, 2]) + expectEqualSequences(a[i], [1, 2]) } - + func testWindowsIndexTraversals() { let validator = IndexValidator>( indicesIncludingEnd: { windows in @@ -92,9 +93,9 @@ final class WindowsTests: XCTestCase { let indices = windows.base.indices + [endIndex] return zip(indices, indices.dropFirst(windows.windowSize)) .map { .init(lowerBound: $0, upperBound: $1) } - + [.init(lowerBound: endIndex, upperBound: endIndex)] + + [.init(lowerBound: endIndex, upperBound: endIndex)] }) - + validator.validate("".windows(ofCount: 1), expectedCount: 0) validator.validate("a".windows(ofCount: 1), expectedCount: 1) validator.validate("ab".windows(ofCount: 1), expectedCount: 2) @@ -106,6 +107,6 @@ final class WindowsTests: XCTestCase { } func testWindowsLazy() { - XCTAssertLazyCollection([0, 1, 2, 3].lazy.windows(ofCount: 2)) + requireLazyCollection([0, 1, 2, 3].lazy.windows(ofCount: 2)) } }