| // 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"] |
| } |