packages feed

data-sketches 0.4.0.0 → 0.4.0.1

raw patch · 10 files changed

+246/−252 lines, 10 filesdep −hspec-junit-formatterdep −mtldep −mwc-randomdep ~data-sketches-core

Dependencies removed: hspec-junit-formatter, mtl, mwc-random, vector-algorithms

Dependency ranges changed: data-sketches-core

Files

ChangeLog.md view
@@ -1,5 +1,36 @@ # Changelog for data-sketches +## 0.4.0.1 — 2026-04-08++### Internal cleanup++- Removed dead pure-Haskell REQ internals that were superseded by the C+  backend in 0.4.0.0. No public API changes — `insert`, `quantile`, `rank`,+  `countWithCriterion`, and all other exports continue to work identically.++- Dropped unused `mwc-random`, `vector-algorithms`, `mtl`, and+  `hspec-junit-formatter` dependencies.++### Performance++- HLL `estimate` is ~2.6x faster (C backend: `ldexp` replaced with bit+  manipulation; NEON/SSE2 intrinsics for merge and zero-counting).++- Added estimate, merge, and query benchmarks for HLL, KLL, and Count-Min+  sketches (criterion suite).++### Test improvements++- Added `countWithCriterion` coverage: LT/LE threshold counting, boundary+  behavior at min/max, duplicate values, estimation mode, and non-finite+  exception handling.++- Added quantile monotonicity tests: verifies `quantile(r1) <= quantile(r2)`+  for `r1 <= r2` in exact mode, HRA estimation mode, and LRA estimation mode.++- Added estimation mode capacity growth tests: 10K inserts with accuracy+  checks; batch vs sequential insert equivalence.+ ## 0.4.0.0  ### New sketch families
bench/Bench.hs view
@@ -3,7 +3,7 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} -import Control.Monad (void)+import Control.Monad (void, forM_) import Criterion.Main import Criterion.Types import Data.Word@@ -97,6 +97,10 @@       [ bench "100-queries-from-10000" $ perRunEnv (mkKllWith 10000) $ \sk ->           loopDoubleStep (void . KLL.rank sk) 0 9900 100       ]+    , bgroup "merge"+      [ bench "10000-into-10000" $ perRunEnv (mkKllPair 10000) $ \(a, b) ->+          KLL.merge a b+      ]     ]   , bgroup "HLL"     [ bgroup "insert"@@ -115,6 +119,20 @@       , bench "100000" $ perRunEnv (HLL.mkHllSketch 12) $ \sk ->           HLL.insertBatch sk (word64Vec 100000)       ]+    , bgroup "estimate"+      [ bench "p=12-after-10000" $ perRunEnv (mkHllWith 12 10000) $ \sk ->+          void (HLL.estimate sk)+      , bench "p=14-after-10000" $ perRunEnv (mkHllWith 14 10000) $ \sk ->+          void (HLL.estimate sk)+      , bench "p=12-after-100000" $ perRunEnv (mkHllWith 12 100000) $ \sk ->+          void (HLL.estimate sk)+      ]+    , bgroup "merge"+      [ bench "p=12" $ perRunEnv (mkHllPair 12 10000) $ \(a, b) ->+          HLL.merge a b+      , bench "p=14" $ perRunEnv (mkHllPair 14 10000) $ \(a, b) ->+          HLL.merge a b+      ]     ]   , bgroup "CountMin"     [ bgroup "insert"@@ -123,6 +141,14 @@       , bench "100000" $ perRunEnv (CM.mkCountMinSketch 0.001 0.01) $ \sk ->           loopWord64 (CM.insert sk) 1 100000       ]+    , bgroup "estimate"+      [ bench "1000-queries" $ perRunEnv (mkCmsWith 100000) $ \sk ->+          loopWord64 (void . CM.estimate sk) 1 1000+      ]+    , bgroup "merge"+      [ bench "e=0.001-d=0.01" $ perRunEnv (mkCmsPair 100000) $ \(a, b) ->+          CM.merge a b+      ]     ]   ] @@ -137,3 +163,39 @@   sk <- KLL.mkKllSketch 200   loopDouble (KLL.insert sk) 1 (fromIntegral n)   pure sk++mkKllPair :: Int -> IO (KLL.KllSketch (PrimState IO), KLL.KllSketch (PrimState IO))+mkKllPair n = do+  a <- KLL.mkKllSketch 200+  b <- KLL.mkKllSketch 200+  loopDouble (KLL.insert a) 1 (fromIntegral n)+  loopDouble (KLL.insert b) (fromIntegral n + 1) (fromIntegral (n * 2))+  pure (a, b)++mkHllWith :: Int -> Int -> IO (HLL.HllSketch (PrimState IO))+mkHllWith p n = do+  sk <- HLL.mkHllSketch p+  loopWord64 (HLL.insert sk) 1 (fromIntegral n)+  pure sk++mkHllPair :: Int -> Int -> IO (HLL.HllSketch (PrimState IO), HLL.HllSketch (PrimState IO))+mkHllPair p n = do+  a <- HLL.mkHllSketch p+  b <- HLL.mkHllSketch p+  loopWord64 (HLL.insert a) 1 (fromIntegral n)+  loopWord64 (HLL.insert b) (fromIntegral n + 1) (fromIntegral (n * 2))+  pure (a, b)++mkCmsWith :: Int -> IO (CM.CountMinSketch (PrimState IO))+mkCmsWith n = do+  sk <- CM.mkCountMinSketch 0.001 0.01+  loopWord64 (CM.insert sk) 1 (fromIntegral n)+  pure sk++mkCmsPair :: Int -> IO (CM.CountMinSketch (PrimState IO), CM.CountMinSketch (PrimState IO))+mkCmsPair n = do+  a <- CM.mkCountMinSketch 0.001 0.01+  b <- CM.mkCountMinSketch 0.001 0.01+  loopWord64 (CM.insert a) 1 (fromIntegral n)+  loopWord64 (CM.insert b) (fromIntegral n + 1) (fromIntegral (n * 2))+  pure (a, b)
data-sketches.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack  name:           data-sketches-version:        0.4.0.0+version:        0.4.0.1 synopsis:       Stochastic streaming algorithms for approximate computation on large datasets. Includes KLL, HLL, Theta, Count-Min, and REQ sketches. description:    Please see the README on GitHub at <https://github.com/iand675/datasketches-haskell#readme> category:       Data@@ -50,25 +50,19 @@       TypeOperators   build-depends:       base >=4.7 && <5-    , data-sketches-core ==0.2.*+    , data-sketches-core ==0.3.*     , ghc-prim-    , mtl-    , mwc-random     , primitive     , vector-    , vector-algorithms   default-language: Haskell2010  test-suite data-sketches-test   type: exitcode-stdio-1.0   main-is: Spec.hs   other-modules:-      AuxiliarySpec       BugFixSpec-      CompactorSpec       CountMinSpec       CrossValidationSpec-      DoubleBufferSpec       HyperLogLogSpec       KllSpec       ProofCheckSpec@@ -95,16 +89,12 @@     , ghc-prim     , hedgehog     , hspec-    , hspec-junit-formatter-    , mtl-    , mwc-random     , pretty-show     , primitive     , process     , statistics     , temporary     , vector-    , vector-algorithms   default-language: Haskell2010  benchmark bench@@ -129,9 +119,6 @@     , data-sketches     , data-sketches-core     , ghc-prim-    , mtl-    , mwc-random     , primitive     , vector-    , vector-algorithms   default-language: Haskell2010
src/DataSketches/Quantiles/RelativeErrorQuantile.hs view
@@ -96,11 +96,9 @@ import Control.Monad.Primitive ( PrimMonad(PrimState) ) import Data.Word ( Word32, Word64 ) import DataSketches.Quantiles.RelativeErrorQuantile.Types-    ( RankAccuracy(..) )+    ( RankAccuracy(..), DoubleIsNonFiniteException(..) ) import DataSketches.Quantiles.RelativeErrorQuantile.Internal.Constants     ( fixRseFactor, initNumberOfSections, relRseFactor )-import DataSketches.Quantiles.RelativeErrorQuantile.Internal.DoubleBuffer-    ( DoubleIsNonFiniteException(..) ) import DataSketches.Quantiles.RelativeErrorQuantile.CInternal import qualified Data.List import qualified Data.Vector.Storable as VS
− test/AuxiliarySpec.hs
@@ -1,39 +0,0 @@-module AuxiliarySpec where--import Data.Primitive.MutVar-import qualified Data.Vector.Unboxed.Mutable as MUVector-import DataSketches.Quantiles.RelativeErrorQuantile.Internal.Auxiliary-import qualified DataSketches.Quantiles.RelativeErrorQuantile.Internal.Auxiliary as Aux-import DataSketches.Quantiles.RelativeErrorQuantile.Types-import Test.Hspec-import qualified Data.Vector.Unboxed as U-import qualified Data.List-import DataSketches.Quantiles.RelativeErrorQuantile.Internal.DoubleBuffer---spec :: Spec-spec = do-  mapM_ checkMergeSortIn [HighRanksAreAccurate, LowRanksAreAccurate]--checkMergeSortIn :: RankAccuracy -> Spec-checkMergeSortIn ra = specify ("mergeSortIn works. hra=" ++ show ra) $ do-  let hraBool = case ra of-        HighRanksAreAccurate -> True-        LowRanksAreAccurate -> False-  let oddItems = [1,3..11] -- 6 items-  let evenItems = [2,4..12]--  buf1 <- mkBuffer 25 0 hraBool-  mapM_ (append buf1) oddItems-  -  buf2 <- mkBuffer 25 0 hraBool-  mapM_ (append buf2) evenItems--  let n = 12-  weightedItems <- newMutVar =<< MUVector.new 25  -  let aux = MReqAuxiliary weightedItems ra n-  Aux.mergeSortIn aux buf1 1 0-  Aux.mergeSortIn aux buf2 2 6-  (items, _) <- fmap U.unzip . U.freeze =<< readMutVar (mraWeightedItems aux)-  let itemsToCheck = U.slice 0 (fromIntegral n) items-  itemsToCheck `shouldBe` U.fromList (Data.List.sort (oddItems ++ evenItems))
test/BugFixSpec.hs view
@@ -1,75 +1,27 @@ {-# 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 Control.Monad (forM_) 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+  describe "Bug fix: rank boundary conditions with LowRanksAreAccurate" $ do+    specify "rank of value above max is correct" $ 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+    specify "rank of value below min is 0" $ 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+    specify "ranks are correct for known values" $ do       sk <- mkReqSketch 50 LowRanksAreAccurate       let values = [5, 5, 5, 6, 6, 6, 7, 8, 8, 8 :: Double]       mapM_ (insert sk) values
− test/CompactorSpec.hs
@@ -1,9 +0,0 @@-module CompactorSpec where--import DataSketches.Quantiles.RelativeErrorQuantile.Internal.Compactor-import Test.Hspec--spec :: Spec-spec = do-  specify "nearestEven behaves as expected" $ do-    nearestEven (-0.9) `shouldBe` 0
− test/DoubleBufferSpec.hs
@@ -1,116 +0,0 @@-module DoubleBufferSpec where--import Control.Monad-import DataSketches.Quantiles.RelativeErrorQuantile.Internal.DoubleBuffer-import DataSketches.Quantiles.RelativeErrorQuantile.Types-import qualified Data.Vector.Unboxed as UV-import Test.Hspec-import qualified Data.List--spec :: Spec-spec = do-  describe "Merge Sort" $ do-    let mergeSortTestImpl direction = do-          buf <- mkBuffer 16 0 direction-          forM [0..31] (append buf)-          buf2 <- copyBuffer buf-          mergeSortIn buf buf2-          v <- getVector buf-          UV.freeze v-    it "works upwards" $ do-      v <- mergeSortTestImpl False-      v `shouldBe` UV.fromList (Data.List.sort ([0..31] ++ [0..31]))-    it "works downwards" $ do-      v <- mergeSortTestImpl True-      v `shouldBe` UV.fromList (Data.List.sort ([0..31] ++ [0..31]))-  describe "getEvensOrOdds" $ do-    checkGetEvensOrOdds False False-    checkGetEvensOrOdds False True-    checkGetEvensOrOdds True False-    checkGetEvensOrOdds True True-  describe "checkAppendAndSpaceTop" $ do-    checkAppendAndSpace True-    checkAppendAndSpace False-  describe "checkEnsureCapacity" $ do-    mapM_ checkEnsureCapacity [True, False]-  describe "checkCountLessThanImpl" $ do-    mapM_ checkCountLessThanImpl [True, False]--checkGetEvensOrOdds :: Bool -> Bool -> Spec-checkGetEvensOrOdds odds spaceAtBottom = -  it ("works for odds=" ++ show odds ++ " spaceAtBottom=" ++ show spaceAtBottom) $ do-    buf <- mkBuffer cap 0 spaceAtBottom-    forM_ [0..cap `div` 2] (append buf . fromIntegral)-    out <- getEvensOrOdds buf 0 (cap `div` 2) odds-    v <- UV.freeze =<< getVector out-    v `shouldSatisfy` UV.all (\x -> if odds then floor x `mod` 2 == 1 else floor x `mod` 2 == 0)-  where-    cap = 16--checkAppendAndSpace :: Bool -> Spec-checkAppendAndSpace spaceAtBottom = it ("works for spaceAtBottom=" ++ show spaceAtBottom) $ do-  buf <- mkBuffer 2 2 spaceAtBottom-  do-    count_ <- getCount buf-    count_ `shouldBe` 0-    capacity_ <- getCapacity buf -    capacity_ `shouldBe` 2-    space_ <- getSpace buf-    space_ `shouldBe` 2-  append buf 1-  do-    count_ <- getCount buf-    count_ `shouldBe` 1-    capacity_ <- getCapacity buf -    capacity_ `shouldBe` 2-    space_ <- getSpace buf-    space_ `shouldBe` 1-  append buf 2-  do-    count_ <- getCount buf-    count_ `shouldBe` 2-    capacity_ <- getCapacity buf -    capacity_ `shouldBe` 2-    space_ <- getSpace buf-    space_ `shouldBe` 0-  append buf 3-  do-    count_ <- getCount buf-    count_ `shouldBe` 3-    capacity_ <- getCapacity buf -    capacity_ `shouldBe` 5-    space_ <- getSpace buf-    space_ `shouldBe` 2--checkEnsureCapacity :: Bool -> Spec-checkEnsureCapacity spaceAtBottom = it ("works for spaceAtBottom=" ++ show spaceAtBottom) $ do-  buf <- mkBuffer 4 2 spaceAtBottom-  append buf 2-  append buf 1-  append buf 3-  ensureCapacity buf 8-  sort buf-  x1 <- buf ! 0-  x1 `shouldBe` 1-  x2 <- buf ! 1-  x2 `shouldBe` 2-  x3 <- buf ! 2-  x3 `shouldBe` 3--checkCountLessThanImpl :: Bool -> Spec-checkCountLessThanImpl spaceAtBottom = it ("works for spaceAtBottom=" ++ show spaceAtBottom) $ do -  buf <- mkBuffer 7 0 spaceAtBottom-  mapM_ (append buf) [1..7]-  sort buf-  buf2 <- mkBuffer 7 0 spaceAtBottom-  mergeSortIn buf2 buf-  do-    count_ <- getCountWithCriterion buf2 4 (:<)-    count_ `shouldBe` 3-  mergeSortIn buf2 buf-  do-    count_ <- getCountWithCriterion buf2 4 (:<)-    count_ `shouldBe` 6-  do-    count_ <- getCount buf2-    count_ `shouldBe` 14
test/RelativeErrorQuantileSpec.hs view
@@ -4,12 +4,14 @@  import Control.Monad import Control.Monad.Primitive-import DataSketches.Quantiles.RelativeErrorQuantile+import DataSketches.Quantiles.RelativeErrorQuantile hiding (null, minimum, maximum)+import qualified DataSketches.Quantiles.RelativeErrorQuantile as REQ import DataSketches.Quantiles.RelativeErrorQuantile.Types-import Data.List hiding (insert, null)+import Data.List hiding (insert, null, minimum, maximum) import qualified Data.List import Data.Word import Test.Hspec+import qualified Data.Vector.Storable as VS  spec :: Spec spec = do@@ -20,7 +22,7 @@   specify "updating a sketch with NaN should ignore it" $ asIO $ do     sk <- mkReqSketch 6 HighRanksAreAccurate     insert sk (0 / 0)-    isEmpty <- DataSketches.Quantiles.RelativeErrorQuantile.null sk+    isEmpty <- REQ.null sk     isEmpty `shouldBe` True   specify "non finite rank should throw" $ asIO $ do     let infinity = read "Infinity"::Double@@ -90,6 +92,143 @@       insert sk 1       r <- quantile sk 0.5       r `shouldBe` 1.0++  describe "quantile monotonicity" $ do+    specify "quantiles are non-decreasing for increasing normalized ranks (exact mode)" $ asIO $ do+      sk <- mkReqSketch 50 HighRanksAreAccurate+      mapM_ (insert sk) [1..100 :: Double]+      let rankPoints = [0, 0.1 .. 1.0]+      qs <- quantiles sk rankPoints+      forM_ (zip qs (drop 1 qs)) $ \(q1, q2) ->+        q2 `shouldSatisfy` (>= q1)++    specify "quantiles are non-decreasing (estimation mode, HRA)" $ asIO $ do+      sk <- mkReqSketch 6 HighRanksAreAccurate+      mapM_ (insert sk) [1..2000 :: Double]+      estimation <- isEstimationMode sk+      estimation `shouldBe` True+      let rankPoints = [0, 0.01 .. 1.0]+      qs <- quantiles sk rankPoints+      forM_ (zip qs (drop 1 qs)) $ \(q1, q2) ->+        q2 `shouldSatisfy` (>= q1)++    specify "quantiles are non-decreasing (estimation mode, LRA)" $ asIO $ do+      sk <- mkReqSketch 6 LowRanksAreAccurate+      mapM_ (insert sk) [1..2000 :: Double]+      estimation <- isEstimationMode sk+      estimation `shouldBe` True+      let rankPoints = [0, 0.01 .. 1.0]+      qs <- quantiles sk rankPoints+      forM_ (zip qs (drop 1 qs)) $ \(q1, q2) ->+        q2 `shouldSatisfy` (>= q1)++  describe "countWithCriterion" $ do+    specify "counts items strictly less than threshold (LT)" $ asIO $ do+      sk <- mkReqSketch 50 HighRanksAreAccurate+      mapM_ (insert sk) [10, 20, 30, 40, 50 :: Double]+      setCriterionLT sk+      cnt <- countWithCriterion sk 30+      cnt `shouldBe` 2+      cntAll <- countWithCriterion sk 55+      cntAll `shouldBe` 5+      cntNone <- countWithCriterion sk 5+      cntNone `shouldBe` 0++    specify "counts items less than or equal to threshold (LE)" $ asIO $ do+      sk <- mkReqSketch 50 HighRanksAreAccurate+      mapM_ (insert sk) [10, 20, 30, 40, 50 :: Double]+      setCriterionLE sk+      cnt <- countWithCriterion sk 30+      cnt `shouldBe` 3+      cntAll <- countWithCriterion sk 50+      cntAll `shouldBe` 5+      cntNone <- countWithCriterion sk 5+      cntNone `shouldBe` 0++    specify "boundary: at exact min value" $ asIO $ do+      sk <- mkReqSketch 50 HighRanksAreAccurate+      mapM_ (insert sk) [10, 20, 30, 40, 50 :: Double]+      setCriterionLT sk+      cntLt <- countWithCriterion sk 10+      cntLt `shouldBe` 0+      setCriterionLE sk+      cntLe <- countWithCriterion sk 10+      cntLe `shouldBe` 1++    specify "boundary: at exact max value" $ asIO $ do+      sk <- mkReqSketch 50 HighRanksAreAccurate+      mapM_ (insert sk) [10, 20, 30, 40, 50 :: Double]+      setCriterionLT sk+      cntLt <- countWithCriterion sk 50+      cntLt `shouldBe` 4+      setCriterionLE sk+      cntLe <- countWithCriterion sk 50+      cntLe `shouldBe` 5++    specify "with duplicate values" $ asIO $ do+      sk <- mkReqSketch 50 HighRanksAreAccurate+      mapM_ (insert sk) [1, 1, 1, 2, 2, 3 :: Double]+      setCriterionLT sk+      cntLt <- countWithCriterion sk 2+      cntLt `shouldBe` 3+      setCriterionLE sk+      cntLe <- countWithCriterion sk 2+      cntLe `shouldBe` 5++    specify "works in estimation mode after compaction" $ asIO $ do+      sk <- mkReqSketch 6 HighRanksAreAccurate+      mapM_ (insert sk) [1..2000 :: Double]+      estimation <- isEstimationMode sk+      estimation `shouldBe` True+      n <- count sk+      n `shouldBe` 2000+      setCriterionLE sk+      cntMax <- countWithCriterion sk 2000+      cntMax `shouldBe` n+      setCriterionLT sk+      cntMin <- countWithCriterion sk 0+      cntMin `shouldBe` 0++    specify "non-finite value throws DoubleIsNonFiniteException" $ asIO $ do+      sk <- mkReqSketch 6 HighRanksAreAccurate+      insert sk 1+      countWithCriterion sk (0 / 0) `shouldThrow` (\(DoubleIsNonFiniteException _) -> True)++  describe "estimation mode capacity growth" $ do+    specify "10,000 inserts followed by correct quantile queries" $ asIO $ do+      sk <- mkReqSketch 6 HighRanksAreAccurate+      forM_ [1..10_000 :: Double] $ insert sk+      n <- count sk+      n `shouldBe` 10_000+      estimation <- isEstimationMode sk+      estimation `shouldBe` True+      p50 <- quantile sk 0.5+      p50 `shouldSatisfy` (\v -> v >= 4000 && v <= 6000)+      p99 <- quantile sk 0.99+      p99 `shouldSatisfy` (\v -> v >= 9800 && v <= 10_000)+      mn <- REQ.minimum sk+      mn `shouldBe` 1+      mx <- REQ.maximum sk+      mx `shouldBe` 10_000++    specify "batch insert matches sequential insert" $ asIO $ do+      skSeq <- mkReqSketch 12 HighRanksAreAccurate+      forM_ [1..500 :: Double] $ insert skSeq++      skBatch <- mkReqSketch 12 HighRanksAreAccurate+      insertBatch skBatch (VS.fromList [1..500])++      seqCount <- count skSeq+      batchCount <- count skBatch+      seqCount `shouldBe` batchCount++      seqMin <- REQ.minimum skSeq+      batchMin <- REQ.minimum skBatch+      seqMin `shouldBe` batchMin++      seqMax <- REQ.maximum skSeq+      batchMax <- REQ.maximum skBatch+      seqMax `shouldBe` batchMax   bigTest :: Word32 -> Int -> Int -> RankAccuracy -> Bool -> Bool -> Spec
test/Spec.hs view
@@ -1,7 +1,4 @@ import Test.Hspec-import qualified AuxiliarySpec-import qualified CompactorSpec-import qualified DoubleBufferSpec import qualified ProofCheckSpec import qualified RelativeErrorQuantileSpec import qualified KllSpec@@ -11,23 +8,16 @@ import qualified BugFixSpec import qualified CrossValidationSpec import System.Environment-import Test.HSpec.JUnit import Test.Hspec.Runner  main :: IO ()-main = +main =       getArgs-  >>= readConfig config+  >>= readConfig defaultConfig   >>= withArgs [] . runSpec specs   >>= evaluateSummary   where-    config = defaultConfig -      { configFormat = Just $ junitFormat "test-results.xml" "data-sketches" -      }     specs = do-      describe "Auxiliary" AuxiliarySpec.spec-      describe "Compactor" CompactorSpec.spec-      describe "DoubleBuffer" DoubleBufferSpec.spec       describe "ProofCheck" ProofCheckSpec.spec       describe "RelativeErrorQuantile" RelativeErrorQuantileSpec.spec       describe "KLL" KllSpec.spec@@ -36,4 +26,3 @@       describe "CountMin" CountMinSpec.spec       describe "BugFix" BugFixSpec.spec       describe "CrossValidation" CrossValidationSpec.spec-