// RUN: %target-run-simple-swift // REQUIRES: executable_test import StdlibUnittest import StdlibCollectionUnittest @available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) extension RangeSet: ExpressibleByArrayLiteral { public init(arrayLiteral elements: Range...) { self.init(elements) } } extension Collection { func every(_ n: Int) -> [Element] { sequence(first: startIndex) { i in self.index(i, offsetBy: n, limitedBy: self.endIndex) }.map { self[$0] } } } let RangeSetTests = TestSuite("RangeSet") if #available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) { let parent = -200..<200 let source: RangeSet = [1..<5, 8..<10, 20..<22, 27..<29] let letterString = "ABCdefGHIjklMNOpqrStUvWxyz" let lowercaseLetters = letterString.filter { $0.isLowercase } let uppercaseLetters = letterString.filter { $0.isUppercase } func buildRandomRangeSet(iterations: Int = 100) -> RangeSet { var set = RangeSet() for _ in 0..<100 { var (a, b) = (Int.random(in: -100...100), Int.random(in: -100...100)) if (a > b) { swap(&a, &b) } if Double.random(in: 0..<1) > 0.3 { set.insert(contentsOf: a.., _ s2: RangeSet) -> RangeSet { let set1 = Set(parent.indices[s1]) let set2 = Set(parent.indices[s2]) return RangeSet(set1.intersection(set2), within: parent) } do { // Simple test let set1: RangeSet = [0..<5, 9..<14] let set2: RangeSet = [1..<3, 4..<6, 8..<12] let intersection: RangeSet = [1..<3, 4..<5, 9..<12] expectEqual(set1.intersection(set2), intersection) expectEqual(set2.intersection(set1), intersection) } do { // Test with upper bound / lower bound equality let set1: RangeSet = [10..<20, 30..<40] let set2: RangeSet = [15..<30, 40..<50] let intersection: RangeSet = [15..<20] expectEqual(set1.intersection(set2), intersection) expectEqual(set2.intersection(set1), intersection) } for _ in 0..<100 { let set1 = buildRandomRangeSet() let set2 = buildRandomRangeSet() let rangeSetIntersection = set1.intersection(set2) let stdlibSetIntersection = intersectionViaSet(set1, set2) expectEqual(rangeSetIntersection, stdlibSetIntersection) } } RangeSetTests.test("symmetricDifference") { func symmetricDifferenceViaSet(_ s1: RangeSet, _ s2: RangeSet) -> RangeSet { let set1 = Set(parent.indices[s1]) let set2 = Set(parent.indices[s2]) return RangeSet(set1.symmetricDifference(set2), within: parent) } do { // Simple test let set1: RangeSet = [0..<5, 9..<14] let set2: RangeSet = [1..<3, 4..<6, 8..<12] let difference: RangeSet = [0..<1, 3..<4, 5..<6, 8..<9, 12..<14] expectEqual(set1.symmetricDifference(set2), difference) expectEqual(set2.symmetricDifference(set1), difference) } do { // Test with upper bound / lower bound equality let set1: RangeSet = [10..<20, 30..<40] let set2: RangeSet = [15..<30, 40..<50] let difference: RangeSet = [10..<15, 20..<50] expectEqual(set1.symmetricDifference(set2), difference) expectEqual(set2.symmetricDifference(set1), difference) } for _ in 0..<100 { let set1 = buildRandomRangeSet() let set2 = buildRandomRangeSet() let rangeSetDifference = set1.symmetricDifference(set2) let stdlibSetDifference = symmetricDifferenceViaSet(set1, set2) expectEqual(rangeSetDifference, stdlibSetDifference) } } RangeSetTests.test("subranges(of:/where:)") { let a = [1, 2, 3, 4, 3, 3, 4, 5, 3, 4, 3, 3, 3] let indices = a.subranges(of: 3) expectEqual(indices, [2..<3, 4..<6, 8..<9, 10..<13]) let allTheThrees = a[indices] expectEqual(allTheThrees.count, 7) expectTrue(allTheThrees.allSatisfy { $0 == 3 }) expectEqual(Array(allTheThrees), Array(repeating: 3, count: 7)) let lowerIndices = letterString.subranges(where: { $0.isLowercase }) let lowerOnly = letterString[lowerIndices] expectEqualSequence(lowerOnly, lowercaseLetters) expectEqualSequence(lowerOnly.reversed(), lowercaseLetters.reversed()) let upperOnly = letterString.removingSubranges(lowerIndices) expectEqualSequence(upperOnly, uppercaseLetters) expectEqualSequence(upperOnly.reversed(), uppercaseLetters.reversed()) } RangeSetTests.test("removeSubranges") { var a = [1, 2, 3, 4, 3, 3, 4, 5, 3, 4, 3, 3, 3] let indices = a.subranges(of: 3) a.removeSubranges(indices) expectEqual(a, [1, 2, 4, 4, 5, 4]) var numbers = Array(1...20) numbers.removeSubranges(RangeSet([2..<5, 10..<15, 18..<20])) expectEqual(numbers, [1, 2, 6, 7, 8, 9, 10, 16, 17, 18]) numbers = Array(1...20) numbers.removeSubranges([]) expectEqual(numbers, Array(1...20)) let sameNumbers = numbers.removingSubranges([]) expectEqualSequence(numbers, sameNumbers) let noNumbers = numbers.removingSubranges(RangeSet(numbers.indices)) expectEqualSequence(EmptyCollection(), noNumbers) var str = letterString let lowerIndices = str.subranges(where: { $0.isLowercase }) let upperOnly = str.removingSubranges(lowerIndices) expectEqualSequence(upperOnly, uppercaseLetters) str.removeSubranges(lowerIndices) expectEqualSequence(str, uppercaseLetters) } RangeSetTests.test("moveSubranges/rangeset") { // Move before var numbers = Array(1...20) let range1 = numbers.moveSubranges(RangeSet([10..<15, 18..<20]), to: 4) expectEqual(range1, 4..<11) expectEqual(numbers, [ 1, 2, 3, 4, 11, 12, 13, 14, 15, 19, 20, 5, 6, 7, 8, 9, 10, 16, 17, 18]) // Move to start numbers = Array(1...20) let range2 = numbers.moveSubranges(RangeSet([10..<15, 18..<20]), to: 0) expectEqual(range2, 0..<7) expectEqual(numbers, [ 11, 12, 13, 14, 15, 19, 20, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 16, 17, 18]) // Move to end numbers = Array(1...20) let range3 = numbers.moveSubranges(RangeSet([10..<15, 18..<20]), to: 20) expectEqual(range3, 13..<20) expectEqual(numbers, [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 16, 17, 18, 11, 12, 13, 14, 15, 19, 20, ]) // Move to middle of selected elements numbers = Array(1...20) let range4 = numbers.moveSubranges(RangeSet([10..<15, 18..<20]), to: 14) expectEqual(range4, 10..<17) expectEqual(numbers, [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 19, 20, 16, 17, 18]) // Move none numbers = Array(1...20) let range5 = numbers.moveSubranges(RangeSet(), to: 10) expectEqual(range5, 10..<10) expectEqual(numbers, Array(1...20)) } RangeSetTests.test("moveSubranges/noCOW") { let numbers = Array(1...20) expectNoCopyOnWrite(numbers) { numbers in numbers.moveSubranges(RangeSet([10..<15, 18..<20]), to: 4) } expectNoCopyOnWrite(numbers) { numbers in numbers.removeSubranges(RangeSet([2..<5, 10..<15, 18..<20])) } } RangeSetTests.test("DiscontiguousSliceSlicing") { let initial = 1...100 // Build an array of ranges that include alternating groups of 5 elements // e.g. 1...5, 11...15, etc let rangeStarts = initial.indices.every(10) let rangeEnds = rangeStarts.compactMap { initial.index($0, offsetBy: 5, limitedBy: initial.endIndex) } let ranges = zip(rangeStarts, rangeEnds).map(Range.init) // Create a collection of the elements represented by `ranges` without // using `RangeSet` let chosenElements = ranges.map { initial[$0] }.joined() let set = RangeSet(ranges) let discontiguousSlice = initial[set] expectEqualSequence(discontiguousSlice, chosenElements) for (chosenIdx, disIdx) in zip(chosenElements.indices, discontiguousSlice.indices) { expectEqualSequence(chosenElements[chosenIdx...], discontiguousSlice[disIdx...]) expectEqualSequence(chosenElements[..