content: {-# LANGUAGE BangPatterns #-}
  {-# LANGUAGE OverloadedStrings #-}
  
  module Main
    ( main
    ) where
  
  import Criterion.Main (bgroup, bench, defaultMain, nf)
  
  import qualified Data.Text as Text
  import qualified Data.Text.AhoCorasick.Automaton as Aho
  import qualified Data.Text.BoyerMoore.Automaton as BoyerMoore
  import qualified Data.Text.BoyerMooreCI.Automaton as BoyerMooreCI
  import qualified Data.Text.Utf8 as Utf8
  
  --
  -- @
  -- stack bench alfred-margaret:bm --ba '--output bm.html'
  -- @
  
  main :: IO ()
  main = defaultMain
    [
      bgroup "Big needle, large haystack" $
        benchVariants needle1 haystack1
  
    , bgroup "Big needle, small haystack" $
        benchVariants needle2 haystack2
  
    , bgroup "Small needle, large haystack" $
        benchVariants needle3 haystack3
  
    , bgroup "Small needle, small haystack" $
        benchVariants needle4 haystack4
  
    , bgroup "Cyrillic, medium needle, large haystack" $
        benchVariants needle5 haystack5
  
    ]
    where
      benchVariants needle haystack =
        [ bench "Count Aho" $ nf (ahoCount needle) haystack
        , bench "Count Aho CI" $ nf (ahociCount needle) haystack
        , bench "Count BoyerMoore" $ nf (bmCount needle) haystack
        , bench "Count BoyerMoore CI" $ nf (bmciCount needle) haystack
        , bench "Count Aho CI multineedle" $ nf (ahocimultiCount needle) haystack
        , bench "Count Text.count" $ nf (Text.count needle) haystack
        -- This naive case insensitive count is really slow. We leave it out
        -- because it throws off the plot scale.
        -- , bench "Count Text.count . lowerUtf8" $ nf (Text.count needle . Utf8.lowerUtf8) haystack
        , bench "Contains Aho" $ nf (ahoContains needle) haystack
        , bench "Contains Aho CI" $ nf (ahociContains needle) haystack
        , bench "Contains BoyerMoore" $ nf (bmContains needle) haystack
        , bench "Contains BoyerMoore CI" $ nf (bmciContains needle) haystack
        , bench "Contains Text.isInfixOf" $ nf (Text.isInfixOf needle) haystack
        ]
  
  
  -- | The NOINLINE annotation is used so that the needle doesn't get inlined at compile time. The
  -- function does get partially applied with the needle so that the automaton is constructed only
  -- once.
  ahoCount :: Text.Text -> Text.Text -> Int
  {-# NOINLINE ahoCount #-}
  ahoCount needle =
    let
      !automaton = Aho.build [(needle, ())]
      onMatch !n _match = Aho.Step (n + 1)
    in
      -- Eta expansion of the haystack variable is necessary for proper inlining of runText
      \haystack -> Aho.runText 0 onMatch automaton haystack
  
  ahoContains :: Text.Text -> Text.Text -> Bool
  {-# NOINLINE ahoContains #-}
  ahoContains needle =
    let
      !automaton = Aho.build [(needle, ())]
      onMatch _ _match = Aho.Done True
    in
      \haystack -> Aho.runText False onMatch automaton haystack
  
  ahociCount :: Text.Text -> Text.Text -> Int
  {-# NOINLINE ahociCount #-}
  ahociCount needle =
    let
      !automaton = Aho.build [(needle, ())]
      onMatch !n _match = Aho.Step (n + 1)
    in
      \haystack -> Aho.runLower 0 onMatch automaton haystack
  
  
  ahocimultiCount :: Text.Text -> Text.Text -> Int
  {-# NOINLINE ahocimultiCount #-}
  ahocimultiCount needle =
    let
      !automaton = Aho.build $ map (\n -> (n, ())) $ Aho.needleCasings needle
      onMatch !n _match = Aho.Step (n + 1)
    in
      \haystack -> Aho.runText 0 onMatch automaton haystack
  
  ahociContains :: Text.Text -> Text.Text -> Bool
  {-# NOINLINE ahociContains #-}
  ahociContains needle =
    let
      !automaton = Aho.build [(needle, ())]
      onMatch _ _match = Aho.Done True
    in
      \haystack -> Aho.runLower False onMatch automaton haystack
  
  bmciCount :: Text.Text -> Text.Text -> Int
  {-# NOINLINE bmciCount #-}
  bmciCount needle =
    let
      !automaton = BoyerMooreCI.buildAutomaton needle
      onMatch !n _matchStart _matchEnd = BoyerMooreCI.Step (n + 1)
    in
      \haystack -> BoyerMooreCI.runText 0 onMatch automaton haystack
  
  bmciContains :: Text.Text -> Text.Text -> Bool
  {-# NOINLINE bmciContains #-}
  bmciContains needle =
    let
      !automaton = BoyerMooreCI.buildAutomaton needle
      onMatch _ _matchStart _matchEnd = BoyerMooreCI.Done True
    in
      \haystack -> BoyerMooreCI.runText False onMatch automaton haystack
  
  bmCount :: Text.Text -> Text.Text -> Int
  {-# NOINLINE bmCount #-}
  bmCount needle =
    let
      !automaton = BoyerMoore.buildAutomaton needle
      onMatch !n _match = BoyerMoore.Step (n + 1)
    in
      \haystack -> BoyerMoore.runText 0 onMatch automaton haystack
  
  bmContains :: Text.Text -> Text.Text -> Bool
  {-# NOINLINE bmContains #-}
  bmContains needle =
    let
      !automaton = BoyerMoore.buildAutomaton needle
      onMatch _ _match = BoyerMoore.Done True
    in
      \haystack -> BoyerMoore.runText False onMatch automaton haystack
  
  
  needle1 :: Text.Text
  needle1 = "necessitatibus"
  
  haystack1 :: Text.Text
  haystack1 =
    Text.unlines
      [ "Lorem ipsum dolor sit amet. Et sint voluptatibus est vero maxime vel explicabo reprehenderit non molestiae quisquam sit dolores facere qui cumque quibusdam 33 impedit deserunt! Aut libero harum et quis quasi qui cupiditate autem."
      , "Qui quia totam non rerum eveniet sed tempora repellendus ab enim consequatur eum quaerat iste. Sed amet nihil sed voluptate aspernatur ut rerum facilis est officia earum aut molestiae tenetur non autem nulla. Qui deserunt necessitatibus ab accusamus doloremque non sint aspernatur."
      , "Et officia illum non quaerat obcaecati cum accusamus minus rem quae quis sed rerum omnis sed inventore quasi. Qui totam deserunt sit minima ullam sit debitis dolores. Est debitis explicabo ut temporibus corporis nam harum dolore est fuga numquam non exercitationem Quis cum amet fuga."
      , "Aut incidunt provident et sequi nulla est molestias perferendis. Hic exercitationem modi ex optio cumque nam voluptate debitis nam iste consequatur non nihil rerum ut accusantium nihil. Sed ullam maiores nobis dolorem sit galisum maiores eum reprehenderit maxime sed galisum placeat cum molestiae quia id similique velit. Aut quasi autem non illo reiciendis sit ullam tempora."
      , "Ut corporis exercitationem sed dicta autem ut voluptatem dolorem vel dolores dolores. Qui ipsam quisquam sed facere porro rem autem necessitatibus nam beatae quisquam. Quo voluptatem optio hic quod reprehenderit ut nostrum voluptatem."
      , "Et consequuntur quia vel unde laudantium non voluptatum magnam. Ut quam autem rem fugit quia ut assumenda error quam amet in omnis quia ut rerum soluta sed consequatur fuga. Ut blanditiis quia et facilis ratione aut blanditiis dolorum aut itaque excepturi eos iste incidunt qui blanditiis velit et magni autem."
      ]
  
  needle2 :: Text.Text
  needle2 = "necessitatibus"
  
  haystack2 :: Text.Text
  haystack2 =
    Text.unlines
      [ "Lorem ipsum dolor sit amet. Et sint voluptatibus est vero maxime vel explicabo reprehenderit non molestiae quisquam sit dolores facere qui cumque quibusdam 33 impedit deserunt! Aut libero harum et quis quasi qui cupiditate autem."
      ]
  
  needle3 :: Text.Text
  needle3 = "sit"
  
  haystack3 :: Text.Text
  haystack3 =
    Text.unlines
      [ "Lorem ipsum dolor sit amet. Et sint voluptatibus est vero maxime vel explicabo reprehenderit non molestiae quisquam sit dolores facere qui cumque quibusdam 33 impedit deserunt! Aut libero harum et quis quasi qui cupiditate autem."
      , "Qui quia totam non rerum eveniet sed tempora repellendus ab enim consequatur eum quaerat iste. Sed amet nihil sed voluptate aspernatur ut rerum facilis est officia earum aut molestiae tenetur non autem nulla. Qui deserunt necessitatibus ab accusamus doloremque non sint aspernatur."
      , "Et officia illum non quaerat obcaecati cum accusamus minus rem quae quis sed rerum omnis sed inventore quasi. Qui totam deserunt sit minima ullam sit debitis dolores. Est debitis explicabo ut temporibus corporis nam harum dolore est fuga numquam non exercitationem Quis cum amet fuga."
      , "Aut incidunt provident et sequi nulla est molestias perferendis. Hic exercitationem modi ex optio cumque nam voluptate debitis nam iste consequatur non nihil rerum ut accusantium nihil. Sed ullam maiores nobis dolorem sit galisum maiores eum reprehenderit maxime sed galisum placeat cum molestiae quia id similique velit. Aut quasi autem non illo reiciendis sit ullam tempora."
      , "Ut corporis exercitationem sed dicta autem ut voluptatem dolorem vel dolores dolores. Qui ipsam quisquam sed facere porro rem autem necessitatibus nam beatae quisquam. Quo voluptatem optio hic quod reprehenderit ut nostrum voluptatem."
      , "Et consequuntur quia vel unde laudantium non voluptatum magnam. Ut quam autem rem fugit quia ut assumenda error quam amet in omnis quia ut rerum soluta sed consequatur fuga. Ut blanditiis quia et facilis ratione aut blanditiis dolorum aut itaque excepturi eos iste incidunt qui blanditiis velit et magni autem."
      ]
  
  needle4 :: Text.Text
  needle4 = "sit"
  
  haystack4 :: Text.Text
  haystack4 =
    Text.unlines
      [ "Lorem ipsum dolor sit amet. Et sint voluptatibus est vero maxime vel explicabo reprehenderit non molestiae quisquam sit dolores facere qui cumque quibusdam 33 impedit deserunt! Aut libero harum et quis quasi qui cupiditate autem."
      ]
  
  
  
  needle5 :: Text.Text
  needle5 = "сусципиантур"
  
  -- Cyrillic lipsum from https://generator.lorem-ipsum.info/_russian
  haystack5 :: Text.Text
  haystack5 =
    Text.unlines
      [ "Лорем ипсум долор сит амет, ин вис вирис маиорум инсоленс, алиа агам иудицабит цу нец, ут адмодум инцидеринт еффициантур цум. Вих те номинави диспутатиони, не дуо путент дицунт. Дебет репудиандае яуо ан, вих тамяуам репудиандае цонцлусионемяуе но, цу иус регионе урбанитас. Персиус дицерет форенсибус ат цум, еум яуаеяуе аппеллантур еу. Хас вивендо цонсететур те, ад хас меис пхаедрум, репудиаре демоцритум вих еу."
      , "Пер ут нулла минимум цонтентионес, сеа ат мунере алтера медиоцрем. Но вим лабитур цонсететур, нам ут ипсум фацилис феугаит. Не дицат феугиат аппареат нам, дуис адиписцинг ех еам. Ут еуисмод веритус интерпретарис усу. Сед цасе дицунт ехпетендис ех. Ад лудус урбанитас вим."
      , "Перфецто инсоленс ут меи, адмодум ментитум партиендо усу ат. Ин солеат еирмод аперири дуо, еи мазим фацилиси делицата сед. Дицо неглегентур ут хис, еи хис афферт аудире. Ат аццусата глориатур вел."
      , "Персиус омиттам при ад, цонгуе воцент репудиаре усу ет. Ат детрахит салутатус вим. Еа сеа яуис тациматес. Ид фацер дицтас еум, вери дицам еум еу, при ид тантас омиттам. Суавитате персецути еам те, ет яуот омиттантур ест."
      , "Еа тациматес евертитур дуо, меа ин вениам ассентиор. Еос ессе мунди аццусата цу. Ат партем медиоцрем иус, ан мел алии плацерат. Еам еи суммо ерант тинцидунт. Меа еа алиа сцрибентур."
      , "Еи вих малис омиттам сплендиде, ид витае ностер алияуандо еам, те новум репудиаре пер. Вис цу еррор алтерум садипсцинг, ан ерат фацете аудире ест. Меи ат виде ассуеверит. Вис либрис популо ид, ат хис симилияуе репудиандае, нибх салутатус ех сеа."
      , "Сеа пондерум адиписцинг ех, ет нонумы анциллае вих. Цу иллум ессент медиоцрем вис, еу мовет мунди еос, яуо зрил долорем партиендо но. Хис ат диам цоммодо демоцритум, вери цаусае инцидеринт усу ин. Темпорибус диссентиунт вис еа, ех цум цонсул поссим алтерум. Вим ин веро нулла, еиус реяуе ех меа."
      , "Омнес трацтатос цу еос. Пер ехерци модератиус еа. Нец оффендит сусципит ех. Пер ех постеа фацете, те еум еиус игнота легендос. Ат поссе воцибус пер, ин про лаудем сплендиде цотидиеяуе, но адхуц инермис аццоммодаре еос. Ин денияуе малуиссет еам, ин яуис доцтус нец."
      , "Идяуе елояуентиам еу яуи, витае интерессет не вис, хис диссентиет сусципиантур цу. Тибияуе елаборарет еам еа. Оратио вивендум цонсулату ид пер. Еа путант еррорибус сит, цум ад алияуид аццоммодаре. Алтерум делецтус алияуандо еу иус, ан иус поссит персиус тибияуе. Сеа не нумяуам фуиссет, ат виси аудире при. Суас граеце цопиосае дуо ан, но мунди опортере цонвенире яуи."
      , "Мел фугит путант опортере но, цопиосае оффициис еос еа. Вис инвидунт еффициенди ат, те луцилиус сцрибентур хис, вих не поссит вертерем. Дуо омнис дицат вирис еи, ет адхуц цетеро вис. Еа вим утинам инвенире, вих ин еяуидем ассентиор."
      ]
  