[stdlib] Use merge sort for comparables too

This commit is contained in:
Ilya Matveev
2018-08-22 16:38:26 +07:00
committed by ilmat192
parent 04c0cd3dd4
commit 902b9f6f48
3 changed files with 85 additions and 34 deletions
+5 -1
View File
@@ -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())
}
}
}
}
+55 -32
View File
@@ -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)
}
/**