597 lines
19 KiB
Kotlin
597 lines
19 KiB
Kotlin
/*
|
|
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
|
*/
|
|
|
|
package test.collections
|
|
|
|
import test.*
|
|
import test.collections.js.linkedStringSetOf
|
|
import kotlin.test.*
|
|
|
|
fun <T> iterableOf(vararg items: T): Iterable<T> = Iterable { items.iterator() }
|
|
fun <T> Iterable<T>.toIterable(): Iterable<T> = Iterable { this.iterator() }
|
|
|
|
class IterableTest : OrderedIterableTests<Iterable<String>>({ iterableOf(*it) }, iterableOf<String>())
|
|
class SetTest : IterableTests<Set<String>>({ setOf(*it) }, setOf())
|
|
class LinkedSetTest : OrderedIterableTests<LinkedHashSet<String>>({ linkedSetOf(*it) }, linkedSetOf())
|
|
class LinkedStringSetTest : OrderedIterableTests<LinkedHashSet<String>>({ linkedStringSetOf(*it) }, linkedStringSetOf())
|
|
class ListTest : OrderedIterableTests<List<String>>({ listOf(*it) }, listOf<String>())
|
|
class ArrayListTest : OrderedIterableTests<ArrayList<String>>({ arrayListOf(*it) }, arrayListOf<String>())
|
|
|
|
abstract class OrderedIterableTests<T : Iterable<String>>(createFrom: (Array<out String>) -> T, empty: T) : IterableTests<T>(createFrom, empty) {
|
|
@Test
|
|
fun indexOf() {
|
|
expect(0) { data.indexOf("foo") }
|
|
expect(-1) { empty.indexOf("foo") }
|
|
expect(1) { data.indexOf("bar") }
|
|
expect(-1) { data.indexOf("zap") }
|
|
}
|
|
|
|
@Test
|
|
fun lastIndexOf() {
|
|
expect(0) { data.lastIndexOf("foo") }
|
|
expect(-1) { empty.lastIndexOf("foo") }
|
|
expect(1) { data.lastIndexOf("bar") }
|
|
expect(-1) { data.lastIndexOf("zap") }
|
|
}
|
|
|
|
@Test
|
|
fun indexOfFirst() {
|
|
expect(-1) { data.indexOfFirst { it.contains("p") } }
|
|
expect(0) { data.indexOfFirst { it.startsWith('f') } }
|
|
expect(-1) { empty.indexOfFirst { it.startsWith('f') } }
|
|
}
|
|
|
|
@Test
|
|
fun indexOfLast() {
|
|
expect(-1) { data.indexOfLast { it.contains("p") } }
|
|
expect(1) { data.indexOfLast { it.length == 3 } }
|
|
expect(-1) { empty.indexOfLast { it.startsWith('f') } }
|
|
}
|
|
|
|
@Test
|
|
fun elementAt() {
|
|
expect("foo") { data.elementAt(0) }
|
|
expect("bar") { data.elementAt(1) }
|
|
assertFails { data.elementAt(2) }
|
|
assertFails { data.elementAt(-1) }
|
|
assertFails { empty.elementAt(0) }
|
|
|
|
expect("foo") { data.elementAtOrElse(0, { "" }) }
|
|
expect("zoo") { data.elementAtOrElse(-1, { "zoo" }) }
|
|
expect("zoo") { data.elementAtOrElse(2, { "zoo" }) }
|
|
expect("zoo") { empty.elementAtOrElse(0) { "zoo" } }
|
|
|
|
expect(null) { empty.elementAtOrNull(0) }
|
|
|
|
}
|
|
|
|
@Test
|
|
fun first() {
|
|
expect("foo") { data.first() }
|
|
assertFails {
|
|
data.first { it.startsWith("x") }
|
|
}
|
|
assertFails {
|
|
empty.first()
|
|
}
|
|
expect("foo") { data.first { it.startsWith("f") } }
|
|
}
|
|
|
|
@Test
|
|
fun firstOrNull() {
|
|
expect(null) { data.firstOrNull { it.startsWith("x") } }
|
|
expect(null) { empty.firstOrNull() }
|
|
|
|
val f = data.firstOrNull { it.startsWith("f") }
|
|
assertEquals("foo", f)
|
|
}
|
|
|
|
@Test
|
|
fun last() {
|
|
assertEquals("bar", data.last())
|
|
assertFails {
|
|
data.last { it.startsWith("x") }
|
|
}
|
|
assertFails {
|
|
empty.last()
|
|
}
|
|
expect("foo") { data.last { it.startsWith("f") } }
|
|
}
|
|
|
|
@Test
|
|
fun lastOrNull() {
|
|
expect(null) { data.lastOrNull { it.startsWith("x") } }
|
|
expect(null) { empty.lastOrNull() }
|
|
expect("foo") { data.lastOrNull { it.startsWith("f") } }
|
|
}
|
|
|
|
|
|
@Test
|
|
fun zipWithNext() {
|
|
val data = createFrom("", "a", "xyz")
|
|
val lengthDeltas = data.zipWithNext { a: String, b: String -> b.length - a.length }
|
|
assertEquals(listOf(1, 2), lengthDeltas)
|
|
|
|
assertTrue(empty.zipWithNext { a: String, b: String -> a + b }.isEmpty())
|
|
assertTrue(createFrom("foo").zipWithNext { a: String, b: String -> a + b }.isEmpty())
|
|
}
|
|
|
|
@Test
|
|
fun zipWithNextPairs() {
|
|
assertTrue(empty.zipWithNext().isEmpty())
|
|
assertTrue(createFrom("foo").zipWithNext().isEmpty())
|
|
assertEquals(listOf("a" to "b"), createFrom("a", "b").zipWithNext())
|
|
assertEquals(listOf("a" to "b", "b" to "c"), createFrom("a", "b", "c").zipWithNext())
|
|
}
|
|
|
|
@Test
|
|
fun chunked() {
|
|
val size = 7
|
|
val data = createFrom(Array(size) { "$it" })
|
|
val result = data.chunked(4)
|
|
assertEquals(listOf(
|
|
listOf("0", "1", "2", "3"),
|
|
listOf("4", "5", "6")
|
|
), result)
|
|
|
|
val result2 = data.chunked(3) { it.joinToString("") }
|
|
assertEquals(listOf("012", "345", "6"), result2)
|
|
|
|
data.toList().let { expectedSingleChunk ->
|
|
assertEquals(expectedSingleChunk, data.chunked(size).single())
|
|
assertEquals(expectedSingleChunk, data.chunked(size + 3).single())
|
|
assertEquals(expectedSingleChunk, data.chunked(Int.MAX_VALUE).single())
|
|
}
|
|
|
|
createFrom("a", "b").let { iterable ->
|
|
assertEquals(iterable.toList(), iterable.chunked(Int.MAX_VALUE).single())
|
|
}
|
|
|
|
assertTrue(empty.chunked(3).isEmpty())
|
|
|
|
for (illegalValue in listOf(Int.MIN_VALUE, -1, 0)) {
|
|
assertFailsWith<IllegalArgumentException>("size $illegalValue") { data.chunked(illegalValue) }
|
|
}
|
|
}
|
|
|
|
|
|
@Test
|
|
fun windowed() {
|
|
val size = 7
|
|
val data = createFrom(Array(size) { "$it" })
|
|
val result = data.windowed(4, 2)
|
|
assertEquals(listOf(
|
|
listOf("0", "1", "2", "3"),
|
|
listOf("2", "3", "4", "5")
|
|
), result)
|
|
|
|
val resultPartial = data.windowed(4, 2, partialWindows = true)
|
|
assertEquals(listOf(
|
|
listOf("0", "1", "2", "3"),
|
|
listOf("2", "3", "4", "5"),
|
|
listOf("4", "5", "6"),
|
|
listOf("6")
|
|
), resultPartial)
|
|
|
|
|
|
val result2 = data.windowed(2, 3) { it.joinToString("") }
|
|
assertEquals(listOf("01", "34"), result2)
|
|
|
|
val result2partial = data.windowed(2, 3, partialWindows = true) { it.joinToString("") }
|
|
assertEquals(listOf("01", "34", "6"), result2partial)
|
|
|
|
assertEquals(data.chunked(2), data.windowed(2, 2, partialWindows = true))
|
|
|
|
assertEquals(data.take(2), data.windowed(2, size).single())
|
|
assertEquals(data.take(3), data.windowed(3, size + 3).single())
|
|
|
|
assertEquals(data.toList(), data.windowed(size, 1).single())
|
|
assertTrue(data.windowed(size + 1, 1).isEmpty())
|
|
|
|
val result3partial = data.windowed(size, 1, partialWindows = true)
|
|
result3partial.forEachIndexed { index, window ->
|
|
assertEquals(size - index, window.size, "size of window#$index")
|
|
}
|
|
|
|
assertTrue(empty.windowed(3, 2).isEmpty())
|
|
|
|
for (illegalValue in listOf(Int.MIN_VALUE, -1, 0)) {
|
|
assertFailsWith<IllegalArgumentException>("size $illegalValue") { data.windowed(illegalValue, 1) }
|
|
assertFailsWith<IllegalArgumentException>("step $illegalValue") { data.windowed(1, illegalValue) }
|
|
}
|
|
|
|
// index overflow tests
|
|
for (partialWindows in listOf(true, false)) {
|
|
|
|
val windowed1 = data.windowed(5, Int.MAX_VALUE, partialWindows)
|
|
assertEquals(data.take(5), windowed1.single())
|
|
val windowed2 = data.windowed(Int.MAX_VALUE, 5, partialWindows)
|
|
assertEquals(if (partialWindows) listOf(data.toList(), listOf("5", "6")) else listOf(), windowed2)
|
|
val windowed3 = data.windowed(Int.MAX_VALUE, Int.MAX_VALUE, partialWindows)
|
|
assertEquals(if (partialWindows) listOf(data.toList()) else listOf(), windowed3)
|
|
|
|
val windowedTransform1 = data.windowed(5, Int.MAX_VALUE, partialWindows) { it.joinToString("") }
|
|
assertEquals("01234", windowedTransform1.single())
|
|
val windowedTransform2 = data.windowed(Int.MAX_VALUE, 5, partialWindows) { it.joinToString("") }
|
|
assertEquals(if (partialWindows) listOf("0123456", "56") else listOf(), windowedTransform2)
|
|
val windowedTransform3 = data.windowed(Int.MAX_VALUE, Int.MAX_VALUE, partialWindows) { it.joinToString("") }
|
|
assertEquals(if (partialWindows) listOf("0123456") else listOf(), windowedTransform3)
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
abstract class IterableTests<T : Iterable<String>>(val createFrom: (Array<out String>) -> T, val empty: T) {
|
|
fun createFrom(vararg items: String): T = createFrom(items)
|
|
|
|
val data = createFrom("foo", "bar")
|
|
|
|
@Test
|
|
fun any() {
|
|
expect(true) { data.any() }
|
|
expect(false) { empty.any() }
|
|
expect(true) { data.any { it.startsWith("f") } }
|
|
expect(false) { data.any { it.startsWith("x") } }
|
|
expect(false) { empty.any { it.startsWith("x") } }
|
|
}
|
|
|
|
@Test
|
|
fun all() {
|
|
expect(true) { data.all { it.length == 3 } }
|
|
expect(false) { data.all { it.startsWith("b") } }
|
|
expect(true) { empty.all { it.startsWith("b") } }
|
|
}
|
|
|
|
@Test
|
|
fun none() {
|
|
expect(false) { data.none() }
|
|
expect(true) { empty.none() }
|
|
expect(false) { data.none { it.length == 3 } }
|
|
expect(false) { data.none { it.startsWith("b") } }
|
|
expect(true) { data.none { it.startsWith("x") } }
|
|
expect(true) { empty.none { it.startsWith("b") } }
|
|
}
|
|
|
|
@Test
|
|
fun filter() {
|
|
val foo = data.filter { it.startsWith("f") }
|
|
assertStaticAndRuntimeTypeIs<List<String>>(foo)
|
|
expect(true) { foo.all { it.startsWith("f") } }
|
|
expect(1) { foo.size }
|
|
assertEquals(listOf("foo"), foo)
|
|
}
|
|
|
|
@Test
|
|
fun filterIndexed() {
|
|
val result = data.filterIndexed { index, value -> value.first() == ('a' + index) }
|
|
assertEquals(listOf("bar"), result)
|
|
}
|
|
|
|
@Test
|
|
fun drop() {
|
|
val foo = data.drop(1)
|
|
assertStaticAndRuntimeTypeIs<List<String>>(foo)
|
|
expect(true) { foo.all { it.startsWith("b") } }
|
|
expect(1) { foo.size }
|
|
assertEquals(listOf("bar"), foo)
|
|
}
|
|
|
|
@Test
|
|
fun dropWhile() {
|
|
val foo = data.dropWhile { it[0] == 'f' }
|
|
assertStaticAndRuntimeTypeIs<List<String>>(foo)
|
|
expect(true) { foo.all { it.startsWith("b") } }
|
|
expect(1) { foo.size }
|
|
assertEquals(listOf("bar"), foo)
|
|
}
|
|
|
|
@Test
|
|
fun filterNot() {
|
|
val notFoo = data.filterNot { it.startsWith("f") }
|
|
assertStaticAndRuntimeTypeIs<List<String>>(notFoo)
|
|
expect(true) { notFoo.none { it.startsWith("f") } }
|
|
expect(1) { notFoo.size }
|
|
assertEquals(listOf("bar"), notFoo)
|
|
}
|
|
|
|
@Test
|
|
fun forEach() {
|
|
var count = 0
|
|
data.forEach { count += it.length }
|
|
assertEquals(6, count)
|
|
}
|
|
|
|
@Test
|
|
fun onEach() {
|
|
var count = 0
|
|
val newData = data.onEach { count += it.length }
|
|
assertEquals(6, count)
|
|
assertTrue(data === newData)
|
|
|
|
// static types test
|
|
assertStaticTypeIs<ArrayList<Int>>(arrayListOf(1, 2, 3).onEach { })
|
|
}
|
|
|
|
@Test
|
|
fun onEachIndexed() {
|
|
var count = 0
|
|
val newData = data.onEachIndexed { i, e -> count += i + e.length }
|
|
assertEquals(7, count)
|
|
assertSame(data, newData)
|
|
|
|
// static types test
|
|
assertStaticTypeIs<ArrayList<Int>>(arrayListOf(1, 2, 3).onEachIndexed { _, _ -> })
|
|
}
|
|
|
|
@Test
|
|
fun contains() {
|
|
assertTrue(data.contains("foo"))
|
|
assertTrue("bar" in data)
|
|
assertTrue("baz" !in data)
|
|
assertFalse("baz" in empty)
|
|
}
|
|
|
|
@Test
|
|
fun single() {
|
|
assertFails { data.single() }
|
|
assertFails { empty.single() }
|
|
expect("foo") { data.single { it.startsWith("f") } }
|
|
expect("bar") { data.single { it.startsWith("b") } }
|
|
assertFails {
|
|
data.single { it.length == 3 }
|
|
}
|
|
}
|
|
|
|
@Test
|
|
fun singleOrNull() {
|
|
expect(null) { data.singleOrNull() }
|
|
expect(null) { empty.singleOrNull() }
|
|
expect("foo") { data.singleOrNull { it.startsWith("f") } }
|
|
expect("bar") { data.singleOrNull { it.startsWith("b") } }
|
|
expect(null) {
|
|
data.singleOrNull { it.length == 3 }
|
|
}
|
|
}
|
|
|
|
@Test
|
|
fun map() {
|
|
val lengths = data.map { it.length }
|
|
assertTrue {
|
|
lengths.all { it == 3 }
|
|
}
|
|
assertEquals(2, lengths.size)
|
|
assertEquals(listOf(3, 3), lengths)
|
|
}
|
|
|
|
@Test
|
|
fun flatten() {
|
|
assertEquals(listOf(0, 1, 2, 3, 0, 1, 2, 3), data.map { 0..it.length }.flatten())
|
|
}
|
|
|
|
@Test
|
|
fun mapIndexed() {
|
|
val shortened = data.mapIndexed { index, value -> value.substring(0..index) }
|
|
assertEquals(2, shortened.size)
|
|
assertEquals(listOf("f", "ba"), shortened)
|
|
}
|
|
|
|
@Test
|
|
fun withIndex() {
|
|
val indexed = data.withIndex().map { it.value.substring(0..it.index) }
|
|
assertEquals(2, indexed.size)
|
|
assertEquals(listOf("f", "ba"), indexed)
|
|
}
|
|
|
|
@Test
|
|
fun mapNotNull() {
|
|
assertEquals(listOf('o'), data.mapNotNull { it.firstOrNull { c -> c in "oui" } })
|
|
}
|
|
|
|
@Test
|
|
fun mapIndexedNotNull() {
|
|
assertEquals(listOf('b'), data.mapIndexedNotNull { index, s -> s.getOrNull(index - 1) })
|
|
}
|
|
|
|
@Test
|
|
fun maxOrNull() {
|
|
expect("foo") { data.maxOrNull() }
|
|
expect("bar") { data.maxByOrNull { it.last() } }
|
|
}
|
|
|
|
@Test
|
|
fun minOrNull() {
|
|
expect("bar") { data.minOrNull() }
|
|
expect("foo") { data.minByOrNull { it.last() } }
|
|
}
|
|
|
|
@Test
|
|
fun count() {
|
|
expect(2) { data.count() }
|
|
expect(0) { empty.count() }
|
|
|
|
expect(1) { data.count { it.startsWith("f") } }
|
|
expect(0) { empty.count { it.startsWith("f") } }
|
|
|
|
expect(0) { data.count { it.startsWith("x") } }
|
|
expect(0) { empty.count { it.startsWith("x") } }
|
|
}
|
|
|
|
@Suppress("DEPRECATION")
|
|
@Test
|
|
fun sumBy() {
|
|
expect(6) { data.sumBy { it.length } }
|
|
expect(0) { empty.sumBy { it.length } }
|
|
|
|
expect(3.0) { data.sumByDouble { it.length.toDouble() / 2 } }
|
|
expect(0.0) { empty.sumByDouble { it.length.toDouble() / 2 } }
|
|
}
|
|
|
|
@Test
|
|
fun withIndices() {
|
|
var index = 0
|
|
for ((i, d) in data.withIndex()) {
|
|
assertEquals(i, index)
|
|
assertEquals(d, data.elementAt(index))
|
|
index++
|
|
}
|
|
assertEquals(data.count(), index)
|
|
}
|
|
|
|
@Test
|
|
fun fold() {
|
|
expect(231) { data.fold(1, { a, b -> a + if (b == "foo") 200 else 30 }) }
|
|
}
|
|
|
|
@Test
|
|
fun reduce() {
|
|
val reduced = data.reduce { a, b -> a + b }
|
|
assertEquals(6, reduced.length)
|
|
assertTrue(reduced == "foobar" || reduced == "barfoo")
|
|
}
|
|
|
|
@Test
|
|
fun scan() {
|
|
val accumulators = data.scan("baz") { acc, e -> acc + e }
|
|
assertEquals(3, accumulators.size)
|
|
assertEquals("baz", accumulators.first())
|
|
assertTrue(accumulators.elementAt(1) in listOf("bazfoo", "bazbar"))
|
|
assertTrue(accumulators.last() in listOf("bazfoobar", "bazbarfoo"))
|
|
}
|
|
|
|
@Test
|
|
fun scanIndexed() {
|
|
val accumulators = data.scanIndexed("baz") { i, acc, e -> acc + i + e }
|
|
assertEquals(3, accumulators.size)
|
|
assertEquals("baz", accumulators.first())
|
|
assertTrue(accumulators.elementAt(1) in listOf("baz0foo", "baz0bar"))
|
|
assertTrue(accumulators.last() in listOf("baz0foo1bar", "baz0bar1foo"))
|
|
}
|
|
|
|
@Test
|
|
fun runningReduce() {
|
|
val accumulators = data.runningReduce { acc, e -> acc + e }
|
|
assertEquals(2, accumulators.size)
|
|
assertTrue(accumulators.first() in listOf("foo", "bar"))
|
|
assertTrue(accumulators.last() in listOf("foobar", "barfoo"))
|
|
}
|
|
|
|
@Test
|
|
fun runningReduceIndexed() {
|
|
val accumulators = data.runningReduceIndexed { i, acc, e -> acc + i + e }
|
|
assertEquals(2, accumulators.size)
|
|
assertTrue(accumulators.first() in listOf("foo", "bar"))
|
|
assertTrue(accumulators.last() in listOf("foo1bar", "bar1foo"))
|
|
}
|
|
|
|
@Test
|
|
fun mapAndJoinToString() {
|
|
val result = data.joinToString(separator = "-") { it.uppercase() }
|
|
assertEquals("FOO-BAR", result)
|
|
}
|
|
|
|
fun testPlus(doPlus: (Iterable<String>) -> List<String>) {
|
|
val result: List<String> = doPlus(data)
|
|
assertEquals(listOf("foo", "bar", "zoo", "g"), result)
|
|
assertFalse(result === data)
|
|
}
|
|
|
|
@Test
|
|
fun plusElement() = testPlus { it + "zoo" + "g" }
|
|
@Test
|
|
fun plusCollection() = testPlus { it + listOf("zoo", "g") }
|
|
@Test
|
|
fun plusArray() = testPlus { it + arrayOf("zoo", "g") }
|
|
@Test
|
|
fun plusSequence() = testPlus { it + sequenceOf("zoo", "g") }
|
|
|
|
@Test
|
|
fun plusAssign() {
|
|
// lets use a mutable variable
|
|
var result: Iterable<String> = data
|
|
result += "foo"
|
|
result += listOf("beer")
|
|
result += arrayOf("cheese", "wine")
|
|
result += sequenceOf("zoo", "g")
|
|
assertEquals(listOf("foo", "bar", "foo", "beer", "cheese", "wine", "zoo", "g"), result)
|
|
}
|
|
|
|
@Test
|
|
fun minusElement() {
|
|
val result = data - "foo" - "g"
|
|
assertEquals(listOf("bar"), result)
|
|
}
|
|
|
|
@Test
|
|
fun minusCollection() {
|
|
val result = data - listOf("foo", "g")
|
|
assertEquals(listOf("bar"), result)
|
|
}
|
|
|
|
@Test
|
|
fun minusArray() {
|
|
val result = data - arrayOf("foo", "g")
|
|
assertEquals(listOf("bar"), result)
|
|
}
|
|
|
|
@Test
|
|
fun minusSequence() {
|
|
val result = data - sequenceOf("foo", "g")
|
|
assertEquals(listOf("bar"), result)
|
|
}
|
|
|
|
@Test
|
|
@Suppress("USELESS_CAST") // remove suppress and casts when tests are finally compiled only with K2
|
|
fun minusAssign() {
|
|
// lets use a mutable variable
|
|
var result: Iterable<String> = data
|
|
result -= "foo"
|
|
assertEquals(listOf("bar"), result as? List)
|
|
result = data
|
|
result -= listOf("beer", "bar")
|
|
assertEquals(listOf("foo"), result as? List)
|
|
result = data
|
|
result -= arrayOf("bar", "foo")
|
|
assertEquals(emptyList<String>(), result as? List)
|
|
result = data
|
|
result -= sequenceOf("foo", "g")
|
|
assertEquals(listOf("bar"), result as? List)
|
|
}
|
|
|
|
}
|
|
|
|
|
|
fun <T> Iterable<T>.assertSorted(isInOrder: (T, T) -> Boolean) {
|
|
this.iterator().assertSorted(isInOrder)
|
|
}
|
|
|
|
fun <T> Iterator<T>.assertSorted(isInOrder: (T, T) -> Boolean) {
|
|
if (!hasNext()) return
|
|
var index = 0
|
|
var prev = next()
|
|
while (hasNext()) {
|
|
index += 1
|
|
val next = next()
|
|
assertTrue(isInOrder(prev, next), "Not in order at position $index, element[${index - 1}]: $prev, element[$index]: $next")
|
|
prev = next
|
|
}
|
|
return
|
|
}
|
|
|
|
data class Sortable<K : Comparable<K>>(val key: K, val index: Int) : Comparable<Sortable<K>> {
|
|
override fun compareTo(other: Sortable<K>): Int = this.key compareTo other.key
|
|
}
|
|
|
|
|
|
fun <K : Comparable<K>> Iterator<Sortable<K>>.assertStableSorted(descending: Boolean = false) {
|
|
assertSorted { a, b ->
|
|
val relation = a.key compareTo b.key
|
|
(if (descending) relation > 0 else relation < 0) || relation == 0 && a.index < b.index
|
|
}
|
|
}
|
|
|
|
fun <K : Comparable<K>> Iterable<Sortable<K>>.assertStableSorted(descending: Boolean = false) =
|
|
iterator().assertStableSorted(descending = descending)
|