diff --git a/CHANGELOG.md b/CHANGELOG.md index 84f0a457..794921be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,18 @@ package updates, you can specify your package dependency using ## [Unreleased] -*No changes yet.* +### Additions + +- The `overwrite(prefixWith:)` method has been added to element-mutable + collections. It takes a sequence with the same element type as the receiver; + copies the leading elements from that source on top of the leading elements + of the receiver, in order, until at least one sequence runs out; and returns + the index after the last element of the receiver that was overwritten. The + similar `overwrite(suffixWith:)` method is restricted to bidirectional + element-mutable collections, uses the trailing elements of the receiver as + the destination, and returns the index to the first element of the receiver + that was overwritten instead. There are variants of these methods for when + the source is an iterator, collection, or subsequence. --- diff --git a/Guides/Overwrite.md b/Guides/Overwrite.md new file mode 100644 index 00000000..5617a771 --- /dev/null +++ b/Guides/Overwrite.md @@ -0,0 +1,124 @@ +# Overwrite + +[[Source](../Sources/Algorithms/Overwrite.swift) | + [Tests](../Tests/SwiftAlgorithmsTests/OverwriteTests.swift)] + +Copy a sequence onto an element-mutable collection. + +```swift +var destination = Array("abcde") +print(String(destination)) // "abcde" + +let source = "123" +let changedEnd = destination.overwrite(prefixWith: source) +print(String(destination)) // "123de" +print(String(destination[changedEnd...])) // "de" +``` + +`overwrite(prefixWith:)` takes a source sequence and replaces the first `k` +elements of the receiver with the first `k` elements of the source, where *k* +is the length of the shorter sequence. `overwrite(forwardsWith:)` does the same +thing with a source collection, and `overwrite(prefixUsing:)` with an `inout` +source iterator. To preserve memory exclusivity, the +`overwrite(forwardsFrom:to:)` overload is required to copy between subsequences +of the same collection, where the source and destination are given as index +ranges. + +When the receiving element-mutable collection supports bidirectional traversal, +variants of the previous methods are defined that copy the source elements on +top of the receiver's suffix instead. The `overwrite(suffixWith:)` and +`overwrite(suffixUsing:)` methods use their source's prefix, while the +`overwrite(backwardsWith:)` and `overwrite(backwardsFrom:to:)` methods use +their source's suffix. + +## Detailed Design + +New methods are added to element-mutable collections: + +```swift +extension MutableCollection { + mutating func overwrite(prefixUsing source: inout I) -> Index + where I : IteratorProtocol, Self.Element == I.Element + + mutating func overwrite(prefixWith source: S) -> Index + where S : Sequence, Self.Element == S.Element + + mutating func overwrite(forwardsWith source: C) + -> (readEnd: C.Index, writtenEnd: Index) + where C : Collection, Self.Element == C.Element + + mutating func overwrite(forwardsFrom source: R, to destination: S) + -> (sourceRead: Range, destinationWritten: Range) + where R : RangeExpression, S : RangeExpression, Self.Index == R.Bound, + R.Bound == S.Bound +} + +extension MutableCollection where Self: BidirectionalCollection { + mutating func overwrite(suffixUsing source: inout I) -> Index + where I : IteratorProtocol, Self.Element == I.Element + + mutating func overwrite(suffixWith source: S) -> Index + where S : Sequence, Self.Element == S.Element + + mutating func overwrite(backwardsWith source: C) + -> (readStart: C.Index, writtenStart: Index) + where C : BidirectionalCollection, Self.Element == C.Element + + mutating func overwrite(backwardsFrom source: R, to destination: S) + -> (sourceRead: Range, destinationWritten: Range) + where R : RangeExpression, S : RangeExpression, Self.Index == R.Bound, + R.Bound == S.Bound +} +``` + +When the source is an iterator or sequence, the return value from `overwrite` +is a single index value within the receiver that is the non-anchored end of the +range of overwritten elements. The prefix-overwriting methods return the upper +bound, *i.e.* the index after the last touched element, and assume the lower +bound is the receiver's `startIndex`. The suffix-overwriting methods return the +lower bound, *i.e.* the index of the first touched element, and assume the +upper bound is the receiver's `endIndex`. Use of the return value is optional +to support casual use of copying without caring about the precise range of +effect. + +When the source is a collection, the return value from `overwrite` has two +components. The second component is the same as the sole value returned from +the overloads with iterator or sequence sources. The first component is the +non-anchored end of the range of the elements actually read from the source. +When the source is a subsequence, the return value's components are index +ranges fully bounding the touched elements instead of ranges implied from +isolated indices. + +### Complexity + +Calling these methods is O(_k_), where _k_ is the length of the shorter +(virtual) sequence between the receiver (subsequence) and the source. + +### Naming + +The initial development version of this library used the term-of-art "`copy`" +as the base name of this family of methods. But since the insertion-copying +methods (in `RangeReplaceableCollection`) do not use the term, and the term is +used for object copying in Foundation, a substitute term was chosen here. The +term "`overwrite`" gives a precise description of the kind of copying employed. + +### Comparison with other languages + +**C++:** Has a [`copy`][C++Copy] function in the `algorithm` library that takes +a bounding pair of input iterators for the source and a single output iterator +for the destination, returning one-past the last output iterator written over. +The `copy_if` function does not have an analogue, since it can be simulated by +submitting the result from `filter(_:)` as the source. There is a +[`copy_backward`][C++CopyBackward] function that copies elements backwards from +the far end of the source and destination, returning the near end of the +destination that got written. These functions take their buffer arguments as +separate iterator/pointer values; as such, the functions can handle the source +and destination buffers having overlap or otherwise being sub-buffers of a +shared collection. Swift's memory safety model prevents it from doing the +same, necessitating it to use customized methods when the source and +destination buffers subset the same super-buffer. + + + +[C++Copy]: https://en.cppreference.com/w/cpp/algorithm/copy +[C++CopyBackward]: https://en.cppreference.com/w/cpp/algorithm/copy_backward diff --git a/README.md b/README.md index 9033c90a..ddb34cdc 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ Read more about the package, and the intent behind it, in the [announcement on s - [`rotate(toStartAt:)`, `rotate(subrange:toStartAt:)`](https://github.com/apple/swift-algorithms/blob/main/Guides/Rotate.md): In-place rotation of elements. - [`stablePartition(by:)`, `stablePartition(subrange:by:)`](https://github.com/apple/swift-algorithms/blob/main/Guides/Partition.md): A partition that preserves the relative order of the resulting prefix and suffix. +- [`overwrite(prefixWith:)`, `overwrite(suffixWith:)`](./Guides/Overwrite.md): Copying from a sequence via overwriting elements. #### Combining collections diff --git a/Sources/Algorithms/Overwrite.swift b/Sources/Algorithms/Overwrite.swift new file mode 100644 index 00000000..b4d64c32 --- /dev/null +++ b/Sources/Algorithms/Overwrite.swift @@ -0,0 +1,377 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Algorithms open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// overwrite(prefixUsing:), overwrite(prefixWith:), overwrite(forwardsWith:), +// overwrite(forwardsFrom:to:) +//===----------------------------------------------------------------------===// + +extension MutableCollection { + /// Copies the prefix of the given iterator's virtual sequence on top of the + /// prefix of this collection. + /// + /// Copying stops when either the iterator runs out of elements or every + /// element of this collection has been overwritten. If you want to limit how + /// much of this collection can be overrun, call this method on the limiting + /// subsequence instead. + /// + /// - Parameters: + /// - source: The iterator with the virtual sequence of replacement values. + /// - Returns: The index that is one past-the-end of the elements of this + /// collection that were overwritten. It will be `endIndex` if all elements + /// of this collection were touched, but `startIndex` if none were. + /// - Postcondition: Let *k* be the lesser of `count` and the number of + /// elements in `source`'s virtual sequence. Then the next *k* elements + /// from `source` will have been extracted, `prefix(k)` will be equivalent + /// to that extracted sequence (in emission order), and `dropFirst(k)` will + /// be unchanged. + /// + /// - Complexity: O(*n*), where *n* is is the length of the shorter between + /// `self` and `source`'s virtual sequence. + @discardableResult + public mutating func overwrite( + prefixUsing source: inout I + ) -> Index where I.Element == Element { + // The second argument should be "\Element.self," but that's blocked by bug + // SR-12897. + return overwrite(prefixUsing: &source, { $0 }) + } + + /// Copies the prefix of the given sequence on top of the prefix of this + /// collection. + /// + /// Copying stops when the end of the shorter sequence is reached. If you + /// want to limit how much of this collection can be overrun, call this method + /// on the limiting subsequence instead. If you need access to the elements + /// of `source` that were not read, make an iterator from `source` and call + /// `overwrite(prefixUsing:)` instead. + /// + /// - Parameters: + /// - source: The sequence to read the replacement values from. + /// - Returns: The index after the last element of the overwritten prefix. It + /// will be `endIndex` if every element of this collection was touched, but + /// `startIndex` if none were. + /// - Postcondition: Let *k* be the element count of the shorter of `self` and + /// `source`. Then `prefix(k)` will be equivalent to `source.prefix(k)`, + /// while `dropFirst(k)` will be unchanged. + /// + /// - Complexity: O(*n*), where *n* is the length of the shorter sequence + /// between `self` and `source`. + @discardableResult + @inlinable + public mutating func overwrite( + prefixWith source: S + ) -> Index where S.Element == Element { + var iterator = source.makeIterator() + return overwrite(prefixUsing: &iterator) + } + + /// Copies the prefix of the given collection on top of the prefix of this + /// collection. + /// + /// Copying stops when the end of the shorter collection is reached. If you + /// want to limit how much of this collection can be overrun, call this method + /// on the limiting subsequence instead. + /// + /// - Parameters: + /// - source: The collection to read the replacement values from. + /// - Returns: A two-member tuple where the first member is the past-the-end + /// index for the range of `source` elements that were actually read and the + /// second member is the past-the-end index for the range of `self` elements + /// that were actually overwritten. The lower bound for each range is the + /// corresponding collection's `startIndex`. + /// - Postcondition: Let *r* be the returned value from the call to this + /// method. Then `self[..( + forwardsWith source: C + ) -> (readEnd: C.Index, writtenEnd: Index) + where C.Element == Element { + var indexIterator = source.indices.makeIterator() + let end = overwrite(prefixUsing: &indexIterator) { source[$0] } + return (indexIterator.next() ?? source.endIndex, end) + } + + /// Copies, in forward traversal, the prefix of a subsequence on top of the + /// prefix of another, using the given bounds to demarcate the subsequences. + /// + /// Copying stops when the end of the shorter subsequence is reached. + /// + /// - Precondition: + /// - `source` and `destination` must bound valid subsequences of this collection. + /// - Either `source` and `destination` are disjoint, or + /// `self[source].startIndex >= self[destination].startIndex`. + /// + /// - Parameters: + /// - source: The index range bounding the subsequence to read the + /// replacement values from. + /// - destination: The index range bounding the subsequence whose elements + /// will be overwritten. + /// - Returns: A two-member tuple where the first member are the indices of + /// `self[source]` that were read for copying and the second member are the + /// indices of `self[destination]` that were written over during copying. + /// - Postcondition: Let *k* be the element count of the shorter of + /// `self[source]` and `self[destination]`, and *c* be the pre-call value of + /// `self[source]`. Then `self[destination].prefix(k)` will be equivalent + /// to `c.prefix(k)`, while `self[destination].dropFirst(k)` is unchanged. + /// + /// - Complexity: O(*n*), where *n* is the length of the shorter subsequence + /// between `self[source]` and `self[destination]`. + public mutating func overwrite( + forwardsFrom source: R, + to destination: S + ) -> (sourceRead: Range, destinationWritten: Range) + where R.Bound == Index, S.Bound == Index { + let rangeS = source.relative(to: self), + rangeD = destination.relative(to: self) + var sourceIndex = rangeS.lowerBound, destinationIndex = rangeD.lowerBound + while sourceIndex < rangeS.upperBound, + destinationIndex < rangeD.upperBound { + self[destinationIndex] = self[sourceIndex] + formIndex(after: &sourceIndex) + formIndex(after: &destinationIndex) + } + return (rangeS.lowerBound ..< sourceIndex, + rangeD.lowerBound ..< destinationIndex) + } +} + +//===----------------------------------------------------------------------===// +// overwrite(suffixUsing:), overwrite(suffixWith:), overwrite(backwardsWith:), +// overwrite(backwardsFrom:to:) +//===----------------------------------------------------------------------===// + +extension MutableCollection where Self: BidirectionalCollection { + /// Copies the prefix of the given iterator's virtual sequence on top of the + /// suffix of this collection. + /// + /// Copying stops when either the iterator runs out of elements or every + /// element of this collection has been overwritten. If you want to limit how + /// much of this collection can be overrun, call this method on the limiting + /// subsequence instead. + /// + /// - Parameters: + /// - source: The iterator with the virtual sequence of replacement values. + /// - Returns: The index for the first element of this collection that was + /// overwritten. It will be `startIndex` if all elements of this collection + /// were touched, but `endIndex` if none were. + /// - Postcondition: Let *k* be the lesser of `count` and the number of + /// elements in `source`'s virtual sequence. Then the next *k* elements + /// from `source` will have been extracted, `suffix(k)` will be equivalent + /// to that extracted sequence (in emission order), and `dropLast(k)` will + /// be unchanged. + /// + /// - Complexity: O(*n*), where *n* is is the length of the shorter between + /// `self` and `source`'s virtual sequence. + @discardableResult + public mutating func overwrite( + suffixUsing source: inout I + ) -> Index where I.Element == Element { + // The second argument should be "\Element.self," but that's blocked by bug + // SR-12897. + return overwrite(suffixUsing: &source, doCorrect: true, { $0 }) + } + + /// Copies the prefix of the given sequence on top of the suffix of this + /// collection. + /// + /// Copying stops when either the sequence is exhausted or every element of + /// this collection is touched. If you want to limit how much of this + /// collection can be overrun, call this method on the limiting subsequence + /// instead. If you need access to the elements of `source` that were not + /// read, make an iterator from `source` and call `overwrite(suffixUsing:)` + /// instead. + /// + /// - Parameters: + /// - source: The sequence to read the replacement values from. + /// - Returns: The index for the first element of the overwritten sufffix. It + /// will be `startIndex` if every element of this collection was touched, + /// but `endIndex` if none were. + /// - Postcondition: Let *k* be the element count of the shorter of `self` and + /// `source`. Then `suffix(k)` will be equivalent to `source.prefix(k)`, + /// while `dropLast(k)` will be unchanged. + /// + /// - Complexity: O(*n*), where *n* is the length of the shorter sequence + /// between `self` and `source`. + @discardableResult + @inlinable + public mutating func overwrite( + suffixWith source: S + ) -> Index where S.Element == Element { + var iterator = source.makeIterator() + return overwrite(suffixUsing: &iterator) + } + + /// Copies the suffix of the given collection on top of the suffix of this + /// collection. + /// + /// Copying occurs backwards, and stops when the beginning of the shorter + /// collection is reached. If you want to limit how much of this collection + /// can be overrun, call this method on the limiting subsequence instead. + /// + /// - Parameters: + /// - source: The collection to read the replacement values from. + /// - Returns: A two-member tuple where the first member is the starting index + /// for the range of `source` elements that were actually read and the + /// second member is the starting index for the range of `self` elements + /// that were actually overwritten. The upper bound for each range is the + /// corresponding collection's `endIndex`. + /// - Postcondition: Let *r* be the returned value from the call to this + /// method. Then `self[r.writtenStart...]` will be equivalent to + /// `source[r.readStart...]`. Both subsequences will have an element count + /// of *k*, where *k* is minimum of `count` and `source.count`. + /// + /// - Complexity: O(*n*), where *n* is the length of the shorter collection + /// between `self` and `source`. + public mutating func overwrite( + backwardsWith source: C + ) -> (readStart: C.Index, writtenStart: Index) + where C.Element == Element { + var indexIterator = source.reversed().indices.makeIterator() + let start = overwrite(suffixUsing: &indexIterator, doCorrect: false) { + source[source.index(before: $0.base)] + } + return (indexIterator.next().map(\.base) ?? source.startIndex, start) + } + + /// Copies, in reverse traversal, the suffix of a subsequence on top of the + /// suffix of another, using the given bounds to demarcate the subsequences. + /// + /// Copying stops when the beginning of the shorter subsequence is reached. + /// + /// - Precondition: + /// - `source` and `destination` must bound valid subsequences of this collection. + /// - Either `source` and `destination` are disjoint, or + /// `self[source].endIndex <= self[destination].endIndex`. + /// + /// - Parameters: + /// - source: The index range bounding the subsequence to read the + /// replacement values from. + /// - destination: The index range bounding the subsequence whose elements + /// will be overwritten. + /// - Returns: A two-member tuple where the first member are the indices of + /// `self[source]` that were read for copying and the second member are the + /// indices of `self[destination]` that were written over during copying. + /// - Postcondition: Let *k* be the element count of the shorter of + /// `self[source]` and `self[destination]`, and *c* be the pre-call value of + /// `self[source]`. Then `self[destination].suffix(k)` will be equivalent + /// to `c.suffix(k)`, while `self[destination].dropLast(k)` is unchanged. + /// + /// - Complexity: O(*n*), where *n* is the length of the shorter subsequence + /// between `self[source]` and `self[destination]`. + public mutating func overwrite( + backwardsFrom source: R, + to destination: S + ) -> (sourceRead: Range, destinationWritten: Range) + where R.Bound == Index, S.Bound == Index { + let rangeS = source.relative(to: self), + rangeD = destination.relative(to: self) + var sourceIndex = rangeS.upperBound, destinationIndex = rangeD.upperBound + while sourceIndex > rangeS.lowerBound, + destinationIndex > rangeD.lowerBound { + formIndex(before: &destinationIndex) + formIndex(before: &sourceIndex) + self[destinationIndex] = self[sourceIndex] + } + return (sourceIndex ..< rangeS.upperBound, + destinationIndex ..< rangeD.upperBound) + } +} + +//===----------------------------------------------------------------------===// +// overwrite(prefixUsing:_:), overwrite(suffixUsing:doCorrect:_:) +//===----------------------------------------------------------------------===// + +fileprivate extension MutableCollection { + /// Copies the transformed prefix of the given iterator's virtual sequence on + /// top of the prefix of this collection, using the given closure for mapping. + /// + /// Copying stops when either the iterator runs out of elements or every + /// element of this collection has been overwritten. If you want to limit how + /// much of this collection can be overrun, call this method on the limiting + /// subsequence instead. + /// + /// - Parameters: + /// - source: The iterator with the virtual sequence of the seeds for the + /// replacement values. + /// - transform: The closure mapping seed values to the actual replacement + /// values. + /// - Returns: The index that is one past-the-end of the elements of this + /// collection that were overwritten. It will be `endIndex` if all elements + /// of this collection were touched, but `startIndex` if none were. + /// - Postcondition: Let *k* be the lesser of `count` and the number of + /// elements in `source`'s virtual sequence. Then `prefix(k)` will be + /// equivalent to the first *k* elements emitted from `source` and mapped + /// with `transform`, while `dropFirst(k)` is unchanged. + /// + /// - Complexity: O(*n*), where *n* is is the length of the shorter between + /// `self` and `source`'s virtual sequence. + mutating func overwrite( + prefixUsing source: inout I, + _ transform: (I.Element) -> Element + ) -> Index { + var current = startIndex + let end = endIndex + while current < end, let seed = source.next() { + self[current] = transform(seed) + formIndex(after: ¤t) + } + return current + } +} + +fileprivate extension MutableCollection where Self: BidirectionalCollection { + /// Copies the transformed prefix of the given iterator's virtual sequence on + /// top of the suffix of this collection, using the given closure for mapping. + /// + /// Copying stops when either the iterator runs out of elements or every + /// element of this collection has been overwritten. If you want to limit how + /// much of this collection can be overrun, call this method on the limiting + /// subsequence instead. + /// + /// - Parameters: + /// - source: The iterator with the virtual sequence of the seeds for the + /// replacement values. + /// - doCorrect: Whether to reverse the elements after replacement so their + /// order maintains their orientation from `source`, or not. + /// - transform: The closure mapping seed values to the actual replacement + /// values. + /// - Returns: The index for the first element of this collection that was + /// overwritten. It will be `startIndex` if all elements of this collection + /// were touched, but `endIndex` if none were. + /// - Postcondition: Let *k* be the lesser of `count` and the number of + /// elements in `source`'s virtual sequence. Then `suffix(k)` will be + /// equivalent to the first *k* elements emitted from `source` and mapped + /// with `transform`, while `dropLast(k)` is unchanged. + /// + /// - Complexity: O(*n*), where *n* is is the length of the shorter between + /// `self` and `source`'s virtual sequence. + mutating func overwrite( + suffixUsing source: inout I, + doCorrect: Bool, + _ transform: (I.Element) -> Element + ) -> Index { + var current = endIndex + let start = startIndex + while current > start, let seed = source.next() { + formIndex(before: ¤t) + self[current] = transform(seed) + } + if doCorrect { + self[current...].reverse() + } + return current + } +} diff --git a/Tests/SwiftAlgorithmsTests/OverwriteTests.swift b/Tests/SwiftAlgorithmsTests/OverwriteTests.swift new file mode 100644 index 00000000..40db5050 --- /dev/null +++ b/Tests/SwiftAlgorithmsTests/OverwriteTests.swift @@ -0,0 +1,729 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift Algorithms open source project +// +// Copyright (c) 2020 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// +//===----------------------------------------------------------------------===// + +import XCTest +import Algorithms + +/// Unit tests for the `overwrite` methods. +final class OverwriteTests: XCTestCase { + /// Test using an iterator as the source for prefix copying. + func testIteratorSourcePrefix() { + // Empty source and destination + let source1 = EmptyCollection() + var destination1 = source1, iterator1 = source1.makeIterator() + XCTAssertEqualSequences(destination1, []) + + let result1 = destination1.overwrite(prefixUsing: &iterator1) + XCTAssertEqual(result1, destination1.startIndex) + XCTAssertEqualSequences(IteratorSequence(iterator1), []) + XCTAssertEqualSequences(destination1, []) + + // Nonempty source with empty destination + let source2 = CollectionOfOne(1.1) + var destination2 = EmptyCollection(), + iterator2 = source2.makeIterator() + XCTAssertEqualSequences(destination2, []) + + let result2 = destination2.overwrite(prefixUsing: &iterator2) + XCTAssertEqual(result2, destination2.startIndex) + XCTAssertEqualSequences(IteratorSequence(iterator2), [1.1]) + XCTAssertEqualSequences(destination2, []) + + // Empty source with nonempty destination + let source3 = EmptyCollection() + var destination3 = CollectionOfOne(2.2), iterator3 = source3.makeIterator() + XCTAssertEqualSequences(destination3, [2.2]) + + let result3 = destination3.overwrite(prefixUsing: &iterator3) + XCTAssertEqual(result3, destination3.startIndex) + XCTAssertEqualSequences(IteratorSequence(iterator3), []) + XCTAssertEqualSequences(destination3, [2.2]) + + // Two one-element collections + let source4 = CollectionOfOne(3.3) + var destination4 = CollectionOfOne(4.4), iterator4 = source4.makeIterator() + XCTAssertEqualSequences(destination4, [4.4]) + + let result4 = destination4.overwrite(prefixUsing: &iterator4) + XCTAssertEqual(result4, destination4.endIndex) + XCTAssertEqualSequences(IteratorSequence(iterator4), []) + XCTAssertEqualSequences(destination4, [3.3]) + + // Two equal-length multi-element collections + let source5 = 1...5 + var destination5 = Array(6...10), iterator5 = source5.makeIterator() + XCTAssertEqualSequences(destination5, 6...10) + XCTAssertEqual(source5.count, destination5.count) + + let result5 = destination5.overwrite(prefixUsing: &iterator5) + XCTAssertEqual(result5, destination5.endIndex) + XCTAssertEqualSequences(IteratorSequence(iterator5), []) + XCTAssertEqualSequences(destination5, 1...5) + + // Source longer than multi-element destination + let source6 = 10..<20 + var destination6 = Array(1...5), iterator6 = source6.makeIterator() + XCTAssertEqualSequences(destination6, 1...5) + XCTAssertGreaterThan(source6.count, destination6.count) + + let result6 = destination6.overwrite(prefixUsing: &iterator6) + XCTAssertEqual(result6, destination6.endIndex) + XCTAssertEqualSequences(IteratorSequence(iterator6), 15..<20) + XCTAssertEqualSequences(destination6, 10..<15) + + // Multi-element source shorter than destination + let source7 = -5..<1 + var destination7 = Array(0..<10), iterator7 = source7.makeIterator() + XCTAssertEqualSequences(destination7, 0..<10) + XCTAssertLessThan(source7.count, destination7.count) + + let result7 = destination7.overwrite(prefixUsing: &iterator7) + XCTAssertEqual(result7, 6) + XCTAssertEqualSequences(IteratorSequence(iterator7), []) + XCTAssertEqualSequences(destination7, [-5, -4, -3, -2, -1, 0, 6, 7, 8, 9]) + + // Copying over part of the destination + var destination8 = Array("abcdefghijklm") + XCTAssertEqualSequences(destination8, "abcdefghijklm") + + let source8a = EmptyCollection() + var iterator8a = source8a.makeIterator() + let result8a = destination8[3..<7].overwrite(prefixUsing: &iterator8a) + XCTAssertTrue(source8a.isEmpty) + XCTAssertEqual(result8a, 3) + XCTAssertEqualSequences(IteratorSequence(iterator8a), []) + XCTAssertEqualSequences(destination8, "abcdefghijklm") + + let source8b = "12" + var iterator8b = source8b.makeIterator() + let result8b = destination8[3..<7].overwrite(prefixUsing: &iterator8b) + XCTAssertLessThan(source8b.count, destination8[3..<7].count) + XCTAssertEqual(result8b, 5) + XCTAssertEqualSequences(IteratorSequence(iterator8b), []) + XCTAssertEqualSequences(destination8, "abc12fghijklm") + + let source8c = "!@#$" + var iterator8c = source8c.makeIterator() + let result8c = destination8[3..<7].overwrite(prefixUsing: &iterator8c) + XCTAssertEqual(source8c.count, destination8[3..<7].count) + XCTAssertEqual(result8c, 7) + XCTAssertEqualSequences(IteratorSequence(iterator8c), []) + XCTAssertEqualSequences(destination8, "abc!@#$hijklm") + + let source8d = "NOPQRST" + var iterator8d = source8d.makeIterator() + let result8d = destination8[3..<7].overwrite(prefixUsing: &iterator8d) + XCTAssertGreaterThan(source8d.count, destination8[3..<7].count) + XCTAssertEqual(result8d, 7) + XCTAssertEqualSequences(IteratorSequence(iterator8d), "RST") + XCTAssertEqualSequences(destination8, "abcNOPQhijklm") + } + + /// Test using a sequence as the source for prefix copying. + func testSequenceSourcePrefix() { + // Empty source and destination + var destination1 = EmptyCollection() + XCTAssertEqualSequences(destination1, []) + XCTAssertEqual(destination1.overwrite(prefixWith: EmptyCollection()), + destination1.startIndex) + XCTAssertEqualSequences(destination1, []) + + // Nonempty source with empty destination + var destination2 = EmptyCollection() + XCTAssertEqualSequences(destination2, []) + XCTAssertEqual(destination2.overwrite(prefixWith: CollectionOfOne(1.1)), + destination2.startIndex) + XCTAssertEqualSequences(destination2, []) + + // Empty source with nonempty destination + var destination3 = CollectionOfOne(2.2) + XCTAssertEqualSequences(destination3, [2.2]) + XCTAssertEqual(destination3.overwrite(prefixWith: EmptyCollection()), + destination3.startIndex) + XCTAssertEqualSequences(destination3, [2.2]) + + // Two one-element collections + var destination4 = CollectionOfOne(3.3) + XCTAssertEqualSequences(destination4, [3.3]) + XCTAssertEqual(destination4.overwrite(prefixWith: CollectionOfOne(4.4)), + destination4.endIndex) + XCTAssertEqualSequences(destination4, [4.4]) + + // Two equal-length multi-element collections + var destination5 = Array(6...10) + XCTAssertEqualSequences(destination5, 6...10) + XCTAssertEqual(destination5.overwrite(prefixWith: 1...5), + destination5.endIndex) + XCTAssertEqualSequences(destination5, 1...5) + + // Source longer than multi-element destination + var destination6 = Array(1...5) + XCTAssertEqualSequences(destination6, 1...5) + XCTAssertEqual(destination6.overwrite(prefixWith: 10..<20), + destination6.endIndex) + XCTAssertEqualSequences(destination6, 10..<15) + + // Multi-element source shorter than destination + var destination7 = Array(0..<10) + XCTAssertEqualSequences(destination7, 0..<10) + XCTAssertEqual(destination7.overwrite(prefixWith: -5..<1), 6) + XCTAssertEqualSequences(destination7, [-5, -4, -3, -2, -1, 0, 6, 7, 8, 9]) + + // Copying over part of the destination + var destination8 = Array("abcdefghijklm") + XCTAssertEqualSequences(destination8, "abcdefghijklm") + XCTAssertEqual(destination8[3..<7].overwrite(prefixWith: EmptyCollection()), + 3) + XCTAssertEqualSequences(destination8, "abcdefghijklm") + XCTAssertEqual(destination8[3..<7].overwrite(prefixWith: "12"), 5) + XCTAssertEqualSequences(destination8, "abc12fghijklm") + XCTAssertEqual(destination8[3..<7].overwrite(prefixWith: "!@#$"), 7) + XCTAssertEqualSequences(destination8, "abc!@#$hijklm") + XCTAssertEqual(destination8[3..<7].overwrite(prefixWith: "NOPQRST"), 7) + XCTAssertEqualSequences(destination8, "abcNOPQhijklm") + } + + /// Test using a collection as the source for prefix copying. + func testCollectionSourcePrefix() { + // Empty source and destination + var destination1 = EmptyCollection() + XCTAssertEqualSequences(destination1, []) + + let source1 = EmptyCollection() + let (sEnd1, dEnd1) = destination1.overwrite(forwardsWith: source1) + XCTAssertEqual(sEnd1, source1.startIndex) + XCTAssertEqual(dEnd1, destination1.startIndex) + XCTAssertEqualSequences(destination1, []) + XCTAssertEqualSequences(source1[..() + XCTAssertEqualSequences(destination2, []) + + let source2 = CollectionOfOne(1.1) + let (sEnd2, dEnd2) = destination2.overwrite(forwardsWith: source2) + XCTAssertEqual(sEnd2, source2.startIndex) + XCTAssertEqual(dEnd2, destination2.startIndex) + XCTAssertEqualSequences(destination2, []) + XCTAssertEqualSequences(source2[..() + let (sEnd3, dEnd3) = destination3.overwrite(forwardsWith: source3) + XCTAssertEqual(sEnd3, source3.startIndex) + XCTAssertEqual(dEnd3, destination3.startIndex) + XCTAssertEqualSequences(destination3, [2.2]) + XCTAssertEqualSequences(source3[..() + var destination1 = source1, iterator1 = source1.makeIterator() + XCTAssertEqualSequences(destination1, []) + + let result1 = destination1.overwrite(suffixUsing: &iterator1) + XCTAssertEqual(result1, destination1.endIndex) + XCTAssertEqualSequences(IteratorSequence(iterator1), []) + XCTAssertEqualSequences(destination1, []) + + // Nonempty source with empty destination + let source2 = CollectionOfOne(1.1) + var destination2 = EmptyCollection(), + iterator2 = source2.makeIterator() + XCTAssertEqualSequences(destination2, []) + + let result2 = destination2.overwrite(suffixUsing: &iterator2) + XCTAssertEqual(result2, destination2.endIndex) + XCTAssertEqualSequences(IteratorSequence(iterator2), [1.1]) + XCTAssertEqualSequences(destination2, []) + + // Empty source with nonempty destination + let source3 = EmptyCollection() + var destination3 = CollectionOfOne(2.2), iterator3 = source3.makeIterator() + XCTAssertEqualSequences(destination3, [2.2]) + + let result3 = destination3.overwrite(suffixUsing: &iterator3) + XCTAssertEqual(result3, destination3.endIndex) + XCTAssertEqualSequences(IteratorSequence(iterator3), []) + XCTAssertEqualSequences(destination3, [2.2]) + + // Two one-element collections + let source4 = CollectionOfOne(3.3) + var destination4 = CollectionOfOne(4.4), iterator4 = source4.makeIterator() + XCTAssertEqualSequences(destination4, [4.4]) + + let result4 = destination4.overwrite(suffixUsing: &iterator4) + XCTAssertEqual(result4, destination4.startIndex) + XCTAssertEqualSequences(IteratorSequence(iterator4), []) + XCTAssertEqualSequences(destination4, [3.3]) + + // Two equal-length multi-element collections + let source5 = 1...5 + var destination5 = Array(6...10), iterator5 = source5.makeIterator() + XCTAssertEqualSequences(destination5, 6...10) + XCTAssertEqual(source5.count, destination5.count) + + let result5 = destination5.overwrite(suffixUsing: &iterator5) + XCTAssertEqual(result5, destination5.startIndex) + XCTAssertEqualSequences(IteratorSequence(iterator5), []) + XCTAssertEqualSequences(destination5, 1...5) + + // Source longer than multi-element destination + let source6 = 10..<20 + var destination6 = Array(1...5), iterator6 = source6.makeIterator() + XCTAssertEqualSequences(destination6, 1...5) + XCTAssertGreaterThan(source6.count, destination6.count) + + let result6 = destination6.overwrite(suffixUsing: &iterator6) + XCTAssertEqual(result6, destination6.startIndex) + XCTAssertEqualSequences(IteratorSequence(iterator6), 15..<20) + XCTAssertEqualSequences(destination6, 10..<15) + + // Multi-element source shorter than destination + let source7 = -5..<1 + var destination7 = Array(0..<10), iterator7 = source7.makeIterator() + XCTAssertEqualSequences(destination7, 0..<10) + XCTAssertLessThan(source7.count, destination7.count) + + let result7 = destination7.overwrite(suffixUsing: &iterator7) + XCTAssertEqual(result7, 4) + XCTAssertEqualSequences(IteratorSequence(iterator7), []) + XCTAssertEqualSequences(destination7, [0, 1, 2, 3, -5, -4, -3, -2, -1, 0]) + + // Copying over part of the destination + var destination8 = Array("abcdefghijklm") + XCTAssertEqualSequences(destination8, "abcdefghijklm") + + let source8a = EmptyCollection() + var iterator8a = source8a.makeIterator() + let result8a = destination8[3..<7].overwrite(suffixUsing: &iterator8a) + XCTAssertTrue(source8a.isEmpty) + XCTAssertEqual(result8a, 7) + XCTAssertEqualSequences(IteratorSequence(iterator8a), []) + XCTAssertEqualSequences(destination8, "abcdefghijklm") + + let source8b = "12" + var iterator8b = source8b.makeIterator() + let result8b = destination8[3..<7].overwrite(suffixUsing: &iterator8b) + XCTAssertLessThan(source8b.count, destination8[3..<7].count) + XCTAssertEqual(result8b, 5) + XCTAssertEqualSequences(IteratorSequence(iterator8b), []) + XCTAssertEqualSequences(destination8, "abcde12hijklm") + + let source8c = "!@#$" + var iterator8c = source8c.makeIterator() + let result8c = destination8[3..<7].overwrite(suffixUsing: &iterator8c) + XCTAssertEqual(source8c.count, destination8[3..<7].count) + XCTAssertEqual(result8c, 3) + XCTAssertEqualSequences(IteratorSequence(iterator8c), []) + XCTAssertEqualSequences(destination8, "abc!@#$hijklm") + + let source8d = "NOPQRST" + var iterator8d = source8d.makeIterator() + let result8d = destination8[3..<7].overwrite(suffixUsing: &iterator8d) + XCTAssertGreaterThan(source8d.count, destination8[3..<7].count) + XCTAssertEqual(result8d, 3) + XCTAssertEqualSequences(IteratorSequence(iterator8d), "RST") + XCTAssertEqualSequences(destination8, "abcNOPQhijklm") + } + + /// Test using a sequence as the source for suffix copying. + func testSequenceSourceSuffix() { + // Empty source and destination + var destination1 = EmptyCollection() + XCTAssertEqualSequences(destination1, []) + XCTAssertEqual(destination1.overwrite(suffixWith: []), + destination1.endIndex) + XCTAssertEqualSequences(destination1, []) + + // Nonempty source with empty destination + var destination2 = EmptyCollection() + XCTAssertEqualSequences(destination2, []) + XCTAssertEqual(destination2.overwrite(suffixWith: CollectionOfOne(1.1)), + destination2.endIndex) + XCTAssertEqualSequences(destination2, []) + + // Empty source with nonempty destination + var destination3 = CollectionOfOne(2.2) + XCTAssertEqualSequences(destination3, [2.2]) + XCTAssertEqual(destination3.overwrite(suffixWith: []), + destination3.endIndex) + XCTAssertEqualSequences(destination3, [2.2]) + + // Two one-element collections + var destination4 = CollectionOfOne(4.4) + XCTAssertEqualSequences(destination4, [4.4]) + XCTAssertEqual(destination4.overwrite(suffixWith: CollectionOfOne(3.3)), + destination4.startIndex) + XCTAssertEqualSequences(destination4, [3.3]) + + // Two equal-length multi-element collections + var destination5 = Array(6...10) + XCTAssertEqualSequences(destination5, 6...10) + XCTAssertEqual(destination5.overwrite(suffixWith: 1...5), + destination5.startIndex) + XCTAssertEqualSequences(destination5, 1...5) + + // Source longer than multi-element destination + var destination6 = Array(1...5) + XCTAssertEqualSequences(destination6, 1...5) + XCTAssertEqual(destination6.overwrite(suffixWith: 10..<20), + destination6.startIndex) + XCTAssertEqualSequences(destination6, 10..<15) + + // Multi-element source shorter than destination + var destination7 = Array(0..<10) + XCTAssertEqualSequences(destination7, 0..<10) + XCTAssertEqual(destination7.overwrite(suffixWith: -5..<1), 4) + XCTAssertEqualSequences(destination7, [0, 1, 2, 3, -5, -4, -3, -2, -1, 0]) + + // Copying over part of the destination + var destination8 = Array("abcdefghijklm") + XCTAssertEqualSequences(destination8, "abcdefghijklm") + XCTAssertEqual(destination8[3..<7].overwrite(suffixWith: []), 7) + XCTAssertEqualSequences(destination8, "abcdefghijklm") + XCTAssertEqual(destination8[3..<7].overwrite(suffixWith: "12"), 5) + XCTAssertEqualSequences(destination8, "abcde12hijklm") + XCTAssertEqual(destination8[3..<7].overwrite(suffixWith: "!@#$"), 3) + XCTAssertEqualSequences(destination8, "abc!@#$hijklm") + XCTAssertEqual(destination8[3..<7].overwrite(suffixWith: "NOPQRST"), 3) + XCTAssertEqualSequences(destination8, "abcNOPQhijklm") + } + + /// Test using a collection as the source for suffix copying. + func testCollectionSourceSuffix() { + // Empty source and destination + var destination1 = EmptyCollection() + XCTAssertEqualSequences(destination1, []) + + let source1 = EmptyCollection() + let (sStart1, dStart1) = destination1.overwrite(backwardsWith: source1) + XCTAssertEqual(sStart1, source1.endIndex) + XCTAssertEqual(dStart1, destination1.endIndex) + XCTAssertEqualSequences(destination1, []) + XCTAssertEqualSequences(source1[sStart1...], destination1[dStart1...]) + + // Nonempty source with empty destination + var destination2 = EmptyCollection() + XCTAssertEqualSequences(destination2, []) + + let source2 = CollectionOfOne(1.1) + let (sStart2, dStart2) = destination2.overwrite(backwardsWith: source2) + XCTAssertEqual(sStart2, source2.endIndex) + XCTAssertEqual(dStart2, destination2.endIndex) + XCTAssertEqualSequences(destination2, []) + XCTAssertEqualSequences(source2[sStart2...], destination2[dStart2...]) + + // Empty source with nonempty destination + var destination3 = CollectionOfOne(2.2) + XCTAssertEqualSequences(destination3, [2.2]) + + let source3 = EmptyCollection() + let (sStart3, dStart3) = destination3.overwrite(backwardsWith: source3) + XCTAssertEqual(sStart3, source3.endIndex) + XCTAssertEqual(dStart3, destination3.endIndex) + XCTAssertEqualSequences(destination3, [2.2]) + XCTAssertEqualSequences(source3[sStart3...], destination3[dStart3...]) + + // Two one-element collections + var destination4 = CollectionOfOne(3.3) + XCTAssertEqualSequences(destination4, [3.3]) + + let source4 = CollectionOfOne(4.4) + let (sStart4, dStart4) = destination4.overwrite(backwardsWith: source4) + XCTAssertEqual(sStart4, source4.startIndex) + XCTAssertEqual(dStart4, destination4.startIndex) + XCTAssertEqualSequences(destination4, [4.4]) + XCTAssertEqualSequences(source4[sStart4...], destination4[dStart4...]) + + // Two equal-length multi-element collections + var destination5 = Array(6...10) + XCTAssertEqualSequences(destination5, 6...10) + + let source5 = 1...5 + let (sStart5, dStart5) = destination5.overwrite(backwardsWith: source5) + XCTAssertEqual(sStart5, source5.startIndex) + XCTAssertEqual(dStart5, destination5.startIndex) + XCTAssertEqualSequences(destination5, 1...5) + XCTAssertEqualSequences(source5[sStart5...], destination5[dStart5...]) + + // Source longer than multi-element destination + var destination6 = Array(1...5) + XCTAssertEqualSequences(destination6, 1...5) + + let source6 = 10..<20 + let (sStart6, dStart6) = destination6.overwrite(backwardsWith: source6) + XCTAssertEqual(sStart6, 15) + XCTAssertEqual(dStart6, destination6.startIndex) + XCTAssertEqualSequences(destination6, 15..<20) + XCTAssertEqualSequences(source6[sStart6...], destination6[dStart6...]) + + // Multi-element source shorter than destination + var destination7 = Array(0..<10) + XCTAssertEqualSequences(destination7, 0..<10) + + let source7 = -5..<1 + let (sStart7, dStart7) = destination7.overwrite(backwardsWith: source7) + XCTAssertEqual(sStart7, source7.startIndex) + XCTAssertEqual(dStart7, 4) + XCTAssertEqualSequences(destination7, [0, 1, 2, 3, -5, -4, -3, -2, -1, 0]) + XCTAssertEqualSequences(source7[sStart7...], destination7[dStart7...]) + + // Copying over part of the destination + var destination8 = Array("abcdefghijklm") + XCTAssertEqualSequences(destination8, "abcdefghijklm") + + let source8a = "" + let (sStart8a, dStart8a) = destination8[3..<7] + .overwrite(backwardsWith: source8a) + XCTAssertEqual(sStart8a, source8a.endIndex) + XCTAssertEqual(dStart8a, 7) + XCTAssertEqualSequences(destination8, "abcdefghijklm") + XCTAssertEqualSequences(source8a[sStart8a...], destination8[dStart8a..<7]) + + let source8b = "12" + let (sStart8b, dStart8b) = destination8[3..<7] + .overwrite(backwardsWith: source8b) + XCTAssertEqual(sStart8b, source8b.startIndex) + XCTAssertEqual(dStart8b, 5) + XCTAssertEqualSequences(destination8, "abcde12hijklm") + XCTAssertEqualSequences(source8b[sStart8b...], destination8[dStart8b..<7]) + + let source8c = "!@#$" + let (sStart8c, dStart8c) = destination8[3..<7] + .overwrite(backwardsWith: source8c) + XCTAssertEqual(sStart8c, source8c.startIndex) + XCTAssertEqual(dStart8c, 3) + XCTAssertEqualSequences(destination8, "abc!@#$hijklm") + XCTAssertEqualSequences(source8c[sStart8c...], destination8[dStart8c..<7]) + + let source8d = "NOPQRST" + let (sStart8d, dStart8d) = destination8[3..<7] + .overwrite(backwardsWith: source8d) + XCTAssertEqual(sStart8d, source8d.index(source8d.endIndex, offsetBy: -4)) + XCTAssertEqual(dStart8d, 3) + XCTAssertEqualSequences(destination8, "abcQRSThijklm") + XCTAssertEqualSequences(source8d[.. + XCTAssertEqualSequences(sample, untarnished) + (sRange, dRange) = sample.overwrite(forwardsFrom: 1..<1, to: 6..<6) + XCTAssertEqualSequences(sample, untarnished) + XCTAssertEqual(sRange, 1..<1) + XCTAssertEqual(dRange, 6..<6) + + // Empty source + (sRange, dRange) = sample.overwrite(forwardsFrom: 2..<2, to: 7..<8) + XCTAssertEqualSequences(sample, untarnished) + XCTAssertEqual(sRange, 2..<2) + XCTAssertEqual(dRange, 7..<7) + + // Empty destination + (sRange, dRange) = sample.overwrite(forwardsFrom: 3..<4, to: 9..<9) + XCTAssertEqualSequences(sample, untarnished) + XCTAssertEqual(sRange, 3..<3) + XCTAssertEqual(dRange, 9..<9) + + // Equal nonempty source and destination + (sRange, dRange) = sample.overwrite(forwardsFrom: 5..<8, to: 5..<8) + XCTAssertEqualSequences(sample, untarnished) + XCTAssertEqual(sRange, 5..<8) + XCTAssertEqual(dRange, 5..<8) + + // Overlapping nonempty source and destination + (sRange, dRange) = sample.overwrite(forwardsFrom: 5..<9, to: 3..<7) + XCTAssertEqualSequences(sample, [0, 1, 2, 5, 6, 7, 8, 7, 8, 9]) + XCTAssertEqual(sRange, 5..<9) + XCTAssertEqual(dRange, 3..<7) + + // Disjoint but nonempty equal-sized source and destination + sample = untarnished + (sRange, dRange) = sample.overwrite(forwardsFrom: 7..<9, to: 2..<4) + XCTAssertEqualSequences(sample, [0, 1, 7, 8, 4, 5, 6, 7, 8, 9]) + XCTAssertEqual(sRange, 7..<9) + XCTAssertEqual(dRange, 2..<4) + + // Source longer than nonempty destination + sample = untarnished + (sRange, dRange) = sample.overwrite(forwardsFrom: 2..<6, to: 7..<10) + XCTAssertEqualSequences(sample, [0, 1, 2, 3, 4, 5, 6, 2, 3, 4]) + XCTAssertEqual(sRange, 2..<5) + XCTAssertEqual(dRange, 7..<10) + + // Nonempty source shorter than destination + sample = untarnished + (sRange, dRange) = sample.overwrite(forwardsFrom: 5..<7, to: 1..<9) + XCTAssertEqualSequences(sample, [0, 5, 6, 3, 4, 5, 6, 7, 8, 9]) + XCTAssertEqual(sRange, 5..<7) + XCTAssertEqual(dRange, 1..<3) + + // Using expressions other than `Range` + sample = untarnished + (sRange, dRange) = sample.overwrite(forwardsFrom: ..<2, to: 8...) + XCTAssertEqualSequences(sample, [0, 1, 2, 3, 4, 5, 6, 7, 0, 1]) + XCTAssertEqual(sRange, 0..<2) + XCTAssertEqual(dRange, 8..<10) + } + + /// Test backward copying within a collection. + func testInternalBackward() { + // Empty source and destination + let untarnished = (0..<10).map(Double.init) + var sample = untarnished, + sRange, dRange: Range + XCTAssertEqualSequences(sample, untarnished) + (sRange, dRange) = sample.overwrite(backwardsFrom: 1..<1, to: 6..<6) + XCTAssertEqualSequences(sample, untarnished) + XCTAssertEqual(sRange, 1..<1) + XCTAssertEqual(dRange, 6..<6) + + // Empty source + (sRange, dRange) = sample.overwrite(backwardsFrom: 2..<2, to: 7..<8) + XCTAssertEqualSequences(sample, untarnished) + XCTAssertEqual(sRange, 2..<2) + XCTAssertEqual(dRange, 8..<8) + + // Empty destination + (sRange, dRange) = sample.overwrite(backwardsFrom: 3..<4, to: 9..<9) + XCTAssertEqualSequences(sample, untarnished) + XCTAssertEqual(sRange, 4..<4) + XCTAssertEqual(dRange, 9..<9) + + // Equal nonempty source and destination + (sRange, dRange) = sample.overwrite(backwardsFrom: 5..<8, to: 5..<8) + XCTAssertEqualSequences(sample, untarnished) + XCTAssertEqual(sRange, 5..<8) + XCTAssertEqual(dRange, 5..<8) + + // Overlapping nonempty source and destination + (sRange, dRange) = sample.overwrite(backwardsFrom: 3..<7, to: 5..<9) + XCTAssertEqualSequences(sample, [0, 1, 2, 3, 4, 3, 4, 5, 6, 9]) + XCTAssertEqual(sRange, 3..<7) + XCTAssertEqual(dRange, 5..<9) + + // Disjoint but nonempty equal-sized source and destination + sample = untarnished + (sRange, dRange) = sample.overwrite(backwardsFrom: 7..<9, to: 2..<4) + XCTAssertEqualSequences(sample, [0, 1, 7, 8, 4, 5, 6, 7, 8, 9]) + XCTAssertEqual(sRange, 7..<9) + XCTAssertEqual(dRange, 2..<4) + + // Source longer than nonempty destination + sample = untarnished + (sRange, dRange) = sample.overwrite(backwardsFrom: 2..<6, to: 7..<10) + XCTAssertEqualSequences(sample, [0, 1, 2, 3, 4, 5, 6, 3, 4, 5]) + XCTAssertEqual(sRange, 3..<6) + XCTAssertEqual(dRange, 7..<10) + + // Nonempty source shorter than destination + sample = untarnished + (sRange, dRange) = sample.overwrite(backwardsFrom: 5..<7, to: 1..<9) + XCTAssertEqualSequences(sample, [0, 1, 2, 3, 4, 5, 6, 5, 6, 9]) + XCTAssertEqual(sRange, 5..<7) + XCTAssertEqual(dRange, 7..<9) + + // Using expressions other than `Range` + sample = untarnished + (sRange, dRange) = sample.overwrite(backwardsFrom: 8..., to: ..<2) + XCTAssertEqualSequences(sample, [8, 9, 2, 3, 4, 5, 6, 7, 8, 9]) + XCTAssertEqual(sRange, 8..<10) + XCTAssertEqual(dRange, 0..<2) + } +}