src/Data/HdrHistogram.hs
{-|
Module : Data.HdrHistogram
Copyright : (c) Josh Bohde, 2015
License : GPL-3
Maintainer : josh@joshbohde.com
Stability : experimental
Portability : POSIX
A Haskell implementation of <http://www.hdrhistogram.org/ HdrHistogram>.
It allows storing counts of observed values within a range,
while maintaining precision to a configurable number of significant
digits.
-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Data.HdrHistogram (
-- * Histogram
Histogram(..), empty, fromConfig,
-- * Writing
record, recordValues,
-- * Reading
Range(..),
percentile,
-- * Re-exports
Config, HasConfig
) where
import Data.Bits (Bits, FiniteBits)
import Data.HdrHistogram.Config
import Data.HdrHistogram.Config.Internal
import Data.Proxy (Proxy (Proxy))
import Data.Tagged (Tagged (Tagged))
import Data.Vector.Unboxed ((!), (//))
import qualified Data.Vector.Unboxed as U
-- | A pure 'Histogram'
data Histogram config value count = Histogram {
_config :: HistogramConfig value,
totalCount :: count,
counts :: U.Vector count
} deriving (Eq, Show)
-- | Construct a 'Histogram'.
empty :: forall config value count. (HasConfig config, Integral value, FiniteBits value, U.Unbox count, Integral count) => Histogram config value count
empty = fromConfig (Tagged c :: Tagged config (HistogramConfig value))
where
p = Proxy :: Proxy config
c = getConfig p
-- | Construct a 'Histogram' from the given 'HistogramConfig'. In this
-- case 'c' is a phantom type.
fromConfig :: (U.Unbox count, Integral count) => Tagged c (HistogramConfig value) -> Histogram c value count
fromConfig (Tagged c) = Histogram {
_config = c,
totalCount = 0,
counts = U.replicate (size c) 0
}
instance (HasConfig config, Integral value, FiniteBits value, U.Unbox count, Integral count) =>
Monoid (Histogram config value count) where
mempty = empty
Histogram config' t c `mappend` Histogram _ t' c' = Histogram config' (t + t') (U.zipWith (+) c c')
-- | Record a single value to the 'Histogram'
record :: (U.Unbox count,
Integral count, Integral value, FiniteBits value)
=> Histogram config value count
-> value
-> Histogram config value count
record h val = recordValues h val 1
-- | Record a multiple instances of a value value to the 'Histogram'
recordValues :: (U.Unbox count, Integral count, Integral value, FiniteBits value) => Histogram config value count -> value -> count -> Histogram config value count
recordValues h val count = h {
totalCount = totalCount h + count,
counts = counts h // [(index, (counts h ! index) + count)]
}
where
index = indexForValue (_config h) val
-- recordCorrectedValues :: Integral value => Histogram value count -> value -> value -> Histogram value count
-- recordCorrectedValues = undefined
-- | Calculate the 'Range' of values at the given percentile
percentile :: (Integral value, Integral count, U.Unbox count, Bits value)
=> Histogram config value count
-> Float -- ^ The percentile in the range 0 to 100
-> Range value
percentile h q = case U.find ((>= count) . snd) totals of
Nothing -> Range 0 0
Just (i, _) -> rangeForIndex c i
where
c = _config h
q' = min q 100
count = floor $ (q' / 100) * fromIntegral (totalCount h) + 0.5
totals = U.scanl f (0 :: Int, 0) withIndex
where
f (_, v') (i, v) = (i, v' + v)
withIndex = U.imap (,) (counts h)