```-- | Generic properties of functions
module Test.QuickCheck.Property.Generic (
module Test.QuickCheck.Property.Common
-- * General
, prop_Reflexive
-- * Monoids & group
, prop_Associative
, prop_Commutative
, prop_LeftIdentity
, prop_RightIdentity
, prop_Identity
, prop_GroupInverse
-- ** General
, prop_GenMonoid
, prop_Group
) where

import Test.QuickCheck.Property.Common
import Test.QuickCheck.Property.Common.Internal

-- | Test that relation is reflective.
--
-- > f x x = True
prop_Reflexive :: (a -> a -> Bool) -> T a -> a -> Bool
prop_Reflexive f T x = f x x

----------------------------------------------------------------
-- Monoids & Co
----------------------------------------------------------------

-- | Test that function is commutative
prop_Commutative :: (a -> a -> b) -> T (a,b) -> a -> a -> Equal b
prop_Commutative f T x y = f x y .==. f y x

-- | Test that function is associative
prop_Associative :: (a -> a -> a) -> T a -> a -> a -> a -> Equal a
prop_Associative (<>) T a b c = ((a <> b) <> c) .==. (a <> (b <> c))

-- | Test that value is a left identity
prop_LeftIdentity :: a             -- ^ Left identity
-> (a -> a -> a) -- ^ Associative operation
-> T a -> a -> Equal a
prop_LeftIdentity e (<>) T x = (e <> x) .==. x

-- | Test that value is a left identity
prop_RightIdentity :: a             -- ^ Right identity
-> (a -> a -> a) -- ^ Associative operation
-> T a -> a -> Equal a
prop_RightIdentity e (<>) T x = (x <> e) .==. x

-- | Test that value is both right and left identity
prop_Identity :: a             -- ^ Identity element
-> (a -> a -> a) -- ^ Associative operation
-> T a -> a -> Equal a
prop_Identity = prop_LeftIdentity .&&. prop_RightIdentity

-- | Test that inverse operation is correct.
prop_GroupInverse :: a             -- ^ Identity element
-> (a -> a -> a) -- ^ Group operation
-> (a -> a)      -- ^ Find inverse
-> T a -> a -> Equal a
prop_GroupInverse e (<>) inv T x =  ((x <> inv x) .==. e)
.&&. ((inv x <> x) .==. e)

-- | Test that identity and associative operation satisfy monoid laws.
prop_GenMonoid :: a             -- ^ Identity element
-> (a -> a -> a) -- ^ Associative operation
-> T a -> a -> a -> a -> Equal a
prop_GenMonoid e (<>) T x y z =  prop_Associative   (<>) T x y z
.&&. prop_Identity    e (<>) T x

-- | Test that identity, associative operation and inverse satisfy group laws
prop_Group :: a                -- ^ Identity element
-> (a -> a -> a)    -- ^ Associative operation
-> (a -> a)         -- ^ Find inverse element
-> T a -> a -> a -> a -> Equal a
prop_Group e (<>) inv T x y z =  prop_Identity     e (<>)     T x
.&&. prop_Associative    (<>)     T x y z
.&&. prop_GroupInverse e (<>) inv T x
```