{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-} module UniqueLabelsGraph ( insUqNode, insUqNodes, mkUqGraph, lnodesetFromGraph, labelsetFromGraph, empty, matchLabel, labelExists, addLab, modLab, DynGraphUqL ) where import Data.Graph.Inductive import Control.Monad.State import Data.Maybe import qualified Data.Set as S import Data.List (foldl',find) -- like insNode(s) from Data.Graph.Inductive, but only works if node label is unique -- a class for graphs with unique labels -- you get an error if you try to insert duplicate labels -- and there's a function for returning all node labels as a set. -- wouldn't Ord constraint be better? -- minimum definition: insUqNode class (Ord a, DynGraph gr) => DynGraphUqL a gr where insUqNode :: LNode a -> gr a b -> gr a b insUqNode (v,l) gr | found = error "insNode, duplicate Node " | otherwise = insNode (v,l) gr where found = isJust . find (\(_,lab) -> lab==l) . labNodes $ gr matchLabel :: a -> gr a b -> (Maybe (Data.Graph.Inductive.Context a b), gr a b) matchLabel l g = case filter ( (==l) . snd) . labNodes $ g of [] -> (Nothing,g) [(nodeI,_)] -> match nodeI g otherwise -> error "matchLabel, duplidate labels" instance (Ord a, DynGraph gr) => DynGraphUqL a gr insUqNodes vs g = foldr insUqNode g vs lnodesetFromGraph = S.fromAscList . labNodes labelsetFromGraph = (S.map snd) . lnodesetFromGraph mkUqGraph vs es = (insEdges' . insUqNodes vs) empty where insEdges' g = foldl' (flip insEdge) g es labelExists l g = let (mcontext,gr) = matchLabel l g in isJust mcontext -- these fail because of duplicate nodes, which is the right thing. t, t1 :: Gr String Float t = mkUqGraph [(1,""), (0,"")] [] t1 = insUqNode (1,"") . insUqNode (0, "") $ empty addLab :: (DynGraph g, Ord a) => a -> g a b -> g a b addLab a g = run_ g $ insMapNodeM a -- fails, as it should, because dupe t2 :: Gr String () t2 = addLab "" . addLab "" $ empty modLab :: (Ord a, DynGraph g) => (a -> a) -> a -> g a b -> g a b modLab f nl g = run_ g $ do oldgraph <- return . snd =<< get -- couldn't we just say: g instead of oldgraph and skip this? let ( mbOldContext, oldRemainingGr ) = matchLabel nl oldgraph mbNewGraph = do (fr, nodeId, nodeLabel, to) <- ( mbOldContext ) return $ (fr, nodeId, f nodeLabel, to) & oldRemainingGr return $ fromMaybe oldgraph mbNewGraph