diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index a57118bc17c..a7309246aad 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -1468,6 +1468,10 @@ task sort1(type: RunKonanTest) { source = "runtime/collections/sort1.kt" } +task sortWith(type: RunKonanTest) { + source = "runtime/collections/SortWith.kt" +} + task if_else(type: RunKonanTest) { source = "codegen/branching/if_else.kt" } diff --git a/backend.native/tests/runtime/collections/SortWith.kt b/backend.native/tests/runtime/collections/SortWith.kt new file mode 100644 index 00000000000..38f0f6a98cc --- /dev/null +++ b/backend.native/tests/runtime/collections/SortWith.kt @@ -0,0 +1,83 @@ +package runtime.collections.SortWith + +import kotlin.test.* + +val correctIncreasing = Comparator { a, b -> // correct one. + when { + a > b -> 1 + a < b -> -1 + else -> 0 + } +} + +val correctDecreasing = Comparator { a, b -> // correct one. + when { + a < b -> 1 + a > b -> -1 + else -> 0 + } +} + +fun Array.assertSorted(cmp: Comparator, message: String = "") { + for (i in 1 until size) { + assertTrue(cmp.compare(this[i - 1], this[i]) <= 0, message) + } +} + +data class ComparatorInfo(val name: String, val comparator: Comparator, val isCorrect: Boolean) + +// Assert that the array is sorted in terms of a comparator only for correct/partially correct cases +val comparators = listOf( + ComparatorInfo("Correct increasing", correctIncreasing , true), + ComparatorInfo("Correct decreasing", correctDecreasing , true), + ComparatorInfo("Incorrect increasing", Comparator { a ,b -> if (a > b) 1 else -1 }, false), + ComparatorInfo("Incorrect decreasing", Comparator { a ,b -> if (a < b) 1 else -1 }, false), + ComparatorInfo("Always 1", Comparator { a ,b -> 1 }, false), + ComparatorInfo("Always -1", Comparator { a ,b -> -1 }, false), + ComparatorInfo("Alwasy 0", Comparator { a ,b -> 0 }, false) +) + +val arrays = listOf>( + arrayOf(), + arrayOf(1), + arrayOf(Int.MIN_VALUE, 0, Int.MAX_VALUE), + arrayOf(Int.MAX_VALUE, 0, Int.MIN_VALUE), + arrayOf(1, 2, 3), + arrayOf(-2, -1, 99, 1, 2), + arrayOf(90, 91, 0, 98, 99), + arrayOf(2, 1, 99, -1, 2), + arrayOf(99, 98, 0, 91, 90), + arrayOf(42, 42, 42), + arrayOf(99, 42, 0, 42, 50), + arrayOf( + 100000, 190001, 200002, 200003, 200004, 210005, 220006, 250007, 300008, 310009, 360010, 365011, + 380012, 390013, 390014, 399015, 400016, 400017, 400018, 400019, 400020, 400021, 400022, 400023, + 400024, 400025, 400026, 450027, 450028, 480029, 480030, 500031, 500032, 500033, 500034, 500035, + 500036, 500037, 500038, 500039, 500040, 500041, 500042, 500043, 500044, 500045, 500046, 500047, + 500048, 500049, 500050, 500051, 500052, 500053, 505054, 510055, 510056, 510057, 510058, 510059, + 510060, 510061, 510062, 510063, 410064, 410065, 511066, 511067, 520068, 520069, 420070, 520071, + 530072, 530073, 530074, 430075, 430076, 530077, 540078, 540079, 540080, 540081, 540082, 540083, + 540084, 490085, 540086, 540087, 542088, 544089, 546090, 550091, 550092, 550093, 550094, 590095, + 590096, 595097, 600098, 600099, 600100, 600101, 600102, 600103, 600104, 550105, 600106, 600107, + 600108, 600109, 600110, 610111, 610112, 610113, 620114, 620115, 620116, 620117, 620118, 620119, + 640120, 640121, 645122, 645123, 645124, 645125, 645126, 645127, 650128, 700129, 700130 + ) +) + + +@Test fun runTest() { + arrays.forEach { array -> + comparators.forEach { + val arrayUnderTest = array.copyOf() + + arrayUnderTest.sortWith(it.comparator) + if (it.isCorrect) { + arrayUnderTest.assertSorted(it.comparator, """ + Assert sorted failed for comparator: "${it.name}" + Array: ${array.joinToString()} + Array after sorting: ${arrayUnderTest.joinToString()} + """.trimIndent()) + } + } + } +} diff --git a/runtime/src/main/kotlin/kotlin/util/Sort.kt b/runtime/src/main/kotlin/kotlin/util/Sort.kt index 3eeff31c476..03476c9ae9d 100644 --- a/runtime/src/main/kotlin/kotlin/util/Sort.kt +++ b/runtime/src/main/kotlin/kotlin/util/Sort.kt @@ -53,34 +53,58 @@ private fun quickSort(array: Array, left: Int, right: Int) { quickSort(array, index, right) } -private fun partition( - array: Array, left: Int, right: Int, comparator: Comparator): Int { - var i = left - var j = right - val pivot = array[(left + right) / 2] - while (i <= j) { - while (comparator.compare(array[i], pivot) < 0) - i++ - while (comparator.compare(array[j], pivot) > 0) - j-- - if (i <= j) { - val tmp = array[i] - array[i] = array[j] - array[j] = tmp - i++ - j-- - } +// 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). +private fun mergeSort(array: Array, start: Int, endInclusive: Int, comparator: Comparator) { + @Suppress("UNCHECKED_CAST") + val buffer = arrayOfNulls(array.size) as Array + val result = mergeSort(array, buffer, start, endInclusive, comparator) + if (result !== array) { + result.forEachIndexed { i, v-> array[i] = v } } - return i } -private fun quickSort( - array: Array, left: Int, right: Int, comparator: Comparator) { - val index = partition(array, left, right, comparator) - if (left < index - 1) - quickSort(array, left, index - 1, comparator) - if (index < right) - quickSort(array, index, right, comparator) +// Both start and end are inclusive indices. +private fun mergeSort(array: Array, buffer: Array, start: Int, end: Int, comparator: Comparator): Array { + if (start == end) { + return array + } + + 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 + 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 (comparator.compare(leftValue, rightValue) < 0) { + 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 target } // ByteArray ============================================================================= @@ -339,7 +363,7 @@ private fun quickSort( internal fun sortArrayWith( array: Array, fromIndex: Int = 0, toIndex: Int = array.size, comparator: Comparator) { @Suppress("UNCHECKED_CAST") - quickSort(array as Array, fromIndex, toIndex - 1, comparator) + mergeSort(array as Array, fromIndex, toIndex - 1, comparator) } /**