module Data.Metrics.Snapshot (
Snapshot(..),
quantile,
size,
median,
get75thPercentile,
get95thPercentile,
get98thPercentile,
get99thPercentile,
get999thPercentile,
takeSnapshot
) where
import Control.Monad.Primitive
import Data.Vector.Algorithms.Intro
import qualified Data.Vector.Unboxed as I
import qualified Data.Vector.Unboxed.Mutable as V
newtype Snapshot = Snapshot
{ fromSnapshot :: I.Vector Double
}
deriving (Show)
medianQ :: Double
medianQ = 0.5
p75Q :: Double
p75Q = 0.75
p95Q :: Double
p95Q = 0.95
p98Q :: Double
p98Q = 0.98
p99Q :: Double
p99Q = 0.99
p999Q :: Double
p999Q = 0.999
clamp :: Double -> Double
clamp x | x > 1 = 1
| x < 0 = 0
| otherwise = x
takeSnapshot :: PrimMonad m => V.MVector (PrimState m) Double -> m Snapshot
takeSnapshot v = fmap Snapshot (V.clone v >>= \v' -> sort v' >> I.unsafeFreeze v')
quantile :: Double -> Snapshot -> Double
quantile quant (Snapshot s)
| I.length s == 0 = 0
| pos > fromIntegral (I.length s) = I.last s
| pos' < 1 = I.head s
| otherwise =
lower + (pos fromIntegral (floor pos :: Int)) * (upper lower)
where
q = clamp quant
pos = q * (1 + fromIntegral (I.length s))
pos' = truncate pos
lower = I.unsafeIndex s (pos' 1)
upper = I.unsafeIndex s pos'
size :: Snapshot -> Int
size (Snapshot s) = I.length s
median :: Snapshot -> Double
median = quantile medianQ
get75thPercentile :: Snapshot -> Double
get75thPercentile = quantile p75Q
get95thPercentile :: Snapshot -> Double
get95thPercentile = quantile p95Q
get98thPercentile :: Snapshot -> Double
get98thPercentile = quantile p98Q
get99thPercentile :: Snapshot -> Double
get99thPercentile = quantile p99Q
get999thPercentile :: Snapshot -> Double
get999thPercentile = quantile p999Q