packages feed

data-sketches-0.4.0.0: test/CountMinSpec.hs

{-# LANGUAGE TypeApplications #-}
module CountMinSpec where

import Control.Monad (forM_)
import Data.Word
import Test.Hspec
import qualified DataSketches.Frequencies.CountMin as CM

spec :: Spec
spec = describe "Count-Min Sketch" $ do
  specify "empty sketch returns 0 for any item" $ do
    sk <- CM.mkCountMinSketch 0.001 0.01
    est <- CM.estimate sk 42
    est `shouldBe` 0

  specify "single insert returns at least 1" $ do
    sk <- CM.mkCountMinSketch 0.001 0.01
    CM.insert sk 42
    est <- CM.estimate sk 42
    est `shouldSatisfy` (>= 1)

  specify "multiple inserts of same item are counted" $ do
    sk <- CM.mkCountMinSketch 0.001 0.01
    forM_ [1..100 :: Int] $ \_ -> CM.insert sk 42
    est <- CM.estimate sk 42
    est `shouldBe` 100

  specify "insertN works correctly" $ do
    sk <- CM.mkCountMinSketch 0.001 0.01
    CM.insertN sk 42 50
    est <- CM.estimate sk 42
    est `shouldBe` 50

  specify "never undercounts" $ do
    sk <- CM.mkCountMinSketch 0.01 0.01
    forM_ [1..1000 :: Word64] $ \i -> CM.insert sk i
    -- Each item inserted once, estimate should be >= 1
    est <- CM.estimate sk 500
    est `shouldSatisfy` (>= 1)

  specify "unseen items may return small positive values" $ do
    sk <- CM.mkCountMinSketch 0.01 0.01
    forM_ [1..10000 :: Word64] $ \i -> CM.insert sk i
    -- Item 99999 was never inserted
    est <- CM.estimate sk 99999
    -- Should be 0 or a small number due to collisions
    est `shouldSatisfy` (<= 200)

  specify "heavy hitter detection" $ do
    sk <- CM.mkCountMinSketch 0.001 0.01
    -- Insert item 42 many times
    CM.insertN sk 42 10000
    -- Insert many other items once each
    forM_ [1..1000 :: Word64] $ \i -> CM.insert sk i
    est42 <- CM.estimate sk 42
    estOther <- CM.estimate sk 500
    est42 `shouldSatisfy` (> estOther)
    est42 `shouldSatisfy` (>= 10000)

  specify "merge combines two sketches" $ do
    sk1 <- CM.mkCountMinSketch 0.001 0.01
    CM.insertN sk1 42 100
    sk2 <- CM.mkCountMinSketch 0.001 0.01
    CM.insertN sk2 42 200
    CM.merge sk1 sk2
    est <- CM.estimate sk1 42
    est `shouldBe` 300

  specify "dimensions match epsilon and delta" $ do
    sk <- CM.mkCountMinSketch 0.001 0.01
    w <- CM.width sk
    w `shouldSatisfy` (> 0)
    d <- CM.depth sk
    d `shouldSatisfy` (> 0)