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