Ensure stable sorting in MutableList.sort/sortWith
MutableList.sortWith now works correctly in JS_IR backend too #KT-12473
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun sort(list: MutableList<String>, comparator: (String, String) -> Int) {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun box(): String {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun box(): String {
|
||||
|
||||
@@ -109,8 +109,7 @@ private fun <T> collectionsSort(list: MutableList<T>, comparator: Comparator<in
|
||||
if (list.size <= 1) return
|
||||
|
||||
val array = copyToArray(list)
|
||||
|
||||
array.asDynamic().sort(comparator.asDynamic().compare.bind(comparator))
|
||||
sortArrayWith(array, comparator)
|
||||
|
||||
for (i in 0 until array.size) {
|
||||
list[i] = array[i]
|
||||
|
||||
@@ -1451,8 +1451,8 @@ class ArraysTest {
|
||||
for (size in listOf(10, 100, 2000)) {
|
||||
val array = Array(size) { index -> Sortable(keyRange.random(), index) }
|
||||
|
||||
array.sortedBy { it.key }.iterator().assertStableSorted()
|
||||
array.sortedByDescending { it.key }.iterator().assertStableSorted(descending = true)
|
||||
array.sortedBy { it.key }.assertStableSorted()
|
||||
array.sortedByDescending { it.key }.assertStableSorted(descending = true)
|
||||
|
||||
array.sortBy { it.key }
|
||||
array.assertStableSorted()
|
||||
@@ -1488,20 +1488,10 @@ class ArraysTest {
|
||||
}
|
||||
}
|
||||
|
||||
private 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)
|
||||
}
|
||||
|
||||
private fun <K : Comparable<K>> Array<out Sortable<K>>.assertStableSorted(descending: Boolean = false) =
|
||||
fun <K : Comparable<K>> Array<out Sortable<K>>.assertStableSorted(descending: Boolean = false) =
|
||||
iterator().assertStableSorted(descending = descending)
|
||||
|
||||
private 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
|
||||
}
|
||||
}
|
||||
|
||||
private class ArraySortedChecker<A, T>(val array: A, val comparator: Comparator<in T>) {
|
||||
public fun <R> checkSorted(sorted: A.() -> R, sortedDescending: A.() -> R, iterator: R.() -> Iterator<T>) {
|
||||
array.sorted().iterator().assertSorted { a, b -> comparator.compare(a, b) <= 0 }
|
||||
|
||||
@@ -9,7 +9,6 @@ import test.assertStaticAndRuntimeTypeIs
|
||||
import kotlin.test.*
|
||||
import test.collections.behaviors.*
|
||||
import test.comparisons.STRING_CASE_INSENSITIVE_ORDER
|
||||
import kotlin.math.sin
|
||||
import kotlin.random.Random
|
||||
|
||||
class CollectionTest {
|
||||
@@ -829,6 +828,21 @@ class CollectionTest {
|
||||
assertEquals(listOf("aa" to 20, "aa" to 3, "ab" to 3), data)
|
||||
}
|
||||
|
||||
@Test fun sortStable() {
|
||||
val keyRange = 'A'..'D'
|
||||
for (size in listOf(10, 100, 2000)) {
|
||||
val list = MutableList(size) { index -> Sortable(keyRange.random(), index) }
|
||||
|
||||
list.sorted().assertStableSorted()
|
||||
list.sortedDescending().assertStableSorted(descending = true)
|
||||
|
||||
list.sort()
|
||||
list.assertStableSorted()
|
||||
list.sortDescending()
|
||||
list.assertStableSorted(descending = true)
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun sortedBy() {
|
||||
assertEquals(listOf("two" to 3, "three" to 20), listOf("three" to 20, "two" to 3).sortedBy { it.second })
|
||||
assertEquals(listOf("three" to 20, "two" to 3), listOf("three" to 20, "two" to 3).sortedBy { it.first })
|
||||
@@ -862,6 +876,22 @@ class CollectionTest {
|
||||
expect(listOf("BAD", "dad", "cat")) { data.sortedWith(comparator.reversed().reversed()) }
|
||||
}
|
||||
|
||||
@Test fun sortByStable() {
|
||||
val keyRange = 'A'..'D'
|
||||
for (size in listOf(10, 100, 2000)) {
|
||||
val list = MutableList(size) { index -> Sortable(keyRange.random(), index) }
|
||||
|
||||
list.sortedBy { it.key }.assertStableSorted()
|
||||
list.sortedByDescending { it.key }.assertStableSorted(descending = true)
|
||||
|
||||
list.sortBy { it.key }
|
||||
list.assertStableSorted()
|
||||
|
||||
list.sortByDescending { it.key }
|
||||
list.assertStableSorted(descending = true)
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun decomposeFirst() {
|
||||
val (first) = listOf(1, 2)
|
||||
assertEquals(first, 1)
|
||||
|
||||
@@ -508,3 +508,17 @@ fun <T> Iterator<T>.assertSorted(isInOrder: (T, T) -> Boolean) {
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user