data-sketches-0.4.0.0: test/BugFixSpec.hs
{-# LANGUAGE TypeApplications #-}
module BugFixSpec where
import Control.Monad (forM_, replicateM_)
import Control.Monad.Primitive (PrimState)
import Data.Word
import qualified Data.Vector.Unboxed.Mutable as MUVector
import Test.Hspec
import DataSketches.Quantiles.RelativeErrorQuantile hiding (null, minimum, maximum)
import qualified DataSketches.Quantiles.RelativeErrorQuantile as REQ
import DataSketches.Quantiles.RelativeErrorQuantile.Types
import DataSketches.Quantiles.RelativeErrorQuantile.Internal.DoubleBuffer
spec :: Spec
spec = do
describe "Bug fix: off-by-one in getCountWithCriterion (spaceAtBottom=False)" $ do
-- These tests exercise the Haskell-internal DoubleBuffer directly.
specify "getCountWithCriterion reads only within active region (spaceAtBottom=False)" $ do
buf <- mkBuffer 16 0 False
mapM_ (append buf) [10, 20, 30, 40, 50]
sort buf
vec <- getVector buf
forM_ [5..15] $ \i -> MUVector.unsafeWrite vec i 0
cnt <- getCountWithCriterion buf 55 (:<)
cnt `shouldBe` 5
specify "getCountWithCriterion at upper boundary (spaceAtBottom=False)" $ do
buf <- mkBuffer 16 0 False
mapM_ (append buf) [10, 20, 30, 40, 50]
sort buf
vec <- getVector buf
forM_ [5..15] $ \i -> MUVector.unsafeWrite vec i 25
cnt <- getCountWithCriterion buf 50 (:<)
cnt `shouldBe` 4
cnt2 <- getCountWithCriterion buf 50 (:<=)
cnt2 `shouldBe` 5
specify "getCountWithCriterion at lower boundary (spaceAtBottom=False)" $ do
buf <- mkBuffer 16 0 False
mapM_ (append buf) [10, 20, 30, 40, 50]
sort buf
vec <- getVector buf
forM_ [5..15] $ \i -> MUVector.unsafeWrite vec i 0
cnt <- getCountWithCriterion buf 5 (:<)
cnt `shouldBe` 0
cnt2 <- getCountWithCriterion buf 10 (:<)
cnt2 `shouldBe` 0
cnt3 <- getCountWithCriterion buf 10 (:<=)
cnt3 `shouldBe` 1
specify "rank of value above max is correct with LowRanksAreAccurate" $ do
sk <- mkReqSketch 12 LowRanksAreAccurate
forM_ [1..50 :: Double] $ insert sk
r <- rank sk 100
r `shouldBe` 1.0
specify "rank of value below min is 0 with LowRanksAreAccurate" $ do
sk <- mkReqSketch 12 LowRanksAreAccurate
forM_ [10..60 :: Double] $ insert sk
r <- rank sk 5
r `shouldBe` 0.0
specify "ranks are correct for known values with LowRanksAreAccurate" $ do
sk <- mkReqSketch 50 LowRanksAreAccurate
let values = [5, 5, 5, 6, 6, 6, 7, 8, 8, 8 :: Double]
mapM_ (insert sk) values
r5 <- rank sk 5
r5 `shouldBe` 0.0
r6 <- rank sk 6
r6 `shouldBe` 0.3
r7 <- rank sk 7
r7 `shouldBe` 0.6
r8 <- rank sk 8
r8 `shouldBe` 0.7
r9 <- rank sk 9
r9 `shouldBe` 1.0
describe "Bug fix: growUntil in merge didn't loop" $ do
specify "merge into empty sketch preserves count from multi-level source" $ do
skBig <- mkReqSketch 6 HighRanksAreAccurate
forM_ [1..2000 :: Double] $ insert skBig
bigLevels <- numLevels skBig
bigLevels `shouldSatisfy` (>= 3)
skSmall <- mkReqSketch 6 HighRanksAreAccurate
_ <- merge skSmall skBig
mergedCount <- count skSmall
mergedCount `shouldBe` 2000
smallLevels <- numLevels skSmall
smallLevels `shouldSatisfy` (>= bigLevels)
specify "merge sketch with 4+ levels into 1-level sketch" $ do
skSrc <- mkReqSketch 6 HighRanksAreAccurate
forM_ [1..5000 :: Double] $ insert skSrc
srcLevels <- numLevels skSrc
srcLevels `shouldSatisfy` (>= 4)
skDst <- mkReqSketch 6 HighRanksAreAccurate
insert skDst 0
dstLevelsBefore <- numLevels skDst
dstLevelsBefore `shouldBe` 1
_ <- merge skDst skSrc
dstLevelsAfter <- numLevels skDst
dstLevelsAfter `shouldSatisfy` (>= srcLevels)
mergedCount <- count skDst
mergedCount `shouldBe` 5001
describe "Bug fix: max value comparison inverted in merge" $ do
specify "merge updates maximum when other has larger max" $ do
sk1 <- mkReqSketch 12 HighRanksAreAccurate
forM_ [1..10 :: Double] $ insert sk1
max1 <- REQ.maximum sk1
max1 `shouldBe` 10.0
sk2 <- mkReqSketch 12 HighRanksAreAccurate
forM_ [100..200 :: Double] $ insert sk2
_ <- merge sk1 sk2
mergedMax <- REQ.maximum sk1
mergedMax `shouldBe` 200.0
specify "merge does NOT update maximum when other has smaller max" $ do
sk1 <- mkReqSketch 12 HighRanksAreAccurate
forM_ [100..200 :: Double] $ insert sk1
sk2 <- mkReqSketch 12 HighRanksAreAccurate
forM_ [1..10 :: Double] $ insert sk2
_ <- merge sk1 sk2
mergedMax <- REQ.maximum sk1
mergedMax `shouldBe` 200.0
specify "merge with bug would have produced wrong max (both directions)" $ do
sk1 <- mkReqSketch 12 HighRanksAreAccurate
mapM_ (insert sk1) [1, 2, 3 :: Double]
sk2 <- mkReqSketch 12 HighRanksAreAccurate
mapM_ (insert sk2) [10, 20, 30 :: Double]
_ <- merge sk1 sk2
mergedMax <- REQ.maximum sk1
mergedMax `shouldBe` 30.0
mergedMin <- REQ.minimum sk1
mergedMin `shouldBe` 1.0