[stdlib] Use merge sort for comparables too
This commit is contained in:
@@ -1468,8 +1468,12 @@ task sort1(type: RunKonanTest) {
|
||||
source = "runtime/collections/sort1.kt"
|
||||
}
|
||||
|
||||
task sortWith(type: RunKonanTest) {
|
||||
// TODO: Enable devirtualization and make the test not standalone
|
||||
// when KT-26315 is fixed.
|
||||
task sortWith(type: RunStandaloneKonanTest) {
|
||||
source = "runtime/collections/SortWith.kt"
|
||||
flags = ['-tr', '--disable', 'devirtualization']
|
||||
arguments = ['--ktest_logger=SILENT']
|
||||
}
|
||||
|
||||
task if_else(type: RunKonanTest) {
|
||||
|
||||
@@ -24,8 +24,18 @@ fun Array<Int>.assertSorted(cmp: Comparator<Int>, message: String = "") {
|
||||
}
|
||||
}
|
||||
|
||||
fun Array<MyComparable>.assertSorted(message: String = "") {
|
||||
for (i in 1 until size) {
|
||||
assertTrue(this[i - 1] <= this[i], message)
|
||||
}
|
||||
}
|
||||
|
||||
data class ComparatorInfo(val name: String, val comparator: Comparator<Int>, val isCorrect: Boolean)
|
||||
|
||||
class MyComparable (val value: Int, val comparator: Comparator<Int>): Comparable<MyComparable> {
|
||||
override fun compareTo(other: MyComparable): Int = comparator.compare(value, other.value)
|
||||
}
|
||||
|
||||
// Assert that the array is sorted in terms of a comparator only for correct/partially correct cases
|
||||
val comparators = listOf<ComparatorInfo>(
|
||||
ComparatorInfo("Correct increasing", correctIncreasing , true),
|
||||
@@ -68,8 +78,8 @@ val arrays = listOf<Array<Int>>(
|
||||
@Test fun runTest() {
|
||||
arrays.forEach { array ->
|
||||
comparators.forEach {
|
||||
// Test with custom comparator
|
||||
val arrayUnderTest = array.copyOf()
|
||||
|
||||
arrayUnderTest.sortWith(it.comparator)
|
||||
if (it.isCorrect) {
|
||||
arrayUnderTest.assertSorted(it.comparator, """
|
||||
@@ -78,6 +88,20 @@ val arrays = listOf<Array<Int>>(
|
||||
Array after sorting: ${arrayUnderTest.joinToString()}
|
||||
""".trimIndent())
|
||||
}
|
||||
|
||||
// Test of a custom comparable
|
||||
val comparableArrayUnderTest = Array(array.size) { i ->
|
||||
MyComparable(array[i], it.comparator)
|
||||
}
|
||||
comparableArrayUnderTest.sort()
|
||||
|
||||
if (it.isCorrect) {
|
||||
comparableArrayUnderTest.assertSorted("""
|
||||
Assert sorted failed for Comparable: "${it.name}"
|
||||
Array: ${array.joinToString()}
|
||||
Array after sorting: ${comparableArrayUnderTest.joinToString()}
|
||||
""".trimIndent())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,41 +20,64 @@ import kotlin.comparisons.*
|
||||
|
||||
// TODO: Implement sort for primitives with a custom comparator.
|
||||
|
||||
// Yes, rather naive qsort for now.
|
||||
// Array<T> =============================================================================
|
||||
private fun <T> partition(array: Array<T>, left: Int, right: Int): Int {
|
||||
var i = left
|
||||
var j = right
|
||||
// We use merge becuase the quick sort may cause segfaults if
|
||||
// the comparator or the comparable implementation is incorrect (e.g. if it never returns 0).
|
||||
|
||||
// Sort of comparables.
|
||||
private fun <T: Comparable<T>> mergeSort(array: Array<T>, start: Int, endInclusive: Int) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val pivot = array[(left + right) / 2] as Comparable<T>
|
||||
while (i <= j) {
|
||||
while (pivot.compareTo(array[i]) > 0) {
|
||||
i++
|
||||
}
|
||||
while (pivot.compareTo(array[j]) < 0) {
|
||||
j--
|
||||
}
|
||||
if (i <= j) {
|
||||
val tmp = array[i]
|
||||
array[i] = array[j]
|
||||
array[j] = tmp
|
||||
i++
|
||||
j--
|
||||
val buffer = arrayOfNulls<Any?>(array.size) as Array<T>
|
||||
val result = mergeSort(array, buffer, start, endInclusive)
|
||||
if (result !== array) {
|
||||
result.forEachIndexed { i, v-> array[i] = v }
|
||||
}
|
||||
}
|
||||
|
||||
// Both start and end are inclusive indices.
|
||||
private fun <T: Comparable<T>> mergeSort(array: Array<T>, buffer: Array<T>, start: Int, end: Int): Array<T> {
|
||||
if (start == end) {
|
||||
return array
|
||||
}
|
||||
|
||||
val median = (start + end) / 2
|
||||
val left = mergeSort(array, buffer, start, median)
|
||||
val right = mergeSort(array, buffer, median + 1, end)
|
||||
|
||||
val target = if (left === buffer) array else buffer
|
||||
|
||||
// Merge
|
||||
var leftIndex = start
|
||||
var rightIndex = median + 1
|
||||
for (i in start..end) {
|
||||
when {
|
||||
leftIndex <= median && rightIndex <= end -> {
|
||||
val leftValue = left[leftIndex]
|
||||
val rightValue = right[rightIndex]
|
||||
|
||||
if (leftValue < rightValue) {
|
||||
target[i] = leftValue
|
||||
leftIndex++
|
||||
} else {
|
||||
target[i] = rightValue
|
||||
rightIndex++
|
||||
}
|
||||
}
|
||||
leftIndex <= median -> {
|
||||
target[i] = left[leftIndex]
|
||||
leftIndex++
|
||||
}
|
||||
else /* rightIndex <= end */ -> {
|
||||
target[i] = right[rightIndex]
|
||||
rightIndex++
|
||||
}
|
||||
}
|
||||
}
|
||||
return i
|
||||
|
||||
return target
|
||||
}
|
||||
|
||||
private fun <T> quickSort(array: Array<T>, left: Int, right: Int) {
|
||||
val index = partition(array, left, right)
|
||||
if (left < index - 1)
|
||||
quickSort(array, left, index - 1)
|
||||
if (index < right)
|
||||
quickSort(array, index, right)
|
||||
}
|
||||
|
||||
// We use merge sort with comparators becuase the quick sort may cause segfaults if
|
||||
// the comparator is incorrect (e.g. if it never returns 0).
|
||||
// Sort with comparator
|
||||
private fun <T> mergeSort(array: Array<T>, start: Int, endInclusive: Int, comparator: Comparator<T>) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val buffer = arrayOfNulls<Any?>(array.size) as Array<T>
|
||||
@@ -73,7 +96,7 @@ private fun <T> mergeSort(array: Array<T>, buffer: Array<T>, start: Int, end: In
|
||||
val median = (start + end) / 2
|
||||
val left = mergeSort(array, buffer, start, median, comparator)
|
||||
val right = mergeSort(array, buffer, median + 1, end, comparator)
|
||||
|
||||
|
||||
val target = if (left === buffer) array else buffer
|
||||
|
||||
// Merge
|
||||
@@ -370,9 +393,9 @@ internal fun <T> sortArrayWith(
|
||||
* Sorts a subarray of [Comparable] elements specified by [fromIndex] (inclusive) and
|
||||
* [toIndex] (exclusive) parameters using the qsort algorithm.
|
||||
*/
|
||||
internal fun <T> sortArrayComparable(array: Array<out T>) {
|
||||
internal fun <T: Comparable<T>> sortArrayComparable(array: Array<out T>) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
quickSort(array as Array<T>, 0, array.size - 1)
|
||||
mergeSort(array as Array<T>, 0, array.size - 1)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user