------------------------------------------------------------------------
-- |
-- Module      :  Test.BenchPress
-- Copyright   :  (c) Johan Tibell 2008
-- License     :  BSD3-style (see LICENSE)
--
-- Maintainer  :  me@willsewell.com
-- Stability   :  experimental
-- Portability :  portable
--
-- Benchmarks actions and produces statistics such as min, mean,
-- median, standard deviation, and max execution time.  Also computes
-- execution time percentiles.  Comes with functions to pretty-print
-- the results.
--
-- Here's an example showing a benchmark of copying a file:
--
-- > import Control.Monad (when)
-- > import qualified Data.ByteString as B
-- > import System.IO
-- > import Test.BenchPress
-- >
-- > inpath, outpath :: String
-- > inpath = "/tmp/infile"
-- > outpath = "/tmp/outfile"
-- >
-- > blockSize :: Int
-- > blockSize = 4 * 1024
-- >
-- > copyUsingByteString :: Handle -> Handle -> IO ()
-- > copyUsingByteString inf outf = go
-- >     where
-- >       go = do
-- >         bs <- B.hGet inf blockSize
-- >         let numRead = B.length bs
-- >         when (numRead > 0) $
-- >            B.hPut outf bs >> go
-- >
-- > main :: IO ()
-- > main = bench 100 $ do
-- >          inf <- openBinaryFile inpath ReadMode
-- >          outf <- openBinaryFile outpath WriteMode
-- >          copyUsingByteString inf outf
-- >          hClose outf
-- >          hClose inf
--
------------------------------------------------------------------------

module Test.BenchPress
    ( -- * Running a benchmark
      benchmark,
      bench,
      benchMany,

      -- * Benchmark stats
      Stats(..),

      -- * Pretty-printing stats
      printDetailedStats,
      printStatsSummaries,
    ) where

import Control.Exception (bracket)
import Control.Monad (forM, forM_)
import Data.List (intersperse, sort)
import Data.Time.Clock (NominalDiffTime, diffUTCTime, getCurrentTime)
import qualified Math.Statistics as Math
import Prelude hiding (max, min)
import qualified Prelude
import System.CPUTime (getCPUTime)
import Text.Printf (printf)

-- ---------------------------------------------------------------------
-- Running a benchmark

-- | @benchmark iters setup teardown action@ runs @action@ @iters@
-- times measuring the execution time of each run.  @setup@ and
-- @teardown@ are run before and after each run respectively.
-- @teardown@ is run even if @action@ raises an exception.  Returns
-- statistics for both the measured CPU times and wall clock times, in
-- that order.
benchmark :: Int -> IO a -> (a -> IO b) -> (a -> IO c) -> IO (Stats, Stats)
benchmark :: Int -> IO a -> (a -> IO b) -> (a -> IO c) -> IO (Stats, Stats)
benchmark Int
iters IO a
setup a -> IO b
teardown a -> IO c
action =
  if Int
iters Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
1
    then [Char] -> IO (Stats, Stats)
forall a. HasCallStack => [Char] -> a
error [Char]
"benchmark: iters must be greater than 0"
    else do
      ([Double]
cpuTimes, [Double]
wallTimes) <- [(Double, Double)] -> ([Double], [Double])
forall a b. [(a, b)] -> ([a], [b])
unzip ([(Double, Double)] -> ([Double], [Double]))
-> IO [(Double, Double)] -> IO ([Double], [Double])
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
`fmap` Int -> IO [(Double, Double)]
forall a. (Eq a, Num a) => a -> IO [(Double, Double)]
go Int
iters
      let xs :: [Double]
xs        = [Double] -> [Double]
forall a. Ord a => [a] -> [a]
sort [Double]
cpuTimes
          cpuStats :: Stats
cpuStats  = Stats :: Double
-> Double -> Double -> Double -> Double -> [(Int, Double)] -> Stats
Stats
                      { min :: Double
min         = [Double] -> Double
forall a. [a] -> a
head [Double]
xs
                      , mean :: Double
mean        = [Double] -> Double
forall a. Floating a => [a] -> a
Math.mean [Double]
xs
                      , stddev :: Double
stddev      = [Double] -> Double
forall a. Floating a => [a] -> a
Math.stddev [Double]
xs
                      , median :: Double
median      = [Double] -> Double
forall a. (Floating a, Ord a) => [a] -> a
Math.median [Double]
xs
                      , max :: Double
max         = [Double] -> Double
forall a. [a] -> a
last [Double]
xs
                      , percentiles :: [(Int, Double)]
percentiles = [Double] -> [(Int, Double)]
percentiles' [Double]
xs
                      }
          ys :: [Double]
ys        = [Double] -> [Double]
forall a. Ord a => [a] -> [a]
sort [Double]
wallTimes
          wallStats :: Stats
wallStats = Stats :: Double
-> Double -> Double -> Double -> Double -> [(Int, Double)] -> Stats
Stats
                      { min :: Double
min         = [Double] -> Double
forall a. [a] -> a
head [Double]
ys
                      , mean :: Double
mean        = [Double] -> Double
forall a. Floating a => [a] -> a
Math.mean [Double]
ys
                      , stddev :: Double
stddev      = [Double] -> Double
forall a. Floating a => [a] -> a
Math.stddev [Double]
ys
                      , median :: Double
median      = [Double] -> Double
forall a. (Floating a, Ord a) => [a] -> a
Math.median [Double]
ys
                      , max :: Double
max         = [Double] -> Double
forall a. [a] -> a
last [Double]
ys
                      , percentiles :: [(Int, Double)]
percentiles = [Double] -> [(Int, Double)]
percentiles' [Double]
ys
                      }
      (Stats, Stats) -> IO (Stats, Stats)
forall (m :: * -> *) a. Monad m => a -> m a
return (Stats
cpuStats, Stats
wallStats)
      where
        go :: a -> IO [(Double, Double)]
go a
0 = [(Double, Double)] -> IO [(Double, Double)]
forall (m :: * -> *) a. Monad m => a -> m a
return []
        go a
n = do
          (Double, Double)
elapsed <- IO a
-> (a -> IO b) -> (a -> IO (Double, Double)) -> IO (Double, Double)
forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
bracket IO a
setup a -> IO b
teardown ((a -> IO (Double, Double)) -> IO (Double, Double))
-> (a -> IO (Double, Double)) -> IO (Double, Double)
forall a b. (a -> b) -> a -> b
$ \a
a -> do
            UTCTime
startWall <- IO UTCTime
getCurrentTime
            Integer
startCpu <- IO Integer
getCPUTime
            c
_ <- a -> IO c
action a
a
            Integer
endCpu <- IO Integer
getCPUTime
            UTCTime
endWall <- IO UTCTime
getCurrentTime
            (Double, Double) -> IO (Double, Double)
forall (m :: * -> *) a. Monad m => a -> m a
return (Integer -> Double
picosToMillis (Integer -> Double) -> Integer -> Double
forall a b. (a -> b) -> a -> b
$! Integer
endCpu Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- Integer
startCpu
                   ,NominalDiffTime -> Double
secsToMillis (NominalDiffTime -> Double) -> NominalDiffTime -> Double
forall a b. (a -> b) -> a -> b
$! UTCTime
endWall UTCTime -> UTCTime -> NominalDiffTime
`diffUTCTime` UTCTime
startWall)
          [(Double, Double)]
timings <- a -> IO [(Double, Double)]
go (a -> IO [(Double, Double)]) -> a -> IO [(Double, Double)]
forall a b. (a -> b) -> a -> b
$! a
n a -> a -> a
forall a. Num a => a -> a -> a
- a
1
          [(Double, Double)] -> IO [(Double, Double)]
forall (m :: * -> *) a. Monad m => a -> m a
return ([(Double, Double)] -> IO [(Double, Double)])
-> [(Double, Double)] -> IO [(Double, Double)]
forall a b. (a -> b) -> a -> b
$ (Double, Double)
elapsed (Double, Double) -> [(Double, Double)] -> [(Double, Double)]
forall a. a -> [a] -> [a]
: [(Double, Double)]
timings

-- | Convenience function that runs a benchmark using 'benchmark' and
-- prints timing statistics using 'printDetailedStats'.  The
-- statistics are computed from the measured CPU times.  Writes output
-- to standard output.
bench :: Int -> IO a -> IO ()
bench :: Int -> IO a -> IO ()
bench Int
iters IO a
action = do
  (Stats
stats, Stats
_) <- Int -> IO () -> (() -> IO ()) -> (() -> IO a) -> IO (Stats, Stats)
forall a b c.
Int -> IO a -> (a -> IO b) -> (a -> IO c) -> IO (Stats, Stats)
benchmark Int
iters (() -> IO ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()) (IO () -> () -> IO ()
forall a b. a -> b -> a
const (IO () -> () -> IO ()) -> IO () -> () -> IO ()
forall a b. (a -> b) -> a -> b
$ () -> IO ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()) (IO a -> () -> IO a
forall a b. a -> b -> a
const IO a
action)
  Stats -> IO ()
printDetailedStats Stats
stats

-- | Convenience function that runs several benchmarks using
-- 'benchmark' and prints a timing statistics summary using
-- 'printStatsSummaries'.  The statistics are computed from the
-- measured CPU times.  Each benchmark has an associated label that is
-- used to identify the benchmark in the printed results.  Writes
-- output to standard output.
benchMany :: Int -> [(String, IO a)] -> IO ()
benchMany :: Int -> [([Char], IO a)] -> IO ()
benchMany Int
iters [([Char], IO a)]
bms = do
  [(Stats, Stats)]
results <- [([Char], IO a)]
-> (([Char], IO a) -> IO (Stats, Stats)) -> IO [(Stats, Stats)]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
t a -> (a -> m b) -> m (t b)
forM [([Char], IO a)]
bms ((([Char], IO a) -> IO (Stats, Stats)) -> IO [(Stats, Stats)])
-> (([Char], IO a) -> IO (Stats, Stats)) -> IO [(Stats, Stats)]
forall a b. (a -> b) -> a -> b
$ \([Char]
_, IO a
action) ->
             Int -> IO () -> (() -> IO ()) -> (() -> IO a) -> IO (Stats, Stats)
forall a b c.
Int -> IO a -> (a -> IO b) -> (a -> IO c) -> IO (Stats, Stats)
benchmark Int
iters (() -> IO ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()) (IO () -> () -> IO ()
forall a b. a -> b -> a
const (IO () -> () -> IO ()) -> IO () -> () -> IO ()
forall a b. (a -> b) -> a -> b
$ () -> IO ()
forall (m :: * -> *) a. Monad m => a -> m a
return ()) (IO a -> () -> IO a
forall a b. a -> b -> a
const IO a
action)
  [([Char], Stats)] -> IO ()
printStatsSummaries ([([Char], Stats)] -> IO ()) -> [([Char], Stats)] -> IO ()
forall a b. (a -> b) -> a -> b
$ [[Char]] -> [Stats] -> [([Char], Stats)]
forall a b. [a] -> [b] -> [(a, b)]
zip ((([Char], IO a) -> [Char]) -> [([Char], IO a)] -> [[Char]]
forall a b. (a -> b) -> [a] -> [b]
map ([Char], IO a) -> [Char]
forall a b. (a, b) -> a
fst [([Char], IO a)]
bms) (((Stats, Stats) -> Stats) -> [(Stats, Stats)] -> [Stats]
forall a b. (a -> b) -> [a] -> [b]
map (Stats, Stats) -> Stats
forall a b. (a, b) -> a
fst [(Stats, Stats)]
results)

-- ---------------------------------------------------------------------
-- Benchmark stats

-- | Execution time statistics for a benchmark.  All measured times
-- are given in milliseconds.
data Stats = Stats
    { Stats -> Double
min         :: Double
    -- ^ Shortest execution time.
    , Stats -> Double
mean        :: Double
    -- ^ Mean execution time.
    , Stats -> Double
stddev      :: Double
    -- ^ Execution time standard deviation.
    , Stats -> Double
median      :: Double
    -- ^ Median execution time.
    , Stats -> Double
max         :: Double
    -- ^ Longest execution time.
    , Stats -> [(Int, Double)]
percentiles :: [(Int, Double)]
    -- ^ Execution time divided into percentiles.  The first component
    -- of the pair is the percentile given as an integer between 0 and
    -- 100, inclusive.  The second component is the execution time of
    -- the slowest iteration within the percentile.
    } deriving Int -> Stats -> ShowS
[Stats] -> ShowS
Stats -> [Char]
(Int -> Stats -> ShowS)
-> (Stats -> [Char]) -> ([Stats] -> ShowS) -> Show Stats
forall a.
(Int -> a -> ShowS) -> (a -> [Char]) -> ([a] -> ShowS) -> Show a
showList :: [Stats] -> ShowS
$cshowList :: [Stats] -> ShowS
show :: Stats -> [Char]
$cshow :: Stats -> [Char]
showsPrec :: Int -> Stats -> ShowS
$cshowsPrec :: Int -> Stats -> ShowS
Show

-- ---------------------------------------------------------------------
-- Pretty-printing stats

-- | Prints detailed statistics.  Printed statistics include min,
-- mean, standard deviation, median, and max execution time.  Also
-- prints execution time percentiles.  Writes output to standard
-- output.
printDetailedStats :: Stats -> IO ()
printDetailedStats :: Stats -> IO ()
printDetailedStats Stats
stats = do
  Int -> Int -> IO ()
printSummaryHeader Int
0 Int
colWidth
  Int -> [Char] -> Stats -> IO ()
printSummary Int
colWidth [Char]
"" Stats
stats
  [Char] -> IO ()
putStrLn [Char]
""
  [Char] -> IO ()
putStrLn [Char]
"Percentiles (ms)"
  [Char] -> IO ()
putStr [Char]
psTbl
    where
      columns :: [(Int, Double)] -> [[Char]]
columns  = ((Int, Double) -> [Char]) -> [(Int, Double)] -> [[Char]]
forall a b. (a -> b) -> [a] -> [b]
map ((Int -> Double -> [Char]) -> (Int, Double) -> [Char]
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry ((Int -> Double -> [Char]) -> (Int, Double) -> [Char])
-> (Int -> Double -> [Char]) -> (Int, Double) -> [Char]
forall a b. (a -> b) -> a -> b
$ [Char] -> Int -> Double -> [Char]
forall r. PrintfType r => [Char] -> r
printf [Char]
" %3d%%  %5.3f")
      colWidth :: Int
colWidth = [Stats] -> Int
columnWidth [Stats
stats]
      psTbl :: [Char]
psTbl    = [[Char]] -> [Char]
unlines ([[Char]] -> [Char]) -> [[Char]] -> [Char]
forall a b. (a -> b) -> a -> b
$ [(Int, Double)] -> [[Char]]
columns (Stats -> [(Int, Double)]
percentiles Stats
stats)

-- | Prints a summary row for each benchmark with an associated label.
-- The summary contains the same statistics as in 'printDetailedStats'
-- except for the execution time percentiles.  Writes output to
-- standard output.
printStatsSummaries :: [(String, Stats)] -> IO ()
printStatsSummaries :: [([Char], Stats)] -> IO ()
printStatsSummaries [([Char], Stats)]
rows = do
  Int -> Int -> IO ()
printSummaryHeader Int
lblLen Int
colWidth
  [([Char], Stats)] -> (([Char], Stats) -> IO ()) -> IO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
t a -> (a -> m b) -> m ()
forM_ [([Char], Stats)]
rows ((([Char], Stats) -> IO ()) -> IO ())
-> (([Char], Stats) -> IO ()) -> IO ()
forall a b. (a -> b) -> a -> b
$ \([Char]
label, Stats
stats) ->
      Int -> [Char] -> Stats -> IO ()
printSummary Int
colWidth ([Char] -> Int -> ShowS
forall r. PrintfType r => [Char] -> r
printf [Char]
"%-*s" Int
lblLen ([Char]
label [Char] -> ShowS
forall a. [a] -> [a] -> [a]
++ [Char]
": ")) Stats
stats
    where
      labels :: [[Char]]
labels   = (([Char], Stats) -> [Char]) -> [([Char], Stats)] -> [[Char]]
forall a b. (a -> b) -> [a] -> [b]
map ([Char], Stats) -> [Char]
forall a b. (a, b) -> a
fst [([Char], Stats)]
rows
      results :: [Stats]
results  = (([Char], Stats) -> Stats) -> [([Char], Stats)] -> [Stats]
forall a b. (a -> b) -> [a] -> [b]
map ([Char], Stats) -> Stats
forall a b. (a, b) -> b
snd [([Char], Stats)]
rows
      lblLen :: Int
lblLen   = [Int] -> Int
forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
maximum (([Char] -> Int) -> [[Char]] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map [Char] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [[Char]]
labels) Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
2
      colWidth :: Int
colWidth = [Stats] -> Int
columnWidth [Stats]
results

-- | Column headers.
headers :: [String]
headers :: [[Char]]
headers = [[Char]
"min", [Char]
"mean", [Char]
"+/-sd", [Char]
"median", [Char]
"max"]

-- | Computes the minimum column width needed to print the results
-- table.
columnWidth :: [Stats] -> Int
columnWidth :: [Stats] -> Int
columnWidth = Int -> Int -> Int
forall a. Ord a => a -> a -> a
Prelude.max ([Int] -> Int
forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
maximum ([Int] -> Int) -> [Int] -> Int
forall a b. (a -> b) -> a -> b
$ ([Char] -> Int) -> [[Char]] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map [Char] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [[Char]]
headers) (Int -> Int) -> ([Stats] -> Int) -> [Stats] -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Int] -> Int
forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
maximum ([Int] -> Int) -> ([Stats] -> [Int]) -> [Stats] -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Stats -> Int) -> [Stats] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map Stats -> Int
width
    where
      width :: Stats -> Int
width (Stats Double
min' Double
mean' Double
sd Double
median' Double
max' [(Int, Double)]
_) =
          [Int] -> Int
forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
maximum ([Int] -> Int) -> [Int] -> Int
forall a b. (a -> b) -> a -> b
$ (Double -> Int) -> [Double] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map ([Char] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length ([Char] -> Int) -> (Double -> [Char]) -> Double -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([Char] -> Double -> [Char]
forall r. PrintfType r => [Char] -> r
printf [Char]
"%.3f" :: Double -> String))
                      [Double
min', Double
mean', Double
sd, Double
median', Double
max']

-- | Pad header with spaces up till desired width.
padHeader :: Int -> String -> String
padHeader :: Int -> ShowS
padHeader Int
w [Char]
s
    | Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
w       = [Char]
s
    | Int -> Bool
forall a. Integral a => a -> Bool
odd (Int
w Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
n) = Int -> Char -> [Char]
forall a. Int -> a -> [a]
replicate (Int
amt Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) Char
' ' [Char] -> ShowS
forall a. [a] -> [a] -> [a]
++ [Char]
s [Char] -> ShowS
forall a. [a] -> [a] -> [a]
++ Int -> Char -> [Char]
forall a. Int -> a -> [a]
replicate Int
amt Char
' '
    | Bool
otherwise   = Int -> Char -> [Char]
forall a. Int -> a -> [a]
replicate Int
amt Char
' ' [Char] -> ShowS
forall a. [a] -> [a] -> [a]
++ [Char]
s [Char] -> ShowS
forall a. [a] -> [a] -> [a]
++ Int -> Char -> [Char]
forall a. Int -> a -> [a]
replicate Int
amt Char
' '
    where
      n :: Int
n   = [Char] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Char]
s
      amt :: Int
amt = (Int
w Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
n) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` Int
2

-- | Print table headers.
printSummaryHeader :: Int -> Int -> IO ()
printSummaryHeader :: Int -> Int -> IO ()
printSummaryHeader Int
lblLen Int
colWidth = do
  [Char] -> IO ()
putStrLn [Char]
"Times (ms)"
  [Char] -> IO ()
putStr ([Char] -> IO ()) -> [Char] -> IO ()
forall a b. (a -> b) -> a -> b
$ Int -> Char -> [Char]
forall a. Int -> a -> [a]
replicate Int
lblLen Char
' ' [Char] -> ShowS
forall a. [a] -> [a] -> [a]
++ [Char]
" "
  [Char] -> IO ()
putStrLn ([Char] -> IO ()) -> [Char] -> IO ()
forall a b. (a -> b) -> a -> b
$ [Char] -> [[Char]] -> [Char]
forall a. [a] -> [[a]] -> [a]
intercalate [Char]
"  " ([[Char]] -> [Char]) -> [[Char]] -> [Char]
forall a b. (a -> b) -> a -> b
$ ShowS -> [[Char]] -> [[Char]]
forall a b. (a -> b) -> [a] -> [b]
map (Int -> ShowS
padHeader Int
colWidth) [[Char]]
headers

-- | Print a row showing a summary of the given stats.
printSummary :: Int -> String -> Stats -> IO ()
printSummary :: Int -> [Char] -> Stats -> IO ()
printSummary Int
w [Char]
label (Stats Double
min' Double
mean' Double
sd Double
median' Double
max' [(Int, Double)]
_) =
    [Char] -> IO ()
putStrLn ([Char] -> IO ()) -> [Char] -> IO ()
forall a b. (a -> b) -> a -> b
$ [Char]
-> [Char]
-> Int
-> Double
-> Int
-> Double
-> Int
-> Double
-> Int
-> Double
-> Int
-> Double
-> [Char]
forall r. PrintfType r => [Char] -> r
printf [Char]
"%s %*.3f  %*.3f  %*.3f  %*.3f  %*.3f"
             [Char]
label Int
w Double
min' Int
w Double
mean' Int
w Double
sd Int
w Double
median' Int
w Double
max'

-- ---------------------------------------------------------------------
-- Computing statistics

-- | Compute percentiles given a list of execution times in ascending
-- order.
percentiles' :: [Double] -> [(Int, Double)]
percentiles' :: [Double] -> [(Int, Double)]
percentiles' [Double]
xs = (Int -> (Int, Double)) -> [Int] -> [(Int, Double)]
forall a b. (a -> b) -> [a] -> [b]
map (\Int
p -> (Int
p, [Double]
xs [Double] -> Int -> Double
forall a. [a] -> Int -> a
!! Int -> Int
forall a a. (Integral a, Integral a) => a -> a
rank Int
p)) [Int]
ps
    where
      n :: Int
n      = [Double] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Double]
xs
      rank :: a -> a
rank a
p = Double -> a
forall a b. (RealFrac a, Integral b) => a -> b
ceiling ((Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
n Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
100) Double -> Double -> Double
forall a. Num a => a -> a -> a
* a -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral a
p :: Double) a -> a -> a
forall a. Num a => a -> a -> a
- a
1
      ps :: [Int]
ps     = [Int
50, Int
66, Int
75, Int
80, Int
90, Int
95, Int
98, Int
99, Int
100]

-- ---------------------------------------------------------------------
-- Internal utilities

-- | Converts picoseconds to milliseconds.
picosToMillis :: Integer -> Double
picosToMillis :: Integer -> Double
picosToMillis Integer
t = Integer -> Double
forall a b. (Real a, Fractional b) => a -> b
realToFrac Integer
t Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ (Double
10Double -> Int -> Double
forall a b. (Num a, Integral b) => a -> b -> a
^(Int
9 :: Int))

-- | Converts seconds to milliseconds.
secsToMillis :: NominalDiffTime -> Double
secsToMillis :: NominalDiffTime -> Double
secsToMillis NominalDiffTime
t = NominalDiffTime -> Double
forall a b. (Real a, Fractional b) => a -> b
realToFrac NominalDiffTime
t Double -> Double -> Double
forall a. Num a => a -> a -> a
* (Double
10Double -> Int -> Double
forall a b. (Num a, Integral b) => a -> b -> a
^(Int
3 :: Int))

-- For GHC 6.6 compatibility.

-- | @intercalate xs xss@ inserts the list @xs@ in between the lists
-- in @xss@ and concatenates the result.
intercalate :: [a] -> [[a]] -> [a]
intercalate :: [a] -> [[a]] -> [a]
intercalate [a]
xs = [[a]] -> [a]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([[a]] -> [a]) -> ([[a]] -> [[a]]) -> [[a]] -> [a]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [a] -> [[a]] -> [[a]]
forall a. a -> [a] -> [a]
intersperse [a]
xs