quickspec-0.9.4: src/Test/QuickSpec/Reasoning/UnionFind.hs
-- | A union-find data structure.
module Test.QuickSpec.Reasoning.UnionFind(UF, Replacement((:>)), newSym, (=:=), rep, evalUF, execUF, runUF, S, isRep, initial) where
import Prelude hiding (min)
import Control.Monad
import Control.Monad.Trans.State.Strict
import Data.IntMap(IntMap)
import qualified Data.IntMap as IntMap
data Info = Rep Int | NonRep Int
defaultInfo :: Info
defaultInfo = Rep 1
data S = S {
info :: IntMap Info,
sym :: Int
}
type UF = State S
data Replacement = Int :> Int
runUF :: S -> UF a -> (a, S)
runUF s m = runState m s
evalUF :: S -> UF a -> a
evalUF s m = fst (runUF s m)
execUF :: S -> UF a -> S
execUF s m = snd (runUF s m)
initial :: Int -> S
initial n = S IntMap.empty n
modifyInfo f = modify (\s -> s { info = f (info s) })
modifySym f = modify (\s -> s { sym = f (sym s) })
putInfo i = modifyInfo (const i)
newSym :: UF Int
newSym = do
s <- get
modifySym (+1)
return (sym s)
(=:=) :: Int -> Int -> UF (Maybe Replacement)
s =:= t | s == t = return Nothing
s =:= t = do
rs <- rep s
rt <- rep t
if (rs /= rt) then fmap Just (unifyRep rs rt) else return Nothing
unifyRep :: Int -> Int -> UF Replacement
unifyRep s t = do
ss <- gets (IntMap.findWithDefault 0 s . reps)
st <- gets (IntMap.findWithDefault 0 t . reps)
if ss <= st then replace s t ss st else replace t s st ss
replace :: Int -> Int -> Int -> Int -> UF Replacement
replace s t ss st = do
modifyReps (IntMap.delete s . IntMap.insert t (ss+st+1))
modifyLinks (IntMap.insert s t)
return (s :> t)
rep :: Int -> UF Int
rep t = do
m <- fmap links get
case IntMap.lookup t m of
Nothing -> return t
Just t' -> do
r <- rep t'
when (t' /= r) $ modifyLinks (IntMap.insert t r)
return r
isRep :: Int -> UF Bool
isRep t = do
t' <- rep t
return (t == t')