{-# LANGUAGE ScopedTypeVariables#-} module MultisetOrdering where import Data.Hashable import Language.REST.Dot import qualified Data.Maybe as Mb import qualified Data.List as L import qualified Language.REST.Internal.MultiSet as M import qualified Data.HashMap.Strict as Mp import qualified Data.HashSet as S import Language.REST.Types data Replace a = ReplaceOne a a | Replace a [a] deriving (Show) newtype MultisetGE a = MultisetGE [Replace a] deriving (Show) type GTE a = a -> a -> Bool type Indexed a = (a, Int) type IndexedMultisetPair a = (Indexed (M.MultiSet (Indexed a)) , Indexed (M.MultiSet (Indexed a))) multisetGE :: forall a . Eq a => GTE a -> M.MultiSet a -> M.MultiSet a -> Maybe (MultisetGE a) multisetGE gte ts0 us0 = go [] (M.toList ts0) (M.toList us0) where equiv t u = t `gte` u && u `gte` t gt t u = t `gte` u && not (u `gte` t) go :: [Replace a] -> [a] -> [a] -> Maybe (MultisetGE a) go rs (t : ts) us | Just u <- L.find (equiv t) us = go (ReplaceOne t u:rs) ts (L.delete u us) go rs (t : ts) us = let (lts, us') = L.partition (t `gt`) us in go (Replace t lts : rs) ts us' go rs ts [] = Just $ MultisetGE $ map (`Replace` []) ts ++ rs go _ [] _ = Nothing multisetOrd :: (Eq a, Hashable a, Ord a) => [a] -> [a] -> Maybe (MultisetGE a) multisetOrd ts us = multisetGE (>=) (M.fromList ts) (M.fromList us) zindex :: [a] -> [(a, Int)] zindex xs = zip xs [0 ..] indexMS :: (Eq a, Hashable a) => M.MultiSet a -> M.MultiSet (a, Int) indexMS ms = M.fromList $ zindex (M.toList ms) mkEdge :: NodeID -> NodeID -> Edge mkEdge t u = Edge t u " " "black" " " "solid" botNodeName :: Int -> Int -> String botNodeName tIndex mIndex = "bot_" ++ show tIndex ++ "_" ++ show mIndex botNode :: Int -> Int -> Node botNode tIndex mIndex = Node (botNodeName tIndex mIndex) "⊥" "solid" "black" toGraph' :: forall a. (Eq a, Hashable a, Show a) => GTE a -> [M.MultiSet a] -> DiGraph toGraph' gte mss0 = DiGraph "msograph" (toOrderedSet (S.union elemNodes botNodes)) (toOrderedSet edges) where indexed :: [(M.MultiSet (a, Int), Int)] indexed = zindex (map indexMS mss0) pairs :: [((M.MultiSet (a, Int), Int), (M.MultiSet (a, Int), Int))] pairs = zip indexed (tail indexed) elemNodes = S.fromList $ filter hasEdge $ concatMap toNodes indexed hasEdge node = any (`pointsTo` node) $ S.toList edges pointsTo edge node = from edge == nodeID node || to edge == nodeID node edges :: S.HashSet Edge edges = S.fromList $ topEdges ++ map snd (replEdges pairs) topEdges = map go (M.toList (fst $ head indexed)) where go (_, index) = mkEdge "⊤" (nodeName (index, 0)) botNodes = S.fromList $ Mb.mapMaybe fst (replEdges pairs) nodeName :: (Int, Int) -> String nodeName (elemIndex, msIndex) = "n" ++ show elemIndex ++ "_" ++ show msIndex replEdges = toEdges Mp.empty toEdges :: Mp.HashMap (Int, Int) (Int, Int) -> [IndexedMultisetPair a] -> [(Maybe Node, Edge)] toEdges _ [] = [] toEdges mp (((ts, tsIndex), (us, usIndex)) : mss) = concatMap redges repls ++ toEdges mp' mss where Just (MultisetGE repls) = multisetGE (\t u -> gte (fst t) (fst u)) ts us lookupTIndex :: Int -> (Int, Int) lookupTIndex tindex = Mb.fromMaybe (tindex, tsIndex) (Mp.lookup (tindex, tsIndex) mp) mp' = go mp repls where go mpi [] = mpi go mpi ((ReplaceOne (_, i) (_, j)):repls') = go (Mp.insert (j, usIndex) (lookupTIndex i) mpi) repls' go mpi (_:repls') = go mpi repls' redges (Replace (_, index) []) = [ ( Just (botNode index tsIndex) , mkEdge (nodeName (lookupTIndex index)) (botNodeName index tsIndex) ) ] redges (ReplaceOne _ _) = [] redges (Replace (_, tindex) us') = map go us' where go (_, uindex) = (Nothing, mkEdge (nodeName (lookupTIndex tindex)) (nodeName (uindex, usIndex))) toNodes (ms, index) = map go (M.toList ms) where go (e, elemIndex) = Node (nodeName (elemIndex, index)) (show e) "solid" "black" toGraph :: (Ord a, Eq a, Hashable a, Show a) => [[a]] -> DiGraph toGraph mss = toGraph' (>=) $ map M.fromList mss mkMSOGraph :: (Ord a, Eq a, Hashable a, Show a) => String -> [[a]] -> IO () mkMSOGraph name mss = mkGraph name (toGraph mss) mkMSOGraphs :: (Ord a, Eq a, Hashable a, Show a) => String -> [[a]] -> IO () mkMSOGraphs name mss0 = mapM_ go (drop 1 $ L.inits mss0) where go mss = mkGraph (name ++ show (length mss)) (toGraph mss) multisetGE' :: (Ord a, Hashable a) => [a] -> [a] -> Maybe (MultisetGE a) multisetGE' ts us = multisetGE (>=) (M.fromList ts) (M.fromList us)