mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
2358 lines
72 KiB
Swift
2358 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: .Overestimate)
|
|
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: .Overestimate)
|
|
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: .Overestimate)
|
|
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: .Overestimate)
|
|
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: .Overestimate)
|
|
var result = map(s) {
|
|
(x: Int) -> Int16 in
|
|
expectUnreachable()
|
|
return 42
|
|
}
|
|
expectType([Int16].self, &result)
|
|
expectEqual([], result)
|
|
expectEqual([], Array(s))
|
|
// FIXME: <rdar://problem/19810841> Reserve capacity when running map() over a SequenceType
|
|
// expectLE(s.underestimatedCount, result.capacity)
|
|
}
|
|
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))
|
|
// FIXME: <rdar://problem/19810841> Reserve capacity when running map() over a SequenceType
|
|
// expectLE(s.underestimatedCount, result.capacity)
|
|
}
|
|
if true {
|
|
let s = MinimalSequence(
|
|
[ 0, 30, 10, 90 ], underestimatedCount: .Overestimate)
|
|
let result = map(s) { $0 + 1 }
|
|
expectEqual([ 1, 31, 11, 91 ], result)
|
|
expectEqual([], Array(s))
|
|
// FIXME: <rdar://problem/19810841> Reserve capacity when running map() over a SequenceType
|
|
// expectLE(s.underestimatedCount, result.capacity)
|
|
}
|
|
}
|
|
|
|
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)
|
|
expectGE(result.count + 3, result.capacity) {
|
|
"map() should use the precise element count"
|
|
}
|
|
}
|
|
if true {
|
|
let c = MinimalForwardCollection(
|
|
[ 0, 30, 10, 90 ], underestimatedCount: .Value(0))
|
|
let result = map(c) { $0 + 1 }
|
|
expectEqual([ 1, 31, 11, 91 ], result)
|
|
expectGE(result.count + 3, result.capacity) {
|
|
"map() should use the precise element count"
|
|
}
|
|
}
|
|
}
|
|
|
|
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.Overestimate,
|
|
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.Overestimate,
|
|
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 }),
|
|
]
|
|
|
|
SequenceTypeAlgorithms.test("map/SequenceType") {
|
|
expectEqual(0, LifetimeTracked.instances)
|
|
for test in mapTests {
|
|
for underestimateCountBehavior in [
|
|
UnderestimateCountBehavior.Overestimate,
|
|
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"
|
|
}
|
|
// FIXME: <rdar://problem/19810841> Reserve capacity when running map() over a SequenceType
|
|
// expectLE(s.underestimatedCount, result.capacity) {
|
|
// "map() should reserve capacity"
|
|
// }
|
|
}
|
|
}
|
|
expectEqual(0, LifetimeTracked.instances)
|
|
}
|
|
|
|
SequenceTypeAlgorithms.test("map/CollectionType") {
|
|
expectEqual(0, LifetimeTracked.instances)
|
|
for test in mapTests {
|
|
for underestimateCountBehavior in [
|
|
UnderestimateCountBehavior.Precise,
|
|
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"
|
|
}
|
|
expectGE(result.count + 3, result.capacity) {
|
|
"map() should use the precise element count"
|
|
}
|
|
}
|
|
}
|
|
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.Overestimate,
|
|
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.Overestimate,
|
|
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))
|
|
}
|
|
}
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 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()
|
|
|