    {-# LANGUAGE MultiParamTypeClasses #-}
    {-# LANGUAGE ScopedTypeVariables #-}

    module Test.Validity.Operations.Associativity
      ( associativeOnGens,
        associative,
        associativeOnArbitrary,
      )
    where

    import Data.GenValidity
    import Test.Hspec
    import Test.QuickCheck

    -- |
    --
    -- \[
    --     Associative(\star)
    --     \quad\equiv\quad
    --     \forall a, b, c:
    --     (a \star b) \star c = a \star (b \star c)
    -- \]
    associativeOnGens ::
      (Show a, Eq a) =>
      (a -> a -> a) ->
      Gen (a, a, a) ->
      ((a, a, a) -> [(a, a, a)]) ->
      Property
    associativeOnGens :: forall a.
    (Show a, Eq a) =>
    (a -> a -> a)
    -> Gen (a, a, a) -> ((a, a, a) -> [(a, a, a)]) -> Property
    associativeOnGens a -> a -> a
    op Gen (a, a, a)
    gen (a, a, a) -> [(a, a, a)]
    s =
      Gen (a, a, a)
    -> ((a, a, a) -> [(a, a, a)])
    -> ((a, a, a) -> Expectation)
    -> Property
    forall a prop.
    (Show a, Testable prop) =>
    Gen a -> (a -> [a]) -> (a -> prop) -> Property
    forAllShrink Gen (a, a, a)
    gen (a, a, a) -> [(a, a, a)]
    s (((a, a, a) -> Expectation) -> Property)
    -> ((a, a, a) -> Expectation) -> Property
    forall a b. (a -> b) -> a -> b
    $ \(a
    a, a
    b, a
    c) ->
        ((a
    a a -> a -> a
    `op` a
    b) a -> a -> a
    `op` a
    c) a -> a -> Expectation
    forall a. (HasCallStack, Show a, Eq a) => a -> a -> Expectation
    `shouldBe` (a
    a a -> a -> a
    `op` (a
    b a -> a -> a
    `op` a
    c))

    -- |
    --
    -- prop> associative ((*) :: Int -> Int -> Int)
    -- prop> associative ((+) :: Int -> Int -> Int)
    associative :: (Show a, Eq a, GenValid a) => (a -> a -> a) -> Property
    associative :: forall a. (Show a, Eq a, GenValid a) => (a -> a -> a) -> Property
    associative a -> a -> a
    op = (a -> a -> a)
    -> Gen (a, a, a) -> ((a, a, a) -> [(a, a, a)]) -> Property
    forall a.
    (Show a, Eq a) =>
    (a -> a -> a)
    -> Gen (a, a, a) -> ((a, a, a) -> [(a, a, a)]) -> Property
    associativeOnGens a -> a -> a
    op Gen (a, a, a)
    forall a. GenValid a => Gen a
    genValid (a, a, a) -> [(a, a, a)]
    forall a. GenValid a => a -> [a]
    shrinkValid

    -- |
    --
    -- prop> associativeOnArbitrary ((*) :: Int -> Int -> Int)
    -- prop> associativeOnArbitrary ((+) :: Int -> Int -> Int)
    associativeOnArbitrary ::
      (Show a, Eq a, Arbitrary a) => (a -> a -> a) -> Property
    associativeOnArbitrary :: forall a. (Show a, Eq a, Arbitrary a) => (a -> a -> a) -> Property
    associativeOnArbitrary a -> a -> a
    op = (a -> a -> a)
    -> Gen (a, a, a) -> ((a, a, a) -> [(a, a, a)]) -> Property
    forall a.
    (Show a, Eq a) =>
    (a -> a -> a)
    -> Gen (a, a, a) -> ((a, a, a) -> [(a, a, a)]) -> Property
    associativeOnGens a -> a -> a
    op Gen (a, a, a)
    forall a. Arbitrary a => Gen a
    arbitrary (a, a, a) -> [(a, a, a)]
    forall a. Arbitrary a => a -> [a]
    shrink