diff --git a/Sources/PenguinGraphs/GraphCopying.swift b/Sources/PenguinGraphs/GraphCopying.swift index 5a37d683..8ab8b408 100644 --- a/Sources/PenguinGraphs/GraphCopying.swift +++ b/Sources/PenguinGraphs/GraphCopying.swift @@ -120,7 +120,6 @@ extension MutablePropertyGraph where Self: DefaultInitializable { } } - /// Initializes `self` as a copy of the incidences and properties of `other`. /// /// - Complexity: O(|V| + |E|) @@ -165,3 +164,79 @@ extension MutablePropertyGraph where Self: DefaultInitializable { edgeProperties: InternalEdgePropertyMap(for: other)) } } + +extension IncidenceGraph where Self: MutableGraph { + /// Adds all edges from `other` into `self`, using `vertexMapper` to map vertices, and calling + /// `edgeCreationListener` with every new EdgeId. + public mutating func addEdges( + from other: Other, + mappingVertices vertexMapper: (Other, Other.VertexId, Self) -> VertexId, + _ edgeCreationListener: (EdgeId, inout Self) -> Void = { _, _ in } + ) { + for vSrc in other.vertices { + let v = vertexMapper(other, vSrc, self) + for e in other.edges(from: vSrc) { + let d = vertexMapper(other, other.destination(of: e), self) + let edgeId = addEdge(from: v, to: d) + edgeCreationListener(edgeId, &self) + } + } + } + + /// Adds all edges from `other` into `self`, and calling `edgeCreationListener` with every new + /// EdgeId. + /// + /// - Precondition: (not checked) all of `other`'s VertexId's must be valid VertexId's in `self`. + public mutating func addEdges( + from other: Other, + _ edgeCreationListener: (EdgeId, inout Self) -> Void = { _, _ in } + ) where Other.VertexId == VertexId { + addEdges(from: other, mappingVertices: { _, v, _ in v }, edgeCreationListener) + } +} + +extension IncidenceGraph where Self: MutablePropertyGraph & VertexListGraph { + /// Adds all edges from `other` into `self`, calling `edgeCreationListener` with every new + /// `EdgeId`. + /// + /// - Precondition: all of `other`'s VertexId's must be valid VertexId's in `self`. + public mutating func addEdges( + from other: Other, + _ edgeCreationListener: (EdgeId, inout Self) -> Void = { _, _ in } + ) where Other.VertexId == VertexId, Other.Edge == Edge { + addEdges( + from: other, + storing: InternalEdgePropertyMap(for: other), + mappingVertices: { _, v, _ in v }, + edgeCreationListener) + } + + /// Adds all edges from `other` into `self` storing the corresponding edge property from + /// `edgeProperties`. + public mutating func addEdges< + Other: IncidenceGraph & VertexListGraph, + EdgeProperties: PropertyMap + >( + from other: Other, + storing edgeProperties: EdgeProperties, + mappingVertices vertexMapper: (Other, Other.VertexId, Self) -> VertexId, + _ edgeCreationListener: (EdgeId, inout Self) -> Void = { _, _ in } + ) where + Other.VertexId == VertexId, + EdgeProperties.Graph == Other, + EdgeProperties.Key == Other.EdgeId, + EdgeProperties.Value == Edge + { + for vSrc in other.vertices { + let v = vertexMapper(other, vSrc, self) + for e in other.edges(from: vSrc) { + let d = vertexMapper(other, other.destination(of: e), self) + let edgeId = addEdge( + from: v, + to: d, + storing: edgeProperties.get(e, in: other)) + edgeCreationListener(edgeId, &self) + } + } + } +} diff --git a/Sources/PenguinStructures/Concatenation.swift b/Sources/PenguinStructures/Concatenation.swift new file mode 100644 index 00000000..025ca0eb --- /dev/null +++ b/Sources/PenguinStructures/Concatenation.swift @@ -0,0 +1,184 @@ +// Copyright 2020 Penguin Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +/// A collection that is all the elements of one collection followed by all the elements of a second +/// collection. +public struct Concatenation: Collection where +First.Element == Second.Element { + /// The elements in `self`. + public typealias Element = First.Element + /// The collection whose elements appear first. + @usableFromInline + var first: First + /// The collection whose elements appear second. + @usableFromInline + var second: Second + + /// Concatenates `first` with `second`. + public init(_ first: First, _ second: Second) { + self.first = first + self.second = second + } + + /// A position in a `Concatenation`. + public struct Index: Comparable { + /// A position into one of the two underlying collections. + @usableFromInline + var position: Either + + /// Creates a new index into the first underlying collection. + @usableFromInline + internal init(first i: First.Index) { + self.position = .a(i) + } + + /// Creates a new index into the first underlying collection. + @usableFromInline + internal init(second i: Second.Index) { + self.position = .b(i) + } + + public static func < (lhs: Self, rhs: Self) -> Bool { + return lhs.position < rhs.position + } + } + + /// The position of the first element, or `endIndex` if `self.isEmpty` + @inlinable + public var startIndex: Index { + if !first.isEmpty { return Index(first: first.startIndex) } + return Index(second: second.startIndex) + } + + /// The collection’s “past the end” position—that is, the position one greater than the last valid + /// subscript argument. + @inlinable + public var endIndex: Index { Index(second: second.endIndex) } + + /// Returns the next index after `i`. + public func index(after i: Index) -> Index { + switch i.position { + case .a(let index): + let newIndex = first.index(after: index) + guard newIndex != first.endIndex else { return Index(second: second.startIndex) } + return Index(first: newIndex) + case .b(let index): + return Index(second: second.index(after: index)) + } + } + + /// Accesses the element at `i`. + @inlinable + public subscript(i: Index) -> Element { + switch i.position { + case .a(let index): return first[index] + case .b(let index): return second[index] + } + } + + /// The number of elements in `self`. + @inlinable + public var count: Int { first.count + second.count } + + /// True iff `self` contains no elements. + @inlinable + public var isEmpty: Bool { first.isEmpty && second.isEmpty } + + /// Returns the distance between two indices. + @inlinable + public func distance(from start: Index, to end: Index) -> Int { + switch (start.position, end.position) { + case (.a(let start), .a(let end)): + return first.distance(from: start, to: end) + case (.a(let start), .b(let end)): + return first.distance(from: start, to: first.endIndex) + second.distance(from: second.startIndex, to: end) + case (.b(let start), .a(let end)): + return second.distance(from: start, to: second.startIndex) + first.distance(from: first.endIndex, to: end) + case (.b(let start), .b(let end)): + return second.distance(from: start, to: end) + } + } +} + +extension Concatenation: BidirectionalCollection +where First: BidirectionalCollection, Second: BidirectionalCollection { + /// Returns the next valid index before `i`. + @inlinable + public func index(before i: Index) -> Index { + switch i.position { + case .a(let index): return Index(first: first.index(before: index)) + case .b(let index): + if index == second.startIndex { + return Index(first: first.index(before: first.endIndex)) + } + return Index(second: second.index(before: index)) + } + } +} + +extension Concatenation: RandomAccessCollection + where First: RandomAccessCollection, Second: RandomAccessCollection +{ + @inlinable + public func index(_ i: Index, offsetBy n: Int) -> Index { + if n == 0 { return i } + if n < 0 { return offsetBackward(i, by: n) } + return offsetForward(i, by: n) + } + + @usableFromInline + func offsetForward(_ i: Index, by n: Int) -> Index { + switch i.position { + case .a(let index): + let d = first.distance(from: index, to: first.endIndex) + if n < d { + return Index(first: first.index(index, offsetBy: n)) + } else { + return Index(second: second.index(second.startIndex, offsetBy: n - d)) + } + case .b(let index): + return Index(second: second.index(index, offsetBy: n)) + } + } + + @usableFromInline + func offsetBackward(_ i: Index, by n: Int) -> Index { + switch i.position { + case .a(let index): + return Index(first: first.index(index, offsetBy: n)) + case .b(let index): + let d = second.distance(from: second.startIndex, to: index) + if -n <= d { + return Index(second: second.index(index, offsetBy: n)) + } else { + return Index(first: first.index(first.endIndex, offsetBy: n + d)) + } + } + } +} + +// TODO: Add RandomAccessCollection conformance. +// TODO: Add MutableCollection conformance. + +extension Collection { + /// Returns a new collection where all the elements of `self` appear before all the elements of + /// `other`. + @inlinable + public func concatenated(with other: Other) + -> Concatenation + where Other.Element == Element { + return Concatenation(self, other) + } +} diff --git a/Sources/PenguinStructures/DefaultInitializable.swift b/Sources/PenguinStructures/DefaultInitializable.swift index 3a3c769a..e9ea43d9 100644 --- a/Sources/PenguinStructures/DefaultInitializable.swift +++ b/Sources/PenguinStructures/DefaultInitializable.swift @@ -21,3 +21,9 @@ public protocol DefaultInitializable { extension Array: DefaultInitializable {} extension Dictionary: DefaultInitializable {} extension String: DefaultInitializable {} +extension Int: DefaultInitializable {} +extension UInt: DefaultInitializable {} +extension Int32: DefaultInitializable {} +extension UInt32: DefaultInitializable {} +extension Float: DefaultInitializable {} +extension Double: DefaultInitializable {} diff --git a/Sources/PenguinStructures/Either.swift b/Sources/PenguinStructures/Either.swift new file mode 100644 index 00000000..b23984b3 --- /dev/null +++ b/Sources/PenguinStructures/Either.swift @@ -0,0 +1,184 @@ +// Copyright 2020 Penguin Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// TODO: Consider adding `ABiasedEither` and `BBiasedEither` for biased projection wrapper types of +// `Either`. + +/// An unbiased [tagged union or sum type](https://en.wikipedia.org/wiki/Tagged_union) of exactly +/// two possible cases. +/// +/// **When _NOT_ to use Either**: if there are asymmetrical semantics (e.g. `A` is special in some +/// manner), or when there are better names (i.e. meaning) that can be attached to the cases, a +/// domain-specific `enum` often results in more maintainable code and easier to use APIs. +/// +/// **When to use Either**: good applications of `Either` come up in generic programming where there +/// are no defined semantics or information that can be gained from naming or biasing one of the two +/// cases. +public enum Either { + case a(A) + case b(B) + /// An `A` if `self` represents the `a` case, `nil` otherwise. + var a: A? { if case .a(let x) = self { return x } else {return nil } } + /// A `B` if `self` represents the `b` case, `nil` otherwise. + var b: B? { if case .b(let x) = self { return x } else {return nil } } + /// Creates an `Either` from `x` (`a` case). + public init(_ x: A, or other: Type) { self = .a(x) } + /// Creates an `Either` from `x` (`a` case). + public init(_ x: A) { self = .a(x) } + /// Creates an `Either` from `x` (`b` case). + public init(_ x: B) { self = .b(x) } +} + +extension Either: Equatable where A: Equatable, B: Equatable { + /// True if `lhs` is equivalent to `rhs`. + public static func == (lhs: Self, rhs: Self) -> Bool { + switch (lhs, rhs) { + case (.a(let lhs), .a(let rhs)): return lhs == rhs + case (.b(let lhs), .b(let rhs)): return lhs == rhs + default: return false + } + } +} + +// Note: while there are other possible orderings that could sense, until Swift has reasonable +// rules and tools to resolve typeclass incoherency, we define a single broadly applicable ordering +// here. +extension Either: Comparable where A: Comparable, B: Comparable { + /// True iff `lhs` is less than `rhs` such that all `a`s are ordered before all `b`s. + public static func < (lhs: Self, rhs: Self) -> Bool { + switch (lhs, rhs) { + case (.a(let lhs), .a(let rhs)): return lhs < rhs + case (.a, _): return true + case (.b(let lhs), .b(let rhs)): return lhs < rhs + default: return false + } + } +} + + +extension Either: Hashable where A: Hashable, B: Hashable { + /// Hashes `self` into `hasher`. + public func hash(into hasher: inout Hasher) { + switch self { + case .a(let a): a.hash(into: &hasher) + case .b(let b): b.hash(into: &hasher) + } + } +} + +extension Either: CustomStringConvertible { + /// A textual representation of `self`. + public var description: String { + switch self { + case .a(let a): return "A(\(a))" + case .b(let b): return "B(\(b))" + } + } +} + +/// A sequence of one of two sequence types. +/// +/// An `EitherSequence` can sometimes be used an alternative to `AnySequence`. Advantages of +/// `EitherSequence` include higher performance, as more information is available at compile time, +/// enabling more effective static optimizations. +/// +/// Tip: if code uses `AnySequence`, but most of the time is used with a particular collection type +/// `T` (e.g. `Array`), consider using an `EitherSequence`. +public typealias EitherSequence = Either + where A.Element == B.Element + +extension EitherSequence { + /// A type that provides the sequence’s iteration interface and encapsulates its iteration state. + public struct Iterator: IteratorProtocol { + // Note: although we would ideally use `var underlying = Either`, this + // would result in accidentally quadratic behavior due to the extra copies required. (Enums + // cannot be modified in-place, resulting in a lot of extra copies.) + // + // Future optimization: avoid needing to reserve space for both `a` and `b` iterators. + + /// An iterator for the `A` collection. + var a: A.Iterator? + /// An iterator for the `A` collection. + var b: B.Iterator? + + /// The type of element traversed by the iterator. + public typealias Element = A.Element + + /// Advances to the next element and returns it, or `nil` if no next element exists. + public mutating func next() -> Element? { + a?.next() ?? b?.next() + } + } +} + +extension EitherSequence: Sequence { + /// A type representing the sequence’s elements. + public typealias Element = A.Element + + /// Returns an iterator over the elements of this sequence. + public func makeIterator() -> Iterator { + switch self { + case .a(let a): return Iterator(a: a.makeIterator(), b: nil) + case .b(let b): return Iterator(a: nil, b: b.makeIterator()) + } + } +} + +/// A collection of one of two collection types. +/// +/// - SeeAlso: `EitherSequence`. +public typealias EitherCollection = Either + where A.Element == B.Element + +extension EitherCollection: Collection { + /// A type that represents a position in the collection. + public typealias Index = Either + + /// The position of the first element in a nonempty collection. + public var startIndex: Index { + switch self { + case .a(let c): return Index(c.startIndex) + case .b(let c): return Index(c.startIndex) + } + } + + /// The collection’s “past the end” position—that is, the position one greater than the last valid + /// subscript argument. + public var endIndex: Index { + switch self { + case .a(let c): return Index(c.endIndex) + case .b(let c): return Index(c.endIndex) + } + } + + /// Returns the position immediately after the given index. + public func index(after i: Index) -> Index { + switch (i, self) { + case (.a(let i), .a(let c)): return Index(c.index(after: i)) + case (.b(let i), .b(let c)): return Index(c.index(after: i)) + default: fatalError("Invalid index \(i) used with \(self).") + } + } + + /// Accesses the element at the specified position. + public subscript(position: Index) -> Element { + switch (position, self) { + case (.a(let i), .a(let c)): return c[i] + case (.b(let i), .b(let c)): return c[i] + default: fatalError("Invalid index \(position) used with \(self).") + } + } +} + +// TODO: Bidirectional & RandomAccess conformances & verification tests! diff --git a/Tests/PenguinGraphTests/GraphCopyingTests.swift b/Tests/PenguinGraphTests/GraphCopyingTests.swift index e7b57af5..ea40a5da 100644 --- a/Tests/PenguinGraphTests/GraphCopyingTests.swift +++ b/Tests/PenguinGraphTests/GraphCopyingTests.swift @@ -111,9 +111,40 @@ final class GraphCopyingTests: XCTestCase { XCTAssertEqual(2, dst.inDegree(of: 2)) } + func testAddingEdges() { + var src = DirectedAdjacencyList() + _ = src.addVertex() + let srcV1 = src.addVertex() + let srcV2 = src.addVertex() + + _ = src.addEdge(from: srcV1, to: srcV2, storing: "1->2 (src)") + + var dst = UndirectedAdjacencyList() + let dstV0 = dst.addVertex() + let dstV1 = dst.addVertex() + let dstV2 = dst.addVertex() + + _ = dst.addEdge(from: dstV0, to: dstV1, storing: "0->1 (dst)") + var edgeCallback = false + dst.addEdges(from: src) { e, g in + XCTAssertFalse(edgeCallback) + edgeCallback = true + XCTAssertEqual(srcV1, g.source(of: e)) + XCTAssertEqual(srcV2, g.destination(of: e)) + } + XCTAssert(edgeCallback) + + let edges = dst.edges(from: dstV2) + XCTAssertEqual(1, edges.count) + XCTAssertEqual(dstV2, dst.source(of: edges[dstV0])) + XCTAssertEqual(dstV1, dst.destination(of: edges[0])) + XCTAssertEqual("1->2 (src)", dst[edge: edges[0]]) + } + static var allTests = [ ("testUndirectedCopying", testUndirectedCopying), ("testProperyGraphCopying", testProperyGraphCopying), ("testProperyGraphCopyingBidirectional", testProperyGraphCopyingBidirectional), + ("testAddingEdges", testAddingEdges), ] } diff --git a/Tests/PenguinStructuresTests/ConcatenatedCollectionTests.swift b/Tests/PenguinStructuresTests/ConcatenatedCollectionTests.swift new file mode 100644 index 00000000..ad7864a7 --- /dev/null +++ b/Tests/PenguinStructuresTests/ConcatenatedCollectionTests.swift @@ -0,0 +1,47 @@ +// Copyright 2020 Penguin Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import PenguinStructures +import XCTest + +final class ConcatenatedCollectionTests: XCTestCase { + + func testConcatenateSetAndList() { + let s = Set(["1", "2", "3"]) + let c = s.concatenated(with: ["10", "11", "12"]) + c.checkCollectionSemantics(expectedValues: Array(s) + ["10", "11", "12"]) + } + + func testConcatenateRanges() { + let c = (0..<3).concatenated(with: 3...6) + c.checkRandomAccessCollectionSemantics(expectedValues: 0...6) + } + + func testConcatenatingEmptyPrefix() { + let c = (0..<0).concatenated(with: [1, 2, 3]) + c.checkRandomAccessCollectionSemantics(expectedValues: 1...3) + } + + func testConcatenatingEmptySuffix() { + let c = (1...3).concatenated(with: 1000..<1000) + c.checkRandomAccessCollectionSemantics(expectedValues: [1, 2, 3]) + } + + static var allTests = [ + ("testConcatenateSetAndList", testConcatenateSetAndList), + ("testConcatenateRanges", testConcatenateRanges), + ("testConcatenatingEmptyPrefix", testConcatenatingEmptyPrefix), + ("testConcatenatingEmptySuffix", testConcatenatingEmptySuffix), + ] +} diff --git a/Tests/PenguinStructuresTests/EitherTests.swift b/Tests/PenguinStructuresTests/EitherTests.swift new file mode 100644 index 00000000..dee30093 --- /dev/null +++ b/Tests/PenguinStructuresTests/EitherTests.swift @@ -0,0 +1,32 @@ +//****************************************************************************** +// Copyright 2020 Penguin Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import XCTest +import PenguinStructures + + +class EitherTests: XCTestCase { + + func testComparable() { + Either.checkComparableSemantics(.a(1), .a(2), .a(1000), .b(-3), .b(0), .b(5)) + + Either.checkComparableSemantics( + Either(""), Either("abc"), Either("xyz"), Either(-4), Either(0), Either(10)) + } + + static var allTests = [ + ("testComparable", testComparable), + ] +} diff --git a/Tests/PenguinStructuresTests/StdlibProtocolTests.swift b/Tests/PenguinStructuresTests/StdlibProtocolTests.swift index 1aa76bb2..febf85b0 100644 --- a/Tests/PenguinStructuresTests/StdlibProtocolTests.swift +++ b/Tests/PenguinStructuresTests/StdlibProtocolTests.swift @@ -307,3 +307,42 @@ extension MutableCollection where Element: Equatable { } } +extension Comparable { + /// XCTests `Self`'s semantic conformance to `Comparable` using the `orderedExamples`. + /// + /// - Parameter orderedExamples: a collection of instances of `Self` in ascending order. + static func checkComparableSemantics(_ orderedExamples: Self...) { + precondition(!orderedExamples.isEmpty, "Must have non-empty set of orderedExamples.") + + for i in orderedExamples.indices { + XCTAssert(orderedExamples[i] == orderedExamples[i], "\(orderedExamples[i])") + XCTAssert(orderedExamples[i] <= orderedExamples[i], "\(orderedExamples[i])") + XCTAssert(orderedExamples[i] >= orderedExamples[i], "\(orderedExamples[i])") + XCTAssertFalse(orderedExamples[i] < orderedExamples[i], "\(orderedExamples[i])") + XCTAssertFalse(orderedExamples[i] > orderedExamples[i], "\(orderedExamples[i])") + XCTAssertFalse(orderedExamples[i] != orderedExamples[i], "\(orderedExamples[i])") + + for j in orderedExamples[orderedExamples.index(after: i)...].indices { + // Equality checks. + XCTAssertFalse(orderedExamples[i] == orderedExamples[j], "\(orderedExamples[i]), \(orderedExamples[j])") + XCTAssert(orderedExamples[i] != orderedExamples[j], "\(orderedExamples[i]), \(orderedExamples[j])") + + // Comparison checks. + XCTAssertFalse(orderedExamples[i] > orderedExamples[j], "\(orderedExamples[i]), \(orderedExamples[j])") + XCTAssertFalse(orderedExamples[i] >= orderedExamples[j], "\(orderedExamples[i]), \(orderedExamples[j])") + XCTAssert(orderedExamples[i] < orderedExamples[j], "\(orderedExamples[i]), \(orderedExamples[j])") + XCTAssert(orderedExamples[i] <= orderedExamples[j], "\(orderedExamples[i]), \(orderedExamples[j])") + + // Commutative equality checks. + XCTAssert(orderedExamples[j] != orderedExamples[i], "\(orderedExamples[i]), \(orderedExamples[j])") + XCTAssertFalse(orderedExamples[j] == orderedExamples[i], "\(orderedExamples[i]), \(orderedExamples[j])") + + // Commutative comparison checks. + XCTAssert(orderedExamples[j] > orderedExamples[i], "\(orderedExamples[i]), \(orderedExamples[j])") + XCTAssert(orderedExamples[j] >= orderedExamples[i], "\(orderedExamples[i]), \(orderedExamples[j])") + XCTAssertFalse(orderedExamples[j] < orderedExamples[i], "\(orderedExamples[i]), \(orderedExamples[j])") + XCTAssertFalse(orderedExamples[j] <= orderedExamples[i], "\(orderedExamples[i]), \(orderedExamples[j])") + } + } + } +} diff --git a/Tests/PenguinStructuresTests/XCTestManifests.swift b/Tests/PenguinStructuresTests/XCTestManifests.swift index 8d1aae2d..4436f429 100644 --- a/Tests/PenguinStructuresTests/XCTestManifests.swift +++ b/Tests/PenguinStructuresTests/XCTestManifests.swift @@ -23,8 +23,10 @@ import XCTest testCase(ArrayStorageExtensionTests.allTests), testCase(ArrayStorageTests.allTests), testCase(CollectionAlgorithmTests.allTests), + testCase(ConcatenatedCollectionTests.allTests), testCase(DequeTests.allTests), testCase(DoubleEndedBufferTests.allTests), + testCase(EitherTests.allTests), testCase(FactoryInitializableTests.allTests), testCase(FixedSizeArrayTests.allTests), testCase(TupleTests.allTests),