Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SummingWithHitsCache class to also track key hits. #410

Merged
merged 4 commits into from Feb 6, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -33,7 +33,7 @@ object SummingCache {
/**
* A Stateful Summer on Map[K,V] that keeps a cache of recent keys
*/
class SummingCache[K, V] private (capacity: Int)(implicit sgv: Semigroup[V])
class SummingCache[K, V](capacity: Int)(implicit sgv: Semigroup[V])
extends StatefulSummer[Map[K, V]] {

require(capacity >= 0, "Cannot have negative capacity in SummingIterator")
Expand Down Expand Up @@ -76,3 +76,18 @@ class SummingCache[K, V] private (capacity: Int)(implicit sgv: Semigroup[V])
}
}).asScala
}

object SummingWithHitsCache {
def apply[K, V: Semigroup](cap: Int): SummingWithHitsCache[K, V] = new SummingWithHitsCache[K, V](cap)
}
/**
* A SummingCache that also tracks the number of key hits
*/
class SummingWithHitsCache[K, V] (capacity: Int)(implicit sgv: Semigroup[V])
extends SummingCache[K, V](capacity)(sgv) {

def putWithHits(m: Map[K, V]): (Int, Option[Map[K, V]]) = {
val keyHits = m.keys.count(cache.contains)
(keyHits, put(m))
}
}
Expand Up @@ -53,6 +53,51 @@ class SummingCacheTest extends PropSpec with PropertyChecks with Matchers {
}
}

class SummingWithHitsCacheTest extends SummingCacheTest {
import SummingCacheTest._

val RAND = new scala.util.Random

def getHits[K, V: Monoid](c: Capacity, items: List[(K, V)]) = {
val sc = SummingWithHitsCache[K, V](c.cap)
val mitems = items.map { Map(_) }
mitems.map { sc.putWithHits(_)._1 }.tail
}

property("hit rates will always be 1 for stream with the same key") {
forAll { (c: Capacity, values: List[Int]) =>
// Only run this when we have at least 2 items and non-zero cap
if (values.size > 1 && c.cap > 1) {
val key = RAND.nextInt
val items = values.map { (key, _) }
val keyHits = getHits(c, items)
assert(keyHits.filter{ _ != 1 }.size == 0)
}
}
}

property("hit rates will always be 0 when cap is 0") {
forAll { items: List[(Int, Int)] =>
// Only run this when we have at least 2 items
if (items.size > 1) {
val keyHits = getHits(Capacity(0), items)
assert(keyHits.filter{ _ != 0 }.size == 0)
}
}
}

property("hit rates in general should be between [0, 1] ") {
forAll { (c: Capacity, items: List[(Int, Int)]) =>
// Only run this when we have at least 2 items
if (items.size > 1) {
val keyHits = getHits(c, items)
val hitRate = keyHits.sum / items.size.toDouble
assert(hitRate >= 0 && hitRate <= 1)
}
}
}
}

class SummingQueueTest extends PropSpec with PropertyChecks with Matchers {
val zeroCapQueue = SummingQueue[Int](0) // passes all through

Expand Down