{-# LANGUAGE DataKinds                  #-}
-- | See <https://en.wikipedia.org/wiki/Binary_relation#Properties>.
module Data.Order.Property (
    type Rel
  , (==>), (<=>)
  , xor, xor3
  -- * Orders
  , preorder
  , order
  -- ** Non-strict preorders
  , antisymmetric_le
  , reflexive_le
  , transitive_le
  , connex_le
  -- ** Strict preorders
  , asymmetric_lt
  , transitive_lt
  , irreflexive_lt
  , semiconnex_lt
  , trichotomous_lt
  -- ** Semiorders
  , chain_22
  , chain_31
  -- * Equivalence relations
  , symmetric_eq
  , reflexive_eq
  , transitive_eq
  -- * Properties of generic relations
  , reflexive
  , irreflexive
  , coreflexive
  , quasireflexive
  , transitive
  , euclideanL
  , euclideanR
  , connex
  , semiconnex
  , trichotomous
  , symmetric
  , asymmetric
  , antisymmetric
) where

import Data.Connection.Conn
import Data.Order
import Data.Order.Syntax
import Data.Lattice hiding (not)
import Prelude hiding (Ord(..), Eq(..))

-- | See <https://en.wikipedia.org/wiki/Binary_relation#Properties>.
--
-- Note that these properties do not exhaust all of the possibilities.
--
-- As an example over the natural numbers, the relation \(a \# b \) defined by 
-- \( a > 2 \) is neither symmetric nor antisymmetric, let alone asymmetric.
type Rel r b = r -> r -> b

infix 1 ==>

(==>) :: Bool -> Bool -> Bool
(==>) x y = not x || y

infix 0 <=>

(<=>) :: Bool -> Bool -> Bool
(<=>) x y = (x ==> y) && (y ==> x)



xor3 :: Bool -> Bool -> Bool -> Bool
xor3 a b c = (a `xor` (b `xor` c)) && not (a && b && c)

-- | Check a 'Preorder' is internally consistent.
--
-- This is a required property.
--
preorder :: Preorder r => r -> r -> Bool
preorder x y =
  ((x <~ y) == le x y) &&
  ((x >~ y) == ge x y) &&
  ((x ?~ y) == cp x y) &&
  ((x ~~ y) == eq x y) &&
  ((x /~ y) == ne x y) &&
  ((x <  y) == lt x y) &&
  ((x >  y) == gt x y) &&
  (similar x y == sm x y) &&
  (pcompare x y == pcmp x y)

  where
    le x1 y1 = x1 < y1 || x1 ~~ y1

    ge = flip le

    cp x1 y1 = x1 <~ y1 || x1 >~ y1

    eq x1 y1 = x1 <~ y1 && x1 >~ y1

    ne x1 y1 = not $ eq x1 y1

    lt x1 y1 = x1 <~ y1 && x1 /~ y1

    gt = flip lt

    sm x1 y1 = not (x1 < y1) && not (x1 > y1)

    pcmp x1 y1
      | x1 <~ y1 = Just $ if y1 <~ x1 then EQ else LT
      | y1 <~ x1 = Just GT
      | otherwise = Nothing


-- | Check that an 'Order' is internally consistent.
--
-- This is a required property.
--
order :: Order r => r -> r -> Bool
order x y =
  ((x <= y) == le x y) &&
  ((x >= y) == ge x y) &&
  ((x == y) == eq x y) &&
  ((x /= y) == ne x y)

  where
    le x1 y1 = maybe False (<~ EQ) $ pcompare x1 y1

    ge x1 y1 = maybe False (>~ EQ) $ pcompare x1 y1

    eq x1 y1 = maybe False (~~ EQ) $ pcompare x1 y1

    ne x1 y1 = not $ x1 ~~ y1

---------------------------------------------------------------------
-- Non-strict preorders
---------------------------------------------------------------------

-- | \( \forall a, b: (a \leq b) \wedge (b \leq a) \Rightarrow a = b \)
--
-- '<~' is an antisymmetric relation.
--
-- This is a required property.
--
antisymmetric_le :: Preorder r => r -> r -> Bool
antisymmetric_le = antisymmetric (~~) (<~)

-- | \( \forall a: (a \leq a) \)
--
-- '<~' is a reflexive relation.
--
-- This is a required property.
--
reflexive_le :: Preorder r => r ->  Bool
reflexive_le = reflexive (<~)

-- | \( \forall a, b, c: ((a \leq b) \wedge (b \leq c)) \Rightarrow (a \leq c) \)
--
-- '<~' is an transitive relation.
--
-- This is a required property.
--
transitive_le :: Preorder r => r -> r -> r -> Bool
transitive_le = transitive (<~)

-- | \( \forall a, b: ((a \leq b) \vee (b \leq a)) \)
--
-- '<~' is a connex relation.
-- 
connex_le :: Preorder r => r -> r -> Bool
connex_le = connex (<~)

---------------------------------------------------------------------
-- Strict preorders
---------------------------------------------------------------------

-- | \( \forall a, b: (a \lt b) \Rightarrow \neg (b \lt a) \)
--
-- 'lt' is an asymmetric relation.
--
-- This is a required property.
--
asymmetric_lt :: Preorder r => r -> r -> Bool
asymmetric_lt = asymmetric (<)

-- | \( \forall a: \neg (a \lt a) \)
--
-- 'lt' is an irreflexive relation.
--
-- This is a required property.
--
irreflexive_lt :: Preorder r => r ->  Bool
irreflexive_lt = irreflexive (<)

-- | \( \forall a, b, c: ((a \lt b) \wedge (b \lt c)) \Rightarrow (a \lt c) \)
--
-- 'lt' is a transitive relation.
--
-- This is a required property.
--
transitive_lt :: Preorder r => r -> r -> r -> Bool
transitive_lt = transitive (<)

-- | \( \forall a, b: \neg (a = b) \Rightarrow ((a \lt b) \vee (b \lt a)) \)
--
-- 'lt' is a semiconnex relation.
--
semiconnex_lt :: Preorder r => r -> r -> Bool
semiconnex_lt = semiconnex (~~) (<)

-- | \( \forall a, b, c: ((a \lt b) \vee (a = b) \vee (b \lt a)) \wedge \neg ((a \lt b) \wedge (a = b) \wedge (b \lt a)) \)
--
-- In other words, exactly one of \(a \lt b\), \(a = b\), or \(b \lt a\) holds.
--
-- If 'lt' is a trichotomous relation then the set is totally ordered.
--
trichotomous_lt :: Preorder r => r -> r -> Bool
trichotomous_lt = trichotomous (~~) (<)

---------------------------------------------------------------------
-- Semiorders
---------------------------------------------------------------------

-- | \( \forall x, y, z, w: x \lt y \wedge y \sim z \wedge z \lt w \Rightarrow x \lt w \) 
--
-- A < https://en.wikipedia.org/wiki/Semiorder semiorder > does not allow 2-2 chains.
--
chain_22 :: Preorder r => r -> r -> r -> r -> Bool
chain_22 x y z w = x < y && similar y z && z < w ==> x < w

-- \( \forall x, y, z, w: x \lt y \wedge y \lt z \wedge y \sim w \Rightarrow \neg (x \sim w \wedge z \sim w) \) (3-1 chain)
--
-- A < https://en.wikipedia.org/wiki/Semiorder semiorder > does not allow 3-1 chains.
--
-- /Note/: This library models floats, doubles, rationals etc 
-- as < https://en.wikipedia.org/wiki/Modular_lattice#Examples N5 > lattices,
-- which do not possess the 3-1 chain property and are not semiorders.
--
chain_31 :: Preorder r => r -> r -> r -> r -> Bool
chain_31 x y z w = x < y && y < z && similar y w ==> not (similar x w && similar z w)

---------------------------------------------------------------------
-- Equivalence relations
---------------------------------------------------------------------

-- | \( \forall a, b: (a = b) \Leftrightarrow (b = a) \)
--
-- '~~' is a symmetric relation.
--
-- This is a required property.
--
symmetric_eq :: Preorder r => r -> r -> Bool
symmetric_eq = symmetric (~~)

-- | \( \forall a: (a = a) \)
--
-- '~~' is a reflexive relation.
--
-- This is a required property
--
reflexive_eq :: Preorder r => r ->  Bool
reflexive_eq = reflexive (~~)

-- | \( \forall a, b, c: ((a = b) \wedge (b = c)) \Rightarrow (a = c) \)
--
-- '~~' is a transitive relation.
--
-- This is a required property.
--
transitive_eq :: Preorder r => r -> r -> r -> Bool
transitive_eq = transitive (~~)

---------------------------------------------------------------------
-- Properties of general relations
---------------------------------------------------------------------

-- | \( \forall a: (a \# a) \)
--
-- For example, ≥ is a reflexive relation but > is not.
--
reflexive :: Rel r b -> r -> b
reflexive (#) a = a # a

-- | \( \forall a: \neg (a \# a) \)
--
-- For example, > is an irreflexive relation, but ≥ is not.
--
irreflexive :: Rel r Bool -> r -> Bool
irreflexive (#) a = not $ a # a

-- | \( \forall a, b: ((a \# b) \wedge (b \# a)) \Rightarrow (a \equiv b) \)
--
-- For example, the relation over the integers in which each odd number is 
-- related to itself is a coreflexive relation. The equality relation is the 
-- only example of a relation that is both reflexive and coreflexive, and any 
-- coreflexive relation is a subset of the equality relation.
--
coreflexive :: Rel r Bool -> Rel r Bool -> r -> r -> Bool
coreflexive (%) (#) a b = (a # b) && (b # a) ==> (a % b)

-- | \( \forall a, b: (a \# b) \Rightarrow ((a \# a) \wedge (b \# b)) \)
--
quasireflexive :: Rel r Bool -> r -> r -> Bool
quasireflexive (#) a b = (a # b) ==> (a # a) && (b # b)

-- | \( \forall a, b, c: ((a \# b) \wedge (a \# c)) \Rightarrow (b \# c) \)
--
-- For example, /=/ is a right Euclidean relation because if /x = y/ and /x = z/ then /y = z/.
--
euclideanR :: Rel r Bool -> r -> r -> r -> Bool
euclideanR (#) a b c = (a # b) && (a # c) ==> b # c

-- |  \( \forall a, b, c: ((b \# a) \wedge (c \# a)) \Rightarrow (b \# c) \)
--
-- For example, /=/ is a left Euclidean relation because if /x = y/ and /x = z/ then /y = z/.
--
euclideanL :: Rel r Bool -> r -> Rel r Bool
euclideanL (#) a b c = (b # a) && (c # a) ==> b # c

-- | \( \forall a, b, c: ((a \# b) \wedge (b \# c)) \Rightarrow (a \# c) \)
--
-- For example, "is ancestor of" is a transitive relation, while "is parent of" is not.
--
transitive :: Rel r Bool -> r -> r -> r -> Bool
transitive (#) a b c = (a # b) && (b # c) ==> a # c

-- | \( \forall a, b: ((a \# b) \vee (b \# a)) \)
--
-- For example, ≥ is a connex relation, while 'divides evenly' is not.
-- 
-- A connex relation cannot be symmetric, except for the universal relation.
--
connex :: Rel r Bool -> r -> r -> Bool
connex (#) a b = (a # b) || (b # a)

-- | \( \forall a, b: \neg (a \equiv b) \Rightarrow ((a \# b) \vee (b \# a)) \)
--
-- A binary relation is semiconnex if it relates all pairs of _distinct_ elements in some way.
--
-- A relation is connex if and only if it is semiconnex and reflexive.
--
semiconnex :: Rel r Bool -> Rel r Bool -> r -> r -> Bool
semiconnex (%) (#) a b = not (a % b) ==> connex (#) a b

-- | \( \forall a, b, c: ((a \# b) \vee (a \doteq b) \vee (b \# a)) \wedge \neg ((a \# b) \wedge (a \doteq b) \wedge (b \# a)) \)
--
-- In other words, exactly one of \(a \# b\), \(a \doteq b\), or \(b \# a\) holds.
-- 	
-- For example, > is a trichotomous relation, while ≥ is not.
--
-- Note that @ trichotomous (>) @ should hold for any 'Ord' instance.
--
trichotomous :: Rel r Bool -> Rel r Bool -> r -> r -> Bool
trichotomous (%) (#) a b = xor3 (a # b) (a % b) (b # a)

-- | \( \forall a, b: (a \# b) \Leftrightarrow (b \# a) \)
--
-- For example, "is a blood relative of" is a symmetric relation, because 
-- A is a blood relative of B if and only if B is a blood relative of A.
--
symmetric :: Rel r Bool -> r -> r -> Bool
symmetric (#) a b = (a # b) <=> (b # a)

-- | \( \forall a, b: (a \# b) \Rightarrow \neg (b \# a) \)
--
-- For example, > is an asymmetric relation, but ≥ is not.
--
-- A relation is asymmetric if and only if it is both antisymmetric and irreflexive.
-- 
asymmetric :: Rel r Bool -> r -> r -> Bool
asymmetric (#) a b = (a # b) ==> not (b # a)

-- | \( \forall a, b: (a \# b) \wedge (b \# a) \Rightarrow a \equiv b \)
--
-- For example, ≥ is an antisymmetric relation; so is >, but vacuously 
-- (the condition in the definition is always false).
--
antisymmetric :: Rel r Bool -> Rel r Bool -> r -> r -> Bool
antisymmetric (%) (#) a b = (a # b) && (b # a) ==> (a % b)