From 550c74bb6bb4e0998bd02ae451582d08cb28f047 Mon Sep 17 00:00:00 2001 From: Abduqodiri Qurbonzoda Date: Wed, 23 Jan 2019 18:47:36 +0300 Subject: [PATCH] Implement binarySearch method for UArrays (KT-27262) --- .../stdlib/jvm/src/generated/_UArraysJvm.kt | 144 ++++++++++++++++++ .../jvm/test/collections/UArrayJVMTest.kt | 90 +++++++++++ .../kotlin-stdlib-runtime-merged.txt | 8 + .../kotlin-stdlib-gen/src/templates/Arrays.kt | 51 ++++++- 4 files changed, 292 insertions(+), 1 deletion(-) create mode 100644 libraries/stdlib/jvm/src/generated/_UArraysJvm.kt create mode 100644 libraries/stdlib/jvm/test/collections/UArrayJVMTest.kt diff --git a/libraries/stdlib/jvm/src/generated/_UArraysJvm.kt b/libraries/stdlib/jvm/src/generated/_UArraysJvm.kt new file mode 100644 index 00000000000..8982f688630 --- /dev/null +++ b/libraries/stdlib/jvm/src/generated/_UArraysJvm.kt @@ -0,0 +1,144 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +@file:kotlin.jvm.JvmMultifileClass +@file:kotlin.jvm.JvmName("UArraysKt") + +package kotlin.collections + +// +// NOTE: THIS FILE IS AUTO-GENERATED by the GenerateStandardLib.kt +// See: https://github.com/JetBrains/kotlin/tree/master/libraries/stdlib +// + + +/** + * Searches the array or the range of the array for the provided [element] using the binary search algorithm. + * The array is expected to be sorted, otherwise the result is undefined. + * + * If the array contains multiple elements equal to the specified [element], there is no guarantee which one will be found. + * + * @return the index of the element, if it is contained in the array within the specified range; + * otherwise, the inverted insertion point `(-insertion point - 1)`. + * The insertion point is defined as the index at which the element should be inserted, + * so that the array (or the specified subrange of array) still remains sorted. + */ +@SinceKotlin("1.3") +@ExperimentalUnsignedTypes +public fun UIntArray.binarySearch(element: UInt, fromIndex: Int = 0, toIndex: Int = size): Int { + AbstractList.checkRangeIndexes(fromIndex, toIndex, size) + val signedElement = element.toInt() + var low = fromIndex + var high = toIndex - 1 + while (low <= high) { + val mid = (low + high).ushr(1) // safe from overflows + val midVal = storage[mid] + val cmp = uintCompare(midVal, signedElement) + if (cmp < 0) + low = mid + 1 + else if (cmp > 0) + high = mid - 1 + else + return mid // key found + } + return -(low + 1) // key not found +} + +/** + * Searches the array or the range of the array for the provided [element] using the binary search algorithm. + * The array is expected to be sorted, otherwise the result is undefined. + * + * If the array contains multiple elements equal to the specified [element], there is no guarantee which one will be found. + * + * @return the index of the element, if it is contained in the array within the specified range; + * otherwise, the inverted insertion point `(-insertion point - 1)`. + * The insertion point is defined as the index at which the element should be inserted, + * so that the array (or the specified subrange of array) still remains sorted. + */ +@SinceKotlin("1.3") +@ExperimentalUnsignedTypes +public fun ULongArray.binarySearch(element: ULong, fromIndex: Int = 0, toIndex: Int = size): Int { + AbstractList.checkRangeIndexes(fromIndex, toIndex, size) + val signedElement = element.toLong() + var low = fromIndex + var high = toIndex - 1 + while (low <= high) { + val mid = (low + high).ushr(1) // safe from overflows + val midVal = storage[mid] + val cmp = ulongCompare(midVal, signedElement) + if (cmp < 0) + low = mid + 1 + else if (cmp > 0) + high = mid - 1 + else + return mid // key found + } + return -(low + 1) // key not found +} + +/** + * Searches the array or the range of the array for the provided [element] using the binary search algorithm. + * The array is expected to be sorted, otherwise the result is undefined. + * + * If the array contains multiple elements equal to the specified [element], there is no guarantee which one will be found. + * + * @return the index of the element, if it is contained in the array within the specified range; + * otherwise, the inverted insertion point `(-insertion point - 1)`. + * The insertion point is defined as the index at which the element should be inserted, + * so that the array (or the specified subrange of array) still remains sorted. + */ +@SinceKotlin("1.3") +@ExperimentalUnsignedTypes +public fun UByteArray.binarySearch(element: UByte, fromIndex: Int = 0, toIndex: Int = size): Int { + AbstractList.checkRangeIndexes(fromIndex, toIndex, size) + val signedElement = element.toInt() + var low = fromIndex + var high = toIndex - 1 + while (low <= high) { + val mid = (low + high).ushr(1) // safe from overflows + val midVal = storage[mid] + val cmp = uintCompare(midVal.toInt(), signedElement) + if (cmp < 0) + low = mid + 1 + else if (cmp > 0) + high = mid - 1 + else + return mid // key found + } + return -(low + 1) // key not found +} + +/** + * Searches the array or the range of the array for the provided [element] using the binary search algorithm. + * The array is expected to be sorted, otherwise the result is undefined. + * + * If the array contains multiple elements equal to the specified [element], there is no guarantee which one will be found. + * + * @return the index of the element, if it is contained in the array within the specified range; + * otherwise, the inverted insertion point `(-insertion point - 1)`. + * The insertion point is defined as the index at which the element should be inserted, + * so that the array (or the specified subrange of array) still remains sorted. + */ +@SinceKotlin("1.3") +@ExperimentalUnsignedTypes +public fun UShortArray.binarySearch(element: UShort, fromIndex: Int = 0, toIndex: Int = size): Int { + AbstractList.checkRangeIndexes(fromIndex, toIndex, size) + val signedElement = element.toInt() + var low = fromIndex + var high = toIndex - 1 + while (low <= high) { + val mid = (low + high).ushr(1) // safe from overflows + val midVal = storage[mid] + val cmp = uintCompare(midVal.toInt(), signedElement) + if (cmp < 0) + low = mid + 1 + else if (cmp > 0) + high = mid - 1 + else + return mid // key found + } + return -(low + 1) // key not found +} + diff --git a/libraries/stdlib/jvm/test/collections/UArrayJVMTest.kt b/libraries/stdlib/jvm/test/collections/UArrayJVMTest.kt new file mode 100644 index 00000000000..8e6dba314e7 --- /dev/null +++ b/libraries/stdlib/jvm/test/collections/UArrayJVMTest.kt @@ -0,0 +1,90 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. 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 kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith + +class UArrayJVMTest { + @Test + fun binarySearch() { + + fun testFailures(array: A, binarySearch: A.(E, Int, Int) -> Int, element: E, arraySize: Int) { + assertFailsWith { + array.binarySearch(element, 1, 0) + } + assertFailsWith { + array.binarySearch(element, 1, arraySize + 1) + } + assertFailsWith { + array.binarySearch(element, -1, 1) + } + } + + val array = uintArrayOf(1u, 0u, 4u, 5u, 8u, 12u, 2u, 3u, 7u, 7u, 7u, 9u, 2u) + + testFailures(array.toUByteArray(), UByteArray::binarySearch, 0u, array.size) + testFailures(array.toUShortArray(), UShortArray::binarySearch, 0u, array.size) + testFailures(array, UIntArray::binarySearch, 0u, array.size) + testFailures(array.toULongArray(), ULongArray::binarySearch, 0u, array.size) + + fun test( + array: A, + binarySearch: A.(E, Int, Int) -> Int, + operations: List>, + transform: UInt.() -> E + ) { + operations.forEach { o -> + val result = array.binarySearch(o.element.transform(), o.fromIndex, o.toIndex) + assertEquals(o.expectedResult, result) + } + } + + val operations = listOf( + OperationOnRange(0u, 1, 6, 1), + OperationOnRange(12u, 1, 6, 5), + OperationOnRange(8u, 1, 6, 4), + OperationOnRange(4u, 1, 6, 2), + OperationOnRange(5u, 2, 6, 3), + OperationOnRange(8u, 2, 5, 4), + OperationOnRange(5u, 3, 4, 3), + + OperationOnRange(5u, 3, 3, -4), + OperationOnRange(7u, 1, 6, -5), + OperationOnRange(8u, 6, 12, -12), + OperationOnRange(5u, 6, 12, -9), + + OperationOnRange(7u, 6, 12, 8) + ) + + test(array.toUByteArray(), UByteArray::binarySearch, operations, UInt::toUByte) + test(array.toUShortArray(), UShortArray::binarySearch, operations, UInt::toUShort) + test(array, UIntArray::binarySearch, operations, UInt::toUInt) + test(array.toULongArray(), ULongArray::binarySearch, operations, UInt::toULong) + } +} + + +private class OperationOnRange( + val element: E, + val fromIndex: Int, + val toIndex: Int, + val expectedResult: R +) + + +private fun UIntArray.toUByteArray(): UByteArray { + return UByteArray(size) { get(it).toUByte() } +} + +private fun UIntArray.toUShortArray(): UShortArray { + return UShortArray(size) { get(it).toUShort() } +} + +private fun UIntArray.toULongArray(): ULongArray { + return ULongArray(size) { get(it).toULong() } +} diff --git a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt index e0101ddada7..ce60bc5ef3e 100644 --- a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt +++ b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt @@ -2287,6 +2287,14 @@ public abstract class kotlin/collections/ShortIterator : java/util/Iterator, kot } public final class kotlin/collections/UArraysKt { + public static final fun binarySearch-2fe2U9s ([IIII)I + public static synthetic fun binarySearch-2fe2U9s$default ([IIIIILjava/lang/Object;)I + public static final fun binarySearch-EtDCXyQ ([SSII)I + public static synthetic fun binarySearch-EtDCXyQ$default ([SSIIILjava/lang/Object;)I + public static final fun binarySearch-K6DWlUc ([JJII)I + public static synthetic fun binarySearch-K6DWlUc$default ([JJIIILjava/lang/Object;)I + public static final fun binarySearch-WpHrYlw ([BBII)I + public static synthetic fun binarySearch-WpHrYlw$default ([BBIIILjava/lang/Object;)I public static final fun contentEquals-ctEhBpI ([I[I)Z public static final fun contentEquals-kdPth3s ([B[B)Z public static final fun contentEquals-mazbYpA ([S[S)Z diff --git a/libraries/tools/kotlin-stdlib-gen/src/templates/Arrays.kt b/libraries/tools/kotlin-stdlib-gen/src/templates/Arrays.kt index 19c750b4e53..46ae9416daa 100644 --- a/libraries/tools/kotlin-stdlib-gen/src/templates/Arrays.kt +++ b/libraries/tools/kotlin-stdlib-gen/src/templates/Arrays.kt @@ -1218,7 +1218,7 @@ object ArrayOps : TemplateGroupBase() { val f_binarySearch = fn("binarySearch(element: T, fromIndex: Int = 0, toIndex: Int = size)") { platforms(Platform.JVM) - include(ArraysOfObjects, ArraysOfPrimitives) + include(ArraysOfObjects, ArraysOfPrimitives, ArraysOfUnsigned) exclude(PrimitiveType.Boolean) } builder { doc { @@ -1238,6 +1238,55 @@ object ArrayOps : TemplateGroupBase() { body { "return java.util.Arrays.binarySearch(this, fromIndex, toIndex, element)" } + + specialFor(ArraysOfUnsigned) { + val elementConversion: String + val midValConversion: String + val compareFunction: String + when (primitive!!) { + PrimitiveType.UByte, PrimitiveType.UShort -> { + elementConversion = ".toInt()" + midValConversion = ".toInt()" + compareFunction = "uintCompare" + } + PrimitiveType.UInt -> { + elementConversion = ".toInt()" + midValConversion = "" + compareFunction = "uintCompare" + } + PrimitiveType.ULong -> { + elementConversion = ".toLong()" + midValConversion = "" + compareFunction = "ulongCompare" + } + else -> error(primitive!!) + } + + + body { + """ + AbstractList.checkRangeIndexes(fromIndex, toIndex, size) + + val signedElement = element$elementConversion + var low = fromIndex + var high = toIndex - 1 + + while (low <= high) { + val mid = (low + high).ushr(1) // safe from overflows + val midVal = storage[mid] + val cmp = $compareFunction(midVal$midValConversion, signedElement) + + if (cmp < 0) + low = mid + 1 + else if (cmp > 0) + high = mid - 1 + else + return mid // key found + } + return -(low + 1) // key not found + """ + } + } } val f_binarySearch_comparator = fn("binarySearch(element: T, comparator: Comparator, fromIndex: Int = 0, toIndex: Int = size)") {