9f9287fb2e
#KT-61751 Fixed
132 lines
3.5 KiB
Kotlin
Vendored
132 lines
3.5 KiB
Kotlin
Vendored
// TARGET_BACKEND: JVM
|
|
// WITH_STDLIB
|
|
|
|
// binary representation of fractional part of phi = (sqrt(5) - 1) / 2
|
|
private const val MAGIC: Int = 0x9E3779B9L.toInt() // ((sqrt(5.0) - 1) / 2 * pow(2.0, 32.0)).toLong().toString(16)
|
|
private const val MAX_SHIFT = 27
|
|
private const val THRESHOLD = ((1L shl 31) - 1).toInt() // 50% fill factor for speed
|
|
private val EMPTY_ARRAY = arrayOf<Any?>()
|
|
|
|
|
|
// For more details see for Knuth's multiplicative hash with golden ratio
|
|
// Shortly, we're trying to keep distribution of it uniform independently of input
|
|
// It's necessary because we use very simple linear probing
|
|
@Suppress("NOTHING_TO_INLINE")
|
|
private inline fun Any.computeHash(shift: Int) = ((hashCode() * MAGIC) ushr shift) shl 1
|
|
|
|
|
|
internal class OpenAddressLinearProbingHashTable<K : Any, V : Any> : AbstractMutableMap<K, V>() {
|
|
// fields be initialized later in `clear()`
|
|
|
|
// capacity = 1 << (32 - shift)
|
|
private var shift = 0
|
|
// keys are stored in even elements, values are in odd ones
|
|
private var array = EMPTY_ARRAY
|
|
private var size_ = 0
|
|
|
|
init {
|
|
clear()
|
|
}
|
|
|
|
override val size
|
|
get() = size_
|
|
|
|
override fun get(key: K): V? {
|
|
var i = key.computeHash(shift)
|
|
var k = array[i]
|
|
|
|
while (true) {
|
|
if (k === null) return null
|
|
@Suppress("UNCHECKED_CAST")
|
|
if (k == key) return array[i + 1] as V
|
|
if (i == 0) {
|
|
i = array.size
|
|
}
|
|
i -= 2
|
|
k = array[i]
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Never returns previous values
|
|
*/
|
|
override fun put(key: K, value: V): V? {
|
|
if (put(array, shift, key, value)) {
|
|
if (++size_ >= (THRESHOLD ushr shift)) {
|
|
rehash()
|
|
}
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
private fun rehash() {
|
|
val newShift = maxOf(shift - 3, 0)
|
|
val newArraySize = 1 shl (33 - newShift)
|
|
val newArray = arrayOfNulls<Any>(newArraySize)
|
|
|
|
var i = 0
|
|
val arraySize = array.size
|
|
while (i < arraySize) {
|
|
val key = array[i]
|
|
if (key != null) {
|
|
put(newArray, newShift, key, array[i + 1])
|
|
}
|
|
i += 2
|
|
}
|
|
|
|
shift = newShift
|
|
array = newArray
|
|
}
|
|
|
|
override fun clear() {
|
|
shift = MAX_SHIFT
|
|
array = arrayOfNulls(1 shl (33 - shift))
|
|
|
|
size_ = 0
|
|
}
|
|
|
|
override val entries: MutableSet<MutableMap.MutableEntry<K, V>>
|
|
get() {
|
|
|
|
throw IllegalStateException("OpenAddressLinearProbingHashTable::entries is not supported and hardly will be")
|
|
}
|
|
|
|
private class Entry<K, V>(override val key: K, override val value: V) : MutableMap.MutableEntry<K, V> {
|
|
override fun setValue(newValue: V): V = throw UnsupportedOperationException("This Entry is not mutable.")
|
|
}
|
|
|
|
companion object {
|
|
// Change to "true" to be able to see the contents of the map in debugger views
|
|
private const val DEBUG = false
|
|
}
|
|
}
|
|
|
|
private fun put(array: Array<Any?>, aShift: Int, key: Any, value: Any?): Boolean {
|
|
var i = key.computeHash(aShift)
|
|
|
|
while (true) {
|
|
val k = array[i]
|
|
if (k == null) {
|
|
array[i] = key
|
|
array[i + 1] = value
|
|
return true
|
|
}
|
|
if (k == key) break
|
|
if (i == 0) {
|
|
i = array.size
|
|
}
|
|
i -= 2
|
|
}
|
|
|
|
array[i + 1] = value
|
|
|
|
return false
|
|
}
|
|
|
|
fun box(): String {
|
|
val map = OpenAddressLinearProbingHashTable<String, String>()
|
|
map.put("O", "K")
|
|
return "O" + map["O"]
|
|
}
|