union-find-0.2: src/Data/UnionFind/IntMap.hs
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE BangPatterns #-}
{-# OPTIONS_GHC -funbox-strict-fields #-}
--
-- NOTE: Does not currently ensure that a 'Point' was indeed generated
-- by the specified 'PointSupply'.
--
module Data.UnionFind.IntMap
( newPointSupply, fresh, repr, descriptor, union, equivalent,
PointSupply, Point ) where
import qualified Data.IntMap as IM
data PointSupply a = PointSupply !Int (IM.IntMap (Link a))
deriving Show
data Link a
= Info {-# UNPACK #-} !Int a
-- ^ This is the descriptive element of the equivalence class
-- and its rank.
| Link {-# UNPACK #-} !Int
-- ^ Pointer to some other element of the equivalence class.
deriving Show
newtype Point a = Point Int
newPointSupply :: PointSupply a
newPointSupply = PointSupply 0 IM.empty
fresh :: PointSupply a -> a -> (PointSupply a, Point a)
fresh (PointSupply next eqs) a =
(PointSupply (next + 1) (IM.insert next (Info 0 a) eqs), Point next)
-- freshList :: PointSupply a -> [a] -> (PointSupply a, [Point a])
-- freshList
repr :: PointSupply a -> Point a -> Point a
repr ps p = reprInfo ps p (\n _rank _a -> Point n)
reprInfo :: PointSupply a -> Point a -> (Int -> Int -> a -> r) -> r
reprInfo (PointSupply _next eqs) (Point n) k = go n
where
go !i =
case eqs IM.! i of
Link i' -> go i'
Info r a -> k i r a
union :: PointSupply a -> Point a -> Point a -> PointSupply a
union ps@(PointSupply next eqs) p1 p2 =
reprInfo ps p1 $ \i1 r1 _a1 ->
reprInfo ps p2 $ \i2 r2 a2 ->
if i1 == i2 then ps else
case r1 `compare` r2 of
LT ->
-- No rank or descriptor update necessary
let !eqs1 = IM.insert i1 (Link i2) eqs in
PointSupply next eqs1
EQ ->
let !eqs1 = IM.insert i1 (Link i2) eqs
!eqs2 = IM.insert i2 (Info (r2 + 1) a2) eqs1 in
PointSupply next eqs2
GT ->
let !eqs1 = IM.insert i1 (Info r2 a2) eqs
!eqs2 = IM.insert i2 (Link i1) eqs1 in
PointSupply next eqs2
descriptor :: PointSupply a -> Point a -> a
descriptor ps p = reprInfo ps p (\_ _ a -> a)
equivalent :: PointSupply a -> Point a -> Point a -> Bool
equivalent ps p1 p2 =
reprInfo ps p1 $ \i1 _ _ ->
reprInfo ps p2 $ \i2 _ _ ->
i1 == i2
{-
tst1 :: IO ()
tst1 = do
let ps0 = newPointSupply
(ps1, p1) = fresh ps0 "hello"
(ps2, p2) = fresh ps1 "world"
(ps3, p3) = fresh ps2 "you"
(ps, p4) = fresh ps3 "there"
let ps' = union ps p1 p2
print (descr ps p1, descr ps p2, equivalent ps p1 p2)
print (descr ps' p1, descr ps' p2, equivalent ps' p1 p2)
let ps'' = union ps' p3 p1
print (descr ps'' p1, descr ps'' p3, equivalent ps'' p1 p3)
print ps''
-}
-- TODO: should fail
{-
tst2 :: IO ()
tst2 = do
let as0 = newPointSupply
(as, a1) = fresh as0 "foo"
bs0 = newPointSupply
(bs, b1) = fresh bs0 "bar"
print $ union as a1 b1
-}