{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# OPTIONS_GHC -Wall #-}

-- | Statistical choices for multiple performance measurements.
module Perf.Stats
  ( average,
    median,
    tenth,
    averageI,
    averageSecs,
    StatDType (..),
    statD,
    statDs,
    parseStatD,
    -- stat reporting
    addStat,
    ordy,
    allStats,
    statify,
  )
where

import Control.Monad.State.Lazy
import qualified Data.List as List
import qualified Data.Map.Strict as Map
import Data.Text (Text, pack)
import NumHask.Space (quantile)
import Options.Applicative

-- | Compute the median
median :: [Double] -> Double
median :: [Double] -> Double
median = forall (f :: * -> *). Foldable f => Double -> f Double -> Double
quantile Double
0.5

-- | Compute the average
average :: [Double] -> Double
average :: [Double] -> Double
average [Double]
xs = forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum [Double]
xs forall a. Fractional a => a -> a -> a
/ (forall a b. (Integral a, Num b) => a -> b
fromIntegral forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (t :: * -> *) a. Foldable t => t a -> Int
length forall a b. (a -> b) -> a -> b
$ [Double]
xs)

-- | Compute the tenth percentile
tenth :: [Double] -> Double
tenth :: [Double] -> Double
tenth = forall (f :: * -> *). Foldable f => Double -> f Double -> Double
quantile Double
0.1

-- | Compute the average of an Integral
averageI :: (Integral a) => [a] -> Double
averageI :: forall a. Integral a => [a] -> Double
averageI [a]
xs = forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum (forall a b. (Integral a, Num b) => a -> b
fromIntegral forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [a]
xs) forall a. Fractional a => a -> a -> a
/ (forall a b. (Integral a, Num b) => a -> b
fromIntegral forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (t :: * -> *) a. Foldable t => t a -> Int
length forall a b. (a -> b) -> a -> b
$ [a]
xs)

-- | Compute the average time in seconds.
averageSecs :: [Double] -> Double
averageSecs :: [Double] -> Double
averageSecs [Double]
xs = forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum [Double]
xs forall a. Fractional a => a -> a -> a
/ (forall a b. (Integral a, Num b) => a -> b
fromIntegral forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (t :: * -> *) a. Foldable t => t a -> Int
length forall a b. (a -> b) -> a -> b
$ [Double]
xs) forall a. Fractional a => a -> a -> a
/ Double
2.5e9

-- | Command-line options for type of statistic.
data StatDType = StatAverage | StatMedian | StatBest | StatSecs deriving (StatDType -> StatDType -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: StatDType -> StatDType -> Bool
$c/= :: StatDType -> StatDType -> Bool
== :: StatDType -> StatDType -> Bool
$c== :: StatDType -> StatDType -> Bool
Eq, Int -> StatDType -> ShowS
[StatDType] -> ShowS
StatDType -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [StatDType] -> ShowS
$cshowList :: [StatDType] -> ShowS
show :: StatDType -> String
$cshow :: StatDType -> String
showsPrec :: Int -> StatDType -> ShowS
$cshowsPrec :: Int -> StatDType -> ShowS
Show)

-- | Compute a statistic.
statD :: StatDType -> [Double] -> Double
statD :: StatDType -> [Double] -> Double
statD StatDType
StatBest = [Double] -> Double
tenth
statD StatDType
StatMedian = [Double] -> Double
median
statD StatDType
StatAverage = [Double] -> Double
average
statD StatDType
StatSecs = [Double] -> Double
averageSecs

-- | Compute a list of statistics.
statDs :: StatDType -> [[Double]] -> [Double]
statDs :: StatDType -> [[Double]] -> [Double]
statDs StatDType
StatBest = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap [Double] -> Double
tenth forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. [[a]] -> [[a]]
List.transpose
statDs StatDType
StatMedian = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap [Double] -> Double
median forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. [[a]] -> [[a]]
List.transpose
statDs StatDType
StatAverage = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap [Double] -> Double
average forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. [[a]] -> [[a]]
List.transpose
statDs StatDType
StatSecs = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap [Double] -> Double
averageSecs forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. [[a]] -> [[a]]
List.transpose

-- | Parse command-line 'StatDType' options.
parseStatD :: Parser StatDType
parseStatD :: Parser StatDType
parseStatD =
  forall a. a -> Mod FlagFields a -> Parser a
flag' StatDType
StatBest (forall (f :: * -> *) a. HasName f => String -> Mod f a
long String
"best" forall a. Semigroup a => a -> a -> a
<> forall (f :: * -> *) a. String -> Mod f a
help String
"report upper decile")
    forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> forall a. a -> Mod FlagFields a -> Parser a
flag' StatDType
StatMedian (forall (f :: * -> *) a. HasName f => String -> Mod f a
long String
"median" forall a. Semigroup a => a -> a -> a
<> forall (f :: * -> *) a. String -> Mod f a
help String
"report median")
    forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> forall a. a -> Mod FlagFields a -> Parser a
flag' StatDType
StatAverage (forall (f :: * -> *) a. HasName f => String -> Mod f a
long String
"average" forall a. Semigroup a => a -> a -> a
<> forall (f :: * -> *) a. String -> Mod f a
help String
"report average")
    forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> forall a. a -> Mod FlagFields a -> Parser a
flag' StatDType
StatSecs (forall (f :: * -> *) a. HasName f => String -> Mod f a
long String
"averagesecs" forall a. Semigroup a => a -> a -> a
<> forall (f :: * -> *) a. String -> Mod f a
help String
"report average in seconds")
    forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> forall (f :: * -> *) a. Applicative f => a -> f a
pure StatDType
StatAverage

-- | Add a statistic to a State Map
addStat :: (Ord k, Monad m) => k -> s -> StateT (Map.Map k s) m ()
addStat :: forall k (m :: * -> *) s.
(Ord k, Monad m) =>
k -> s -> StateT (Map k s) m ()
addStat k
label s
s = do
  forall s (m :: * -> *). MonadState s m => (s -> s) -> m ()
modify (forall k a. Ord k => k -> a -> Map k a -> Map k a
Map.insert k
label s
s)

-- | Linguistic conversion of an ordinal
ordy :: Int -> [Text]
ordy :: Int -> [Text]
ordy Int
f = forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith (\Int
x Text
s -> (String -> Text
pack forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Show a => a -> String
show) Int
x forall a. Semigroup a => a -> a -> a
<> Text
s) [Int
1 .. Int
f] ([Text
"st", Text
"nd", Text
"rd"] forall a. Semigroup a => a -> a -> a
<> forall a. a -> [a]
repeat Text
"th")

-- | Compute all stats.
allStats :: Int -> Map.Map [Text] [[Double]] -> Map.Map [Text] [Double]
allStats :: Int -> Map [Text] [[Double]] -> Map [Text] [Double]
allStats Int
f Map [Text] [[Double]]
m =
  forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList forall a b. (a -> b) -> a -> b
$
    forall a. Monoid a => [a] -> a
mconcat
      [ forall a. Monoid a => [a] -> a
mconcat ((\([Text]
ks, [[Double]]
xss) -> forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith (\Text
l [Double]
xs -> ([Text]
ks forall a. Semigroup a => a -> a -> a
<> [Text
l], [Double]
xs)) (Int -> [Text]
ordy Int
f) [[Double]]
xss) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [([Text], [[Double]])]
mlist),
        (\([Text]
ks, [[Double]]
xss) -> ([Text]
ks forall a. Semigroup a => a -> a -> a
<> [Text
"best"], forall (f :: * -> *). Foldable f => Double -> f Double -> Double
quantile Double
0.1 forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a. [[a]] -> [[a]]
List.transpose [[Double]]
xss)) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [([Text], [[Double]])]
mlist,
        (\([Text]
ks, [[Double]]
xss) -> ([Text]
ks forall a. Semigroup a => a -> a -> a
<> [Text
"median"], forall (f :: * -> *). Foldable f => Double -> f Double -> Double
quantile Double
0.5 forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a. [[a]] -> [[a]]
List.transpose [[Double]]
xss)) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [([Text], [[Double]])]
mlist,
        (\([Text]
ks, [[Double]]
xss) -> ([Text]
ks forall a. Semigroup a => a -> a -> a
<> [Text
"average"], forall {a} {t :: * -> *}. (Fractional a, Foldable t) => t a -> a
av forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a. [[a]] -> [[a]]
List.transpose [[Double]]
xss)) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [([Text], [[Double]])]
mlist
      ]
  where
    mlist :: [([Text], [[Double]])]
mlist = forall k a. Map k a -> [(k, a)]
Map.toList Map [Text] [[Double]]
m
    av :: t a -> a
av t a
xs = forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum t a
xs forall a. Fractional a => a -> a -> a
/ (forall a b. (Integral a, Num b) => a -> b
fromIntegral forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (t :: * -> *) a. Foldable t => t a -> Int
length forall a b. (a -> b) -> a -> b
$ t a
xs)

-- | Convert a Map of performance result to a statistic.
statify :: (Ord a) => StatDType -> Map.Map a [[Double]] -> Map.Map [a] [Double]
statify :: forall a.
Ord a =>
StatDType -> Map a [[Double]] -> Map [a] [Double]
statify StatDType
s Map a [[Double]]
m = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (StatDType -> [Double] -> Double
statD StatDType
s) forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. [[a]] -> [[a]]
List.transpose forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall k2 k1 a. Ord k2 => (k1 -> k2) -> Map k1 a -> Map k2 a
Map.mapKeys (forall a. a -> [a] -> [a]
: []) Map a [[Double]]
m