Skip to content

Commit

Permalink
+ lru cache
Browse files Browse the repository at this point in the history
  • Loading branch information
gciatto committed May 6, 2020
1 parent 4cae84f commit 0650aa2
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 19 deletions.
5 changes: 4 additions & 1 deletion core/src/commonMain/kotlin/it/unibo/tuprolog/utils/Cache.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
package it.unibo.tuprolog.utils

import it.unibo.tuprolog.utils.impl.LRUCache
import it.unibo.tuprolog.utils.impl.SimpleLRUCache

interface Cache<K, V> {
val capacity: Int

operator fun set(key: K, value: V): K?
operator fun set(key: K, value: V): Optional<out K>

operator fun get(key: K): V?

Expand All @@ -15,5 +16,7 @@ interface Cache<K, V> {

companion object {
fun <K, V> lru(capacity: Int = 5): Cache<K, V> = LRUCache(capacity)

fun <K, V> simpleLru(capacity: Int = 5): Cache<K, V> = SimpleLRUCache(capacity)
}
}
28 changes: 14 additions & 14 deletions core/src/commonMain/kotlin/it/unibo/tuprolog/utils/impl/LRUCache.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,16 @@ internal class LRUCache<K, V>(override val capacity: Int) : Cache<K, V> {
require(capacity > 0)
}

private val cache = HashMap<K, V>()
private val cache = mutableMapOf<K, V>()
private val insertionOrder = (0 until capacity).map { Optional.empty<K>() }.toTypedArray()
private val size get() = cache.size
private var nextFreeIndex = 0

override fun set(key: K, value: V): K? {
val evicted = insertionOrder[nextFreeIndex].value?.also {
cache.remove(it)
override fun set(key: K, value: V): Optional<out K> {
val evicted = insertionOrder[nextFreeIndex].also {
if (it is Optional.Some) {
cache.remove(it.value)
}
}
insertionOrder[nextFreeIndex] = Optional.of(key)
cache[key] = value
Expand Down Expand Up @@ -53,24 +55,22 @@ internal class LRUCache<K, V>(override val capacity: Int) : Cache<K, V> {
}

override fun toSequence(): Sequence<Pair<K, V>> {
val lri = leastRecentIndex
return (0 until size).asSequence()
.map { (it + lri) % capacity }
.map { insertionOrder[it] }
.take(cache.size)
val indexes = if (size < capacity) {
(0 until size).asSequence()
} else {
(0 until capacity).asSequence()
.map { (it + nextFreeIndex) % capacity }
}
return indexes.map { insertionOrder[it] }
.filterIsInstance<Optional.Some<K>>()
.map { it.value }
.map { it to cache[it]!! }
}

override fun toString(): String {
return "LRUCache(${toSequence().map { "${it.first} = ${it.second}" }.joinToString(",")})"
return "LRUCache(${toSequence().map { "${it.first} = ${it.second}" }.joinToString(", ")})"
}


private val leastRecentIndex: Int
get() = (nextFreeIndex + capacity + 1) % capacity

// private fun getKeyToEvict(): K? =
// insertionOrder[(nextFreeIndex + size) % capacity].value
//
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package it.unibo.tuprolog.utils.impl

import it.unibo.tuprolog.utils.Cache
import it.unibo.tuprolog.utils.Optional

internal class SimpleLRUCache<K, V>(override val capacity: Int) : Cache<K, V> {

init {
require(capacity > 0)
}

private val cache = LinkedHashMap<K, V>()

override fun set(key: K, value: V): Optional<out K> {
val evicted = removeLeastRecentIfNecessary()
cache[key] = value
return evicted
}

private fun removeLeastRecent(): Optional<out K> {
val key = cache.iterator().next().key
cache.remove(key)
return Optional.of(key)
}

private fun removeLeastRecentIfNecessary(): Optional<out K> {
return if (cache.size > capacity) {
removeLeastRecent()
} else {
return Optional.empty()
}
}

override fun get(key: K): V? =
cache[key]

override fun toMap(): Map<K, V> =
cache.toMap()

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || this::class != other::class) return false

other as SimpleLRUCache<*, *>

if (capacity != other.capacity) return false
if (cache != other.cache) return false

return true
}

override fun hashCode(): Int {
var result = capacity
result = 31 * result + cache.hashCode()
return result
}

override fun toSequence(): Sequence<Pair<K, V>> {
return cache.entries.asSequence().map { it.toPair() }
}

override fun toString(): String {
return "SimpleLRUCache(${toSequence().map { "${it.first} = ${it.second}" }.joinToString(", ")})"
}
}
17 changes: 13 additions & 4 deletions core/src/jvmTest/kotlin/LRUCacheTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,32 @@ import org.junit.Test
class LRUCacheTest {
@Test
fun test() {
val cache = Cache.lru<String, Int>(4)
val cache = Cache.simpleLru<String, Int>(4)
println(cache)
println(cache.toMap())
cache["A"] = 1
println(cache)
println(cache.toMap())
cache["B"] = 2
println(cache)
println(cache.toMap())
cache["C"] = 3
println(cache)
println(cache.toMap())
cache["D"] = 4
println(cache)
println(cache.toMap())
cache["E"] = 5
println(cache)
cache["F"] = 5
println(cache.toMap())
cache["F"] = 6
println(cache)
cache["G"] = 6
println(cache.toMap())
cache["G"] = 7
println(cache)
cache["H"] = 7
println(cache.toMap())
cache["H"] = 8
println(cache)
println(cache.toMap())
}
}

0 comments on commit 0650aa2

Please sign in to comment.