{-# LANGUAGE CPP #-}
module Agda.Utils.PartialOrd where
import Data.Maybe
#if __GLASGOW_HASKELL__ < 804
import Data.Semigroup
#endif
import Data.Set (Set)
import qualified Data.Set as Set
data PartialOrdering
  = POLT  
  | POLE  
  | POEQ  
  | POGE  
  | POGT  
  | POAny 
  deriving (Eq, Show, Enum, Bounded)
leqPO :: PartialOrdering -> PartialOrdering -> Bool
leqPO _   POAny = True
leqPO POLT POLT = True
leqPO POLT POLE = True
leqPO POLE POLE = True
leqPO POEQ POLE = True
leqPO POEQ POEQ = True
leqPO POEQ POGE = True
leqPO POGE POGE = True
leqPO POGT POGT = True
leqPO POGT POGE = True
leqPO _ _ = False
oppPO :: PartialOrdering -> PartialOrdering
oppPO POLT  = POGT
oppPO POLE  = POGE
oppPO POEQ  = POEQ
oppPO POGE  = POLE
oppPO POGT  = POLT
oppPO POAny = POAny
orPO :: PartialOrdering -> PartialOrdering -> PartialOrdering
orPO POAny _   = POAny   
orPO POLT POLT = POLT   
orPO POLT POLE = POLE
orPO POLT POEQ = POLE
orPO POLE POLT = POLE
orPO POLE POLE = POLE   
orPO POLE POEQ = POLE
orPO POEQ POLT = POLE
orPO POEQ POLE = POLE
orPO POEQ POEQ = POEQ   
orPO POEQ POGE = POGE
orPO POEQ POGT = POGE
orPO POGE POEQ = POGE
orPO POGE POGE = POGE   
orPO POGE POGT = POGE
orPO POGT POEQ = POGE
orPO POGT POGE = POGE
orPO POGT POGT = POGT   
orPO _    _    = POAny
seqPO :: PartialOrdering -> PartialOrdering -> PartialOrdering
seqPO POAny _   = POAny  
seqPO POEQ p    = p      
seqPO POLT POLT = POLT   
seqPO POLT POLE = POLT
seqPO POLT POEQ = POLT   
seqPO POLE POLT = POLT
seqPO POLE POLE = POLE   
seqPO POLE POEQ = POLE   
seqPO POGE POEQ = POGE   
seqPO POGE POGE = POGE   
seqPO POGE POGT = POGT
seqPO POGT POEQ = POGT   
seqPO POGT POGE = POGT
seqPO POGT POGT = POGT   
seqPO _    _    = POAny
instance Semigroup PartialOrdering where
  (<>) = seqPO
instance Monoid PartialOrdering where
  mempty  = POEQ
  mappend = (<>)
fromOrdering :: Ordering -> PartialOrdering
fromOrdering LT = POLT
fromOrdering EQ = POEQ
fromOrdering GT = POGT
fromOrderings :: [Ordering] -> PartialOrdering
fromOrderings = foldr1 orPO . map fromOrdering
toOrderings :: PartialOrdering -> [Ordering]
toOrderings POLT  = [LT]
toOrderings POLE  = [LT, EQ]
toOrderings POEQ  = [EQ]
toOrderings POGE  = [EQ, GT]
toOrderings POGT  = [GT]
toOrderings POAny = [LT, EQ, GT]
type Comparable a = a -> a -> PartialOrdering
class PartialOrd a where
  comparable :: Comparable a
comparableOrd :: Ord a => Comparable a
comparableOrd x y = fromOrdering $ compare x y
related :: PartialOrd a => a -> PartialOrdering -> a -> Bool
related a o b = comparable a b `leqPO` o
instance PartialOrd Int where
  comparable = comparableOrd
instance PartialOrd Integer where
  comparable = comparableOrd
instance PartialOrd () where
  comparable _ _ = POEQ
instance PartialOrd a => PartialOrd (Maybe a) where
  comparable mx my = case (mx, my) of
    (Nothing, Nothing) -> POEQ
    (Nothing, Just{} ) -> POAny
    (Just{} , Nothing) -> POAny
    (Just x , Just y ) -> comparable x y
instance (PartialOrd a, PartialOrd b) => PartialOrd (Either a b) where
  comparable mx my = case (mx, my) of
    (Left  x, Left  y) -> comparable x y
    (Left  _, Right _) -> POAny
    (Right _, Left  _) -> POAny
    (Right x, Right y) -> comparable x y
instance (PartialOrd a, PartialOrd b) => PartialOrd (a, b) where
  comparable (x1, x2) (y1, y2) =
    comparable x1 y1 `orPO`
    comparable x2 y2
newtype Pointwise a = Pointwise { pointwise :: a }
  deriving (Eq, Show, Functor)
instance PartialOrd a => PartialOrd (Pointwise [a]) where
  comparable (Pointwise xs) (Pointwise ys) = loop Nothing xs ys
    
    where
      loop mo []     []     = fromMaybe POEQ mo
      loop _  []     ys     = POAny
      loop _  xs     []     = POAny
      loop mo (x:xs) (y:ys) =
        let o = comparable x y in
        case maybe o (orPO o) mo of
          POAny -> POAny
          o     -> loop (Just o) xs ys
newtype Inclusion a = Inclusion { inclusion :: a }
  deriving (Eq, Ord, Show, Functor)
instance (Ord a) => PartialOrd (Inclusion [a]) where
  comparable (Inclusion xs) (Inclusion ys) = merge POEQ xs ys
    where
      
      
      merge' POAny xs ys = POAny
      merge' o     xs ys = merge o xs ys
      merge o [] [] = o
      merge o [] ys = mappend o POLT
      merge o xs [] = mappend o POGT
      merge o xs@(x:xs') ys@(y:ys') =
        case compare x y of
          
          LT -> merge' (mappend o POGT) xs' ys
          
          EQ -> merge o xs' ys'
          
          GT -> merge' (mappend o POLT) xs ys'
instance Ord a => PartialOrd (Inclusion (Set a)) where
  comparable s t = comparable (Set.toAscList <$> s) (Set.toAscList <$> t)
instance PartialOrd PartialOrdering where
  
  comparable POAny POAny = POEQ
  comparable POAny _     = POGT
  comparable _     POAny = POLT
  
  comparable POLE  POLE  = POEQ
  comparable POLE  POLT  = POGT
  comparable POLE  POEQ  = POGT
  comparable POGE  POGE  = POEQ
  comparable POGE  POGT  = POGT
  comparable POGE  POEQ  = POGT
  
  comparable POLT  POLT  = POEQ
  comparable POLT  POLE  = POLT
  comparable POEQ  POEQ  = POEQ
  comparable POEQ  POLE  = POLT
  comparable POEQ  POGE  = POLT
  comparable POGT  POGT  = POEQ
  comparable POGT  POGE  = POLT
  
  comparable _     _     = POAny