{-# 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