rest-rewrite-0.1: src/Language/REST/WQO.hs
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Language.REST.WQO (
empty
, insert
, insertMaybe
, orderings
, getRelation
, merge
, mergeAll
, notStrongerThan
, WQO
, QORelation(..)
, ExtendOrderingResult(..)
, relevantTo
, singleton
, null
, getPO
, getECs
, elems) where
import Prelude hiding (null, EQ, GT)
import GHC.Generics (Generic)
import qualified Data.Map as M
import Control.Monad
import Data.Hashable
import Data.Maybe
import Debug.Trace
import qualified Data.List as L
import qualified Data.Set as S
import qualified Language.REST.EquivalenceClass as EC
import qualified Language.REST.PartialOrder as PO
import Language.REST.Op
import Language.REST.Types
import Language.REST.SMT
type PartialOrder = PO.PartialOrder
type EquivalenceClass = EC.EquivalenceClass
data QORelation = QGT | QEQ deriving (Ord, Eq, Generic, Hashable)
instance Show QORelation where
show QGT = ">"
show QEQ = "≈"
instance {-# OVERLAPPING #-} ToSMTVar a Int => ToSMT (WQO a) Bool where
toSMT (WQO ecs po) = And $ ecsSMT ++ posSMT where
toSMT' :: a -> SMTExpr Int
toSMT' = toSMT
ecsSMT = do
ec <- S.toList ecs
let ecl = EC.toList ec
guard $ length ecl >= 2
return $ Equal (map toSMT' ecl)
posSMT = do
(ec, vars) <- PO.toDescsList po
var <- S.toList vars
return $ Greater (toSMT $ EC.head ec) (toSMT $ EC.head var)
getPO (WQO _ po) = po
getECs (WQO ecs _) = ecs
-- Invariant: the first set contains all ECs
data WQO a = WQO (S.Set (EquivalenceClass a)) (PartialOrder (EquivalenceClass a))
deriving (Ord, Eq, Generic, Hashable)
instance (Show a, Eq a, Hashable a) => Show (WQO a) where
show (WQO ecs _) | S.null ecs = "⊤"
show (WQO ecs po) = L.intercalate " ∧ " (map show ecs' ++ po')
where
ecs' = filter (not . EC.isSingleton) $ S.toList ecs
po' =
if PO.isEmpty po
then []
else [show po]
-- else [show $ PO.mapUnsafe ecHead po]
-- ecHead (x, y) = (EC.head x, EC.head y)
null :: Eq a => WQO a -> Bool
null wqo = wqo == empty
empty :: WQO a
empty = WQO S.empty PO.empty
singleton :: (Ord a, Eq a, Hashable a) => (a, a, QORelation) -> Maybe (WQO a)
singleton t = insertMaybe empty t
{-# INLINE elems #-}
elems :: (Ord a) => WQO a -> S.Set a
elems (WQO ec _) = S.unions $ map EC.elems (S.toList ec)
{-# INLINE getEquivalenceClasses #-}
getEquivalenceClasses :: (Ord a, Eq a, Hashable a) => WQO a -> a -> a
-> (Maybe (EquivalenceClass a), Maybe (EquivalenceClass a))
getEquivalenceClasses (WQO classes _) source target = (t, u)
where
t = L.find (EC.isMember source) classes'
u = L.find (EC.isMember target) classes'
classes' = S.toList classes
{-# INLINE getEquivalenceClasses' #-}
getEquivalenceClasses' (WQO classes _) source target =
do
t <- L.find (EC.isMember source) classes'
if EC.isMember target t
then return (t, t)
else ((,) t) <$> L.find (EC.isMember target) classes'
where
classes' = S.toList classes
{-# INLINE getRelation #-}
getRelation :: (Ord a, Eq a, Hashable a) => WQO a -> a -> a -> Maybe QORelation
getRelation _ f g | f == g = Just QEQ
getRelation wqo@(WQO _ po) source target
| Just (s, t) <- getEquivalenceClasses' wqo source target
= if s == t
then Just QEQ
else
if PO.gt po s t
then Just QGT
else Nothing
| otherwise = Nothing
expandEC :: (Ord a, Eq a, Hashable a) => WQO a -> EquivalenceClass a -> a -> WQO a
expandEC (WQO ecs po) ec x = WQO ecs' po'
where
ec' = EC.insert x ec
ecs' = S.insert ec' $ S.delete ec ecs
po' = PO.replaceUnsafe [ec] ec' po
mergeECs :: (Ord a, Eq a, Hashable a) => WQO a -> EquivalenceClass a -> EquivalenceClass a -> WQO a
mergeECs (WQO ecs po) ec1 ec2 = WQO ecs' po'
where
ec' = EC.union ec1 ec2
ecs' = S.insert ec' $ S.delete ec2 $ S.delete ec1 ecs
po' = PO.replaceUnsafe [ec1, ec2] ec' po
type ECMap a = M.Map (EquivalenceClass a) (EquivalenceClass a)
{-# SPECIALISE notStrongerThan :: WQO Op -> WQO Op -> Bool #-}
notStrongerThan :: forall a . (Ord a, Eq a, Hashable a) => WQO a -> WQO a -> Bool
notStrongerThan w1 w2 | w1 == w2 = True
notStrongerThan (WQO ecs po) (WQO ecs' po') = result where
result = case mkEcsMap M.empty (S.toList ecs) of
Just ecsMap -> all (gt ecsMap) (PO.toDescsList po)
Nothing -> False
mkEcsMap :: ECMap a -> [EquivalenceClass a] -> Maybe (ECMap a)
mkEcsMap buf [] = Just buf
mkEcsMap buf (ec:rest) =
do
ec' <- L.find (ec `EC.isSubsetOf`) (S.toList ecs')
mkEcsMap (M.insert ec ec' buf) rest
gt ecsMap (ec, descs) =
let
Just ec' = M.lookup ec ecsMap
in
descs `S.isSubsetOf` (PO.descendents ec' po')
mergeAll :: forall a. (Show a, Ord a, Eq a, Hashable a) => [WQO a] -> Maybe (WQO a)
mergeAll [] = Just empty
mergeAll [x] = Just x
mergeAll (x : x' : xs) = do
y <- merge x x'
mergeAll (y : xs)
trace' _ x = x
{-# INLINE merge #-}
merge :: forall a. (Ord a, Eq a, Hashable a) => WQO a -> WQO a -> Maybe (WQO a)
merge lhs@(WQO ecs po) rhs@(WQO ecs' po') | S.disjoint (elems lhs) (elems rhs)
= Just $ WQO (S.union ecs ecs') (PO.unionDisjointUnsafe po po')
merge lhs rhs | otherwise =
if S.size (elems lhs) >= S.size (elems rhs)
then merge' lhs rhs
else merge' rhs lhs
{-# SPECIALISE merge' :: WQO Op -> WQO Op -> Maybe (WQO Op) #-}
merge' :: forall a. (Ord a, Eq a, Hashable a) => WQO a -> WQO a -> Maybe (WQO a)
merge' lhs rhs@(WQO ecs po) = trace' message $ result where
message = "Merge " ++ (show $ hash lhs) ++ " " ++ (show $ hash rhs)
withEQs' = go lhs ecsFacts
result = do
withEQs <- withEQs'
go withEQs poFacts
ecsFacts :: [(a, a, QORelation)]
ecsFacts = concatMap ecFacts (S.toList ecs)
ecFacts ec =
let
xs = EC.toList ec
in
map (\(a, b) -> (a, b, QEQ)) (zip xs (tail xs))
poFacts :: [(a, a, QORelation)]
poFacts =
map (\(a, b) -> (head (EC.toList a), head (EC.toList b), QGT)) (PO.toList po)
go r [] = Just r
go r (x : xs) =
do
r' <- insertMaybe r x
go r' xs
data ExtendOrderingResult a =
ValidExtension (WQO a)
| AlreadyImplied
| Contradicts
relevantTo :: (Ord a, Eq a, Hashable a) => WQO a -> S.Set a -> S.Set a -> WQO a
relevantTo wqo0 as bs = go empty cartesianProduct where
cartesianProduct = do
x <- S.toList as
y <- S.toList bs
return (x, y)
get _ (ValidExtension w) = w
get w AlreadyImplied = w
get _ _ = undefined
go wqo [] = wqo
go wqo ((f, g) : xs) | f == g = go wqo xs
go wqo ((f, g) : xs) | Just r <- getRelation wqo0 f g
, wqo' <- get wqo $ insert wqo (f, g, r)
= go wqo' xs
go wqo ((f, g) : xs) | Just r <- getRelation wqo0 g f
, wqo' <- get wqo $ insert wqo (g, f, r)
= go wqo' xs
go wqo (_ : xs) | otherwise = go wqo xs
{-# INLINE insertMaybe #-}
{-# SPECIALISE insertMaybe :: WQO Op -> (Op, Op, QORelation) -> Maybe (WQO Op) #-}
insertMaybe :: (Ord a, Eq a, Hashable a) => WQO a -> (a, a, QORelation) -> Maybe (WQO a)
insertMaybe wqo t = case insert wqo t of
ValidExtension wqo' -> Just wqo'
AlreadyImplied -> Just wqo
Contradicts -> Nothing
{-# SPECIALISE insert :: WQO Op -> (Op, Op, QORelation) -> ExtendOrderingResult Op #-}
insert :: (Ord a, Eq a, Hashable a) => WQO a -> (a, a, QORelation) -> ExtendOrderingResult a
insert _ (f, g, QGT) | f == g = Contradicts
insert wqo (f, g, r) | Just r' <- getRelation wqo f g
= if r == r' then AlreadyImplied else Contradicts
insert wqo (f, g, _) | isJust $ getRelation wqo g f = Contradicts
insert wqo@(WQO ecs po) (f, g, QEQ) = ValidExtension $
case getEquivalenceClasses wqo f g of
(Nothing, Nothing) ->
let
ecs' = S.insert (EC.fromList [f, g]) ecs
in
WQO ecs' po
(Just ec, Nothing) -> expandEC wqo ec g
(Nothing, Just ec) -> expandEC wqo ec f
(Just ec1, Just ec2) -> mergeECs wqo ec1 ec2
insert wqo@(WQO ecs po) (f, g, QGT) = ValidExtension $
case getEquivalenceClasses wqo f g of
(Nothing, Nothing) ->
let
f' = EC.singleton f
g' = EC.singleton g
ecs' = S.insert f' $ S.insert g' ecs
Just po' = PO.insert po f' g'
in
WQO ecs' po'
(Just ec, Nothing) ->
let
g' = EC.singleton g
ecs' = S.insert g' ecs
Just po' = PO.insert po ec g'
in
WQO ecs' po'
(Nothing, Just ec) ->
let
f' = EC.singleton f
ecs' = S.insert f' ecs
Just po' = PO.insert po f' ec
in
WQO ecs' po'
(Just ec1, Just ec2) ->
WQO ecs (PO.insertUnsafe po ec1 ec2)
orderings :: forall a. (Ord a, Eq a, Hashable a) => S.Set a -> S.Set (WQO a)
orderings ops = go S.empty (S.singleton empty) where
insert' w t | ValidExtension w' <- insert w t = Just w'
insert' _ _ = Nothing
go :: S.Set (WQO a) -> S.Set (WQO a) -> S.Set (WQO a)
go seen acc | S.null acc = seen
go seen acc =
let
ordering = head $ S.toList acc
acc' = S.delete ordering acc
seen' = S.insert ordering seen
newOrderings =
S.fromList $ do
f <- S.toList ops
g <- S.toList (S.delete f ops)
o <- [QEQ, QGT]
maybeToList (insert' ordering (f,g, o))
newOrderings' = S.difference newOrderings seen
in
go seen' (S.union acc' newOrderings')