<commit_before>
module Benchotron.Core
  ( Benchmark()
  , BenchmarkF()
  , BenchmarkFunction()
  , mkBenchmark
  , unpackBenchmark
  , benchFn
  , benchFn'
  , runBenchmark
  , runBenchmarkF
  , BenchM()
  , runBenchM
  , BenchEffects()
  , BenchmarkResult()
  , ResultSeries()
  , DataPoint()
  ) where

import Prelude
import Data.Exists
import Data.Identity
import Data.Tuple
import Data.Array (filter, (..), length, replicateM, zip)
import Data.Array.Unsafe (head)
import Data.String (joinWith)
import Data.Traversable (for)
import Data.Date (Now())
import Data.Date.Locale (Locale())
import Control.Apply ((<*))
import Control.Monad.State.Trans (StateT(), evalStateT)
import Control.Monad.State.Class (get, put)
import Control.Monad.Trans (lift)
import Control.Monad.Eff (Eff())
import Control.Monad.Eff.Exception (EXCEPTION(), Error(), catchException,
                                    throwException, message, error)
import Node.FS (FS())
import Control.Monad.Eff.Console (CONSOLE())
import Control.Monad.Eff.Random  (RANDOM())
import Test.QuickCheck.Gen (Gen(), GenState(), runGen)

import Benchotron.StdIO
import Benchotron.BenchmarkJS
import Benchotron.Utils

-- | A value representing a benchmark to be performed. The type parameter 'e'
-- | is provided to allow you to supply a random value generator with arbitrary
-- | effects, if you need to. The type parameter 'a' is the type of the input
-- | to each of the competing functions in the benchmark.
-- |
-- | **Attributes**
-- |
-- | * `slug`: An identifier for the benchmark. Used for filenames.
-- | * `title`: The title of the benchmark.
-- | * `sizes`: An array of numbers representing each input size you would like
-- |   your benchmark to be run with. The interpretation of 'size' depends on
-- |   the specific case; if the input is an array, for example, the size would
-- |   usually be the number of elements in the array.
-- | * `sizeInterpretation`: A `String` with a human-readable description of
-- |   how the size is meant to be interpreted in this specific case.
-- | * `inputsPerSize`: The number of inputs to be generated for each each
-- |   input size. Often it's acceptable to set this to 1. By using higher
-- |   values, you can have more confidence in your results; however, your
-- |   benchmarks will also take much longer to run.
-- | * `gen`: An Eff action which should produce a random input of the given
-- |   argument size when executed.
-- | * `functions`: An array of competing functions to be benchmarked.
type BenchmarkF a =
  { slug               :: String
  , title              :: String
  , sizes              :: Array Int
  , sizeInterpretation :: String
  , inputsPerSize      :: Int
  , gen                :: Int -> Gen a
  , functions          :: Array (BenchmarkFunction a)
  }

-- This is only necessary because psc doesn't support partially applied type
-- synonyms.
newtype BenchmarkFF a = BenchmarkFF (BenchmarkF a)

newtype Benchmark = Benchmark (Exists (BenchmarkFF))

mkBenchmark :: forall a. BenchmarkF a -> Benchmark
mkBenchmark = Benchmark <<< mkExists <<< BenchmarkFF

unpackBenchmark :: forall r. (forall a. BenchmarkF a -> r) -> Benchmark -> r
unpackBenchmark f (Benchmark b) = runExists f' b
  where
  f' :: forall a. BenchmarkFF a -> r
  f' (BenchmarkFF b') = f b'

newtype BenchmarkFunction a = BenchmarkFunction (Exists (BenchmarkFunctionF a))

newtype BenchmarkFunctionF a b = BenchmarkFunctionF
  { name :: String
  , fn :: b -> Any
  , before :: a -> b
  }

-- | Create a `BenchmarkFunction`, given a name and a function to be
-- | benchmarked.
benchFn :: forall a r. String -> (a -> r) -> BenchmarkFunction a
benchFn name fn = benchFn' name fn id

-- | Create a `BenchmarkFunction`. Like `benchFn`, except that it accepts a
-- | third argument which will be used to preprocess the input, before starting
-- | the benchmark. This is useful if you want to compare two functions which
-- | have different argument types.
benchFn' :: forall a b r. String -> (b -> r) -> (a -> b) -> BenchmarkFunction a
benchFn' name fn before =
  BenchmarkFunction $ mkExists $ BenchmarkFunctionF
    { name: name, fn: toAny <<< fn, before: before }

getName :: forall a. BenchmarkFunction a -> String
getName (BenchmarkFunction f) = runExists go f
  where
  go :: forall b. BenchmarkFunctionF a b -> String
  go (BenchmarkFunctionF o) = o.name

type BenchM e a = StateT GenState (Eff (BenchEffects e)) a

runBenchM :: forall e a. BenchM e a -> GenState -> Eff (BenchEffects e) a
runBenchM = evalStateT

-- | Use the given generator to generate a random value, using (and modifying)
-- | the state of the BenchM computation.
stepGen :: forall e a. Gen a -> BenchM e a
stepGen gen = do
  st <- get
  let out = runGen gen st
  put out.state
  return out.value

runBenchmark :: forall e.
  Benchmark ->
  -- ^ The Benchmark to be run.
  (Int -> Int -> BenchM e Unit) ->
  -- ^ Callback for when the size changes; the arguments are current size index
  --   (1-based) , and the current size.
  BenchM e BenchmarkResult
runBenchmark = unpackBenchmark runBenchmarkF

runBenchmarkF :: forall e a.<commit_msg>Fix compile error introduced by updated quickcheck<commit_after>
module Benchotron.Core
  ( Benchmark()
  , BenchmarkF()
  , BenchmarkFunction()
  , mkBenchmark
  , unpackBenchmark
  , benchFn
  , benchFn'
  , runBenchmark
  , runBenchmarkF
  , BenchM()
  , runBenchM
  , BenchEffects()
  , BenchmarkResult()
  , ResultSeries()
  , DataPoint()
  ) where

import Prelude
import Data.Exists
import Data.Identity
import Data.Tuple
import Data.Array (filter, (..), length, replicateM, zip)
import Data.Array.Unsafe (head)
import Data.String (joinWith)
import Data.Traversable (for)
import Data.Date (Now())
import Data.Date.Locale (Locale())
import Control.Apply ((<*))
import Control.Monad.State.Trans (StateT(), evalStateT)
import Control.Monad.State.Class (get, put)
import Control.Monad.Trans (lift)
import Control.Monad.Eff (Eff())
import Control.Monad.Eff.Exception (EXCEPTION(), Error(), catchException,
                                    throwException, message, error)
import Node.FS (FS())
import Control.Monad.Eff.Console (CONSOLE())
import Control.Monad.Eff.Random  (RANDOM())
import Test.QuickCheck.Gen (Gen(), GenState(), runGen)

import Benchotron.StdIO
import Benchotron.BenchmarkJS
import Benchotron.Utils

-- | A value representing a benchmark to be performed. The type parameter 'e'
-- | is provided to allow you to supply a random value generator with arbitrary
-- | effects, if you need to. The type parameter 'a' is the type of the input
-- | to each of the competing functions in the benchmark.
-- |
-- | **Attributes**
-- |
-- | * `slug`: An identifier for the benchmark. Used for filenames.
-- | * `title`: The title of the benchmark.
-- | * `sizes`: An array of numbers representing each input size you would like
-- |   your benchmark to be run with. The interpretation of 'size' depends on
-- |   the specific case; if the input is an array, for example, the size would
-- |   usually be the number of elements in the array.
-- | * `sizeInterpretation`: A `String` with a human-readable description of
-- |   how the size is meant to be interpreted in this specific case.
-- | * `inputsPerSize`: The number of inputs to be generated for each each
-- |   input size. Often it's acceptable to set this to 1. By using higher
-- |   values, you can have more confidence in your results; however, your
-- |   benchmarks will also take much longer to run.
-- | * `gen`: An Eff action which should produce a random input of the given
-- |   argument size when executed.
-- | * `functions`: An array of competing functions to be benchmarked.
type BenchmarkF a =
  { slug               :: String
  , title              :: String
  , sizes              :: Array Int
  , sizeInterpretation :: String
  , inputsPerSize      :: Int
  , gen                :: Int -> Gen a
  , functions          :: Array (BenchmarkFunction a)
  }

-- This is only necessary because psc doesn't support partially applied type
-- synonyms.
newtype BenchmarkFF a = BenchmarkFF (BenchmarkF a)

newtype Benchmark = Benchmark (Exists (BenchmarkFF))

mkBenchmark :: forall a. BenchmarkF a -> Benchmark
mkBenchmark = Benchmark <<< mkExists <<< BenchmarkFF

unpackBenchmark :: forall r. (forall a. BenchmarkF a -> r) -> Benchmark -> r
unpackBenchmark f (Benchmark b) = runExists f' b
  where
  f' :: forall a. BenchmarkFF a -> r
  f' (BenchmarkFF b') = f b'

newtype BenchmarkFunction a = BenchmarkFunction (Exists (BenchmarkFunctionF a))

newtype BenchmarkFunctionF a b = BenchmarkFunctionF
  { name :: String
  , fn :: b -> Any
  , before :: a -> b
  }

-- | Create a `BenchmarkFunction`, given a name and a function to be
-- | benchmarked.
benchFn :: forall a r. String -> (a -> r) -> BenchmarkFunction a
benchFn name fn = benchFn' name fn id

-- | Create a `BenchmarkFunction`. Like `benchFn`, except that it accepts a
-- | third argument which will be used to preprocess the input, before starting
-- | the benchmark. This is useful if you want to compare two functions which
-- | have different argument types.
benchFn' :: forall a b r. String -> (b -> r) -> (a -> b) -> BenchmarkFunction a
benchFn' name fn before =
  BenchmarkFunction $ mkExists $ BenchmarkFunctionF
    { name: name, fn: toAny <<< fn, before: before }

getName :: forall a. BenchmarkFunction a -> String
getName (BenchmarkFunction f) = runExists go f
  where
  go :: forall b. BenchmarkFunctionF a b -> String
  go (BenchmarkFunctionF o) = o.name

type BenchM e a = StateT GenState (Eff (BenchEffects e)) a

runBenchM :: forall e a. BenchM e a -> GenState -> Eff (BenchEffects e) a
runBenchM = evalStateT

-- | Use the given generator to generate a random value, using (and modifying)
-- | the state of the BenchM computation.
stepGen :: forall e a. Gen a -> BenchM e a
stepGen gen = do
  st <- get
  let out = runGen gen st
  put $ snd out
  return $ fst out

runBenchmark :: forall e.
  Benchmark ->
  -- ^ The Benchmark to be run.
  (Int -> Int -> BenchM e Unit) ->
  -- ^ Callback for when the size changes; the arguments are current size index
  --   (1-based) , and the current size.
  BenchM e BenchmarkResult
runBenchmark = unpackBenchmark runBenchmarkF

runBenchmarkF :: forall e a.
{'fraction_non_alphanumeric': 0.10519138988993863, 'fraction_numerical': 0.00038959774033310606, 'mean_word_length': 3.2447292269532864, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 53, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '323649', 'n_tokens_mistral': 3212, 'n_tokens_neox': 3081, 'n_words': 1247}