blob: e6a2112bea700cd47af8d59dc91c76ef49f7c5a8 [file] [log] [blame]
// RUN: %target-run-simple-swift
// REQUIRES: executable_test
import StdlibUnittest
import StdlibCollectionUnittest
@available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *)
extension RangeSet: ExpressibleByArrayLiteral {
public init(arrayLiteral elements: Range<Bound>...) {
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 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) {
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<Int> {
var set = RangeSet<Int>()
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..<b)
} else {
set.remove(contentsOf: a..<b)
}
}
return set
}
RangeSetTests.test("contains") {
expectFalse(source.contains(0))
expectTrue(source.contains(1))
expectTrue(source.contains(4))
expectFalse(source.contains(5))
expectTrue(source.contains(28))
expectFalse(source.contains(29))
for _ in 0..<1000 {
let set = buildRandomRangeSet()
for i in parent.indices[set] {
expectTrue(set.contains(i))
}
for i in parent.indices.removingSubranges(set) {
expectFalse(set.contains(i))
}
}
}
RangeSetTests.test("insertions") {
do {
// Overlap from middle to middle
var s = source
s.insert(contentsOf: 3..<21)
expectEqualSequence(s.ranges, [1..<22, 27..<29])
}
do {
// insert in middle
var s = source
s.insert(contentsOf: 13..<15)
expectEqualSequence(s.ranges, [1..<5, 8..<10, 13..<15, 20..<22, 27..<29])
}
do {
// extend a range
var s = source
s.insert(contentsOf: 22..<25)
expectEqualSequence(s.ranges, [1..<5, 8..<10, 20..<25, 27..<29])
}
do {
// extend at beginning of range
var s = source
s.insert(contentsOf: 17..<20)
expectEqualSequence(s.ranges, [1..<5, 8..<10, 17..<22, 27..<29])
}
do {
// insert at the beginning
var s = source
s.insert(contentsOf: -10 ..< -5)
expectEqualSequence(s.ranges, [-10 ..< -5, 1..<5, 8..<10, 20..<22, 27..<29])
}
do {
// insert at the end
var s = source
s.insert(contentsOf: 35 ..< 40)
expectEqualSequence(s.ranges, [1..<5, 8..<10, 20..<22, 27..<29, 35..<40])
}
do {
// Overlap multiple ranges
var s = source
s.insert(contentsOf: 0..<21)
expectEqualSequence(s.ranges, [0..<22, 27..<29])
}
do {
// Insert at end of range
var s = source
s.insert(22, within: parent)
expectEqualSequence(s.ranges, [1..<5, 8..<10, 20..<23, 27..<29])
}
do {
// Insert between ranges
var s = source
s.insert(14, within: parent)
expectEqualSequence(s.ranges, [1..<5, 8..<10, 14..<15, 20..<22, 27..<29])
}
}
RangeSetTests.test("removals") {
var s = source
s.remove(contentsOf: 4..<28)
expectEqualSequence(s.ranges, [1..<4, 28..<29])
s.remove(3, within: parent)
expectEqualSequence(s.ranges, [1..<3, 28..<29])
}
RangeSetTests.test("invariants") {
for _ in 0..<1000 {
let set = buildRandomRangeSet()
// No empty ranges allowed
expectTrue(set.ranges.allSatisfy { !$0.isEmpty })
// No overlapping / out-of-order ranges allowed
let adjacentRanges = zip(set.ranges, set.ranges.dropFirst())
expectTrue(adjacentRanges.allSatisfy { $0.upperBound < $1.lowerBound })
}
}
RangeSetTests.test("intersection") {
func intersectionViaSet(_ s1: RangeSet<Int>, _ s2: RangeSet<Int>) -> RangeSet<Int> {
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<Int>, _ s2: RangeSet<Int>) -> RangeSet<Int> {
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[..<chosenIdx], discontiguousSlice[..<disIdx])
for (chosenUpper, disUpper) in
zip(chosenElements.indices[chosenIdx...], discontiguousSlice.indices[disIdx...])
{
expectEqualSequence(
Array(chosenElements[chosenIdx..<chosenUpper]),
Array(discontiguousSlice[disIdx..<disUpper]))
}
}
}
}
runAllTests()