<commit_before>-- |
-- Module      : Test.LeanCheck.Utils.Operators
-- Copyright   : (c) 2015-2018 <NAME>
-- License     : 3-Clause BSD  (see the file LICENSE)
-- Maintainer  : <NAME> <<EMAIL>>
--
-- This module is part of LeanCheck,
-- a simple enumerative property-based testing library.
--
-- Some operators for property-based testing.
module Test.LeanCheck.Utils.Operators
  (

  -- * Combining properties
    (==>)
  , (===), (====)
  , (&&&), (&&&&), (&&&&&)
  , (|||), (||||)

  -- * Properties of unary functions
  , idempotent
  , identity
  , neverIdentity

  -- * Properties of operators (binary functions)
  , isCommutative
  , commutative
  , isAssociative
  , associative
  , isDistributiveOver
  , isLeftDistributiveOver
  , isRightDistributiveOver
  , distributive
  , symmetric2

  -- * Properties of relations (binary functions returning truth values)
  , transitive
  , reflexive
  , irreflexive
  , symmetric
  , asymmetric
  , antisymmetric

  -- ** Order relations
  , equivalence
  , partialOrder
  , strictPartialOrder
  , totalOrder
  , strictTotalOrder
  , comparison

  -- * Ternary comparison operators
  , (=$), ($=)
  , (=|), (|=)

  -- * Properties for typeclass instances
  , okEq
  , okOrd
  , okEqOrd
  , okNum
  , okNumNonNegative
  )
where

-- TODO: Add examples in the haddock documentation of most functions in this
--       module.

-- TODO: review terminology in this module.  Some names aren't quite right!

-- TODO: rename commutative to isCommutative and etc...

import Test.LeanCheck ((==>))

combine :: (b -> c -> d) -> (a -> b) -> (a -> c) -> (a -> d)
combine (?) f g  =  \x -> f x ? g x

-- Uneeded, just food for thought:
-- > combine2 :: (c -> d -> e) -> (a -> b -> c) -> (a -> b -> d) -> (a -> b -> e)
-- Two possible implementations:
-- > combine2 op f g = \x y -> f x y `op` g x y
-- > combine2 = combine . combine

-- | Allows building equality properties between functions.
--
-- > prop_id_idempotent  =  id === id . id
--
-- > > check $ id === (+0)
-- > +++ OK, passed 200 tests.
--
-- > > check $ id === id . id
-- > +++ OK, passed 1 tests (exhausted).
--
-- > > check $ id === (+1)
-- > *** Failed! Falsifiable (after 1 tests):
-- > 0
(===) :: Eq b => (a -> b) -> (a -> b) -> a -> Bool
(===)  =  combine (==)
infix 4 ===

-- | Allows building equality properties between two-argument functions.
--
-- > > holds 100 $ const ==== asTypeOf
-- > True
--
-- > > holds 100 $ (+) ==== flip (+)
-- > True
--
-- > > holds 100 $ (+) ==== (*)
-- > False
(====) :: Eq c => (a -> b -> c) -> (a -> b -> c) -> a -> b -> Bool
(====)  =  combine (===)
infix 4 ====

-- | And ('&&') operator over one-argument properties.
--
-- Allows building conjuntions between one-argument properties:
--
-- > > holds 100 $ id === (+0) &&& id === (id . id)
-- > True
(&&&) :: (a -> Bool) -> (a -> Bool) -> a -> Bool
(&&&)  =  combine (&&)
infixr 3 &&&

-- | And ('&&') operator over two-argument properties.
--
-- Allows building conjuntions between two-argument properties:
--
-- > > holds 100 $ (+) ==== flip (+) &&&& (+) ==== (*)
-- > False
(&&&&) :: (a -> b -> Bool) -> (a -> b -> Bool) -> a -> b -> Bool
(&&&&)  =  combine (&&&)
infixr 3 &&&&

-- | And operator over three-argument properties.
(&&&&&) :: (a -> b -> c -> Bool) -> (a -> b -> c -> Bool) -> a -> b -> c -> Bool
(&&&&&)  =  combine (&&&&)
infixr 3 &&&&&

-- | Or ('||') operator over one-argument properties.
--
-- Allows building disjunctions between one-argument properties:
--
-- > > holds 100 $ id === (+0) ||| id === (id . id)
-- > True
(|||) :: (a -> Bool) -> (a -> Bool) -> a -> Bool
(|||)  =  combine (||)
infixr 2 |||

-- | Or ('||') operator over two-argument properties.
--
-- Allows building conjuntions between two-argument properties:
--
-- > > holds 100 $ (+) ==== flip (+) |||| (+) ==== (*)
-- > True
(||||) :: (a -> b -> Bool) -> (a -> b -> Bool) -> a -> b -> Bool
(||||)  =  combine (|||)
infixr 2 ||||

{-# DEPRECATED commutative "Use isCommutative." #-}
commutative :: Eq b => (a -> a -> b) -> a -> a -> Bool
commutative  =  isCommutative

-- | Is a given operator commutative?  @x + y = y + x@
--
-- > > check $ isCommutative (+)
-- > +++ OK, passed 200 tests.
--
-- > > import Data.List
-- > > check $ isCommutative (union :: [Int]->[Int]->[Int])
-- > *** Failed! Falsifiable (after 4 tests):
-- > [] [0,0]
isCommutative :: Eq b => (a -> a -> b) -> a -> a -> Bool
isCommutative (?)  =  \x y -> x ? y == y ? x

-- | Is a given operator associative?  @x + (y + z) = (x + y) + z@
--
-- > > check $ isAssociative (+)
-- > +++ OK, passed 200 tests.
--
-- > > check $ isAssociative (-)
-- > *** Failed! Falsifiable (after 2 tests):
-- > 0 0 1
isAssociative :: Eq a => (a -> a -> a) -> a -> a -> a -> Bool
isAssociative (?)  =  \x y z -> x ? (y ? z) == (x ? y) ? z

{-# DEPRECATED associative "Use isAssociative." #-}
associative :: Eq a => (a -> a -> a) -> a -> a -> a -> Bool
associative  =  isAssociative

-- | Does the first operator, left-distributes over the second?
--
-- This is an alias to 'isLeftDistributiveOver'.
isDistributiveOver :: Eq a => (a -> a -> a) -> (a -> a -> a) -> a -> a -> a -> Bool
isDistributiveOver  =  isLeftDistributiveOver

-- | Does the first operator, left-distributes over the second?
--   @x * (y + z) = (x * y) + (x * z)@
--
-- > > check $ (*) `isLeftDistributiveOver` (+)
-- > +++ OK, passed 200 tests.<commit_msg>Consistent file ordering<commit_after>-- |
-- Module      : Test.LeanCheck.Utils.Operators
-- Copyright   : (c) 2015-2018 <NAME>
-- License     : 3-Clause BSD  (see the file LICENSE)
-- Maintainer  : <NAME> <<EMAIL>>
--
-- This module is part of LeanCheck,
-- a simple enumerative property-based testing library.
--
-- Some operators for property-based testing.
module Test.LeanCheck.Utils.Operators
  (

  -- * Combining properties
    (==>)
  , (===), (====)
  , (&&&), (&&&&), (&&&&&)
  , (|||), (||||)

  -- * Properties of unary functions
  , idempotent
  , identity
  , neverIdentity

  -- * Properties of operators (binary functions)
  , isCommutative
  , commutative
  , isAssociative
  , associative
  , isDistributiveOver
  , isLeftDistributiveOver
  , isRightDistributiveOver
  , distributive
  , symmetric2

  -- * Properties of relations (binary functions returning truth values)
  , transitive
  , reflexive
  , irreflexive
  , symmetric
  , asymmetric
  , antisymmetric

  -- ** Order relations
  , equivalence
  , partialOrder
  , strictPartialOrder
  , totalOrder
  , strictTotalOrder
  , comparison

  -- * Ternary comparison operators
  , (=$), ($=)
  , (=|), (|=)

  -- * Properties for typeclass instances
  , okEq
  , okOrd
  , okEqOrd
  , okNum
  , okNumNonNegative
  )
where

-- TODO: Add examples in the haddock documentation of most functions in this
--       module.

-- TODO: review terminology in this module.  Some names aren't quite right!

-- TODO: rename commutative to isCommutative and etc...

import Test.LeanCheck ((==>))

combine :: (b -> c -> d) -> (a -> b) -> (a -> c) -> (a -> d)
combine (?) f g  =  \x -> f x ? g x

-- Uneeded, just food for thought:
-- > combine2 :: (c -> d -> e) -> (a -> b -> c) -> (a -> b -> d) -> (a -> b -> e)
-- Two possible implementations:
-- > combine2 op f g = \x y -> f x y `op` g x y
-- > combine2 = combine . combine

-- | Allows building equality properties between functions.
--
-- > prop_id_idempotent  =  id === id . id
--
-- > > check $ id === (+0)
-- > +++ OK, passed 200 tests.
--
-- > > check $ id === id . id
-- > +++ OK, passed 1 tests (exhausted).
--
-- > > check $ id === (+1)
-- > *** Failed! Falsifiable (after 1 tests):
-- > 0
(===) :: Eq b => (a -> b) -> (a -> b) -> a -> Bool
(===)  =  combine (==)
infix 4 ===

-- | Allows building equality properties between two-argument functions.
--
-- > > holds 100 $ const ==== asTypeOf
-- > True
--
-- > > holds 100 $ (+) ==== flip (+)
-- > True
--
-- > > holds 100 $ (+) ==== (*)
-- > False
(====) :: Eq c => (a -> b -> c) -> (a -> b -> c) -> a -> b -> Bool
(====)  =  combine (===)
infix 4 ====

-- | And ('&&') operator over one-argument properties.
--
-- Allows building conjuntions between one-argument properties:
--
-- > > holds 100 $ id === (+0) &&& id === (id . id)
-- > True
(&&&) :: (a -> Bool) -> (a -> Bool) -> a -> Bool
(&&&)  =  combine (&&)
infixr 3 &&&

-- | And ('&&') operator over two-argument properties.
--
-- Allows building conjuntions between two-argument properties:
--
-- > > holds 100 $ (+) ==== flip (+) &&&& (+) ==== (*)
-- > False
(&&&&) :: (a -> b -> Bool) -> (a -> b -> Bool) -> a -> b -> Bool
(&&&&)  =  combine (&&&)
infixr 3 &&&&

-- | And operator over three-argument properties.
(&&&&&) :: (a -> b -> c -> Bool) -> (a -> b -> c -> Bool) -> a -> b -> c -> Bool
(&&&&&)  =  combine (&&&&)
infixr 3 &&&&&

-- | Or ('||') operator over one-argument properties.
--
-- Allows building disjunctions between one-argument properties:
--
-- > > holds 100 $ id === (+0) ||| id === (id . id)
-- > True
(|||) :: (a -> Bool) -> (a -> Bool) -> a -> Bool
(|||)  =  combine (||)
infixr 2 |||

-- | Or ('||') operator over two-argument properties.
--
-- Allows building conjuntions between two-argument properties:
--
-- > > holds 100 $ (+) ==== flip (+) |||| (+) ==== (*)
-- > True
(||||) :: (a -> b -> Bool) -> (a -> b -> Bool) -> a -> b -> Bool
(||||)  =  combine (|||)
infixr 2 ||||

-- | Is a given operator commutative?  @x + y = y + x@
--
-- > > check $ isCommutative (+)
-- > +++ OK, passed 200 tests.
--
-- > > import Data.List
-- > > check $ isCommutative (union :: [Int]->[Int]->[Int])
-- > *** Failed! Falsifiable (after 4 tests):
-- > [] [0,0]
isCommutative :: Eq b => (a -> a -> b) -> a -> a -> Bool
isCommutative (?)  =  \x y -> x ? y == y ? x

{-# DEPRECATED commutative "Use isCommutative." #-}
commutative :: Eq b => (a -> a -> b) -> a -> a -> Bool
commutative  =  isCommutative

-- | Is a given operator associative?  @x + (y + z) = (x + y) + z@
--
-- > > check $ isAssociative (+)
-- > +++ OK, passed 200 tests.
--
-- > > check $ isAssociative (-)
-- > *** Failed! Falsifiable (after 2 tests):
-- > 0 0 1
isAssociative :: Eq a => (a -> a -> a) -> a -> a -> a -> Bool
isAssociative (?)  =  \x y z -> x ? (y ? z) == (x ? y) ? z

{-# DEPRECATED associative "Use isAssociative." #-}
associative :: Eq a => (a -> a -> a) -> a -> a -> a -> Bool
associative  =  isAssociative

-- | Does the first operator, left-distributes over the second?
--
-- This is an alias to 'isLeftDistributiveOver'.
isDistributiveOver :: Eq a => (a -> a -> a) -> (a -> a -> a) -> a -> a -> a -> Bool
isDistributiveOver  =  isLeftDistributiveOver

-- | Does the first operator, left-distributes over the second?
--   @x * (y + z) = (x * y) + (x * z)@
--
-- > > check $ (*) `isLeftDistributiveOver` (+)
-- > +++ OK, passed 200 tests.
{'fraction_non_alphanumeric': 0.23292648840026087, 'fraction_numerical': 0.012484859778253983, 'mean_word_length': 2.380787401574803, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 349, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '806352', 'n_tokens_mistral': 4001, 'n_tokens_neox': 3754, 'n_words': 1125}