mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
3309 lines
98 KiB
Swift
3309 lines
98 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 ]),
|
|
]
|
|
|
|
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("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("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"]) {
|
|
$0.characters.count() > $1.characters.count()
|
|
}
|
|
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, a.indices, $0)
|
|
}
|
|
/*
|
|
// FIXME: Disabled due to <rdar://problem/17734737> Unimplemented:
|
|
// abstraction difference in l-value
|
|
withInvalidOrderings {
|
|
var a = randomArray()
|
|
var pred = $0
|
|
_insertionSort(&a, a.indices, &pred)
|
|
}
|
|
*/
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
|
|
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.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.enumerate()._prext_map {
|
|
MinimalComparableValue($1, identity: $0)
|
|
})
|
|
var maybeResult = s.${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.enumerate()._prext_map {
|
|
OpaqueValue($1, identity: $0)
|
|
})
|
|
var timesClosureWasCalled = 0
|
|
var maybeResult = s.${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.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.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.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.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.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 ElementsEqualTest {
|
|
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() -> ElementsEqualTest {
|
|
return ElementsEqualTest(
|
|
expected, other, sequence,
|
|
expectedLeftoverOther, expectedLeftoverSequence,
|
|
file: loc.file, line: loc.line, comment: " (flipped)")
|
|
}
|
|
}
|
|
|
|
let elementsEqualTests: [ElementsEqualTest] = [
|
|
ElementsEqualTest(true, [], [], [], []),
|
|
|
|
ElementsEqualTest(false, [ 1 ], [], [], []),
|
|
ElementsEqualTest(false, [], [ 1 ], [], []),
|
|
|
|
ElementsEqualTest(false, [ 1, 2 ], [], [ 2 ], []),
|
|
ElementsEqualTest(false, [], [ 1, 2 ], [], [ 2 ]),
|
|
|
|
ElementsEqualTest(false, [ 1, 2, 3, 4 ], [ 1, 2 ], [ 4 ], []),
|
|
ElementsEqualTest(false, [ 1, 2 ], [ 1, 2, 3, 4 ], [], [ 4 ]),
|
|
].flatMap { [ $0, $0.flip() ] }
|
|
|
|
SequenceTypeAlgorithms.test("elementsEqual/WhereElementIsEquatable") {
|
|
for test in elementsEqualTests {
|
|
if true {
|
|
let s = MinimalSequence<MinimalEquatableValue>(
|
|
test.sequence.map { MinimalEquatableValue($0) })
|
|
let other = MinimalSequence<MinimalEquatableValue>(
|
|
test.other.map { MinimalEquatableValue($0) })
|
|
expectEqual(
|
|
test.expected,
|
|
s.elementsEqual(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.elementsEqual(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("elementsEqual/Predicate") {
|
|
for test in elementsEqualTests {
|
|
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.elementsEqual(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.elementsEqual(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.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.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.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.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.contains(element)
|
|
}
|
|
|
|
func callGenericContains<
|
|
S : SequenceType where S.Generator.Element : Equatable
|
|
>(sequence: S, _ element: S.Generator.Element) -> Bool {
|
|
return sequence.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
|
|
|
|
struct SequenceWithCustomContainsMethod : SequenceType {
|
|
static var timesContainsWasCalled: Int = 0
|
|
|
|
internal let _elements: [Int]
|
|
|
|
init(_ elements: [Int]) {
|
|
self._elements = elements
|
|
}
|
|
|
|
func generate() -> MinimalGenerator<MinimalEquatableValue> {
|
|
// Lie from our generate() method about sequence contents.
|
|
// Tests using this type should not call generate() anyway.
|
|
expectUnreachable()
|
|
return MinimalSequence<MinimalEquatableValue>([]).generate()
|
|
}
|
|
|
|
func _customContainsEquatableElement(
|
|
element: MinimalEquatableValue
|
|
) -> Bool? {
|
|
++SequenceWithCustomContainsMethod.timesContainsWasCalled
|
|
for e in _elements {
|
|
if e == element.value {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
|
|
func callStaticContains(
|
|
sequence: SequenceWithCustomContainsMethod,
|
|
_ element: MinimalEquatableValue) -> Bool {
|
|
return sequence.contains(element)
|
|
}
|
|
|
|
% for dispatch in [ 'Static', 'Generic' ]:
|
|
|
|
SequenceTypeAlgorithms.test("contains/WhereElementIsEquatable/CustomImplementation/${dispatch}") {
|
|
for test in findTests {
|
|
let s = SequenceWithCustomContainsMethod(test.sequence)
|
|
SequenceWithCustomContainsMethod.timesContainsWasCalled = 0
|
|
expectEqual(
|
|
test.expected != nil,
|
|
call${dispatch}Contains(s, MinimalEquatableValue(test.element)),
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
expectEqual(1, SequenceWithCustomContainsMethod.timesContainsWasCalled)
|
|
}
|
|
}
|
|
|
|
% end
|
|
|
|
func callStaticContains(
|
|
set: Set<MinimalHashableValue>,
|
|
_ element: MinimalHashableValue
|
|
) -> Bool {
|
|
return set.contains(element)
|
|
}
|
|
|
|
% for dispatch in [ 'Static', 'Generic' ]:
|
|
|
|
// FIXME: implement the same optimization for Dictionary.
|
|
// FIXME: move to the file where other Set tests live.
|
|
SequenceTypeAlgorithms.test("Set<T>.contains/WhereElementIsEquatable/CustomImplementation/${dispatch}") {
|
|
for test in findTests {
|
|
let s = Set<MinimalHashableValue>(
|
|
test.sequence.map { MinimalHashableValue($0) })
|
|
MinimalHashableValue.timesEqualEqualWasCalled = 0
|
|
MinimalHashableValue.timesHashValueWasCalled = 0
|
|
expectEqual(
|
|
test.expected != nil,
|
|
call${dispatch}Contains(s, MinimalHashableValue(test.element)),
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
if test.sequence.isEmpty {
|
|
expectEqual(
|
|
0, MinimalHashableValue.timesEqualEqualWasCalled,
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
expectEqual(
|
|
0, MinimalHashableValue.timesHashValueWasCalled,
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
} else {
|
|
expectNotEqual(
|
|
0, MinimalHashableValue.timesHashValueWasCalled,
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
}
|
|
if test.expected != nil {
|
|
expectNotEqual(
|
|
0, MinimalHashableValue.timesEqualEqualWasCalled,
|
|
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.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.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([], []),
|
|
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.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.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) } as [OpaqueValue<Int>],
|
|
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.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) } as [OpaqueValue<Int>],
|
|
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 elements[i.position]
|
|
}
|
|
|
|
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 * result.count(), 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 }),
|
|
]
|
|
|
|
var MinimalSequenceWithCustomMap_timesMapWasCalled: Int = 0
|
|
|
|
% for Implementation in [ 'Default', 'Custom' ]:
|
|
|
|
struct MinimalSequenceWith${Implementation}Map<Element> : SequenceType {
|
|
init(_ data: [Element], underestimatedCount: UnderestimateCountBehavior) {
|
|
self._data = MinimalSequence(
|
|
data, underestimatedCount: underestimatedCount)
|
|
}
|
|
|
|
func generate() -> MinimalGenerator<Element> {
|
|
return _data.generate()
|
|
}
|
|
|
|
var _data: MinimalSequence<Element>
|
|
|
|
|
|
% if Implementation == 'Custom':
|
|
|
|
static var timesMapWasCalled: Int {
|
|
get {
|
|
return MinimalSequenceWithCustomMap_timesMapWasCalled
|
|
}
|
|
set {
|
|
MinimalSequenceWithCustomMap_timesMapWasCalled = newValue
|
|
}
|
|
}
|
|
|
|
func _prext_map<T>(
|
|
@noescape transform: (Element) -> T
|
|
) -> [T] {
|
|
++MinimalSequenceWithCustomMap.timesMapWasCalled
|
|
return _data._prext_map(transform)
|
|
}
|
|
|
|
% end
|
|
}
|
|
|
|
% end
|
|
|
|
func callStaticSequenceMap<T>(
|
|
sequence: MinimalSequenceWithDefaultMap<OpaqueValue<Int>>,
|
|
@noescape transform: (OpaqueValue<Int>) -> T
|
|
) -> [T] {
|
|
var result = sequence._prext_map(transform)
|
|
expectType([T].self, &result)
|
|
return result
|
|
}
|
|
|
|
func callStaticSequenceMap<T>(
|
|
sequence: MinimalSequenceWithCustomMap<OpaqueValue<Int>>,
|
|
@noescape transform: (OpaqueValue<Int>) -> T
|
|
) -> [T] {
|
|
var result = sequence._prext_map(transform)
|
|
expectType([T].self, &result)
|
|
return result
|
|
}
|
|
|
|
func callGenericSequenceMap<S : SequenceType, T>(
|
|
sequence: S,
|
|
@noescape transform: (S.Generator.Element) -> T
|
|
) -> [T] {
|
|
var result = sequence._prext_map(transform)
|
|
expectType([T].self, &result)
|
|
return result
|
|
}
|
|
|
|
% for Implementation in [ 'Default', 'Custom' ]:
|
|
|
|
% for dispatch in [ 'Static', 'Generic' ]:
|
|
|
|
SequenceTypeAlgorithms.test("map/SequenceType/${Implementation}Implementation/${dispatch}") {
|
|
expectEqual(0, LifetimeTracked.instances)
|
|
for test in mapTests {
|
|
for underestimateCountBehavior in [
|
|
UnderestimateCountBehavior.Precise,
|
|
UnderestimateCountBehavior.Half,
|
|
UnderestimateCountBehavior.Value(0)
|
|
] {
|
|
let s = MinimalSequenceWith${Implementation}Map<OpaqueValue<Int>>(
|
|
test.sequence._prext_map { OpaqueValue($0) },
|
|
underestimatedCount: underestimateCountBehavior)
|
|
let closureLifetimeTracker = LifetimeTracked(0)
|
|
expectEqual(1, LifetimeTracked.instances)
|
|
var timesClosureWasCalled = 0
|
|
% if Implementation == 'Custom':
|
|
MinimalSequenceWithCustomMap<OpaqueValue<Int>>.timesMapWasCalled = 0
|
|
% end
|
|
var result = call${dispatch}SequenceMap(s) {
|
|
(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 })
|
|
% if Implementation == 'Custom':
|
|
expectEqual(1, MinimalSequenceWithCustomMap<OpaqueValue<Int>>.timesMapWasCalled)
|
|
% end
|
|
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)
|
|
}
|
|
|
|
% end
|
|
|
|
% end
|
|
|
|
var MinimalForwardCollectionWithCustomMap_timesMapWasCalled: Int = 0
|
|
|
|
% for Implementation in [ 'Default', 'Custom' ]:
|
|
|
|
struct MinimalForwardCollectionWith${Implementation}Map<Element>
|
|
: CollectionType {
|
|
|
|
init(_ data: [Element], underestimatedCount: UnderestimateCountBehavior) {
|
|
self._data = MinimalForwardCollection(
|
|
data, underestimatedCount: underestimatedCount)
|
|
}
|
|
|
|
func generate() -> MinimalGenerator<Element> {
|
|
return _data.generate()
|
|
}
|
|
|
|
var startIndex: MinimalForwardIndex {
|
|
return _data.startIndex
|
|
}
|
|
|
|
var endIndex: MinimalForwardIndex {
|
|
return _data.endIndex
|
|
}
|
|
|
|
subscript(i: MinimalForwardIndex) -> Element {
|
|
return _data[i]
|
|
}
|
|
|
|
var _data: MinimalForwardCollection<Element>
|
|
|
|
|
|
% if Implementation == 'Custom':
|
|
|
|
static var timesMapWasCalled: Int {
|
|
get {
|
|
return MinimalForwardCollectionWithCustomMap_timesMapWasCalled
|
|
}
|
|
set {
|
|
MinimalForwardCollectionWithCustomMap_timesMapWasCalled = newValue
|
|
}
|
|
}
|
|
|
|
func _prext_map<T>(
|
|
@noescape transform: (Element) -> T
|
|
) -> [T] {
|
|
++MinimalForwardCollectionWithCustomMap.timesMapWasCalled
|
|
return _data._prext_map(transform)
|
|
}
|
|
|
|
% end
|
|
|
|
}
|
|
|
|
% end
|
|
|
|
func callStaticCollectionMap<T>(
|
|
collection: MinimalForwardCollectionWithDefaultMap<OpaqueValue<Int>>,
|
|
@noescape transform: (OpaqueValue<Int>) -> T
|
|
) -> [T] {
|
|
var result = collection._prext_map(transform)
|
|
expectType([T].self, &result)
|
|
return result
|
|
}
|
|
|
|
func callStaticCollectionMap<T>(
|
|
collection: MinimalForwardCollectionWithCustomMap<OpaqueValue<Int>>,
|
|
@noescape transform: (OpaqueValue<Int>) -> T
|
|
) -> [T] {
|
|
var result = collection._prext_map(transform)
|
|
expectType([T].self, &result)
|
|
return result
|
|
}
|
|
|
|
func callGenericCollectionMap<C : CollectionType, T>(
|
|
collection: C,
|
|
@noescape transform: (C.Generator.Element) -> T
|
|
) -> [T] {
|
|
var result = collection._prext_map(transform)
|
|
expectType([T].self, &result)
|
|
return result
|
|
}
|
|
|
|
% for Implementation in [ 'Default', 'Custom' ]:
|
|
|
|
% for dispatch in [ 'Static', 'Generic' ]:
|
|
|
|
SequenceTypeAlgorithms.test("map/CollectionType/${Implementation}Implementation/${dispatch}") {
|
|
expectEqual(0, LifetimeTracked.instances)
|
|
for test in mapTests {
|
|
for underestimateCountBehavior in [
|
|
UnderestimateCountBehavior.Precise,
|
|
UnderestimateCountBehavior.Half,
|
|
UnderestimateCountBehavior.Value(0)
|
|
] {
|
|
let s = MinimalForwardCollectionWith${Implementation}Map<OpaqueValue<Int>>(
|
|
test.sequence.map { OpaqueValue($0) },
|
|
underestimatedCount: underestimateCountBehavior)
|
|
let closureLifetimeTracker = LifetimeTracked(0)
|
|
expectEqual(1, LifetimeTracked.instances)
|
|
var timesClosureWasCalled = 0
|
|
% if Implementation == 'Custom':
|
|
MinimalForwardCollectionWithCustomMap<OpaqueValue<Int>>.timesMapWasCalled = 0
|
|
% end
|
|
var result = call${dispatch}CollectionMap(s) {
|
|
(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 })
|
|
% if Implementation == 'Custom':
|
|
expectEqual(1, MinimalForwardCollectionWithCustomMap<OpaqueValue<Int>>.timesMapWasCalled)
|
|
% end
|
|
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)
|
|
}
|
|
|
|
% end
|
|
|
|
% end
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 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
|
|
let range = (1..<(repetitions+1))
|
|
return range.map { Int32($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") {
|
|
expectEqual(0, LifetimeTracked.instances)
|
|
for test in flatMapTests {
|
|
for underestimateCountBehavior in [
|
|
UnderestimateCountBehavior.Precise,
|
|
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.flatMap {
|
|
(element: OpaqueValue<Int>) -> MinimalSequence<OpaqueValue<Int32>> in
|
|
_blackHole(closureLifetimeTracker)
|
|
++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 * result.count(), result.capacity) {
|
|
"flatMap() should not reserve capacity"
|
|
}
|
|
}
|
|
}
|
|
expectEqual(0, LifetimeTracked.instances)
|
|
}
|
|
|
|
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") {
|
|
expectEqual(0, LifetimeTracked.instances)
|
|
for test in flatMapToOptionalTests {
|
|
for underestimateCountBehavior in [
|
|
UnderestimateCountBehavior.Precise,
|
|
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.flatMap {
|
|
(element: OpaqueValue<Int>) -> OpaqueValue<Int32>? in
|
|
_blackHole(closureLifetimeTracker)
|
|
++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 * result.count(), result.capacity) {
|
|
"flatMap() should not reserve capacity"
|
|
}
|
|
}
|
|
}
|
|
expectEqual(0, LifetimeTracked.instances)
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// 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 = zip(s, 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.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 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 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)
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// CollectionType.generate(), CollectionType.Generator
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
struct MinimalForwardCollectionWithDefaultGenerator : CollectionType {
|
|
init(count: Int) {
|
|
self._count = count
|
|
}
|
|
|
|
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> {
|
|
return OpaqueValue(i.position + 1)
|
|
}
|
|
|
|
var _count: Int
|
|
}
|
|
|
|
func callGenericGenerate<S : SequenceType>(sequence: S) -> S.Generator {
|
|
return sequence.generate()
|
|
}
|
|
|
|
SequenceTypeAlgorithms.test("CollectionType.generate()/DefaultImplementation") {
|
|
for count in [ 0, 5 ] {
|
|
let collection = MinimalForwardCollectionWithDefaultGenerator(count: count)
|
|
|
|
if true {
|
|
// Check the return type of the function when called statically.
|
|
var generator = collection.generate()
|
|
expectType(
|
|
IndexingGenerator<MinimalForwardCollectionWithDefaultGenerator>.self,
|
|
&generator)
|
|
}
|
|
|
|
if true {
|
|
// Check the return type of the function when called generically.
|
|
var generator = callGenericGenerate(collection)
|
|
expectType(
|
|
IndexingGenerator<MinimalForwardCollectionWithDefaultGenerator>.self,
|
|
&generator)
|
|
}
|
|
|
|
checkForwardCollection(
|
|
Array(1..<count+1)._prext_map { OpaqueValue($0) } as [OpaqueValue<Int>],
|
|
collection,
|
|
{ $0.value == $1.value },
|
|
SourceLocStack().withCurrentLoc())
|
|
}
|
|
}
|
|
|
|
struct MinimalForwardCollectionWithCustomGenerator : CollectionType {
|
|
init(count: Int) {
|
|
self._count = count
|
|
}
|
|
|
|
static var timesGenerateWasCalled: Int = 0
|
|
|
|
func generate() -> MinimalGenerator<OpaqueValue<Int>> {
|
|
++MinimalForwardCollectionWithCustomGenerator.timesGenerateWasCalled
|
|
return MinimalGenerator<OpaqueValue<Int>>(
|
|
Array(1..<_count+1)._prext_map { OpaqueValue($0) } as [OpaqueValue<Int>]
|
|
)
|
|
}
|
|
|
|
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> {
|
|
return OpaqueValue(i.position + 1)
|
|
}
|
|
|
|
var _count: Int
|
|
}
|
|
|
|
SequenceTypeAlgorithms.test("CollectionType.generate()/CustomImplementation") {
|
|
for count in [ 0, 5 ] {
|
|
let collection = MinimalForwardCollectionWithCustomGenerator(count: count)
|
|
|
|
if true {
|
|
// Check the return type of the function when called statically.
|
|
MinimalForwardCollectionWithCustomGenerator.timesGenerateWasCalled = 0
|
|
var generator = collection.generate()
|
|
expectType(
|
|
MinimalGenerator<OpaqueValue<Int>>.self,
|
|
&generator)
|
|
expectEqual(1, MinimalForwardCollectionWithCustomGenerator.timesGenerateWasCalled)
|
|
}
|
|
|
|
if true {
|
|
MinimalForwardCollectionWithCustomGenerator.timesGenerateWasCalled = 0
|
|
// Check the return type of the function when called generically.
|
|
var generator = callGenericGenerate(collection)
|
|
expectType(
|
|
MinimalGenerator<OpaqueValue<Int>>.self,
|
|
&generator)
|
|
expectEqual(1, MinimalForwardCollectionWithCustomGenerator.timesGenerateWasCalled)
|
|
}
|
|
|
|
checkForwardCollection(
|
|
Array(1..<count+1)._prext_map { OpaqueValue($0) } as [OpaqueValue<Int>],
|
|
collection,
|
|
{ $0.value == $1.value },
|
|
resiliencyChecks: .none,
|
|
SourceLocStack().withCurrentLoc())
|
|
}
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// CollectionType.subscript(range:), CollectionType.SubSlice
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
struct MinimalForwardCollectionWithDefaultSlicing : CollectionType {
|
|
init(count: Int) {
|
|
self._count = count
|
|
}
|
|
|
|
// FIXME: should return MinimalGenerator<OpaqueValue<Int>>
|
|
func generate() -> MinimalGenerator<OpaqueValue<Int>> {
|
|
return MinimalGenerator((1..<_count+1)._prext_map { OpaqueValue($0) })
|
|
}
|
|
|
|
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> {
|
|
return OpaqueValue(i.position + 1)
|
|
}
|
|
|
|
var _count: Int
|
|
}
|
|
|
|
func callGenericSlicing<C : CollectionType>(
|
|
collection: C,
|
|
_ bounds: Range<C.Index>
|
|
) -> C._prext_SubSlice {
|
|
return collection[bounds]
|
|
}
|
|
|
|
// FIXME: need separate tests for the Slice type itself.
|
|
SequenceTypeAlgorithms.test("subscript(range:)/DefaultImplementation") {
|
|
for count in [ 0, 5 ] {
|
|
let collection = MinimalForwardCollectionWithDefaultSlicing(count: count)
|
|
|
|
if true {
|
|
// Check the return type of the function when called statically.
|
|
var slice = collection[collection.startIndex..<collection.endIndex]
|
|
expectType(
|
|
_prext_Slice<MinimalForwardCollectionWithDefaultSlicing>.self,
|
|
&slice)
|
|
}
|
|
|
|
if true {
|
|
// Check the return type of the function when called generically.
|
|
var slice = callGenericSlicing(
|
|
collection,
|
|
collection.startIndex..<collection.endIndex)
|
|
expectType(
|
|
_prext_Slice<MinimalForwardCollectionWithDefaultSlicing>.self,
|
|
&slice)
|
|
}
|
|
|
|
// FIXME: improve checkForwardCollection to check the SubSlice type.
|
|
checkForwardCollection(
|
|
Array(1..<count+1)._prext_map { OpaqueValue($0) } as [OpaqueValue<Int>],
|
|
collection,
|
|
{ $0.value == $1.value },
|
|
resiliencyChecks: .none,
|
|
SourceLocStack().withCurrentLoc())
|
|
}
|
|
}
|
|
|
|
struct MinimalForwardCollectionWithCustomSlicing : CollectionType {
|
|
init(count: Int) {
|
|
self._count = count
|
|
}
|
|
|
|
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> {
|
|
return OpaqueValue(i.position + 1)
|
|
}
|
|
|
|
subscript(bounds: Range<MinimalForwardIndex>) -> CustomSlice {
|
|
return CustomSlice(
|
|
start: bounds.startIndex.position,
|
|
end: bounds.endIndex.position)
|
|
}
|
|
|
|
var _count: Int
|
|
}
|
|
|
|
struct CustomSlice : CollectionType {
|
|
init(start: Int, end: Int) {
|
|
self._start = start
|
|
self._end = end
|
|
}
|
|
|
|
var startIndex: MinimalForwardIndex {
|
|
return MinimalForwardIndex(
|
|
position: _start,
|
|
startIndex: _start,
|
|
endIndex: _end)
|
|
}
|
|
|
|
var endIndex: MinimalForwardIndex {
|
|
return MinimalForwardIndex(
|
|
position: _end,
|
|
startIndex: _start,
|
|
endIndex: _end)
|
|
}
|
|
|
|
subscript(i: MinimalForwardIndex) -> OpaqueValue<Int> {
|
|
return OpaqueValue(i.position + 1)
|
|
}
|
|
|
|
var _start: Int
|
|
var _end: Int
|
|
}
|
|
|
|
// FIXME: need separate tests for the Slice type itself.
|
|
SequenceTypeAlgorithms.test("subscript(range:)/CustomImplementation") {
|
|
for count in [ 0, 5 ] {
|
|
let collection = MinimalForwardCollectionWithCustomSlicing(count: count)
|
|
|
|
if true {
|
|
// Check the return type of the function when called statically.
|
|
var slice = collection[collection.startIndex..<collection.endIndex]
|
|
expectType(CustomSlice.self, &slice)
|
|
}
|
|
|
|
if true {
|
|
// Check the return type of the function when called generically.
|
|
var slice = callGenericSlicing(
|
|
collection,
|
|
collection.startIndex..<collection.endIndex)
|
|
expectType(CustomSlice.self, &slice)
|
|
}
|
|
|
|
// FIXME: improve checkForwardCollection to check the SubSlice type.
|
|
checkForwardCollection(
|
|
Array(1..<count+1)._prext_map { OpaqueValue($0) } as [OpaqueValue<Int>],
|
|
collection,
|
|
{ $0.value == $1.value },
|
|
SourceLocStack().withCurrentLoc())
|
|
}
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// isEmpty
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
struct MinimalForwardCollectionWithDefaultIsEmpty : CollectionType {
|
|
init(count: Int) {
|
|
self._count = count
|
|
}
|
|
|
|
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(i.position + 1)
|
|
}
|
|
|
|
var _count: Int
|
|
}
|
|
|
|
func callStaticIsEmpty(
|
|
collection: MinimalForwardCollectionWithDefaultIsEmpty
|
|
) -> Bool {
|
|
return collection.isEmpty
|
|
}
|
|
|
|
func callGenericIsEmpty<C : CollectionType>(collection: C) -> Bool {
|
|
return collection.isEmpty
|
|
}
|
|
|
|
% for dispatch in [ 'Static', 'Generic' ]:
|
|
|
|
SequenceTypeAlgorithms.test("isEmpty/DefaultImplementation/${dispatch}") {
|
|
if true {
|
|
let s = MinimalForwardCollectionWithDefaultIsEmpty(count: 0)
|
|
expectTrue(call${dispatch}IsEmpty(s))
|
|
}
|
|
if true {
|
|
let s = MinimalForwardCollectionWithDefaultIsEmpty(count: 1)
|
|
expectFalse(call${dispatch}IsEmpty(s))
|
|
}
|
|
}
|
|
|
|
% end
|
|
|
|
struct MinimalForwardCollectionWithCustomIsEmpty : CollectionType {
|
|
static var timesIsEmptyWasColled: Int = 0
|
|
|
|
init(count: Int) {
|
|
self._count = count
|
|
}
|
|
|
|
var startIndex: MinimalForwardIndex {
|
|
expectUnreachable()
|
|
return MinimalForwardIndex(position: 0, startIndex: 0, endIndex: _count)
|
|
}
|
|
|
|
var endIndex: MinimalForwardIndex {
|
|
expectUnreachable()
|
|
return MinimalForwardIndex(
|
|
position: _count, startIndex: 0, endIndex: _count)
|
|
}
|
|
|
|
subscript(i: MinimalForwardIndex) -> OpaqueValue<Int> {
|
|
expectUnreachable()
|
|
return OpaqueValue(i.position + 1)
|
|
}
|
|
|
|
var isEmpty: Bool {
|
|
++MinimalForwardCollectionWithCustomIsEmpty.timesIsEmptyWasColled
|
|
return _count == 0
|
|
}
|
|
|
|
var _count: Int
|
|
}
|
|
|
|
func callStaticIsEmpty(
|
|
collection: MinimalForwardCollectionWithCustomIsEmpty
|
|
) -> Bool {
|
|
return collection.isEmpty
|
|
}
|
|
|
|
% for dispatch in [ 'Static', 'Generic' ]:
|
|
|
|
SequenceTypeAlgorithms.test("isEmpty/DefaultImplementation/${dispatch}") {
|
|
if true {
|
|
let s = MinimalForwardCollectionWithCustomIsEmpty(count: 0)
|
|
MinimalForwardCollectionWithCustomIsEmpty.timesIsEmptyWasColled = 0
|
|
expectTrue(call${dispatch}IsEmpty(s))
|
|
expectEqual(1, MinimalForwardCollectionWithCustomIsEmpty.timesIsEmptyWasColled)
|
|
}
|
|
if true {
|
|
let s = MinimalForwardCollectionWithCustomIsEmpty(count: 1)
|
|
MinimalForwardCollectionWithCustomIsEmpty.timesIsEmptyWasColled = 0
|
|
expectFalse(call${dispatch}IsEmpty(s))
|
|
expectEqual(1, MinimalForwardCollectionWithCustomIsEmpty.timesIsEmptyWasColled)
|
|
}
|
|
}
|
|
|
|
% end
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// first
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
SequenceTypeAlgorithms.test("first") {
|
|
if true {
|
|
let s = MinimalForwardCollection<OpaqueValue<Int>>([])
|
|
expectEmpty(s.first)
|
|
}
|
|
if true {
|
|
let s = MinimalForwardCollection<OpaqueValue<Int>>([
|
|
OpaqueValue<Int>(1, identity: 10),
|
|
OpaqueValue<Int>(2, identity: 20)
|
|
])
|
|
if let result = s.first {
|
|
expectEqual(1, result.value)
|
|
expectEqual(10, result.identity)
|
|
} else {
|
|
expectUnreachable()
|
|
}
|
|
}
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// last
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
SequenceTypeAlgorithms.test("last") {
|
|
if true {
|
|
let s = MinimalBidirectionalCollection<OpaqueValue<Int>>([])
|
|
expectEmpty(s.last)
|
|
}
|
|
if true {
|
|
let s = MinimalBidirectionalCollection<OpaqueValue<Int>>([
|
|
OpaqueValue<Int>(1, identity: 10),
|
|
OpaqueValue<Int>(2, identity: 20)
|
|
])
|
|
if let result = s.last {
|
|
expectEqual(2, result.value)
|
|
expectEqual(20, result.identity)
|
|
} else {
|
|
expectUnreachable()
|
|
}
|
|
}
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// count()
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
% for Implementation in [ 'Default', 'Custom' ]:
|
|
|
|
struct MinimalForwardCollectionWith${Implementation}Count : CollectionType {
|
|
init(count: Int) {
|
|
self._count = count
|
|
}
|
|
|
|
var startIndex: MinimalForwardInt32Index {
|
|
return MinimalForwardInt32Index(
|
|
position: 0,
|
|
startIndex: 0,
|
|
endIndex: _count)
|
|
}
|
|
|
|
var endIndex: MinimalForwardInt32Index {
|
|
return MinimalForwardInt32Index(
|
|
position: _count,
|
|
startIndex: 0,
|
|
endIndex: _count)
|
|
}
|
|
|
|
subscript(i: MinimalForwardInt32Index) -> OpaqueValue<Int> {
|
|
return OpaqueValue(i.position + 1)
|
|
}
|
|
|
|
var _count: Int
|
|
|
|
% if Implementation == 'Custom':
|
|
|
|
static var timesCountWasCalled: Int = 0
|
|
|
|
func count() -> Int32 {
|
|
++MinimalForwardCollectionWithCustomCount.timesCountWasCalled
|
|
return Int32(_count)
|
|
}
|
|
|
|
% end
|
|
}
|
|
|
|
% end
|
|
|
|
func callGenericCount<C : CollectionType>(collection: C) -> C.Index.Distance {
|
|
return collection.count()
|
|
}
|
|
|
|
% for Implementation in [ 'Default', 'Custom' ]:
|
|
|
|
SequenceTypeAlgorithms.test("CollectionType.count()/${Implementation}Implementation") {
|
|
for count in [ 0, 5 ] {
|
|
let collection = MinimalForwardCollectionWith${Implementation}Count(count: count)
|
|
|
|
if true {
|
|
// Check the return type of the function when called statically.
|
|
% if Implementation == 'Custom':
|
|
MinimalForwardCollectionWithCustomCount.timesCountWasCalled = 0
|
|
% end
|
|
var result = collection.count()
|
|
expectType(Int32.self, &result)
|
|
expectEqual(Int32(count), result)
|
|
% if Implementation == 'Custom':
|
|
expectEqual(1, MinimalForwardCollectionWithCustomCount.timesCountWasCalled)
|
|
% end
|
|
}
|
|
|
|
if true {
|
|
// Check the return type of the function when called generically.
|
|
% if Implementation == 'Custom':
|
|
MinimalForwardCollectionWithCustomCount.timesCountWasCalled = 0
|
|
% end
|
|
var result = callGenericCount(collection)
|
|
expectType(Int32.self, &result)
|
|
expectEqual(Int32(count), result)
|
|
% if Implementation == 'Custom':
|
|
expectEqual(1, MinimalForwardCollectionWithCustomCount.timesCountWasCalled)
|
|
% end
|
|
}
|
|
|
|
checkForwardCollection(
|
|
Array(1..<count+1)._prext_map { OpaqueValue($0) } as [OpaqueValue<Int>],
|
|
collection,
|
|
{ $0.value == $1.value },
|
|
SourceLocStack().withCurrentLoc())
|
|
}
|
|
}
|
|
|
|
% end
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// find()
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
func callStaticFind(
|
|
collection: MinimalForwardCollection<MinimalEquatableValue>,
|
|
_ element: MinimalEquatableValue
|
|
) -> MinimalForwardCollection<MinimalEquatableValue>.Index? {
|
|
return collection.indexOf(element)
|
|
}
|
|
|
|
func callGenericFind<
|
|
C : CollectionType where C.Generator.Element : Equatable
|
|
>(collection: C, _ element: C.Generator.Element) -> C.Index? {
|
|
return collection.indexOf(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
|
|
|
|
struct CollectionWithCustomFindMethod : CollectionType {
|
|
static var timesFindWasCalled: Int = 0
|
|
|
|
internal let _elements: [Int]
|
|
|
|
init(_ elements: [Int]) {
|
|
self._elements = elements
|
|
}
|
|
|
|
func generate() -> MinimalGenerator<MinimalEquatableValue> {
|
|
// Lie from our generate() method about sequence contents.
|
|
// Tests using this type should not call generate() anyway.
|
|
expectUnreachable()
|
|
return MinimalSequence<MinimalEquatableValue>([]).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) -> MinimalEquatableValue {
|
|
return MinimalEquatableValue(_elements[i.position])
|
|
}
|
|
|
|
func _customIndexOfEquatableElement(
|
|
element: MinimalEquatableValue
|
|
) -> MinimalForwardIndex?? {
|
|
++CollectionWithCustomFindMethod.timesFindWasCalled
|
|
for i in _elements.indices {
|
|
if _elements[i] == element.value {
|
|
return MinimalForwardIndex(
|
|
position: i,
|
|
startIndex: 0,
|
|
endIndex: _elements.endIndex)
|
|
}
|
|
}
|
|
return Optional(nil)
|
|
}
|
|
}
|
|
|
|
func callStaticFind(
|
|
sequence: CollectionWithCustomFindMethod,
|
|
_ element: MinimalEquatableValue
|
|
) -> CollectionWithCustomFindMethod.Index? {
|
|
return sequence.indexOf(element)
|
|
}
|
|
|
|
% for dispatch in [ 'Static', 'Generic' ]:
|
|
|
|
SequenceTypeAlgorithms.test("find/WhereElementIsEquatable/CustomImplementation/${dispatch}") {
|
|
for test in findTests {
|
|
let s = CollectionWithCustomFindMethod(test.sequence)
|
|
CollectionWithCustomFindMethod.timesFindWasCalled = 0
|
|
expectEqual(
|
|
test.expected,
|
|
call${dispatch}Find(s, MinimalEquatableValue(test.element))
|
|
.map { $0.position },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
expectEqual(1, CollectionWithCustomFindMethod.timesFindWasCalled)
|
|
}
|
|
}
|
|
|
|
% end
|
|
|
|
// FIXME: underscores are a workaround for:
|
|
// <rdar://problem/20582358> Commenting out one line determines whether a
|
|
// completely different line type-checks
|
|
func callGenericFind_<
|
|
C : CollectionType where C.Generator.Element : Equatable
|
|
>(collection: C, _ element: C.Generator.Element) -> C.Index? {
|
|
return collection.indexOf(element)
|
|
}
|
|
|
|
func callStaticFind_(
|
|
set: Set<MinimalHashableValue>,
|
|
_ element: MinimalHashableValue
|
|
) -> Set<MinimalHashableValue>.Index? {
|
|
return set.indexOf(element)
|
|
}
|
|
|
|
% for dispatch in [ 'Static', 'Generic' ]:
|
|
|
|
// FIXME: implement the same optimization for Dictionary.
|
|
// FIXME: move to the file where other Set tests live.
|
|
SequenceTypeAlgorithms.test("Set<T>.find/WhereElementIsEquatable/CustomImplementation/${dispatch}") {
|
|
for test in findTests {
|
|
let s = Set<MinimalHashableValue>(
|
|
test.sequence.map { MinimalHashableValue($0) })
|
|
MinimalHashableValue.timesEqualEqualWasCalled = 0
|
|
MinimalHashableValue.timesHashValueWasCalled = 0
|
|
expectEqual(
|
|
test.expected
|
|
.map { _ in MinimalHashableValue(test.element) },
|
|
call${dispatch}Find_(s, MinimalHashableValue(test.element))
|
|
.map { s[$0] },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
if test.sequence.isEmpty {
|
|
expectEqual(
|
|
0, MinimalHashableValue.timesEqualEqualWasCalled,
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
expectEqual(
|
|
0, MinimalHashableValue.timesHashValueWasCalled,
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
} else {
|
|
expectNotEqual(
|
|
0, MinimalHashableValue.timesHashValueWasCalled,
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
}
|
|
if test.expected != nil {
|
|
expectNotEqual(
|
|
0, MinimalHashableValue.timesEqualEqualWasCalled,
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
}
|
|
}
|
|
}
|
|
|
|
% end
|
|
|
|
SequenceTypeAlgorithms.test("find/Predicate") {
|
|
expectEqual(0, LifetimeTracked.instances)
|
|
for test in findTests {
|
|
let s = MinimalForwardCollection<MinimalEquatableValue>(
|
|
test.sequence.map { MinimalEquatableValue($0) })
|
|
let closureLifetimeTracker = LifetimeTracked(0)
|
|
expectEqual(1, LifetimeTracked.instances)
|
|
let result = s.indexOf {
|
|
(candidate) in
|
|
_blackHole(closureLifetimeTracker)
|
|
return candidate.value == test.element
|
|
}
|
|
expectEqual(
|
|
test.expected,
|
|
result.map { $0.position },
|
|
stackTrace: test.loc.withCurrentLoc())
|
|
}
|
|
expectEqual(0, LifetimeTracked.instances)
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// indices
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
SequenceTypeAlgorithms.test("indices") {
|
|
if true {
|
|
let s = MinimalForwardCollection<OpaqueValue<Int>>([])
|
|
let indices = s.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.indices
|
|
expectEqual(s.startIndex, indices.startIndex)
|
|
expectEqual(s.endIndex, indices.endIndex)
|
|
}
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// partition()
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
func _forAllPermutationsImpl(
|
|
index: Int, _ size: Int,
|
|
inout _ perm: [Int], inout _ visited: [Bool],
|
|
_ body: ([Int]) -> ()
|
|
) {
|
|
if index == size {
|
|
body(perm)
|
|
return
|
|
}
|
|
|
|
for i in 0..<size {
|
|
if visited[i] {
|
|
continue
|
|
}
|
|
visited[i] = true
|
|
perm[index] = i
|
|
_forAllPermutationsImpl(index + 1, size, &perm, &visited, body)
|
|
visited[i] = false
|
|
}
|
|
}
|
|
|
|
/// Generate all permutations.
|
|
func forAllPermutations(size: Int, body: ([Int]) -> ()) {
|
|
if size == 0 {
|
|
return
|
|
}
|
|
|
|
var permutation = [Int](count: size, repeatedValue: 0)
|
|
var visited = [Bool](count: size, repeatedValue: false)
|
|
_forAllPermutationsImpl(0, size, &permutation, &visited, body)
|
|
}
|
|
|
|
/// Generate all permutations.
|
|
func forAllPermutations<S : SequenceType>(
|
|
sequence: S, body: ([S.Generator.Element]) -> ()
|
|
) {
|
|
let data = Array(sequence)
|
|
forAllPermutations(data.count) {
|
|
(indices: [Int]) in
|
|
body(indices._prext_map { data[$0] })
|
|
return ()
|
|
}
|
|
}
|
|
|
|
SequenceTypeAlgorithms.test("forAllPermutations") {
|
|
if true {
|
|
var permutations: [[Int]] = []
|
|
forAllPermutations(0) {
|
|
permutations.append($0)
|
|
}
|
|
expectEqualSequence([] as [[Int]], permutations, { $0 == $1 })
|
|
}
|
|
|
|
if true {
|
|
var permutations: [[Int]] = []
|
|
forAllPermutations(1) {
|
|
permutations.append($0)
|
|
}
|
|
expectEqualSequence([ [ 0 ] ] as [[Int]], permutations, { $0 == $1 })
|
|
}
|
|
|
|
if true {
|
|
var permutations: [[Int]] = []
|
|
forAllPermutations(2) {
|
|
permutations.append($0)
|
|
}
|
|
expectEqualSequence(
|
|
[
|
|
[ 0, 1 ],
|
|
[ 1, 0 ]
|
|
] as [[Int]],
|
|
permutations,
|
|
{ $0 == $1 })
|
|
}
|
|
|
|
if true {
|
|
var permutations: [[Int]] = []
|
|
forAllPermutations(3) {
|
|
permutations.append($0)
|
|
}
|
|
expectEqualSequence(
|
|
[
|
|
[ 0, 1, 2 ],
|
|
[ 0, 2, 1 ],
|
|
[ 1, 0, 2 ],
|
|
[ 1, 2, 0 ],
|
|
[ 2, 0, 1 ],
|
|
[ 2, 1, 0 ],
|
|
] as [[Int]],
|
|
permutations,
|
|
{ $0 == $1 })
|
|
}
|
|
|
|
if true {
|
|
var permutations: [[Int]] = []
|
|
forAllPermutations([ 10, 20, 30 ]) {
|
|
permutations.append($0)
|
|
}
|
|
expectEqualSequence(
|
|
[
|
|
[ 10, 20, 30 ],
|
|
[ 10, 30, 20 ],
|
|
[ 20, 10, 30 ],
|
|
[ 20, 30, 10 ],
|
|
[ 30, 10, 20 ],
|
|
[ 30, 20, 10 ],
|
|
] as [[Int]],
|
|
permutations,
|
|
{ $0 == $1 })
|
|
}
|
|
}
|
|
|
|
public struct CustomComparableValue : Equatable, Comparable {
|
|
public static var timesEqualEqualWasCalled: Int = 0
|
|
public static var timesLessWasCalled: Int = 0
|
|
|
|
public static var equalImpl: (Int, Int) -> Bool = { $0 == $1 }
|
|
public static var lessImpl: (Int, Int) -> Bool = { $0 < $1 }
|
|
|
|
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: CustomComparableValue,
|
|
rhs: CustomComparableValue
|
|
) -> Bool {
|
|
++CustomComparableValue.timesEqualEqualWasCalled
|
|
return CustomComparableValue.equalImpl(lhs.value, rhs.value)
|
|
}
|
|
|
|
public func < (
|
|
lhs: CustomComparableValue,
|
|
rhs: CustomComparableValue
|
|
) -> Bool {
|
|
++CustomComparableValue.timesLessWasCalled
|
|
return CustomComparableValue.lessImpl(lhs.value, rhs.value)
|
|
}
|
|
|
|
struct PartitionExhaustiveTest {
|
|
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 partitionExhaustiveTests = [
|
|
PartitionExhaustiveTest([]),
|
|
PartitionExhaustiveTest([ 10 ]),
|
|
PartitionExhaustiveTest([ 10, 10 ]),
|
|
PartitionExhaustiveTest([ 10, 20 ]),
|
|
PartitionExhaustiveTest([ 10, 10, 10 ]),
|
|
PartitionExhaustiveTest([ 10, 10, 20 ]),
|
|
PartitionExhaustiveTest([ 10, 20, 20 ]),
|
|
PartitionExhaustiveTest([ 10, 20, 30 ]),
|
|
PartitionExhaustiveTest([ 10, 10, 10, 10 ]),
|
|
PartitionExhaustiveTest([ 10, 10, 10, 20 ]),
|
|
PartitionExhaustiveTest([ 10, 10, 20, 20 ]),
|
|
PartitionExhaustiveTest([ 10, 20, 30, 40 ]),
|
|
PartitionExhaustiveTest([ 10, 10, 10, 10, 10 ]),
|
|
PartitionExhaustiveTest([ 10, 10, 10, 20, 20 ]),
|
|
PartitionExhaustiveTest([ 10, 10, 10, 20, 30 ]),
|
|
PartitionExhaustiveTest([ 10, 10, 20, 20, 30 ]),
|
|
PartitionExhaustiveTest([ 10, 10, 20, 30, 40 ]),
|
|
PartitionExhaustiveTest([ 10, 20, 30, 40, 50 ]),
|
|
PartitionExhaustiveTest([ 10, 20, 30, 40, 50, 60 ]),
|
|
PartitionExhaustiveTest([ 10, 10, 10, 10, 10, 20, 20 ]),
|
|
PartitionExhaustiveTest([ 10, 20, 30, 40, 50, 60, 70 ]),
|
|
]
|
|
|
|
% for predicate in [ False, True ]:
|
|
|
|
SequenceTypeAlgorithms.test("partition/${'Predicate' if predicate else 'WhereElementIsEquatable'}") {
|
|
% if not predicate:
|
|
CustomComparableValue.equalImpl = { $0 == $1 }
|
|
CustomComparableValue.lessImpl = { $0 < $1 }
|
|
% end
|
|
expectEqual(0, LifetimeTracked.instances)
|
|
|
|
for test in partitionExhaustiveTests {
|
|
forAllPermutations(test.sequence) { (sequence) in
|
|
% for slice in [ False, True ]:
|
|
if true {
|
|
% if predicate:
|
|
var sequenceAsArray: [OpaqueValue<Int>] =
|
|
zip(sequence, 0..<sequence.count)._prext_map {
|
|
OpaqueValue($0, identity: $1)
|
|
}
|
|
|
|
% if slice:
|
|
sequenceAsArray.insert(
|
|
OpaqueValue(0xfffe, identity: 0xfffe), atIndex: 0)
|
|
sequenceAsArray.append(OpaqueValue(0xffff, identity: 0xffff))
|
|
% end
|
|
|
|
var s = MinimalMutableRandomAccessCollection<OpaqueValue<Int>>(
|
|
sequenceAsArray)
|
|
|
|
% if slice:
|
|
let indices = s.startIndex.successor()..<s.endIndex.predecessor()
|
|
% else:
|
|
let indices = s.indices
|
|
% end
|
|
|
|
let closureLifetimeTracker = LifetimeTracked(0)
|
|
expectEqual(1, LifetimeTracked.instances)
|
|
let pivot = s._prext_partition(indices) {
|
|
(lhs, rhs) in
|
|
_blackHole(closureLifetimeTracker)
|
|
return lhs.value < rhs.value
|
|
}
|
|
% else:
|
|
var sequenceAsArray: [CustomComparableValue] =
|
|
zip(sequence, 0..<sequence.count)._prext_map {
|
|
CustomComparableValue($0, identity: $1)
|
|
}
|
|
|
|
% if slice:
|
|
sequenceAsArray.insert(
|
|
CustomComparableValue(0xfffe, identity: 0xfffe), atIndex: 0)
|
|
sequenceAsArray.append(CustomComparableValue(0xffff, identity: 0xffff))
|
|
% end
|
|
|
|
var s = MinimalMutableRandomAccessCollection<CustomComparableValue>(
|
|
sequenceAsArray)
|
|
|
|
% if slice:
|
|
let indices = s.startIndex.successor()..<s.endIndex.predecessor()
|
|
% else:
|
|
let indices = s.indices
|
|
% end
|
|
|
|
let pivot = s._prext_partition(indices)
|
|
% end
|
|
|
|
// Check that we didn't lose any values.
|
|
% if slice:
|
|
expectEqual(0xfffe, s.first!.identity)
|
|
expectEqual(0xffff, s.last!.identity)
|
|
var identities = s._prext_map { $0.identity }
|
|
identities.removeLast()
|
|
identities.removeAtIndex(0)
|
|
expectEqualsUnordered(0..<sequence.count, identities)
|
|
% else:
|
|
expectEqualsUnordered(0..<sequence.count, s._prext_map { $0.identity })
|
|
% end
|
|
|
|
// All the elements in the first partition are less than the pivot
|
|
// value.
|
|
for i in indices.startIndex..<pivot {
|
|
expectLT(s[i].value, s[pivot].value)
|
|
}
|
|
// All the elements in the second partition are greater or equal to
|
|
// the pivot value.
|
|
for i in pivot..<indices.endIndex {
|
|
expectLE(s[pivot].value, s[i].value)
|
|
}
|
|
}
|
|
% end
|
|
}
|
|
}
|
|
expectEqual(0, LifetimeTracked.instances)
|
|
}
|
|
|
|
% end
|
|
|
|
% for predicate in [ False, True ]:
|
|
|
|
SequenceTypeAlgorithms.test("partition/${'Predicate' if predicate else 'WhereElementIsEquatable'}/InvalidOrderings") {
|
|
expectEqual(0, LifetimeTracked.instances)
|
|
withInvalidOrderings { (comparisonPredicate) in
|
|
for i in 0..<7 {
|
|
forAllPermutations(i) { (sequence) in
|
|
% if predicate:
|
|
var s = MinimalMutableRandomAccessCollection<OpaqueValue<Int>>(
|
|
sequence._prext_map { OpaqueValue($0) })
|
|
let closureLifetimeTracker = LifetimeTracked(0)
|
|
expectEqual(1, LifetimeTracked.instances)
|
|
let pivot = s._prext_partition(s.indices) {
|
|
(lhs, rhs) in
|
|
_blackHole(closureLifetimeTracker)
|
|
return comparisonPredicate(lhs.value, rhs.value)
|
|
}
|
|
% else:
|
|
var s = MinimalMutableRandomAccessCollection<CustomComparableValue>(
|
|
sequence._prext_map { CustomComparableValue($0) })
|
|
CustomComparableValue.equalImpl = {
|
|
!comparisonPredicate($0, $1) &&
|
|
!comparisonPredicate($1, $0)
|
|
}
|
|
CustomComparableValue.lessImpl = {
|
|
comparisonPredicate($0, $1)
|
|
}
|
|
let pivot = s._prext_partition(s.indices)
|
|
% end
|
|
|
|
// Weak postcondition: we didn't lose any values.
|
|
expectEqualsUnordered(0..<i, s._prext_map { $0.value })
|
|
|
|
expectTrue(0 <= s[pivot].value && s[pivot].value < i)
|
|
}
|
|
}
|
|
}
|
|
expectEqual(0, LifetimeTracked.instances)
|
|
}
|
|
|
|
% end
|
|
|
|
runAllTests()
|
|
|