module Data.Metrics.Histogram.Internal (
Histogram,
histogram,
clear,
update,
mean,
stddev,
variance,
minVal,
maxVal,
count,
snapshot
) where
import Data.Time.Clock
import qualified Data.Metrics.Reservoir as R
import Data.Metrics.Snapshot (Snapshot)
data Histogram = Histogram
{ histogramReservoir :: !R.Reservoir
, histogramCount :: !Int
, histogramMinVal :: !Double
, histogramMaxVal :: !Double
, histogramSum :: !Double
, histogramVariance :: !(Double, Double)
}
histogram :: R.Reservoir -> Histogram
histogram r = Histogram r 0 nan nan 0 (0, 0)
nan :: Double
nan = 0 / 0
clear :: NominalDiffTime -> Histogram -> Histogram
clear = go
where
go t s = s
{ histogramReservoir = R.clear t $ histogramReservoir s
, histogramCount = 0
, histogramMinVal = nan
, histogramMaxVal = nan
, histogramSum = 0
, histogramVariance = (1, 0)
}
update :: Double -> NominalDiffTime -> Histogram -> Histogram
update = go
where
go v t s = s
{ histogramReservoir = updatedReservoir
, histogramCount = updatedCount
, histogramMinVal = updateMin (histogramMinVal s) v
, histogramMaxVal = updateMax (histogramMaxVal s) v
, histogramSum = histogramSum s + v
, histogramVariance = updateVariance updatedCount v $ histogramVariance s
}
where
updatedCount = succ $ histogramCount s
updatedReservoir = R.update v t $ histogramReservoir s
updateMin :: Double -> Double -> Double
updateMin ox x = if isNaN ox || ox > x then x else ox
updateMax :: Double -> Double -> Double
updateMax ox x = if isNaN ox || ox < x then x else ox
mean :: Histogram -> Double
mean = go
where
go s = if histogramCount s > 0
then histogramSum s / fromIntegral (histogramCount s)
else 0
stddev :: Histogram -> Double
stddev = go
where
go s = if c > 0
then (calculateVariance c $ snd $ histogramVariance s) ** 0.5
else 0
where c = histogramCount s
variance :: Histogram -> Double
variance = go
where
go s = if c <= 1
then 0
else calculateVariance c $ snd $ histogramVariance s
where c = histogramCount s
minVal :: Histogram -> Double
minVal = histogramMinVal
maxVal :: Histogram -> Double
maxVal = histogramMaxVal
count :: Histogram -> Int
count = histogramCount
snapshot :: Histogram -> Snapshot
snapshot = R.snapshot . histogramReservoir
calculateVariance :: Int -> Double -> Double
calculateVariance c v = if c <= 1 then 0 else v / (fromIntegral c 1)
updateVariance :: Int -> Double -> (Double, Double) -> (Double, Double)
updateVariance _ !c (1, y) = (c, 0)
updateVariance count c (x, y) = (l, r)
where
c' = fromIntegral count
diff = c x
!l = x + diff / c'
!r = y + diff * (c l)