mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Now we initialize the array buffer from sequences using unsafe pointer arithmetic, which avoids extra retain/release traffic. But since the size of the sequence is not known in advance, we only initialize as much as underestimateCount() promises, and fall back to the slow approach for the tail. Nevertheless, for collections, where the size is known precisely, this technique is suboptimal only by one branch. rdar://20530390 Swift SVN r27380
2356 lines
72 KiB
Swift
2356 lines
72 KiB
Swift
// -*- swift -*-
|
|
// RUN: rm -rf %t ; mkdir -p %t
|
|
// RUN: %S/../../utils/gyb %s -o %t/Algorithm.swift
|
|
// RUN: %S/../../utils/line-directive %t/Algorithm.swift -- %target-build-swift %t/Algorithm.swift -o %t/a.out
|
|
// RUN: %S/../../utils/line-directive %t/Algorithm.swift -- %target-run %t/a.out
|
|
|
|
import StdlibUnittest
|
|
import SwiftPrivate
|
|
|
|
var Algorithm = TestSuite("Algorithm")
|
|
|
|
// FIXME(prext): remove this conformance.
|
|
extension String.UnicodeScalarView : Equatable {}
|
|
|
|
// FIXME(prext): remove this function.
|
|
public func == (
|
|
lhs: String.UnicodeScalarView, rhs: String.UnicodeScalarView) -> Bool {
|
|
return Array(lhs) == Array(rhs)
|
|
}
|
|
|
|
Algorithm.test("split") {
|
|
expectEqual(
|
|
[ "foo", " bar baz " ].map { $0.unicodeScalars },
|
|
split(" foo bar baz ".unicodeScalars, maxSplit: 1) { $0._isSpace() })
|
|
|
|
expectEqual(
|
|
[ "foo", "bar", "baz" ].map { $0.unicodeScalars },
|
|
split(
|
|
" foo bar baz ".unicodeScalars, allowEmptySlices: false) {
|
|
$0._isSpace()
|
|
})
|
|
|
|
expectEqual(
|
|
[ "", "", "foo", "", "", "bar", "baz", "" ].map { $0.unicodeScalars },
|
|
split(
|
|
" foo bar baz ".unicodeScalars, allowEmptySlices: true) {
|
|
$0._isSpace()
|
|
})
|
|
|
|
expectEqual(
|
|
[ "", "", "foo bar baz " ].map { $0.unicodeScalars },
|
|
split(
|
|
" foo bar baz ".unicodeScalars, allowEmptySlices: true, maxSplit: 2,
|
|
isSeparator: { $0._isSpace() }))
|
|
}
|
|
|
|
// FIXME(prext): move this struct to the point of use.
|
|
struct StartsWithTest {
|
|
let expected: Bool
|
|
let sequence: [Int]
|
|
let prefix: [Int]
|
|
let expectedLeftoverSequence: [Int]
|
|
let expectedLeftoverPrefix: [Int]
|
|
let loc: SourceLoc
|
|
|
|
init(
|
|
_ expected: Bool, _ sequence: [Int], _ prefix: [Int],
|
|
_ expectedLeftoverSequence: [Int],
|
|
_ expectedLeftoverPrefix: [Int],
|
|
file: String = __FILE__, line: UWord = __LINE__
|
|
) {
|
|
self.expected = expected
|
|
self.sequence = sequence
|
|
self.prefix = prefix
|
|
self.expectedLeftoverSequence = expectedLeftoverSequence
|
|
self.expectedLeftoverPrefix = expectedLeftoverPrefix
|
|
self.loc = SourceLoc(file, line, comment: "test data")
|
|
}
|
|
}
|
|
|
|
let startsWithTests = [
|
|
// Corner cases.
|
|
StartsWithTest(true, [], [], [], []),
|
|
|
|
StartsWithTest(false, [], [ 1 ], [], []),
|
|
StartsWithTest(true, [ 1 ], [], [], []),
|
|
|
|
// Equal sequences.
|
|
StartsWithTest(true, [ 1 ], [ 1 ], [], []),
|
|
StartsWithTest(true, [ 1, 2 ], [ 1, 2 ], [], []),
|
|
|
|
// Proper prefix.
|
|
StartsWithTest(true, [ 0, 1, 2 ], [ 0, 1 ], [], []),
|
|
StartsWithTest(false, [ 0, 1 ], [ 0, 1, 2 ], [], []),
|
|
|
|
StartsWithTest(true, [ 1, 2, 3, 4 ], [ 1, 2 ], [ 4 ], []),
|
|
StartsWithTest(false, [ 1, 2 ], [ 1, 2, 3, 4 ], [], [ 4 ]),
|
|
|
|
// Not a prefix.
|
|
StartsWithTest(false, [ 1, 2, 3, 4 ], [ 1, 2, 10 ], [ 4 ], []),
|
|
StartsWithTest(false, [ 1, 2, 10 ], [ 1, 2, 3, 4 ], [], [ 4 ]),
|
|
|
|
StartsWithTest(false, [ 1, 2, 3, 4, 10 ], [ 1, 2, 10 ], [ 4, 10 ], []),
|
|
StartsWithTest(false, [ 1, 2, 10 ], [ 1, 2, 3, 4, 10 ], [], [ 4, 10 ]),
|
|
]
|
|
|
|
// FIXME(prext): remove this function.
|
|
func checkStartsWith(
|
|
expected: Bool, sequence: [Int], prefix: [Int],
|
|
stackTrace: SourceLocStack
|
|
) {
|
|
expectEqual(expected, startsWith(sequence, prefix), stackTrace: stackTrace)
|
|
expectEqual(
|
|
expected, startsWith(sequence, prefix, (==)),
|
|
stackTrace: stackTrace)
|
|
expectEqual(
|
|
expected, startsWith(sequence.map { $0 * 2 }, prefix) { $0 / 2 == $1 },
|
|
stackTrace: stackTrace)
|
|
|
|
// Test using different types for the sequence and prefix.
|
|
expectEqual(
|
|
expected, startsWith(ContiguousArray(sequence), prefix),
|
|
stackTrace: stackTrace)
|
|
expectEqual(
|
|
expected, startsWith(ContiguousArray(sequence), prefix, (==)),
|
|
stackTrace: stackTrace)
|
|
}
|
|
|
|
Algorithm.test("startsWith") {
|
|
// FIXME(prext): remove these tests together with the startsWith() function when
|
|
// protocol extensions land. These tests have been migrated to the new API.
|
|
|
|
for test in startsWithTests {
|
|
checkStartsWith(
|
|
test.expected, test.sequence, test.prefix, test.loc.withCurrentLoc())
|
|
}
|
|
}
|
|
|
|
Algorithm.test("enumerate") {
|
|
// FIXME(prext): remove these tests together with the enumerate() function when
|
|
// protocol extensions land. These tests have been migrated to the new API.
|
|
|
|
var result = [String]()
|
|
for (i, s) in enumerate( "You will never retrieve the necronomicon!"._split(" ") ) {
|
|
result.append("\(i) \(s)")
|
|
}
|
|
expectEqual(
|
|
[ "0 You", "1 will", "2 never", "3 retrieve", "4 the", "5 necronomicon!" ],
|
|
result)
|
|
}
|
|
|
|
Algorithm.test("equal") {
|
|
// FIXME(prext): remove these tests together with the equal() function when
|
|
// protocol extensions land. These tests have been migrated to the new API.
|
|
|
|
var _0_4 = [0, 1, 2, 3]
|
|
expectFalse(equal(_0_4, 0..<3))
|
|
expectTrue(equal(_0_4, 0..<4))
|
|
expectFalse(equal(_0_4, 0..<5))
|
|
expectFalse(equal(_0_4, 1..<4))
|
|
}
|
|
|
|
Algorithm.test("equal/predicate") {
|
|
func compare(lhs: (Int, Int), rhs: (Int, Int)) -> Bool {
|
|
return lhs.0 == rhs.0 && lhs.1 == rhs.1
|
|
}
|
|
|
|
var _0_4 = [(0, 10), (1, 11), (2, 12), (3, 13)]
|
|
expectFalse(equal(_0_4, [(0, 10), (1, 11), (2, 12)], compare))
|
|
expectTrue(equal(_0_4, [(0, 10), (1, 11), (2, 12), (3, 13)], compare))
|
|
expectFalse(equal(_0_4, [(0, 10), (1, 11), (2, 12), (3, 13), (4, 14)], compare))
|
|
expectFalse(equal(_0_4, [(1, 11), (2, 12), (3, 13)], compare))
|
|
}
|
|
|
|
Algorithm.test("contains") {
|
|
// FIXME(prext): remove these tests together with the contains() function when
|
|
// protocol extensions land. These tests have been migrated to the new API.
|
|
|
|
let _0_4 = [0, 1, 2, 3]
|
|
expectFalse(contains(_0_4, 7))
|
|
expectTrue(contains(_0_4, 2))
|
|
expectFalse(contains(_0_4, { $0 - 10 > 0 }))
|
|
expectTrue(contains(_0_4, { $0 % 3 == 0 }))
|
|
}
|
|
|
|
Algorithm.test("min,max") {
|
|
expectEqual(2, min(3, 2))
|
|
expectEqual(3, min(3, 7, 5))
|
|
expectEqual(3, max(3, 2))
|
|
expectEqual(7, max(3, 7, 5))
|
|
|
|
// FIXME: add tests that check that min/max return the
|
|
// first element of the sequence (by reference equailty) that satisfy the
|
|
// condition.
|
|
}
|
|
|
|
Algorithm.test("minElement,maxElement") {
|
|
// FIXME(prext): remove these tests together with the minElement() function
|
|
// when protocol extensions land. These tests have been migrated to the new
|
|
// API.
|
|
|
|
var arr = [Int](count: 10, repeatedValue: 0)
|
|
for i in 0..<10 {
|
|
arr[i] = i % 7 + 2
|
|
}
|
|
expectEqual([2, 3, 4, 5, 6, 7, 8, 2, 3, 4], arr)
|
|
|
|
expectEqual(2, minElement(arr))
|
|
expectEqual(8, maxElement(arr))
|
|
|
|
// min and max element of a slice
|
|
expectEqual(3, minElement(arr[1..<5]))
|
|
expectEqual(6, maxElement(arr[1..<5]))
|
|
|
|
// FIXME: add tests that check that minElement/maxElement return the
|
|
// first element of the sequence (by reference equailty) that satisfy the
|
|
// condition.
|
|
}
|
|
|
|
Algorithm.test("filter/SequenceType") {
|
|
// FIXME(prext): remove these tests together with the filter() function when
|
|
// protocol extensions land. These tests have been migrated to the new API.
|
|
if true {
|
|
let s = MinimalSequence<Int>([], underestimatedCount: .Precise)
|
|
var result = filter(s) {
|
|
(x: Int) -> Bool in
|
|
expectUnreachable()
|
|
return true
|
|
}
|
|
expectType([Int].self, &result)
|
|
expectEqual([], result)
|
|
expectEqual([], Array(s)) { "sequence should be consumed" }
|
|
expectEqual(0, result.capacity)
|
|
}
|
|
if true {
|
|
let s = MinimalSequence(
|
|
[ 0, 30, 10, 90 ], underestimatedCount: .Value(0))
|
|
let result = filter(s) { (x: Int) -> Bool in false }
|
|
expectEqual([], result)
|
|
expectEqual([], Array(s)) { "sequence should be consumed" }
|
|
expectEqual(0, result.capacity)
|
|
}
|
|
if true {
|
|
let s = MinimalSequence(
|
|
[ 0, 30, 10, 90 ], underestimatedCount: .Value(0))
|
|
let result = filter(s) { (x: Int) -> Bool in true }
|
|
expectEqual([ 0, 30, 10, 90 ], result)
|
|
expectEqual([], Array(s)) { "sequence should be consumed" }
|
|
expectGE(2 * result.count, result.capacity)
|
|
}
|
|
if true {
|
|
let s = MinimalSequence(
|
|
[ 0, 30, 10, 90 ], underestimatedCount: .Value(0))
|
|
let result = filter(s) { $0 % 3 == 0 }
|
|
expectEqual([ 0, 30, 90 ], result)
|
|
expectEqual([], Array(s)) { "sequence should be consumed" }
|
|
expectGE(2 * result.count, result.capacity)
|
|
}
|
|
if true {
|
|
let s = MinimalSequence(
|
|
[ 0, 30, 10, 90 ], underestimatedCount: .Precise)
|
|
let result = filter(s) { $0 % 3 == 0 }
|
|
expectEqual([ 0, 30, 90 ], result)
|
|
expectEqual([], Array(s)) { "sequence should be consumed" }
|
|
expectGE(2 * result.count, result.capacity)
|
|
}
|
|
}
|
|
|
|
Algorithm.test("filter/CollectionType") {
|
|
// FIXME(prext): remove these tests together with the filter() function when
|
|
// protocol extensions land. These tests have been migrated to the new API.
|
|
if true {
|
|
let c = MinimalForwardCollection<Int>([])
|
|
var result = filter(c) {
|
|
(x: Int) -> Bool in
|
|
expectUnreachable()
|
|
return true
|
|
}
|
|
expectEqual([], result)
|
|
expectType([Int].self, &result)
|
|
expectEqual(0, result.capacity)
|
|
}
|
|
if true {
|
|
let c = MinimalForwardCollection([ 0, 30, 10, 90 ])
|
|
let result = filter(c) { (x: Int) -> Bool in false }
|
|
expectEqual([], result)
|
|
expectEqual(0, result.capacity)
|
|
}
|
|
if true {
|
|
let c = MinimalForwardCollection([ 0, 30, 10, 90 ])
|
|
let result = filter(c) { (x: Int) -> Bool in true }
|
|
expectEqual([ 0, 30, 10, 90 ], result)
|
|
expectGE(2 * result.count, result.capacity)
|
|
}
|
|
if true {
|
|
let c = MinimalForwardCollection([ 0, 30, 10, 90 ])
|
|
let result = filter(c) { $0 % 3 == 0 }
|
|
expectEqual([ 0, 30, 90 ], result)
|
|
expectGE(2 * result.count, result.capacity)
|
|
}
|
|
}
|
|
|
|
Algorithm.test("filter/eager") {
|
|
// Make sure filter is eager and only calls its predicate once per element.
|
|
|
|
// FIXME(prext): remove these tests together with the filter() function when
|
|
// protocol extensions land. These tests have been migrated to the new API.
|
|
var count = 0
|
|
let one = filter(0..<10) {
|
|
(x: Int)->Bool in ++count; return x == 1
|
|
}
|
|
for x in one {}
|
|
expectEqual(10, count)
|
|
for x in one {}
|
|
expectEqual(10, count)
|
|
}
|
|
|
|
Algorithm.test("map/SequenceType") {
|
|
// FIXME(prext): remove these tests together with the map() function when
|
|
// protocol extensions land. These tests have been migrated to the new API.
|
|
|
|
if true {
|
|
let s = MinimalSequence<Int>([], underestimatedCount: .Precise)
|
|
var result = map(s) {
|
|
(x: Int) -> Int16 in
|
|
expectUnreachable()
|
|
return 42
|
|
}
|
|
expectType([Int16].self, &result)
|
|
expectEqual([], result)
|
|
expectEqual([], Array(s))
|
|
}
|
|
if true {
|
|
let s = MinimalSequence(
|
|
[ 0, 30, 10, 90 ], underestimatedCount: .Value(0))
|
|
let result = map(s) { $0 + 1 }
|
|
expectEqual([ 1, 31, 11, 91 ], result)
|
|
expectEqual([], Array(s))
|
|
}
|
|
if true {
|
|
let s = MinimalSequence(
|
|
[ 0, 30, 10, 90 ], underestimatedCount: .Precise)
|
|
let result = map(s) { $0 + 1 }
|
|
expectEqual([ 1, 31, 11, 91 ], result)
|
|
expectEqual([], Array(s))
|
|
}
|
|
}
|
|
|
|
Algorithm.test("map/CollectionType") {
|
|
// FIXME(prext): remove these tests together with the map() function when
|
|
// protocol extensions land. These tests have been migrated to the new API.
|
|
|
|
if true {
|
|
let c = MinimalForwardCollection<Int>([])
|
|
var result = map(c) {
|
|
(x: Int) -> Int16 in
|
|
expectUnreachable()
|
|
return 42
|
|
}
|
|
expectType([Int16].self, &result)
|
|
expectEqual([], result)
|
|
expectLE(c.underestimatedCount, result.capacity)
|
|
}
|
|
if true {
|
|
let c = MinimalForwardCollection(
|
|
[ 0, 30, 10, 90 ], underestimatedCount: .Value(0))
|
|
let result = map(c) { $0 + 1 }
|
|
expectEqual([ 1, 31, 11, 91 ], result)
|
|
}
|
|
}
|
|
|
|
Algorithm.test("flatMap/SequenceType") {
|
|
// FIXME(prext): remove these tests together with the flatMap() function when
|
|
// protocol extensions land. These tests have been migrated to the new API.
|
|
|
|
if true {
|
|
let s = MinimalSequence<Int>([])
|
|
var result = flatMap(s) {
|
|
(x: Int) -> [Int16] in
|
|
expectUnreachable()
|
|
return [42]
|
|
}
|
|
expectType([Int16].self, &result)
|
|
expectEqual([], result)
|
|
expectEqual([], Array(s))
|
|
}
|
|
if true {
|
|
let s = MinimalSequence([ 0, 30, 10, 90 ])
|
|
let result = flatMap(s) { [$0 + 1] }
|
|
expectEqual([ 1, 31, 11, 91 ], result)
|
|
expectEqual([], Array(s))
|
|
}
|
|
}
|
|
|
|
Algorithm.test("flatMap/CollectionType") {
|
|
// FIXME(prext): remove these tests together with the flatMap() function when
|
|
// protocol extensions land. These tests have been migrated to the new API.
|
|
|
|
if true {
|
|
let c = MinimalForwardCollection<Int>([])
|
|
var result = flatMap(c) {
|
|
(x: Int) -> [Int16] in
|
|
expectUnreachable()
|
|
return [42]
|
|
}
|
|
expectType([Int16].self, &result)
|
|
expectEqual([], result)
|
|
}
|
|
if true {
|
|
let c = MinimalForwardCollection([ 0, 30, 10, 90 ])
|
|
let result = flatMap(c) { [$0 + 1] }
|
|
expectEqual([ 1, 31, 11, 91 ], result)
|
|
}
|
|
}
|
|
|
|
Algorithm.test("sorted/strings")
|
|
.xfail(.LinuxAny(reason: "String comparison: ICU vs. Foundation"))
|
|
.code {
|
|
expectEqual(
|
|
[ "Banana", "apple", "cherry" ],
|
|
sorted([ "apple", "Banana", "cherry" ]))
|
|
|
|
let s = sorted(["apple", "Banana", "cherry"]) {
|
|
count($0) > count($1)
|
|
}
|
|
expectEqual([ "Banana", "cherry", "apple" ], s)
|
|
}
|
|
|
|
// A wrapper around Array<T> that disables any type-specific algorithm
|
|
// optimizations and forces bounds checking on.
|
|
struct A<T> : MutableSliceable {
|
|
init(_ a: Array<T>) {
|
|
impl = a
|
|
}
|
|
|
|
var startIndex: Int {
|
|
return 0
|
|
}
|
|
|
|
var endIndex: Int {
|
|
return impl.count
|
|
}
|
|
|
|
func generate() -> Array<T>.Generator {
|
|
return impl.generate()
|
|
}
|
|
|
|
subscript(i: Int) -> T {
|
|
get {
|
|
expectTrue(i >= 0 && i < impl.count)
|
|
return impl[i]
|
|
}
|
|
set (x) {
|
|
expectTrue(i >= 0 && i < impl.count)
|
|
impl[i] = x
|
|
}
|
|
}
|
|
|
|
subscript(r: Range<Int>) -> Array<T>.SubSlice {
|
|
get {
|
|
expectTrue(r.startIndex >= 0 && r.startIndex <= impl.count)
|
|
expectTrue(r.endIndex >= 0 && r.endIndex <= impl.count)
|
|
return impl[r]
|
|
}
|
|
set (x) {
|
|
expectTrue(r.startIndex >= 0 && r.startIndex <= impl.count)
|
|
expectTrue(r.endIndex >= 0 && r.endIndex <= impl.count)
|
|
impl[r] = x
|
|
}
|
|
}
|
|
|
|
var impl: Array<T>
|
|
}
|
|
|
|
func withInvalidOrderings(body: ((Int,Int)->Bool)->Void) {
|
|
// Test some ordering predicates that don't create strict weak orderings
|
|
body { (_,_) in true }
|
|
body { (_,_) in false }
|
|
var i = 0
|
|
body { (_,_) in i++ % 2 == 0 }
|
|
body { (_,_) in i++ % 3 == 0 }
|
|
body { (_,_) in i++ % 5 == 0 }
|
|
}
|
|
|
|
func randomArray() -> A<Int> {
|
|
let count = Int(rand32(exclusiveUpperBound: 50))
|
|
return A(randArray(count))
|
|
}
|
|
|
|
Algorithm.test("invalidOrderings") {
|
|
withInvalidOrderings {
|
|
var a = randomArray()
|
|
sort(&a, $0)
|
|
}
|
|
withInvalidOrderings {
|
|
var a: A<Int>
|
|
a = randomArray()
|
|
partition(&a, indices(a), $0)
|
|
}
|
|
/*
|
|
// FIXME: Disabled due to <rdar://problem/17734737> Unimplemented:
|
|
// abstraction difference in l-value
|
|
withInvalidOrderings {
|
|
var a = randomArray()
|
|
var pred = $0
|
|
_insertionSort(&a, indices(a), &pred)
|
|
}
|
|
*/
|
|
withInvalidOrderings {
|
|
let predicate: (Int,Int)->Bool = $0
|
|
let result = lexicographicalCompare(randomArray(), randomArray(), isOrderedBefore: predicate)
|
|
}
|
|
}
|
|
|
|
// The routine is based on http://www.cs.dartmouth.edu/~doug/mdmspe.pdf
|
|
func makeQSortKiller(len: Int) -> [Int] {
|
|
var candidate: Int = 0
|
|
var keys = [Int:Int]()
|
|
func Compare(x: Int, y : Int) -> Bool {
|
|
if keys[x] == nil && keys[y] == nil {
|
|
if (x == candidate) {
|
|
keys[x] = keys.count
|
|
} else {
|
|
keys[y] = keys.count
|
|
}
|
|
}
|
|
if keys[x] == nil {
|
|
candidate = x
|
|
return true
|
|
}
|
|
if keys[y] == nil {
|
|
candidate = y
|
|
return false
|
|
}
|
|
return keys[x]! > keys[y]!
|
|
}
|
|
|
|
var ary = [Int](count: len, repeatedValue:0)
|
|
var ret = [Int](count: len, repeatedValue:0)
|
|
for i in 0..<len { ary[i] = i }
|
|
ary = sorted(ary, Compare)
|
|
for i in 0..<len {
|
|
ret[ary[i]] = i
|
|
}
|
|
return ret
|
|
}
|
|
|
|
Algorithm.test("sorted/complexity") {
|
|
var ary: [Int] = []
|
|
|
|
// Check performance of sort on array of repeating values
|
|
var comparisons_100 = 0
|
|
ary = [Int](count: 100, repeatedValue: 0)
|
|
sort(&ary) { comparisons_100++; return $0 < $1 }
|
|
var comparisons_1000 = 0
|
|
ary = [Int](count: 1000, repeatedValue: 0)
|
|
sort(&ary) { comparisons_1000++; return $0 < $1 }
|
|
expectTrue(comparisons_1000/comparisons_100 < 20)
|
|
|
|
// Try to construct 'bad' case for quicksort, on which the algorithm
|
|
// goes quadratic.
|
|
comparisons_100 = 0
|
|
ary = makeQSortKiller(100)
|
|
sort(&ary) { comparisons_100++; return $0 < $1 }
|
|
comparisons_1000 = 0
|
|
ary = makeQSortKiller(1000)
|
|
sort(&ary) { comparisons_1000++; return $0 < $1 }
|
|
expectTrue(comparisons_1000/comparisons_100 < 20)
|
|
}
|
|
|
|
Algorithm.test("sorted/return type") {
|
|
let x: Array = sorted([5, 4, 3, 2, 1] as ArraySlice)
|
|
}
|
|
|
|
/// A type that does not conform to any protocols.
|
|
///
|
|
/// This type can be used to check that generic functions don't rely on any
|
|
/// conformances.
|
|
public struct OpaqueValue<Underlying> {
|
|
public var value: Underlying
|
|
public var identity: Int
|
|
|
|
public init(_ value: Underlying) {
|
|
self.value = value
|
|
self.identity = 0
|
|
}
|
|
|
|
public init(_ value: Underlying, identity: Int) {
|
|
self.value = value
|
|
self.identity = identity
|
|
}
|
|
}
|
|
|
|
/// A type that conforms only to `Equatable`.
|
|
///
|
|
/// This type can be used to check that generic functions don't rely on any
|
|
/// other conformances.
|
|
public struct MinimalEquatableValue : Equatable {
|
|
public static var timesEqualEqualWasCalled: Int = 0
|
|
|
|
public var value: Int
|
|
public var identity: Int
|
|
|
|
public init(_ value: Int) {
|
|
self.value = value
|
|
self.identity = 0
|
|
}
|
|
|
|
public init(_ value: Int, identity: Int) {
|
|
self.value = value
|
|
self.identity = identity
|
|
}
|
|
}
|
|
public func == (
|
|
lhs: MinimalEquatableValue,
|
|
rhs: MinimalEquatableValue
|
|
) -> Bool {
|
|
++MinimalEquatableValue.timesEqualEqualWasCalled
|
|
return lhs.value == rhs.value
|
|
}
|
|
|
|
/// A type that conforms only to `Equatable` and `Hashable`.
|
|
///
|
|
/// This type can be used to check that generic functions don't rely on any
|
|
/// other conformances.
|
|
public struct MinimalHashableValue : Equatable, Hashable {
|
|
public static var timesEqualEqualWasCalled: Int = 0
|
|
public static var timesHashValueWasCalled: Int = 0
|
|
|
|
public var value: Int
|
|
public var identity: Int
|
|
|
|
public init(_ value: Int) {
|
|
self.value = value
|
|
self.identity = 0
|
|
}
|
|
|
|
public init(_ value: Int, identity: Int) {
|
|
self.value = value
|
|
self.identity = identity
|
|
}
|
|
|
|
public var hashValue: Int {
|
|
++MinimalHashableValue.timesHashValueWasCalled
|
|
return value.hashValue
|
|
}
|
|
}
|
|
|
|
public func == (
|
|
lhs: MinimalHashableValue,
|
|
rhs: MinimalHashableValue
|
|
) -> Bool {
|
|
++MinimalHashableValue.timesEqualEqualWasCalled
|
|
return lhs.value == rhs.value
|
|
}
|
|
|
|
/// A type that conforms only to `Equatable` and `Comparable`.
|
|
///
|
|
/// This type can be used to check that generic functions don't rely on any
|
|
/// other conformances.
|
|
public struct MinimalComparableValue : Equatable, Comparable {
|
|
public static var timesEqualEqualWasCalled: Int = 0
|
|
public static var timesLessWasCalled: Int = 0
|
|
|
|
public var value: Int
|
|
public var identity: Int
|
|
|
|
public init(_ value: Int) {
|
|
self.value = value
|
|
self.identity = 0
|
|
}
|
|
|
|
public init(_ value: Int, identity: Int) {
|
|
self.value = value
|
|
self.identity = identity
|
|
}
|
|
}
|
|
|
|
public func == (
|
|
lhs: MinimalComparableValue,
|
|
rhs: MinimalComparableValue
|
|
) -> Bool {
|
|
++MinimalComparableValue.timesEqualEqualWasCalled
|
|
return lhs.value == rhs.value
|
|
}
|
|
|
|
public func < (
|
|
lhs: MinimalComparableValue,
|
|
rhs: MinimalComparableValue
|
|
) -> Bool {
|
|
++MinimalComparableValue.timesLessWasCalled
|
|
return lhs.value < rhs.value
|
|
}
|
|
|
|
var SequenceTypeAlgorithms = TestSuite("SequenceTypeAlgorithms")
|
|
|
|
// FIXME: add tests for:
|
|
//
|
|
// - Array, ContiguousArray and ArraySlice as inputs. These types special-case
|
|
// a lot of collection behavior for performance reasons. Not to even mention
|
|
// that these are important types to test in any case.
|
|
//
|
|
// - NaN behavior of floating point types, combined with these generic
|
|
// algorithms, should make sense if possible. For example,
|
|
// [1.0, Double.NaN].startsWith([1.0, 2.0]) should be false.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// enumerate()
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
struct EnumerateTest {
|
|
let expected: [(Int, Int)]
|
|
let sequence: [Int]
|
|
let loc: SourceLoc
|
|
|
|
init(
|
|
_ expected: [(Int, Int)], _ sequence: [Int],
|
|
file: String = __FILE__, line: UWord = __LINE__,
|
|
comment: String = ""
|
|
) {
|
|
self.expected = expected
|
|
self.sequence = sequence
|
|
self.loc = SourceLoc(file, line, comment: "test data" + comment)
|
|
}
|
|
}
|
|
|
|
let enumerateTests = [
|
|
EnumerateTest([], []),
|
|
EnumerateTest([ (0, 10) ], [ 10 ]),
|
|
EnumerateTest([ (0, 10), (1, 20) ], [ 10, 20 ]),
|
|
EnumerateTest([ (0, 10), (1, 20), (2, 30) ], [ 10, 20, 30 ]),
|
|
]
|
|
|
|
SequenceTypeAlgorithms.test("enumerate") {
|
|
typealias Element = (index: Int, element: OpaqueValue<Int>)
|
|
func compareElements(lhs: Element, rhs: Element) -> Bool {
|
|
return lhs.0 == rhs.0 && lhs.1.value == rhs.1.value
|
|
}
|
|
|
|
for test in enumerateTests {
|
|
let s = MinimalSequence<OpaqueValue<Int>>(
|
|
test.sequence._prext_map { OpaqueValue($0) })
|
|
var result = s._prext_enumerate()
|
|
expectType(
|
|
EnumerateSequence<MinimalSequence<OpaqueValue<Int>>>.self,
|
|
&result)
|
|
checkSequence(
|
|
test.expected._prext_map {
|
|
(index: $0.0, element: OpaqueValue($0.1))
|
|
} as [Element],
|
|
result, compareElements,
|
|
resiliencyChecks: .none,
|
|
test.loc.withCurrentLoc())
|
|
expectEqual([], s._prext_map { $0.value }) { "sequence should be consumed" }
|
|
}
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// minElement(), maxElement()
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
struct MinMaxElementTest {
|
|
let expectedMinValue: Int?
|
|
let expectedMinIndex: Int?
|
|
let expectedMaxValue: Int?
|
|
let expectedMaxIndex: Int?
|
|
let sequence: [Int]
|
|
let loc: SourceLoc
|
|
|
|
init(
|
|
minValue expectedMinValue: Int?,
|
|
index expectedMinIndex: Int?,
|
|
maxValue expectedMaxValue: Int?,
|
|
index expectedMaxIndex: Int?,
|
|
_ sequence: [Int],
|
|
file: String = __FILE__, line: UWord = __LINE__,
|
|
comment: String = ""
|
|
) {
|
|
self.expectedMinValue = expectedMinValue
|
|
self.expectedMinIndex = expectedMinIndex
|
|
self.expectedMaxValue = expectedMaxValue
|
|
self.expectedMaxIndex = expectedMaxIndex
|
|
self.sequence = sequence
|
|
self.loc = SourceLoc(file, line, comment: "test data" + comment)
|
|
}
|
|
}
|
|
|
|
let minMaxElementTests = [
|
|
MinMaxElementTest(
|
|
minValue: nil, index: nil,
|
|
maxValue: nil, index: nil,
|
|
[]),
|
|
MinMaxElementTest(
|
|
minValue: 42, index: 0,
|
|
maxValue: 42, index: 0,
|
|
[ 42 ]),
|
|
MinMaxElementTest(
|
|
minValue: -1, index: 1,
|
|
maxValue: 30, index: 2,
|
|
[ 10, -1, 30, -1, 30 ]),
|
|
MinMaxElementTest(
|
|
minValue: -2, index: 5,
|
|
maxValue: 31, index: 6,
|
|
[ 10, -1, 30, -1, 30, -2, 31 ]),
|
|
]
|
|
|
|
% for algorithmKind in [ 'min', 'max' ]:
|
|
% AlgorithmKind = algorithmKind.capitalize()
|
|
|
|
SequenceTypeAlgorithms.test("${algorithmKind}Element/WhereElementIsComparable") {
|
|
for test in minMaxElementTests {
|
|
let s = MinimalSequence<MinimalComparableValue>(
|
|
test.sequence._prext_enumerate()._prext_map {
|
|
MinimalComparableValue($1, identity: $0)
|
|
})
|
|
var maybeResult = s._prext_${algorithmKind}Element()
|
|
expectType(Optional<MinimalComparableValue>.self, &maybeResult)
|
|
if let result? = maybeResult {
|
|
expectEqual(
|
|
test.expected${AlgorithmKind}Value!, result.value,
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
expectEqual(
|
|
test.expected${AlgorithmKind}Index!, result.identity,
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
} else {
|
|
expectEmpty(
|
|
test.expected${AlgorithmKind}Value,
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
expectEmpty(
|
|
test.expected${AlgorithmKind}Index,
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
}
|
|
expectEqual([], s._prext_map { $0.value }) { "sequence should be consumed" }
|
|
}
|
|
}
|
|
|
|
SequenceTypeAlgorithms.test("${algorithmKind}Element/Predicate") {
|
|
for test in minMaxElementTests {
|
|
let s = MinimalSequence<OpaqueValue<Int>>(
|
|
test.sequence._prext_enumerate()._prext_map {
|
|
OpaqueValue($1, identity: $0)
|
|
})
|
|
var timesClosureWasCalled = 0
|
|
var maybeResult = s._prext_${algorithmKind}Element {
|
|
(lhs, rhs) -> Bool in
|
|
++timesClosureWasCalled
|
|
return lhs.value < rhs.value
|
|
}
|
|
expectType(Optional<OpaqueValue<Int>>.self, &maybeResult)
|
|
if let result? = maybeResult {
|
|
expectEqual(
|
|
test.expected${AlgorithmKind}Value!, result.value,
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
expectEqual(
|
|
test.expected${AlgorithmKind}Index!, result.identity,
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
} else {
|
|
expectEmpty(
|
|
test.expected${AlgorithmKind}Value,
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
expectEmpty(
|
|
test.expected${AlgorithmKind}Index,
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
}
|
|
expectEqual([], s._prext_map { $0.value }) { "sequence should be consumed" }
|
|
expectEqual(max(0, test.sequence.count - 1), timesClosureWasCalled) {
|
|
"maxElement() should be eager and should only call its predicate once per element"
|
|
}
|
|
}
|
|
}
|
|
|
|
% end
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// startsWith()
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
SequenceTypeAlgorithms.test("startsWith/WhereElementIsEquatable") {
|
|
for test in startsWithTests {
|
|
if true {
|
|
let s = MinimalSequence<MinimalEquatableValue>(
|
|
test.sequence.map { MinimalEquatableValue($0) })
|
|
let prefix = MinimalSequence<MinimalEquatableValue>(
|
|
test.prefix.map { MinimalEquatableValue($0) })
|
|
expectEqual(
|
|
test.expected,
|
|
s._prext_startsWith(prefix),
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
expectEqual(
|
|
test.expectedLeftoverSequence, s._prext_map { $0.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
expectEqual(
|
|
test.expectedLeftoverPrefix, prefix._prext_map { $0.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
}
|
|
|
|
// Use different types for the sequence and prefix.
|
|
if true {
|
|
let s = MinimalForwardCollection<MinimalEquatableValue>(
|
|
test.sequence.map { MinimalEquatableValue($0) })
|
|
let prefix = MinimalSequence<MinimalEquatableValue>(
|
|
test.prefix.map { MinimalEquatableValue($0) })
|
|
expectEqual(
|
|
test.expected,
|
|
s._prext_startsWith(prefix),
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
expectEqual(
|
|
test.sequence, s._prext_map { $0.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
expectEqual(
|
|
test.expectedLeftoverPrefix, prefix._prext_map { $0.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
}
|
|
}
|
|
}
|
|
|
|
SequenceTypeAlgorithms.test("startsWith/Predicate") {
|
|
for test in startsWithTests {
|
|
if true {
|
|
let s = MinimalSequence<OpaqueValue<Int>>(
|
|
map(test.sequence) { OpaqueValue($0) })
|
|
let prefix = MinimalSequence<OpaqueValue<Int>>(
|
|
map(test.prefix) { OpaqueValue($0) })
|
|
expectEqual(
|
|
test.expected,
|
|
s._prext_startsWith(prefix) { $0.value == $1.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
expectEqual(
|
|
test.expectedLeftoverSequence, s._prext_map { $0.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
expectEqual(
|
|
test.expectedLeftoverPrefix, prefix._prext_map { $0.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
}
|
|
if true {
|
|
let s = MinimalSequence<OpaqueValue<Int>>(
|
|
map(test.sequence) { OpaqueValue($0 * 2) })
|
|
let prefix = MinimalSequence<OpaqueValue<Int>>(
|
|
map(test.prefix) { OpaqueValue($0) })
|
|
expectEqual(
|
|
test.expected,
|
|
s._prext_startsWith(prefix) { $0.value / 2 == $1.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
expectEqual(
|
|
test.expectedLeftoverSequence, s._prext_map { $0.value / 2 },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
expectEqual(
|
|
test.expectedLeftoverPrefix, prefix._prext_map { $0.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
}
|
|
|
|
// Use different types for the sequence and prefix.
|
|
if true {
|
|
let s = MinimalForwardCollection<OpaqueValue<Int>>(
|
|
map(test.sequence) { OpaqueValue($0) })
|
|
let prefix = MinimalSequence<OpaqueValue<Int>>(
|
|
map(test.prefix) { OpaqueValue($0) })
|
|
expectEqual(
|
|
test.expected,
|
|
s._prext_startsWith(prefix) { $0.value == $1.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
expectEqual(
|
|
test.sequence, s._prext_map { $0.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
expectEqual(
|
|
test.expectedLeftoverPrefix, prefix._prext_map { $0.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
}
|
|
}
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// equal()
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
struct EqualElementsTest {
|
|
let expected: Bool
|
|
let sequence: [Int]
|
|
let other: [Int]
|
|
let expectedLeftoverSequence: [Int]
|
|
let expectedLeftoverOther: [Int]
|
|
let loc: SourceLoc
|
|
|
|
init(
|
|
_ expected: Bool, _ sequence: [Int], _ other: [Int],
|
|
_ expectedLeftoverSequence: [Int],
|
|
_ expectedLeftoverOther: [Int],
|
|
file: String = __FILE__, line: UWord = __LINE__,
|
|
comment: String = ""
|
|
) {
|
|
self.expected = expected
|
|
self.sequence = sequence
|
|
self.other = other
|
|
self.expectedLeftoverSequence = expectedLeftoverSequence
|
|
self.expectedLeftoverOther = expectedLeftoverOther
|
|
self.loc = SourceLoc(file, line, comment: "test data" + comment)
|
|
}
|
|
|
|
func flip() -> EqualElementsTest {
|
|
return EqualElementsTest(
|
|
expected, other, sequence,
|
|
expectedLeftoverOther, expectedLeftoverSequence,
|
|
file: loc.file, line: loc.line, comment: " (flipped)")
|
|
}
|
|
}
|
|
|
|
let equalElementsTests = [
|
|
EqualElementsTest(true, [], [], [], []),
|
|
|
|
EqualElementsTest(false, [ 1 ], [], [], []),
|
|
EqualElementsTest(false, [], [ 1 ], [], []),
|
|
|
|
EqualElementsTest(false, [ 1, 2 ], [], [ 2 ], []),
|
|
EqualElementsTest(false, [], [ 1, 2 ], [], [ 2 ]),
|
|
|
|
EqualElementsTest(false, [ 1, 2, 3, 4 ], [ 1, 2 ], [ 4 ], []),
|
|
EqualElementsTest(false, [ 1, 2 ], [ 1, 2, 3, 4 ], [], [ 4 ]),
|
|
].flatMap { [ $0, $0.flip() ] }
|
|
|
|
SequenceTypeAlgorithms.test("equalElements/WhereElementIsEquatable") {
|
|
for test in equalElementsTests {
|
|
if true {
|
|
let s = MinimalSequence<MinimalEquatableValue>(
|
|
test.sequence.map { MinimalEquatableValue($0) })
|
|
let other = MinimalSequence<MinimalEquatableValue>(
|
|
test.other.map { MinimalEquatableValue($0) })
|
|
expectEqual(
|
|
test.expected,
|
|
s._prext_equalElements(other),
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
expectEqual(
|
|
test.expectedLeftoverSequence, s._prext_map { $0.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
expectEqual(
|
|
test.expectedLeftoverOther, other._prext_map { $0.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
}
|
|
|
|
// Use different types for the sequence and other.
|
|
if true {
|
|
let s = MinimalForwardCollection<MinimalEquatableValue>(
|
|
test.sequence.map { MinimalEquatableValue($0) })
|
|
let other = MinimalSequence<MinimalEquatableValue>(
|
|
test.other.map { MinimalEquatableValue($0) })
|
|
expectEqual(
|
|
test.expected,
|
|
s._prext_equalElements(other),
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
expectEqual(
|
|
test.sequence, s._prext_map { $0.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
expectEqual(
|
|
test.expectedLeftoverOther, other._prext_map { $0.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
}
|
|
}
|
|
}
|
|
|
|
SequenceTypeAlgorithms.test("equalElements/Predicate") {
|
|
for test in equalElementsTests {
|
|
if true {
|
|
let s = MinimalSequence<OpaqueValue<Int>>(
|
|
test.sequence.map { OpaqueValue($0) })
|
|
let other = MinimalSequence<OpaqueValue<Int>>(
|
|
test.other.map { OpaqueValue($0) })
|
|
expectEqual(
|
|
test.expected,
|
|
s._prext_equalElements(other) { $0.value == $1.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
expectEqual(
|
|
test.expectedLeftoverSequence, s._prext_map { $0.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
expectEqual(
|
|
test.expectedLeftoverOther, other._prext_map { $0.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
}
|
|
|
|
// Use different types for the sequence and other.
|
|
if true {
|
|
let s = MinimalForwardCollection<OpaqueValue<Int>>(
|
|
test.sequence.map { OpaqueValue($0) })
|
|
let other = MinimalSequence<OpaqueValue<Int>>(
|
|
test.other.map { OpaqueValue($0) })
|
|
expectEqual(
|
|
test.expected,
|
|
s._prext_equalElements(other) { $0.value == $1.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
expectEqual(
|
|
test.sequence, s._prext_map { $0.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
expectEqual(
|
|
test.expectedLeftoverOther, other._prext_map { $0.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
}
|
|
}
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// lexicographicalCompare()
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
struct LexicographicalCompareTest {
|
|
let expected: ExpectedComparisonResult
|
|
let sequence: [Int]
|
|
let other: [Int]
|
|
let expectedLeftoverSequence: [Int]
|
|
let expectedLeftoverOther: [Int]
|
|
let loc: SourceLoc
|
|
|
|
init(
|
|
_ expected: ExpectedComparisonResult, _ sequence: [Int], _ other: [Int],
|
|
_ expectedLeftoverSequence: [Int],
|
|
_ expectedLeftoverOther: [Int],
|
|
file: String = __FILE__, line: UWord = __LINE__,
|
|
comment: String = ""
|
|
) {
|
|
self.expected = expected
|
|
self.sequence = sequence
|
|
self.other = other
|
|
self.expectedLeftoverSequence = expectedLeftoverSequence
|
|
self.expectedLeftoverOther = expectedLeftoverOther
|
|
self.loc = SourceLoc(file, line, comment: "test data" + comment)
|
|
}
|
|
|
|
func flip() -> LexicographicalCompareTest {
|
|
return LexicographicalCompareTest(
|
|
expected.flip(), other, sequence,
|
|
expectedLeftoverOther, expectedLeftoverSequence,
|
|
file: loc.file, line: loc.line, comment: " (flipped)")
|
|
}
|
|
}
|
|
|
|
let lexicographicalCompareTests = [
|
|
LexicographicalCompareTest(.EQ, [], [], [], []),
|
|
LexicographicalCompareTest(.EQ, [ 1 ], [ 1 ], [], []),
|
|
|
|
LexicographicalCompareTest(.GT, [ 1 ], [], [], []),
|
|
|
|
LexicographicalCompareTest(.GT, [ 1 ], [ 0 ], [], []),
|
|
LexicographicalCompareTest(.EQ, [ 1 ], [ 1 ], [], []),
|
|
LexicographicalCompareTest(.LT, [ 1 ], [ 2 ], [], []),
|
|
|
|
LexicographicalCompareTest(.GT, [ 1, 2 ], [], [ 2 ], []),
|
|
|
|
LexicographicalCompareTest(.GT, [ 1, 2 ], [ 0 ], [ 2 ], []),
|
|
LexicographicalCompareTest(.GT, [ 1, 2 ], [ 1 ], [], []),
|
|
LexicographicalCompareTest(.LT, [ 1, 2 ], [ 2 ], [ 2 ], []),
|
|
|
|
LexicographicalCompareTest(.GT, [ 1, 2 ], [ 0, 0 ], [ 2 ], [ 0 ]),
|
|
LexicographicalCompareTest(.GT, [ 1, 2 ], [ 1, 0 ], [], []),
|
|
LexicographicalCompareTest(.LT, [ 1, 2 ], [ 2, 0 ], [ 2 ], [ 0 ]),
|
|
|
|
LexicographicalCompareTest(.GT, [ 1, 2 ], [ 0, 1 ], [ 2 ], [ 1 ]),
|
|
LexicographicalCompareTest(.GT, [ 1, 2 ], [ 1, 1 ], [], []),
|
|
LexicographicalCompareTest(.LT, [ 1, 2 ], [ 2, 1 ], [ 2 ], [ 1 ]),
|
|
|
|
LexicographicalCompareTest(.GT, [ 1, 2 ], [ 0, 2 ], [ 2 ], [ 2 ]),
|
|
LexicographicalCompareTest(.EQ, [ 1, 2 ], [ 1, 2 ], [], []),
|
|
LexicographicalCompareTest(.LT, [ 1, 2 ], [ 2, 2 ], [ 2 ], [ 2 ]),
|
|
|
|
LexicographicalCompareTest(.GT, [ 1, 2 ], [ 0, 3 ], [ 2 ], [ 3 ]),
|
|
LexicographicalCompareTest(.LT, [ 1, 2 ], [ 1, 3 ], [], []),
|
|
LexicographicalCompareTest(.LT, [ 1, 2 ], [ 2, 3 ], [ 2 ], [ 3 ]),
|
|
].flatMap { [ $0, $0.flip() ] }
|
|
|
|
SequenceTypeAlgorithms.test("lexicographicalCompare/WhereElementIsComparable") {
|
|
for test in lexicographicalCompareTests {
|
|
if true {
|
|
let s = MinimalSequence<MinimalComparableValue>(
|
|
test.sequence.map { MinimalComparableValue($0) })
|
|
let other = MinimalSequence<MinimalComparableValue>(
|
|
test.other.map { MinimalComparableValue($0) })
|
|
expectEqual(
|
|
test.expected.isLT(),
|
|
s._prext_lexicographicalCompare(other),
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
expectEqual(
|
|
test.expectedLeftoverSequence, s._prext_map { $0.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
expectEqual(
|
|
test.expectedLeftoverOther, other._prext_map { $0.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
}
|
|
|
|
// Use different types for the sequence and other.
|
|
if true {
|
|
let s = MinimalForwardCollection<MinimalComparableValue>(
|
|
test.sequence.map { MinimalComparableValue($0) })
|
|
let other = MinimalSequence<MinimalComparableValue>(
|
|
test.other.map { MinimalComparableValue($0) })
|
|
expectEqual(
|
|
test.expected.isLT(),
|
|
s._prext_lexicographicalCompare(other),
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
expectEqual(
|
|
test.sequence, s._prext_map { $0.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
expectEqual(
|
|
test.expectedLeftoverOther, other._prext_map { $0.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
}
|
|
}
|
|
}
|
|
|
|
SequenceTypeAlgorithms.test("lexicographicalCompare/Predicate") {
|
|
for test in lexicographicalCompareTests {
|
|
if true {
|
|
let s = MinimalSequence<OpaqueValue<Int>>(
|
|
test.sequence.map { OpaqueValue($0) })
|
|
let other = MinimalSequence<OpaqueValue<Int>>(
|
|
test.other.map { OpaqueValue($0) })
|
|
expectEqual(
|
|
test.expected.isLT(),
|
|
s._prext_lexicographicalCompare(other) { $0.value < $1.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
expectEqual(
|
|
test.expectedLeftoverSequence, s._prext_map { $0.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
expectEqual(
|
|
test.expectedLeftoverOther, other._prext_map { $0.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
}
|
|
|
|
// Use different types for the sequence and other.
|
|
if true {
|
|
let s = MinimalForwardCollection<OpaqueValue<Int>>(
|
|
test.sequence.map { OpaqueValue($0) })
|
|
let other = MinimalSequence<OpaqueValue<Int>>(
|
|
test.other.map { OpaqueValue($0) })
|
|
expectEqual(
|
|
test.expected.isLT(),
|
|
s._prext_lexicographicalCompare(other) { $0.value < $1.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
expectEqual(
|
|
test.sequence, s._prext_map { $0.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
expectEqual(
|
|
test.expectedLeftoverOther, other._prext_map { $0.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
}
|
|
}
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// contains()
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// These tests are shared between find() and contains().
|
|
struct FindTest {
|
|
let expected: Int?
|
|
let element: Int
|
|
let sequence: [Int]
|
|
let expectedLeftoverSequence: [Int]
|
|
let loc: SourceLoc
|
|
|
|
init(
|
|
_ expected: Int?, _ element: Int, _ sequence: [Int],
|
|
_ expectedLeftoverSequence: [Int],
|
|
file: String = __FILE__, line: UWord = __LINE__
|
|
) {
|
|
self.expected = expected
|
|
self.element = element
|
|
self.sequence = sequence
|
|
self.expectedLeftoverSequence = expectedLeftoverSequence
|
|
self.loc = SourceLoc(file, line, comment: "test data")
|
|
}
|
|
}
|
|
|
|
let findTests = [
|
|
FindTest(nil, 42, [], []),
|
|
|
|
FindTest(nil, 42, [ 1 ], []),
|
|
FindTest(0, 1, [ 1 ], []),
|
|
|
|
FindTest(nil, 42, [ 1, 2, 3, 4 ], []),
|
|
FindTest(0, 1, [ 1, 2, 3, 4 ], [ 2, 3, 4 ]),
|
|
FindTest(1, 2, [ 1, 2, 3, 4 ], [ 3, 4 ]),
|
|
FindTest(2, 3, [ 1, 2, 3, 4 ], [ 4 ]),
|
|
FindTest(3, 4, [ 1, 2, 3, 4 ], []),
|
|
]
|
|
|
|
func callStaticContains(
|
|
sequence: MinimalSequence<MinimalEquatableValue>,
|
|
element: MinimalEquatableValue) -> Bool {
|
|
return sequence._prext_contains(element)
|
|
}
|
|
|
|
func callGenericContains<
|
|
S : SequenceType where S.Generator.Element : Equatable
|
|
>(sequence: S, element: S.Generator.Element) -> Bool {
|
|
return sequence._prext_contains(element)
|
|
}
|
|
|
|
% for dispatch in [ 'Static', 'Generic' ]:
|
|
|
|
SequenceTypeAlgorithms.test("contains/WhereElementIsEquatable/${dispatch}") {
|
|
for test in findTests {
|
|
let s = MinimalSequence<MinimalEquatableValue>(
|
|
test.sequence.map { MinimalEquatableValue($0) })
|
|
expectEqual(
|
|
test.expected != nil,
|
|
call${dispatch}Contains(s, MinimalEquatableValue(test.element)),
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
expectEqual(
|
|
test.expectedLeftoverSequence, s._prext_map { $0.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
}
|
|
}
|
|
|
|
% end
|
|
|
|
SequenceTypeAlgorithms.test("contains/Predicate") {
|
|
for test in findTests {
|
|
let s = MinimalSequence<OpaqueValue<Int>>(
|
|
test.sequence.map { OpaqueValue($0) })
|
|
expectEqual(
|
|
test.expected != nil,
|
|
s._prext_contains { $0.value == test.element },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
expectEqual(
|
|
test.expectedLeftoverSequence, s._prext_map { $0.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
}
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// reduce()
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
struct ReduceTest {
|
|
let sequence: [Int]
|
|
let loc: SourceLoc
|
|
|
|
init(
|
|
_ sequence: [Int],
|
|
file: String = __FILE__, line: UWord = __LINE__
|
|
) {
|
|
self.sequence = sequence
|
|
self.loc = SourceLoc(file, line, comment: "test data")
|
|
}
|
|
}
|
|
|
|
let reduceTests = [
|
|
ReduceTest([]),
|
|
ReduceTest([ 1 ]),
|
|
ReduceTest([ 1, 2 ]),
|
|
ReduceTest([ 1, 2, 3 ]),
|
|
ReduceTest([ 1, 2, 3, 4, 5, 6, 7 ]),
|
|
]
|
|
|
|
SequenceTypeAlgorithms.test("reduce") {
|
|
for test in reduceTests {
|
|
let s = MinimalSequence<OpaqueValue<Int>>(
|
|
test.sequence.map { OpaqueValue($0) })
|
|
var timesClosureWasCalled = 0
|
|
let result = s._prext_reduce(OpaqueValue<[Int]>([])) {
|
|
(partialResult: OpaqueValue<[Int]>, element: OpaqueValue<Int>)
|
|
-> OpaqueValue<[Int]> in
|
|
++timesClosureWasCalled
|
|
return OpaqueValue<[Int]>(partialResult.value + [ element.value ])
|
|
}
|
|
expectEqual(test.sequence, result.value)
|
|
expectEqual([], s._prext_map { $0.value }) { "sequence should be consumed" }
|
|
expectEqual(test.sequence.count, timesClosureWasCalled) {
|
|
"reduce() should be eager and should only call its predicate once per element"
|
|
}
|
|
}
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// reverse()
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
struct ReverseTest {
|
|
let expected: [Int]
|
|
let sequence: [Int]
|
|
let loc: SourceLoc
|
|
|
|
init(
|
|
_ expected: [Int], _ sequence: [Int],
|
|
file: String = __FILE__, line: UWord = __LINE__
|
|
) {
|
|
self.expected = expected
|
|
self.sequence = sequence
|
|
self.loc = SourceLoc(file, line, comment: "test data")
|
|
}
|
|
}
|
|
|
|
let reverseTests = [
|
|
ReverseTest([], []),
|
|
ReverseTest([ 1 ], [ 1 ]),
|
|
ReverseTest([ 2, 1 ], [ 1, 2 ]),
|
|
ReverseTest([ 3, 2, 1 ], [ 1, 2, 3 ]),
|
|
ReverseTest([ 4, 3, 2, 1 ], [ 1, 2, 3, 4]),
|
|
ReverseTest(
|
|
[ 7, 6, 5, 4, 3, 2, 1 ],
|
|
[ 1, 2, 3, 4, 5, 6, 7 ]),
|
|
]
|
|
|
|
SequenceTypeAlgorithms.test("reverse/SequenceType") {
|
|
for test in reverseTests {
|
|
let s = MinimalSequence<OpaqueValue<Int>>(
|
|
test.sequence.map { OpaqueValue($0) })
|
|
var result = s._prext_reverse()
|
|
expectType([OpaqueValue<Int>].self, &result)
|
|
expectEqual(
|
|
test.expected, result.map { $0.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
expectEqual([], s._prext_map { $0.value }) { "sequence should be consumed" }
|
|
}
|
|
}
|
|
|
|
SequenceTypeAlgorithms.test("reverse/WhereIndexIsBidirectional,BidirectionalReverseView") {
|
|
for test in reverseTests {
|
|
let s = MinimalBidirectionalCollection<OpaqueValue<Int>>(
|
|
test.sequence.map { OpaqueValue($0) })
|
|
var result = s._prext_reverse()
|
|
expectType(
|
|
BidirectionalReverseView<MinimalBidirectionalCollection<OpaqueValue<Int>>>.self,
|
|
&result)
|
|
expectEqual(
|
|
test.expected, result._prext_map { $0.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
|
|
// Check BidirectionalReverseView CollectionType conformance.
|
|
checkBidirectionalCollection(
|
|
test.expected.map { OpaqueValue($0) },
|
|
result,
|
|
{ $0.value == $1.value },
|
|
test.loc.withCurrentLoc())
|
|
}
|
|
}
|
|
|
|
SequenceTypeAlgorithms.test("reverse/WhereIndexIsRandomAccess,RandomAccessReverseView") {
|
|
for test in reverseTests {
|
|
let s = MinimalRandomAccessCollection<OpaqueValue<Int>>(
|
|
test.sequence.map { OpaqueValue($0) })
|
|
var result = s._prext_reverse()
|
|
expectType(
|
|
RandomAccessReverseView<MinimalRandomAccessCollection<OpaqueValue<Int>>>.self,
|
|
&result)
|
|
expectEqual(
|
|
test.expected, result._prext_map { $0.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
|
|
// Check RandomAccessReverseView CollectionType conformance.
|
|
checkRandomAccessCollection(
|
|
test.expected.map { OpaqueValue($0) },
|
|
result,
|
|
{ $0.value == $1.value },
|
|
test.loc.withCurrentLoc())
|
|
}
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// filter()
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
struct FilterTest {
|
|
let expected: [Int]
|
|
let sequence: [Int]
|
|
let includeElement: (Int) -> Bool
|
|
let loc: SourceLoc
|
|
|
|
init(
|
|
_ expected: [Int],
|
|
_ sequence: [Int],
|
|
_ includeElement: (Int) -> Bool,
|
|
file: String = __FILE__, line: UWord = __LINE__
|
|
) {
|
|
self.expected = expected
|
|
self.sequence = sequence
|
|
self.includeElement = includeElement
|
|
self.loc = SourceLoc(file, line, comment: "test data")
|
|
}
|
|
}
|
|
|
|
let filterTests = [
|
|
FilterTest(
|
|
[], [],
|
|
{ (x: Int) -> Bool in expectUnreachable(); return true }),
|
|
|
|
FilterTest([], [ 0, 30, 10, 90 ], { (x: Int) -> Bool in false }),
|
|
FilterTest(
|
|
[ 0, 30, 10, 90 ], [ 0, 30, 10, 90 ], { (x: Int) -> Bool in true }
|
|
),
|
|
FilterTest(
|
|
[ 0, 30, 90 ], [ 0, 30, 10, 90 ], { (x: Int) -> Bool in x % 3 == 0 }
|
|
),
|
|
]
|
|
|
|
func callStaticSequenceFilter(
|
|
sequence: MinimalSequence<OpaqueValue<Int>>,
|
|
@noescape includeElement: (OpaqueValue<Int>) -> Bool
|
|
) -> [OpaqueValue<Int>] {
|
|
var result = sequence._prext_filter(includeElement)
|
|
expectType([OpaqueValue<Int>].self, &result)
|
|
return result
|
|
}
|
|
|
|
func callGenericSequenceFilter<S : SequenceType>(
|
|
sequence: S,
|
|
@noescape includeElement: (S.Generator.Element) -> Bool
|
|
) -> [S.Generator.Element] {
|
|
var result = sequence._prext_filter(includeElement)
|
|
expectType(Array<S.Generator.Element>.self, &result)
|
|
return result
|
|
}
|
|
|
|
% for dispatch in [ 'Static', 'Generic' ]:
|
|
|
|
SequenceTypeAlgorithms.test("filter/SequenceType/${dispatch}") {
|
|
expectEqual(0, LifetimeTracked.instances)
|
|
for test in filterTests {
|
|
for underestimateCountBehavior in [
|
|
UnderestimateCountBehavior.Precise,
|
|
UnderestimateCountBehavior.Half,
|
|
UnderestimateCountBehavior.Value(0)
|
|
] {
|
|
let s = MinimalSequence<OpaqueValue<Int>>(
|
|
test.sequence.map { OpaqueValue($0) },
|
|
underestimatedCount: underestimateCountBehavior)
|
|
let closureLifetimeTracker = LifetimeTracked(0)
|
|
expectEqual(1, LifetimeTracked.instances)
|
|
var timesClosureWasCalled = 0
|
|
var result = call${dispatch}SequenceFilter(s) {
|
|
(element) in
|
|
_blackHole(closureLifetimeTracker)
|
|
++timesClosureWasCalled
|
|
return test.includeElement(element.value)
|
|
}
|
|
expectType([OpaqueValue<Int>].self, &result)
|
|
expectEqual(test.expected, map(result) { $0.value })
|
|
expectEqual([], s._prext_map { $0.value }) { "sequence should be consumed" }
|
|
expectEqual(test.sequence.count, timesClosureWasCalled) {
|
|
"filter() should be eager and should only call its predicate once per element"
|
|
}
|
|
expectGE(2 * result.count, result.capacity) {
|
|
"filter() should not reserve capacity (it does not know how much the predicate will filter out)"
|
|
}
|
|
}
|
|
}
|
|
expectEqual(0, LifetimeTracked.instances)
|
|
}
|
|
|
|
% end
|
|
|
|
func callGenericCollectionFilter<C : RangeReplaceableCollectionType>(
|
|
collection: C,
|
|
@noescape includeElement: (C.Generator.Element) -> Bool
|
|
) -> C {
|
|
var result = collection._prext_filter(includeElement)
|
|
expectType(C.self, &result)
|
|
return result
|
|
}
|
|
|
|
% for Implementation in [ 'Default', 'Custom' ]:
|
|
% CollectionType = 'RangeReplaceableCollectionWith%sFilterMethod' % Implementation
|
|
|
|
struct ${CollectionType} : RangeReplaceableCollectionType {
|
|
|
|
% if Implementation == 'Custom':
|
|
static var timesFilterWasCalled = 0
|
|
% end
|
|
|
|
var elements: [OpaqueValue<Int>]
|
|
var reservedCapacity: Int = 0
|
|
|
|
init() {
|
|
self.elements = []
|
|
}
|
|
|
|
init(_ elements: [OpaqueValue<Int>]) {
|
|
self.elements = elements
|
|
}
|
|
|
|
func generate() -> MinimalGenerator<OpaqueValue<Int>> {
|
|
return MinimalSequence<OpaqueValue<Int>>(elements).generate()
|
|
}
|
|
|
|
var startIndex: MinimalForwardIndex {
|
|
return MinimalForwardIndex(
|
|
position: 0,
|
|
startIndex: 0,
|
|
endIndex: elements.endIndex)
|
|
}
|
|
|
|
var endIndex: MinimalForwardIndex {
|
|
return MinimalForwardIndex(
|
|
position: elements.endIndex,
|
|
startIndex: 0,
|
|
endIndex: elements.endIndex)
|
|
}
|
|
|
|
subscript(i: MinimalForwardIndex) -> OpaqueValue<Int> {
|
|
return OpaqueValue(i.position + 1)
|
|
}
|
|
|
|
mutating func reserveCapacity(n: Int) {
|
|
elements.reserveCapacity(n)
|
|
reservedCapacity = max(reservedCapacity, n)
|
|
}
|
|
|
|
mutating func append(x: OpaqueValue<Int>) {
|
|
elements.append(x)
|
|
}
|
|
|
|
mutating func extend<
|
|
S : SequenceType where S.Generator.Element == OpaqueValue<Int>
|
|
>(newElements: S) {
|
|
expectUnreachable()
|
|
}
|
|
|
|
mutating func replaceRange<
|
|
C : CollectionType where C.Generator.Element == OpaqueValue<Int>
|
|
>(
|
|
subRange: Range<MinimalForwardIndex>, with newElements: C
|
|
) {
|
|
expectUnreachable()
|
|
}
|
|
|
|
mutating func insert(
|
|
newElement: OpaqueValue<Int>, atIndex i: MinimalForwardIndex
|
|
) {
|
|
expectUnreachable()
|
|
}
|
|
|
|
mutating func splice<
|
|
S : CollectionType where S.Generator.Element == OpaqueValue<Int>
|
|
>(newElements: S, atIndex i: MinimalForwardIndex) {
|
|
expectUnreachable()
|
|
}
|
|
|
|
mutating func removeAtIndex(i: MinimalForwardIndex) -> OpaqueValue<Int> {
|
|
expectUnreachable()
|
|
return OpaqueValue(0xffff)
|
|
}
|
|
|
|
mutating func removeRange(subRange: Range<MinimalForwardIndex>) {
|
|
expectUnreachable()
|
|
}
|
|
|
|
mutating func removeAll(keepCapacity: Bool = false) {
|
|
expectUnreachable()
|
|
}
|
|
|
|
% if Implementation == 'Custom':
|
|
|
|
func _prext_filter(
|
|
@noescape includeElement: (OpaqueValue<Int>) -> Bool
|
|
) -> ${CollectionType} {
|
|
++RangeReplaceableCollectionWithCustomFilterMethod.timesFilterWasCalled
|
|
return ${CollectionType}(elements._prext_filter(includeElement))
|
|
}
|
|
|
|
% end
|
|
}
|
|
|
|
% end
|
|
|
|
% for (CollectionType, CollectionKind) in [
|
|
% ('MinimalForwardRangeReplaceableCollectionType<OpaqueValue<Int>>', 'Minimal'),
|
|
% ('RangeReplaceableCollectionWithDefaultFilterMethod', 'DefaultImplementation'),
|
|
% ('RangeReplaceableCollectionWithCustomFilterMethod', 'CustomImplementation'),
|
|
% ('Array<OpaqueValue<Int>>', 'StdlibArray'),
|
|
% ('ContiguousArray<OpaqueValue<Int>>', 'StdlibArray'),
|
|
% ('ArraySlice<OpaqueValue<Int>>', 'StdlibArray')
|
|
% ]:
|
|
|
|
% for dispatch in [ 'Static', 'Generic' ]:
|
|
|
|
% if dispatch == 'Static':
|
|
|
|
func callStaticCollectionFilter(
|
|
collection: ${CollectionType},
|
|
@noescape includeElement: (OpaqueValue<Int>) -> Bool
|
|
) -> ${CollectionType} {
|
|
var result = collection._prext_filter(includeElement)
|
|
expectType(${CollectionType}.self, &result)
|
|
return result
|
|
}
|
|
|
|
% end
|
|
|
|
SequenceTypeAlgorithms.test("filter/RangeReplaceableCollectionType/${CollectionType}/${dispatch}") {
|
|
expectEqual(0, LifetimeTracked.instances)
|
|
for test in filterTests {
|
|
for underestimateCountBehavior in [
|
|
UnderestimateCountBehavior.Precise,
|
|
UnderestimateCountBehavior.Half,
|
|
UnderestimateCountBehavior.Value(0)
|
|
] {
|
|
% if CollectionKind == 'Minimal':
|
|
let s = ${CollectionType}(
|
|
test.sequence.map { OpaqueValue($0) },
|
|
underestimatedCount: underestimateCountBehavior)
|
|
% else:
|
|
let s = ${CollectionType}(
|
|
test.sequence.map { OpaqueValue($0) })
|
|
% end
|
|
% if CollectionKind == 'CustomImplementation':
|
|
RangeReplaceableCollectionWithCustomFilterMethod.timesFilterWasCalled = 0
|
|
% end
|
|
let closureLifetimeTracker = LifetimeTracked(0)
|
|
expectEqual(1, LifetimeTracked.instances)
|
|
var timesClosureWasCalled = 0
|
|
var result = call${dispatch}CollectionFilter(s) {
|
|
(element) in
|
|
_blackHole(closureLifetimeTracker)
|
|
++timesClosureWasCalled
|
|
return test.includeElement(element.value)
|
|
}
|
|
expectType(${CollectionType}.self, &result)
|
|
expectEqual(
|
|
test.expected, result._prext_map { $0.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
% if CollectionKind == 'CustomImplementation':
|
|
expectEqual(
|
|
1,
|
|
RangeReplaceableCollectionWithCustomFilterMethod.timesFilterWasCalled)
|
|
% end
|
|
expectEqual(test.sequence, s._prext_map { $0.value }) {
|
|
"collection should not be consumed"
|
|
}
|
|
expectEqual(test.sequence.count, timesClosureWasCalled) {
|
|
"filter() should be eager and should only call its predicate once per element"
|
|
}
|
|
expectGE(2 * count(result), result.${'capacity' if CollectionKind == 'StdlibArray' else 'reservedCapacity'}) {
|
|
"filter() should not reserve capacity (it does not know how much the predicate will filter out)"
|
|
}
|
|
}
|
|
}
|
|
expectEqual(0, LifetimeTracked.instances)
|
|
}
|
|
|
|
% end
|
|
|
|
% end
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// map()
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
struct MapTest {
|
|
let expected: [Int32]
|
|
let sequence: [Int]
|
|
let transform: (Int) -> Int32
|
|
let loc: SourceLoc
|
|
|
|
init(
|
|
_ expected: [Int32],
|
|
_ sequence: [Int],
|
|
_ transform: (Int) -> Int32,
|
|
file: String = __FILE__, line: UWord = __LINE__
|
|
) {
|
|
self.expected = expected
|
|
self.sequence = sequence
|
|
self.transform = transform
|
|
self.loc = SourceLoc(file, line, comment: "test data")
|
|
}
|
|
}
|
|
|
|
let mapTests = [
|
|
MapTest(
|
|
[], [],
|
|
{ (x: Int) -> Int32 in expectUnreachable(); return 0xffff }),
|
|
|
|
MapTest([ 101 ], [ 1 ], { (x: Int) -> Int32 in x + 100 }),
|
|
MapTest([ 101, 102 ], [ 1, 2 ], { (x: Int) -> Int32 in x + 100 }),
|
|
MapTest([ 101, 102, 103 ], [ 1, 2, 3 ], { (x: Int) -> Int32 in x + 100 }),
|
|
MapTest(Array(101..<200), Array(1..<100), { (x: Int) -> Int32 in x + 100 }),
|
|
]
|
|
|
|
SequenceTypeAlgorithms.test("map/SequenceType") {
|
|
expectEqual(0, LifetimeTracked.instances)
|
|
for test in mapTests {
|
|
for underestimateCountBehavior in [
|
|
UnderestimateCountBehavior.Precise,
|
|
UnderestimateCountBehavior.Half,
|
|
UnderestimateCountBehavior.Value(0)
|
|
] {
|
|
let s = MinimalSequence<OpaqueValue<Int>>(
|
|
test.sequence.map { OpaqueValue($0) },
|
|
underestimatedCount: underestimateCountBehavior)
|
|
let closureLifetimeTracker = LifetimeTracked(0)
|
|
expectEqual(1, LifetimeTracked.instances)
|
|
var timesClosureWasCalled = 0
|
|
var result = s._prext_map {
|
|
(element: OpaqueValue<Int>) -> OpaqueValue<Int32> in
|
|
_blackHole(closureLifetimeTracker)
|
|
++timesClosureWasCalled
|
|
return OpaqueValue(Int32(test.transform(element.value)))
|
|
}
|
|
expectType([OpaqueValue<Int32>].self, &result)
|
|
expectEqual(test.expected, result._prext_map { $0.value })
|
|
expectEqual([], s._prext_map { $0.value }) { "sequence should be consumed" }
|
|
expectEqual(test.sequence.count, timesClosureWasCalled) {
|
|
"map() should be eager and should only call its predicate once per element"
|
|
}
|
|
}
|
|
}
|
|
expectEqual(0, LifetimeTracked.instances)
|
|
}
|
|
|
|
SequenceTypeAlgorithms.test("map/CollectionType") {
|
|
expectEqual(0, LifetimeTracked.instances)
|
|
for test in mapTests {
|
|
for underestimateCountBehavior in [
|
|
UnderestimateCountBehavior.Precise,
|
|
UnderestimateCountBehavior.Half,
|
|
UnderestimateCountBehavior.Value(0)
|
|
] {
|
|
let s = MinimalForwardCollection<OpaqueValue<Int>>(
|
|
test.sequence.map { OpaqueValue($0) },
|
|
underestimatedCount: underestimateCountBehavior)
|
|
let closureLifetimeTracker = LifetimeTracked(0)
|
|
expectEqual(1, LifetimeTracked.instances)
|
|
var timesClosureWasCalled = 0
|
|
var result = s._prext_map {
|
|
(element: OpaqueValue<Int>) -> OpaqueValue<Int32> in
|
|
_blackHole(closureLifetimeTracker)
|
|
++timesClosureWasCalled
|
|
return OpaqueValue(Int32(test.transform(element.value)))
|
|
}
|
|
expectType([OpaqueValue<Int32>].self, &result)
|
|
expectEqual(test.expected, result._prext_map { $0.value })
|
|
expectEqual(test.sequence, s._prext_map { $0.value }) {
|
|
"collection should not be consumed"
|
|
}
|
|
expectEqual(test.sequence.count, timesClosureWasCalled) {
|
|
"map() should be eager and should only call its predicate once per element"
|
|
}
|
|
}
|
|
}
|
|
expectEqual(0, LifetimeTracked.instances)
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// flatMap()
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
struct FlatMapTest {
|
|
let expected: [Int32]
|
|
let sequence: [Int]
|
|
let transform: (Int) -> [Int32]
|
|
let loc: SourceLoc
|
|
|
|
init(
|
|
_ expected: [Int32],
|
|
_ sequence: [Int],
|
|
_ transform: (Int) -> [Int32],
|
|
file: String = __FILE__, line: UWord = __LINE__
|
|
) {
|
|
self.expected = expected
|
|
self.sequence = sequence
|
|
self.transform = transform
|
|
self.loc = SourceLoc(file, line, comment: "test data")
|
|
}
|
|
}
|
|
|
|
func flatMapTransformation(x: Int) -> [Int32] {
|
|
let repetitions = x / 10
|
|
let identity = x % 10
|
|
return (1..<(repetitions+1)).map { $0 * 10 + identity }
|
|
}
|
|
|
|
let flatMapTests = [
|
|
FlatMapTest(
|
|
[], [],
|
|
{ (x: Int) -> [Int32] in expectUnreachable(); return [ 0xffff ] }),
|
|
|
|
FlatMapTest([], [ 1 ], { (x: Int) -> [Int32] in [] }),
|
|
FlatMapTest([], [ 1, 2 ], { (x: Int) -> [Int32] in [] }),
|
|
FlatMapTest([], [ 1, 2, 3 ], { (x: Int) -> [Int32] in [] }),
|
|
|
|
FlatMapTest([ 101 ], [ 1 ], { (x: Int) -> [Int32] in [ x + 100 ] }),
|
|
FlatMapTest([ 101, 102 ], [ 1, 2 ], { (x: Int) -> [Int32] in [ x + 100 ] }),
|
|
FlatMapTest(
|
|
[ 101, 102, 103 ], [ 1, 2, 3 ], { (x: Int) -> [Int32] in [ x + 100 ] }),
|
|
|
|
FlatMapTest(
|
|
[ 101, 201 ], [ 1 ],
|
|
{ (x: Int) -> [Int32] in [ x + 100, x + 200 ] }),
|
|
FlatMapTest(
|
|
[ 101, 201, 102, 202 ], [ 1, 2 ],
|
|
{ (x: Int) -> [Int32] in [ x + 100, x + 200 ] }),
|
|
FlatMapTest(
|
|
[ 101, 201, 102, 202, 103, 203 ], [ 1, 2, 3 ],
|
|
{ (x: Int) -> [Int32] in [ x + 100, x + 200 ] }),
|
|
|
|
FlatMapTest([ 11, 15 ], [ 11, 2, 3, 4, 15 ], flatMapTransformation),
|
|
FlatMapTest([ 12, 13, 23 ], [ 1, 12, 23 ], flatMapTransformation),
|
|
FlatMapTest(
|
|
[ 11, 21, 13, 23, 33, 14 ], [ 21, 2, 33, 14 ], flatMapTransformation),
|
|
]
|
|
|
|
SequenceTypeAlgorithms.test("flatMap/SequenceType") {
|
|
for test in flatMapTests {
|
|
for underestimateCountBehavior in [
|
|
UnderestimateCountBehavior.Precise,
|
|
UnderestimateCountBehavior.Value(0)
|
|
] {
|
|
let s = MinimalSequence<OpaqueValue<Int>>(
|
|
test.sequence.map { OpaqueValue($0) },
|
|
underestimatedCount: underestimateCountBehavior)
|
|
var timesClosureWasCalled = 0
|
|
var result = s._prext_flatMap {
|
|
(element: OpaqueValue<Int>) -> MinimalSequence<OpaqueValue<Int32>> in
|
|
++timesClosureWasCalled
|
|
return MinimalSequence<OpaqueValue<Int32>>(
|
|
test.transform(element.value).map { OpaqueValue(Int32($0)) })
|
|
}
|
|
expectType([OpaqueValue<Int32>].self, &result)
|
|
expectEqual(
|
|
test.expected, result._prext_map { $0.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
expectEqual([], s._prext_map { $0.value }) { "sequence should be consumed" }
|
|
expectEqual(test.sequence.count, timesClosureWasCalled) {
|
|
"map() should be eager and should only call its predicate once per element"
|
|
}
|
|
expectGE(2 * count(result), result.capacity) {
|
|
"flatMap() should not reserve capacity"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
struct FlatMapToOptionalTest {
|
|
let expected: [Int32]
|
|
let sequence: [Int]
|
|
let transform: (Int) -> Int32?
|
|
let loc: SourceLoc
|
|
|
|
init(
|
|
_ expected: [Int32],
|
|
_ sequence: [Int],
|
|
_ transform: (Int) -> Int32?,
|
|
file: String = __FILE__, line: UWord = __LINE__
|
|
) {
|
|
self.expected = expected
|
|
self.sequence = sequence
|
|
self.transform = transform
|
|
self.loc = SourceLoc(file, line, comment: "test data")
|
|
}
|
|
}
|
|
|
|
let flatMapToOptionalTests = [
|
|
FlatMapToOptionalTest(
|
|
[], [],
|
|
{ (x: Int) -> Int32? in expectUnreachable(); return 0xffff }),
|
|
|
|
FlatMapToOptionalTest([], [ 1 ], { (x: Int) -> Int32? in nil }),
|
|
FlatMapToOptionalTest([], [ 1, 2 ], { (x: Int) -> Int32? in nil }),
|
|
FlatMapToOptionalTest([], [ 1, 2, 3 ], { (x: Int) -> Int32? in nil }),
|
|
|
|
FlatMapToOptionalTest(
|
|
[ 1 ], [ 1 ],
|
|
{ (x: Int) -> Int32? in x > 10 ? nil : Int32(x) }),
|
|
FlatMapToOptionalTest(
|
|
[ 2 ], [ 11, 2, 13, 14 ],
|
|
{ (x: Int) -> Int32? in x > 10 ? nil : Int32(x) }),
|
|
FlatMapToOptionalTest(
|
|
[ 1, 4 ], [ 1, 12, 13, 4 ],
|
|
{ (x: Int) -> Int32? in x > 10 ? nil : Int32(x) }),
|
|
FlatMapToOptionalTest(
|
|
[ 1, 2, 3 ], [ 1, 2, 3 ],
|
|
{ (x: Int) -> Int32? in x > 10 ? nil : Int32(x) }),
|
|
]
|
|
|
|
SequenceTypeAlgorithms.test("flatMap/SequenceType/TransformProducesOptional") {
|
|
for test in flatMapToOptionalTests {
|
|
for underestimateCountBehavior in [
|
|
UnderestimateCountBehavior.Precise,
|
|
UnderestimateCountBehavior.Value(0)
|
|
] {
|
|
let s = MinimalSequence<OpaqueValue<Int>>(
|
|
test.sequence.map { OpaqueValue($0) },
|
|
underestimatedCount: underestimateCountBehavior)
|
|
var timesClosureWasCalled = 0
|
|
var result = s._prext_flatMap {
|
|
(element: OpaqueValue<Int>) -> OpaqueValue<Int32>? in
|
|
++timesClosureWasCalled
|
|
return test.transform(element.value).map { OpaqueValue(Int32($0)) }
|
|
}
|
|
expectType([OpaqueValue<Int32>].self, &result)
|
|
expectEqual(
|
|
test.expected, result._prext_map { $0.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
expectEqual([], s._prext_map { $0.value }) { "sequence should be consumed" }
|
|
expectEqual(test.sequence.count, timesClosureWasCalled) {
|
|
"flatMap() should be eager and should only call its predicate once per element"
|
|
}
|
|
expectGE(2 * count(result), result.capacity) {
|
|
"flatMap() should not reserve capacity"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// zip()
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
struct ZipTest {
|
|
let expected: [(Int, Int32)]
|
|
let sequence: [Int]
|
|
let other: [Int32]
|
|
let expectedLeftoverSequence: [Int]
|
|
let expectedLeftoverOther: [Int32]
|
|
let loc: SourceLoc
|
|
|
|
init(
|
|
_ expected: [(Int, Int32)],
|
|
sequences sequence: [Int],
|
|
_ other: [Int32],
|
|
leftovers expectedLeftoverSequence: [Int],
|
|
_ expectedLeftoverOther: [Int32],
|
|
file: String = __FILE__, line: UWord = __LINE__
|
|
) {
|
|
self.expected = expected
|
|
self.sequence = sequence
|
|
self.other = other
|
|
self.expectedLeftoverSequence = expectedLeftoverSequence
|
|
self.expectedLeftoverOther = expectedLeftoverOther
|
|
self.loc = SourceLoc(file, line, comment: "test data")
|
|
}
|
|
}
|
|
|
|
let zipTests = [
|
|
ZipTest([], sequences: [], [], leftovers: [], []),
|
|
ZipTest([], sequences: [], [ 1 ], leftovers: [], [ 1 ]),
|
|
ZipTest([], sequences: [], [ 1, 2 ], leftovers: [], [ 1, 2 ]),
|
|
ZipTest([], sequences: [], [ 1, 2, 3 ], leftovers: [], [ 1, 2, 3 ]),
|
|
|
|
ZipTest([], sequences: [ 10 ], [], leftovers: [], []),
|
|
ZipTest([ (10, 1) ], sequences: [ 10 ], [ 1 ], leftovers: [], []),
|
|
ZipTest([ (10, 1) ], sequences: [ 10 ], [ 1, 2 ], leftovers: [], [ 2 ]),
|
|
ZipTest([ (10, 1) ], sequences: [ 10 ], [ 1, 2, 3 ], leftovers: [], [ 2, 3 ]),
|
|
|
|
ZipTest(
|
|
[],
|
|
sequences: [ 10, 20 ], [],
|
|
leftovers: [ 20 ], []),
|
|
ZipTest(
|
|
[ (10, 1) ],
|
|
sequences: [ 10, 20 ], [ 1 ],
|
|
leftovers: [], []),
|
|
ZipTest(
|
|
[ (10, 1), (20, 2) ],
|
|
sequences: [ 10, 20 ], [ 1, 2 ],
|
|
leftovers: [], []),
|
|
ZipTest(
|
|
[ (10, 1), (20, 2) ],
|
|
sequences: [ 10, 20 ], [ 1, 2, 3 ],
|
|
leftovers: [], [ 3 ]),
|
|
|
|
ZipTest(
|
|
[],
|
|
sequences: [ 10, 20, 30 ], [],
|
|
leftovers: [ 20, 30 ], []),
|
|
ZipTest(
|
|
[ (10, 1) ],
|
|
sequences: [ 10, 20, 30 ], [ 1 ],
|
|
leftovers: [ 30 ], []),
|
|
ZipTest(
|
|
[ (10, 1), (20, 2) ],
|
|
sequences: [ 10, 20, 30 ], [ 1, 2 ],
|
|
leftovers: [], []),
|
|
ZipTest(
|
|
[ (10, 1), (20, 2), (30, 3) ],
|
|
sequences: [ 10, 20, 30 ], [ 1, 2, 3 ],
|
|
leftovers: [], []),
|
|
]
|
|
|
|
SequenceTypeAlgorithms.test("zip") {
|
|
typealias Element = (OpaqueValue<Int>, OpaqueValue<Int32>)
|
|
func compareElements(lhs: Element, rhs: Element) -> Bool {
|
|
return lhs.0.value == rhs.0.value && lhs.1.value == rhs.1.value
|
|
}
|
|
|
|
for test in zipTests {
|
|
let s = MinimalSequence<OpaqueValue<Int>>(
|
|
test.sequence.map { OpaqueValue($0) })
|
|
let other = MinimalSequence<OpaqueValue<Int32>>(
|
|
test.other.map { OpaqueValue($0) })
|
|
var result = s._prext_zip(other)
|
|
expectType(
|
|
Zip2<MinimalSequence<OpaqueValue<Int>>, MinimalSequence<OpaqueValue<Int32>>>.self,
|
|
&result)
|
|
|
|
// Check for expected result and check the Zip2 SequenceType conformance.
|
|
checkSequence(
|
|
test.expected._prext_map { (OpaqueValue($0), OpaqueValue($1)) },
|
|
result,
|
|
compareElements,
|
|
test.loc.withCurrentLoc())
|
|
// Check leftovers *after* doing checkSequence(), not before, to ensure
|
|
// that checkSequence() didn't force us to consume more elements than
|
|
// needed.
|
|
expectEqual(
|
|
test.expectedLeftoverSequence, s._prext_map { $0.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
expectEqual(
|
|
test.expectedLeftoverOther, other._prext_map { $0.value },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
}
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// underestimateCount()
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
func callGenericUnderestimatedCount<S : SequenceType>(s: S) -> Int {
|
|
return s._prext_underestimateCount()
|
|
}
|
|
|
|
struct SequenceWithDefaultUnderestimateCount : SequenceType {
|
|
init() {}
|
|
|
|
func generate() -> MinimalSequence<OpaqueValue<Int>>.Generator {
|
|
expectUnreachable()
|
|
return MinimalSequence(
|
|
[ 1, 2, 3 ]._prext_map { OpaqueValue($0) }
|
|
).generate()
|
|
}
|
|
}
|
|
|
|
SequenceTypeAlgorithms.test("underestimateCount/SequenceType/DefaultImplementation") {
|
|
let s = SequenceWithDefaultUnderestimateCount()
|
|
expectEqual(0, callGenericUnderestimatedCount(s))
|
|
}
|
|
|
|
struct SequenceWithCustomUnderestimateCount : SequenceType {
|
|
init(underestimatedCount: Int) {
|
|
self._underestimatedCount = underestimatedCount
|
|
}
|
|
|
|
func generate() -> MinimalSequence<OpaqueValue<Int>>.Generator {
|
|
expectUnreachable()
|
|
return MinimalSequence(
|
|
[ 0xffff, 0xffff, 0xffff ]._prext_map { OpaqueValue($0) }
|
|
).generate()
|
|
}
|
|
|
|
func _prext_underestimateCount() -> Int {
|
|
return _underestimatedCount
|
|
}
|
|
|
|
let _underestimatedCount: Int
|
|
}
|
|
|
|
SequenceTypeAlgorithms.test("underestimateCount/SequenceType/CustomImplementation") {
|
|
if true {
|
|
let s = SequenceWithCustomUnderestimateCount(underestimatedCount: 5)
|
|
expectEqual(5, callGenericUnderestimatedCount(s))
|
|
}
|
|
if true {
|
|
let s = SequenceWithCustomUnderestimateCount(underestimatedCount: 42)
|
|
expectEqual(42, callGenericUnderestimatedCount(s))
|
|
}
|
|
}
|
|
|
|
struct CollectionWithDefaultUnderestimateCount : CollectionType {
|
|
init(count: Int) {
|
|
self._count = count
|
|
}
|
|
|
|
func generate() -> MinimalGenerator<OpaqueValue<Int>> {
|
|
expectUnreachable()
|
|
return MinimalGenerator([])
|
|
}
|
|
|
|
var startIndex: MinimalForwardIndex {
|
|
return MinimalForwardIndex(position: 0, startIndex: 0, endIndex: _count)
|
|
}
|
|
|
|
var endIndex: MinimalForwardIndex {
|
|
return MinimalForwardIndex(
|
|
position: _count, startIndex: 0, endIndex: _count)
|
|
}
|
|
|
|
subscript(i: MinimalForwardIndex) -> OpaqueValue<Int> {
|
|
expectUnreachable()
|
|
return OpaqueValue(0xffff)
|
|
}
|
|
|
|
var _count: Int
|
|
}
|
|
|
|
SequenceTypeAlgorithms.test("underestimateCount/CollectionType/DefaultImplementation") {
|
|
if true {
|
|
let s = CollectionWithDefaultUnderestimateCount(count: 0)
|
|
expectEqual(0, callGenericUnderestimatedCount(s))
|
|
}
|
|
if true {
|
|
let s = CollectionWithDefaultUnderestimateCount(count: 5)
|
|
expectEqual(5, callGenericUnderestimatedCount(s))
|
|
}
|
|
}
|
|
|
|
struct CollectionWithCustomUnderestimateCount : CollectionType {
|
|
init(underestimatedCount: Int) {
|
|
self._underestimatedCount = underestimatedCount
|
|
}
|
|
|
|
func generate() -> MinimalGenerator<OpaqueValue<Int>> {
|
|
expectUnreachable()
|
|
return MinimalGenerator([])
|
|
}
|
|
|
|
var startIndex: MinimalForwardIndex {
|
|
expectUnreachable()
|
|
return MinimalForwardIndex(position: 0, startIndex: 0, endIndex: 0xffff)
|
|
}
|
|
|
|
var endIndex: MinimalForwardIndex {
|
|
expectUnreachable()
|
|
return MinimalForwardIndex(
|
|
position: 0xffff, startIndex: 0, endIndex: 0xffff)
|
|
}
|
|
|
|
subscript(i: MinimalForwardIndex) -> OpaqueValue<Int> {
|
|
expectUnreachable()
|
|
return OpaqueValue(0xffff)
|
|
}
|
|
|
|
func _prext_underestimateCount() -> Int {
|
|
return _underestimatedCount
|
|
}
|
|
|
|
let _underestimatedCount: Int
|
|
}
|
|
|
|
SequenceTypeAlgorithms.test("underestimateCount/CollectionType/CustomImplementation") {
|
|
if true {
|
|
let s = CollectionWithCustomUnderestimateCount(underestimatedCount: 0)
|
|
expectEqual(0, callGenericUnderestimatedCount(s))
|
|
}
|
|
if true {
|
|
let s = CollectionWithCustomUnderestimateCount(underestimatedCount: 5)
|
|
expectEqual(5, callGenericUnderestimatedCount(s))
|
|
}
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// _copyToNativeArrayBuffer()
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
SequenceTypeAlgorithms.test("_copyToNativeArrayBuffer/OverestimatedCount") {
|
|
let s = MinimalSequence<OpaqueValue<Int>>(
|
|
[ 1, 2, 3 ]._prext_map { OpaqueValue($0) },
|
|
underestimatedCount: .Value(4))
|
|
expectCrashLater()
|
|
let array = s~>_copyToNativeArrayBuffer()
|
|
_blackHole(array)
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// isEmpty
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
SequenceTypeAlgorithms.test("isEmpty") {
|
|
if true {
|
|
let s = MinimalForwardCollection<OpaqueValue<Int>>([])
|
|
expectTrue(s._prext_isEmpty)
|
|
}
|
|
if true {
|
|
let s = MinimalForwardCollection<OpaqueValue<Int>>(
|
|
[ 1 ]._prext_map { OpaqueValue($0) })
|
|
expectFalse(s._prext_isEmpty)
|
|
}
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// first
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
SequenceTypeAlgorithms.test("first") {
|
|
if true {
|
|
let s = MinimalForwardCollection<OpaqueValue<Int>>([])
|
|
expectEmpty(s._prext_first)
|
|
}
|
|
if true {
|
|
let s = MinimalForwardCollection<OpaqueValue<Int>>([
|
|
OpaqueValue<Int>(1, identity: 10),
|
|
OpaqueValue<Int>(2, identity: 20)
|
|
])
|
|
if let result? = s._prext_first {
|
|
expectEqual(1, result.value)
|
|
expectEqual(10, result.identity)
|
|
} else {
|
|
expectUnreachable()
|
|
}
|
|
}
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// find()
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
func callStaticFind(
|
|
collection: MinimalForwardCollection<MinimalEquatableValue>,
|
|
element: MinimalEquatableValue
|
|
) -> MinimalForwardCollection<MinimalEquatableValue>.Index? {
|
|
return collection._prext_find(element)
|
|
}
|
|
|
|
func callGenericFind<
|
|
C : CollectionType where C.Generator.Element : Equatable
|
|
>(collection: C, element: C.Generator.Element) -> C.Index? {
|
|
return collection._prext_find(element)
|
|
}
|
|
|
|
% for dispatch in [ 'Static', 'Generic' ]:
|
|
|
|
SequenceTypeAlgorithms.test("find/WhereElementIsEquatable/${dispatch}") {
|
|
for test in findTests {
|
|
let s = MinimalForwardCollection<MinimalEquatableValue>(
|
|
test.sequence.map { MinimalEquatableValue($0) })
|
|
expectEqual(
|
|
test.expected,
|
|
call${dispatch}Find(s, MinimalEquatableValue(test.element))
|
|
.map { $0.position },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
}
|
|
}
|
|
|
|
% end
|
|
|
|
SequenceTypeAlgorithms.test("find/Predicate") {
|
|
for test in findTests {
|
|
let s = MinimalForwardCollection<MinimalEquatableValue>(
|
|
test.sequence.map { MinimalEquatableValue($0) })
|
|
expectEqual(
|
|
test.expected,
|
|
s._prext_find { $0.value == test.element }
|
|
.map { $0.position },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
}
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// indices()
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
SequenceTypeAlgorithms.test("indices") {
|
|
if true {
|
|
let s = MinimalForwardCollection<OpaqueValue<Int>>([])
|
|
let indices = s._prext_indices
|
|
expectEqual(s.startIndex, indices.startIndex)
|
|
expectEqual(s.endIndex, indices.endIndex)
|
|
}
|
|
if true {
|
|
let s = MinimalForwardCollection<OpaqueValue<Int>>([
|
|
OpaqueValue<Int>(1, identity: 10),
|
|
OpaqueValue<Int>(2, identity: 20)
|
|
])
|
|
let indices = s._prext_indices
|
|
expectEqual(s.startIndex, indices.startIndex)
|
|
expectEqual(s.endIndex, indices.endIndex)
|
|
}
|
|
}
|
|
|
|
runAllTests()
|
|
|