Implement binarySearch method for UArrays (KT-27262)

This commit is contained in:
Abduqodiri Qurbonzoda
2019-01-23 18:47:36 +03:00
committed by Ilya Gorbunov
parent 6cd9858147
commit 550c74bb6b
4 changed files with 292 additions and 1 deletions
@@ -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
}
@@ -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 <A, E> testFailures(array: A, binarySearch: A.(E, Int, Int) -> Int, element: E, arraySize: Int) {
assertFailsWith<IllegalArgumentException> {
array.binarySearch(element, 1, 0)
}
assertFailsWith<IndexOutOfBoundsException> {
array.binarySearch(element, 1, arraySize + 1)
}
assertFailsWith<IndexOutOfBoundsException> {
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 <A, E> test(
array: A,
binarySearch: A.(E, Int, Int) -> Int,
operations: List<OperationOnRange<UInt, Int>>,
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<E, R>(
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() }
}
@@ -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
@@ -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<in T>, fromIndex: Int = 0, toIndex: Int = size)") {