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

    module Test.Validity.Operations.Commutativity
      ( commutativeOnGens,
        commutative,
        commutativeOnArbitrary,
      )
    where

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

    -- |
    --
    -- \[
    --     Commutative(\star)
    --     \quad\equiv\quad
    --     \forall a, b:
    --     a \star b = b \star a
    -- \]
    commutativeOnGens ::
      (Show a, Show b, Eq b) =>
      (a -> a -> b) ->
      Gen (a, a) ->
      ((a, a) -> [(a, a)]) ->
      Property
    commutativeOnGens :: forall a b.
    (Show a, Show b, Eq b) =>
    (a -> a -> b) -> Gen (a, a) -> ((a, a) -> [(a, a)]) -> Property
    commutativeOnGens a -> a -> b
    op Gen (a, a)
    gen (a, a) -> [(a, a)]
    s =
      Gen (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)
    gen (a, a) -> [(a, a)]
    s (((a, a) -> Expectation) -> Property)
    -> ((a, a) -> Expectation) -> Property
    forall a b. (a -> b) -> a -> b
    $ \(a
    a, a
    b) -> (a
    a a -> a -> b
    `op` a
    b) b -> b -> Expectation
    forall a. (HasCallStack, Show a, Eq a) => a -> a -> Expectation
    `shouldBe` (a
    b a -> a -> b
    `op` a
    a)

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

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