-- | Text builder helpers.
--
-- Code for printing numbers in 3 or 4 characters adapted from `tasty-bench`:
--   https://github.com/Bodigrim/tasty-bench/blob/412ae68dbd9582e0b19ff5f2c7f9ead25e104cce/Test/Tasty/Bench.hs#L795
module ParkBench.Builder
  ( Builder,
    build,
    bytes4,
    c,
    cs,
    Builder.decimal,
    double,
    double4,
    ParkBench.Builder.empty,
    nanos3,
    nanos4,
    ParkBench.Builder.null,
    percentage,
    sepBy,
    t,
    word3,
  )
where

import qualified Data.List as List
import qualified Data.Text.Lazy as LazyText
import Data.Text.Lazy.Builder (Builder)
import qualified Data.Text.Lazy.Builder as Builder
import qualified Data.Text.Lazy.Builder.Int as Builder (decimal)
import qualified Data.Text.Lazy.Builder.RealFloat as Builder
import ParkBench.Prelude

build :: Builder -> Text
build :: Builder -> Text
build =
  Text -> Text
LazyText.toStrict (Text -> Text) -> (Builder -> Text) -> Builder -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> Text
Builder.toLazyText

-- | Render nanoseconds, trying to fit into 4 characters.
bytes4 :: Double -> Builder
bytes4 :: Double -> Builder
bytes4 Double
b
  | Double
b Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
0.5 = Builder
ParkBench.Builder.empty
  | Double
b Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
995 = Int -> Double -> Builder
double Int
0 Double
b Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
" b"
  | Double
b Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
9_950 = Int -> Double -> Builder
double Int
2 Double
kb Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
" kb"
  | Double
b Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
99_500 = Int -> Double -> Builder
double Int
1 Double
kb Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
" kb"
  | Double
b Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
995_000 = Int -> Double -> Builder
double Int
0 Double
kb Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
" kb"
  | Double
b Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
9_950_000 = Int -> Double -> Builder
double Int
2 Double
mb Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
" mb"
  | Double
b Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
99_500_000 = Int -> Double -> Builder
double Int
1 Double
mb Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
" mb"
  | Double
b Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
995_000_000 = Int -> Double -> Builder
double Int
0 Double
mb Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
" mb"
  | Double
b Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
9_950_000_000 = Int -> Double -> Builder
double Int
2 Double
gb Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
" gb"
  | Double
b Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
99_500_000_000 = Int -> Double -> Builder
double Int
1 Double
gb Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
" gb"
  | Bool
otherwise = Int -> Double -> Builder
double Int
0 Double
gb Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
" gb"
  where
    kb :: Double
kb = Double
b Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
1_000
    mb :: Double
mb = Double
b Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
1_000_000
    gb :: Double
gb = Double
b Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
1_000_000_000

c :: Char -> Builder
c :: Char -> Builder
c =
  Char -> Builder
Builder.singleton
{-# INLINE c #-}

cs :: Int -> Char -> Builder
cs :: Int -> Char -> Builder
cs Int
n =
  [Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat ([Builder] -> Builder) -> (Char -> [Builder]) -> Char -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Builder -> [Builder]
forall a. Int -> a -> [a]
replicate Int
n (Builder -> [Builder]) -> (Char -> Builder) -> Char -> [Builder]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> Builder
Builder.singleton

double :: Int -> Double -> Builder
double :: Int -> Double -> Builder
double Int
i =
  FPFormat -> Maybe Int -> Double -> Builder
forall a. RealFloat a => FPFormat -> Maybe Int -> a -> Builder
Builder.formatRealFloat FPFormat
Builder.Fixed (Int -> Maybe Int
forall a. a -> Maybe a
Just Int
i)

-- | Render a double, trying to fit into 4 characters.
double4 :: Double -> Builder
double4 :: Double -> Builder
double4 Double
n
  | Double
a Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
0.005 = Builder
ParkBench.Builder.empty
  | Double
a Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
9.95 = Int -> Double -> Builder
double Int
2 Double
n
  | Double
a Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
99.5 = Int -> Double -> Builder
double Int
1 Double
n
  | Double
a Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
995 = Int -> Double -> Builder
double Int
0 Double
n
  | Double
a Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
9_950 = Int -> Double -> Builder
double Int
1 Double
k Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"k"
  | Double
a Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
995_000 = Int -> Double -> Builder
double Int
0 Double
k Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"k"
  | Double
a Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
9_950_000 = Int -> Double -> Builder
double Int
1 Double
m Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"m"
  | Double
a Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
995_000_000 = Int -> Double -> Builder
double Int
0 Double
m Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"m"
  | Double
a Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
9_950_000_000 = Int -> Double -> Builder
double Int
1 Double
b Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"b"
  | Bool
otherwise = Int -> Double -> Builder
double Int
0 Double
n Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"b"
  where
    a :: Double
a = Double -> Double
forall a. Num a => a -> a
abs Double
n
    k :: Double
k = Double
n Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
1_000
    m :: Double
m = Double
n Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
1_000_000
    b :: Double
b = Double
n Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
1_000_000_000

empty :: Builder
empty :: Builder
empty =
  Builder
forall a. Monoid a => a
mempty
{-# INLINE empty #-}

-- | Render nanoseconds, trying to fit into 3 characters.
nanos3 :: Rational -> Builder
nanos3 :: Rational -> Builder
nanos3 (Rational -> Double
r2d -> Double
ns)
  | Double
ns Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
0.5 = Builder
ParkBench.Builder.empty
  | Double
ns Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
995 = Int -> Double -> Builder
double Int
0 Double
ns Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
" ns"
  | Double
ns Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
9_950 = Int -> Double -> Builder
double Int
1 Double
us Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
" µs"
  | Double
ns Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
995_000 = Int -> Double -> Builder
double Int
0 Double
us Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
" µs"
  | Double
ns Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
9_950_000 = Int -> Double -> Builder
double Int
1 Double
ms Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
" ms"
  | Double
ns Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
995_000_000 = Int -> Double -> Builder
double Int
0 Double
ms Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
" ms"
  | Double
ns Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
9_950_000_000 = Int -> Double -> Builder
double Int
1 Double
s Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
" s"
  | Bool
otherwise = Int -> Double -> Builder
double Int
0 Double
s Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
" s"
  where
    us :: Double
us = Double
ns Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
1_000
    ms :: Double
ms = Double
ns Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
1_000_000
    s :: Double
s = Double
ns Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
1_000_000_000

-- | Render nanoseconds, trying to fit into 4 characters.
nanos4 :: Double -> Builder
nanos4 :: Double -> Builder
nanos4 Double
ns
  | Double
ns Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
0.5 = Builder
ParkBench.Builder.empty
  | Double
ns Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
995 = Int -> Double -> Builder
double Int
0 Double
ns Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
" ns"
  | Double
ns Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
9_950 = Int -> Double -> Builder
double Int
2 Double
us Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
" µs"
  | Double
ns Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
99_500 = Int -> Double -> Builder
double Int
1 Double
us Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
" µs"
  | Double
ns Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
995_000 = Int -> Double -> Builder
double Int
0 Double
us Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
" µs"
  | Double
ns Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
9_950_000 = Int -> Double -> Builder
double Int
2 Double
ms Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
" ms"
  | Double
ns Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
99_500_000 = Int -> Double -> Builder
double Int
1 Double
ms Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
" ms"
  | Double
ns Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
995_000_000 = Int -> Double -> Builder
double Int
0 Double
ms Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
" ms"
  | Double
ns Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
9_950_000_000 = Int -> Double -> Builder
double Int
2 Double
s Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
" s"
  | Double
ns Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
99_500_000_000 = Int -> Double -> Builder
double Int
1 Double
s Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
" s"
  | Bool
otherwise = Int -> Double -> Builder
double Int
0 Double
s Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
" s"
  where
    us :: Double
us = Double
ns Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
1_000
    ms :: Double
ms = Double
ns Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
1_000_000
    s :: Double
s = Double
ns Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
1_000_000_000

-- | /O(n)/.
null :: Builder -> Bool
null :: Builder -> Bool
null =
  Text -> Bool
LazyText.null (Text -> Bool) -> (Builder -> Text) -> Builder -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> Text
Builder.toLazyText

percentage :: Double -> Builder
percentage :: Double -> Builder
percentage ((Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
100) -> Double
n)
  | Double
a Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
5 Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
1000 = Builder
ParkBench.Builder.empty
  | Double
a Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
995 Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
100 = Int -> Double -> Builder
double Int
2 Double
n Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"%"
  | Double
a Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
100 = Int -> Double -> Builder
double Int
1 Double
n Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"%"
  | Bool
otherwise = Int -> Double -> Builder
double Int
0 Double
n Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"%"
  where
    a :: Double
a = Double -> Double
forall a. Num a => a -> a
abs Double
n

sepBy :: [Builder] -> Builder -> Builder
sepBy :: [Builder] -> Builder -> Builder
sepBy [Builder]
xs Builder
x =
  [Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat (Builder -> [Builder] -> [Builder]
forall a. a -> [a] -> [a]
List.intersperse Builder
x [Builder]
xs)

t :: Text -> Builder
t :: Text -> Builder
t =
  Text -> Builder
Builder.fromText
{-# INLINE t #-}

-- | Render a word, trying to fit into 3 characters.
word3 :: Word64 -> Builder
word3 :: Word64 -> Builder
word3 (Word64 -> Double
w2d -> Double
n)
  | Double
n Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
995 = Int -> Double -> Builder
double Int
0 Double
n
  | Double
n Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
9_950 = Int -> Double -> Builder
double Int
1 Double
k Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"k"
  | Double
n Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
995_000 = Int -> Double -> Builder
double Int
0 Double
k Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"k"
  | Double
n Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
9_950_000 = Int -> Double -> Builder
double Int
1 Double
m Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"m"
  | Double
n Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
995_000_000 = Int -> Double -> Builder
double Int
0 Double
m Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"m"
  | Double
n Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
9_950_000_000 = Int -> Double -> Builder
double Int
1 Double
b Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"b"
  | Bool
otherwise = Int -> Double -> Builder
double Int
0 Double
b Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"b"
  where
    k :: Double
k = Double
n Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
1_000
    m :: Double
m = Double
n Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
1_000_000
    b :: Double
b = Double
n Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
1_000_000_000