packages feed

fresnel-0.0.0.0: test/Monoid/Fork/Test.hs

{-# LANGUAGE TemplateHaskell #-}
module Monoid.Fork.Test
( tests
) where

import Data.Foldable (toList)
import Data.Ratio
import Fresnel.Monoid.Fork (Fork(runFork), singleton)
import Test.Group
import Test.QuickCheck hiding (total)

prop_semigroup_assoc :: (Eq a, Show a) => ArbFork a -> ArbFork a -> ArbFork a -> Property
prop_semigroup_assoc (ArbFork a) (ArbFork b) (ArbFork c) =
  label (summarize a) . label (summarize b) . label (summarize c) $
  (toList (a <> (b <> c)) === toList ((a <> b) <> c))

prop_monoid_identity :: (Eq a, Show a) => ArbFork a -> Property
prop_monoid_identity (ArbFork a) = label (summarize a) $ toList (mempty <> a) === toList a .&&. toList (a <> mempty) === toList a


newtype ArbFork a = ArbFork (Fork a)
  deriving (Show)

instance Arbitrary a => Arbitrary (ArbFork a) where
  arbitrary = ArbFork <$> sized go where
    go 0 = pure mempty
    go i = oneof
      [ chooseInt (0, i) >>= \ j -> (<>) <$> go (i - j) <*> go j
      , singleton <$> arbitrary
      , pure mempty
      ]


summarize :: Fork a -> String
summarize r
  | total' == nils     = "nil"
  | ratio nils > 0.4   = "nil-heavy"
  | total' == leaves   = "leaf"
  | ratio leaves > 0.4 = "leaf-heavy"
  | ratio forks > 0.4  = "fork-heavy"
  | otherwise          = "fork"
  where
  (total', Counts forks leaves nils) = (,) . total <*> id $ runFork r (\ l r -> fork <> l <> r) (const leaf) nil
  ratio a = realToFrac  (a % total') :: Double

data Counts = Counts
  { forks  :: {-# UNPACK #-} !Int
  , leaves :: {-# UNPACK #-} !Int
  , nils   :: {-# UNPACK #-} !Int
  }

instance Semigroup Counts where
  c1 <> c2 = Counts (forks c1 + forks c2) (leaves c1 + leaves c2) (nils c1 + nils c2)

fork :: Counts
fork = Counts 1 0 0

leaf :: Counts
leaf = Counts 0 1 0

nil :: Counts
nil = Counts 0 0 1

total :: Counts -> Int
total (Counts f l n) = f + l + n


pure []

tests :: Entry
tests = $deriveGroup