```{- |
Define common properties that can be used e.g. for automated tests.
Cf. to "Test.QuickCheck.Utils".
-}
module Algebra.Laws where

commutative :: Eq a => (b -> b -> a) -> b -> b -> Bool
commutative op x y  =  x `op` y == y `op` x

associative :: Eq a => (a -> a -> a) -> a -> a -> a -> Bool
associative op x y z  =  (x `op` y) `op` z == x `op` (y `op` z)

leftIdentity :: Eq a => (b -> a -> a) -> b -> a -> Bool
leftIdentity op y x  =  y `op` x == x

rightIdentity :: Eq a => (a -> b -> a) -> b -> a -> Bool
rightIdentity op y x  =  x `op` y == x

identity :: Eq a => (a -> a -> a) -> a -> a -> Bool
identity op x y  =  leftIdentity op x y &&  rightIdentity op x y

leftZero :: Eq a => (a -> a -> a) -> a -> a -> Bool
leftZero  =  flip . rightIdentity

rightZero :: Eq a => (a -> a -> a) -> a -> a -> Bool
rightZero  =  flip . leftIdentity

zero :: Eq a => (a -> a -> a) -> a -> a -> Bool
zero op x y  =  leftZero op x y  &&  rightZero op x y

leftInverse :: Eq a => (b -> b -> a) -> (b -> b) -> a -> b -> Bool
leftInverse op inv y x  =  inv x `op` x == y

rightInverse :: Eq a => (b -> b -> a) -> (b -> b) -> a -> b -> Bool
rightInverse op inv y x  =  x `op` inv x == y

inverse :: Eq a => (b -> b -> a) -> (b -> b) -> a -> b -> Bool
inverse op inv y x  =  leftInverse op inv y x && rightInverse op inv y x

leftDistributive :: Eq a => (a -> b -> a) -> (a -> a -> a) -> b -> a -> a -> Bool
leftDistributive ( # ) op x y z  =  (y `op` z) # x == (y # x) `op` (z # x)

rightDistributive :: Eq a => (b -> a -> a) -> (a -> a -> a) -> b -> a -> a -> Bool
rightDistributive ( # ) op x y z  =  x # (y `op` z) == (x # y) `op` (x # z)

homomorphism :: Eq a =>
(b -> a) -> (b -> b -> b) -> (a -> a -> a) -> b -> b -> Bool
homomorphism f op0 op1 x y  =  f (x `op0` y) == f x `op1` f y

rightCascade :: Eq a =>
(b -> b -> b) -> (a -> b -> a) -> a -> b -> b -> Bool
rightCascade ( # ) op x i j  =  (x `op` i) `op` j == x `op` (i#j)

leftCascade :: Eq a =>
(b -> b -> b) -> (b -> a -> a) -> a -> b -> b -> Bool
leftCascade ( # ) op x i j  =  j `op` (i `op` x) == (j#i) `op` x
```