-- |
-- Copyright   : (c) 2020 Peter Duchovni
-- License     : BSD3-style (see LICENSE)
--
-- Maintainer  : Peter Duchovni <caufeminecraft+github@gmail.com>
--
-- Benchmark elemIndex, findIndex, elemIndices, and findIndices

module BenchIndices (benchIndices) where

import           Data.Foldable                         (foldMap)
import           Data.Maybe                            (listToMaybe)
import           Data.Monoid
import           Data.String
import           Test.Tasty.Bench
import           Prelude                               hiding (words, head, tail)
import           Data.Word                             (Word8)

import qualified Data.ByteString                       as S
import qualified Data.ByteString.Unsafe                as S


------------------------------------------------------------------------------
-- Benchmark
------------------------------------------------------------------------------

-- ASCII \n to ensure no typos
nl :: Word8
nl = 0xa
{-# INLINE nl #-}

-- non-inlined equality test
nilEq :: Word8 -> Word8 -> Bool
{-# NOINLINE nilEq #-}
nilEq = (==)

-- lines of 200 letters from a to e, followed by repeated letter f
absurdlong :: S.ByteString
absurdlong = (S.replicate 200 0x61 <> S.singleton nl
          <> S.replicate 200 0x62 <> S.singleton nl
          <> S.replicate 200 0x63 <> S.singleton nl
          <> S.replicate 200 0x64 <> S.singleton nl
          <> S.replicate 200 0x65 <> S.singleton nl)
          <> S.replicate 999999 0x66

benchIndices :: Benchmark
benchIndices = absurdlong `seq` bgroup "Indices"
    [ bgroup "ByteString strict first index" $
        [ bench "FindIndices" $ nf (listToMaybe . S.findIndices (== nl)) absurdlong
        , bench "ElemIndices" $ nf (listToMaybe . S.elemIndices     nl)  absurdlong
        , bench "FindIndex"   $ nf (S.findIndex (== nl)) absurdlong
        , bench "ElemIndex"   $ nf (S.elemIndex     nl)  absurdlong
        ]
    , bgroup "ByteString strict second index" $
        [ bench "FindIndices" $ nf (listToMaybe . drop 1 . S.findIndices (== nl)) absurdlong
        , bench "ElemIndices" $ nf (listToMaybe . drop 1 . S.elemIndices     nl)  absurdlong
        , bench "FindIndex"   $ nf bench_find_index_second absurdlong
        , bench "ElemIndex"   $ nf bench_elem_index_second absurdlong
        ]
    , bgroup "ByteString index equality inlining" $
        [ bench "FindIndices/inlined"     $ nf (S.findIndices    (== nl)) absurdlong
        , bench "FindIndices/non-inlined" $ nf (S.findIndices (nilEq nl)) absurdlong
        , bench "FindIndex/inlined"       $ nf (S.findIndex      (== nl)) absurdlong
        , bench "FindIndex/non-inlined"   $ nf (S.findIndex   (nilEq nl)) absurdlong
        ]
    ]

bench_find_index_second :: S.ByteString -> Maybe Int
bench_find_index_second bs =
  let isNl = (== nl)
   in case S.findIndex isNl bs of
        Just !i -> S.findIndex isNl (S.unsafeDrop (i+1) bs)
        Nothing -> Nothing
{-# INLINE bench_find_index_second #-}

bench_elem_index_second :: S.ByteString -> Maybe Int
bench_elem_index_second bs =
    case S.elemIndex nl bs of
        Just !i -> S.elemIndex nl (S.unsafeDrop (i+1) bs)
        Nothing -> Nothing
{-# INLINE bench_elem_index_second #-}
