{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE OverloadedStrings #-}
module Data.PlanarGraph(
PlanarGraph
, embedding, vertexData, dartData, faceData, rawDartData
, edgeData
, World(..)
, DualOf
, Arc(..)
, Direction(..), rev
, Dart(..), arc, direction
, twin, isPositive
, VertexId(..), VertexId'
, planarGraph, planarGraph', fromAdjacencyLists
, toAdjacencyLists
, buildFromJSON
, numVertices, numDarts, numEdges, numFaces
, darts', darts, edges', edges, vertices', vertices, faces', faces
, traverseVertices, traverseDarts, traverseFaces
, tailOf, headOf, endPoints
, incidentEdges, incomingEdges, outgoingEdges, neighboursOf
, nextIncidentEdge, prevIncidentEdge
, HasDataOf(..), endPointDataOf, endPointData
, dual
, FaceId(..), FaceId'
, leftFace, rightFace
, boundaryDart, boundary, boundary', boundaryVertices
, nextEdge, prevEdge
, EdgeOracle
, edgeOracle, buildEdgeOracle
, findEdge
, hasEdge, findDart
, allDarts
) where
import Control.Applicative (Alternative(..))
import Control.DeepSeq
import Control.Lens hiding ((.=))
import Control.Monad.ST (ST)
import Control.Monad.State.Strict
import Data.Aeson
import Data.Bifunctor
import Data.Bitraversable
import Data.Ext
import qualified Data.Foldable as F
import Data.Maybe (catMaybes, isJust, fromJust, fromMaybe)
import Data.Permutation
import Data.Traversable (fmapDefault,foldMapDefault)
import Data.Type.Equality (gcastWith, (:~:)(..))
import qualified Data.Vector as V
import qualified Data.Vector.Generic as GV
import qualified Data.Vector.Mutable as MV
import qualified Data.Vector.Unboxed as UV
import qualified Data.Vector.Unboxed.Mutable as UMV
import GHC.Generics (Generic)
import Unsafe.Coerce (unsafeCoerce)
newtype Arc s = Arc { _unArc :: Int } deriving (Eq,Ord,Enum,Bounded,Generic,NFData)
instance Show (Arc s) where
show (Arc i) = "Arc " ++ show i
data Direction = Negative | Positive deriving (Eq,Ord,Bounded,Enum,Generic)
instance NFData Direction
instance Show Direction where
show Positive = "+1"
show Negative = "-1"
instance Read Direction where
readsPrec _ "-1" = [(Negative,"")]
readsPrec _ "+1" = [(Positive,"")]
readsPrec _ _ = []
rev :: Direction -> Direction
rev Negative = Positive
rev Positive = Negative
data Dart s = Dart { _arc :: !(Arc s)
, _direction :: !Direction
} deriving (Eq,Ord,Generic)
makeLenses ''Dart
instance NFData (Dart s)
instance Show (Dart s) where
show (Dart a d) = "Dart (" ++ show a ++ ") " ++ show d
twin :: Dart s -> Dart s
twin (Dart a d) = Dart a (rev d)
isPositive :: Dart s -> Bool
isPositive d = d^.direction == Positive
instance Enum (Dart s) where
toEnum x
| even x = Dart (Arc $ x `div` 2) Positive
| otherwise = Dart (Arc $ x `div` 2) Negative
fromEnum (Dart (Arc i) d) = case d of
Positive -> 2*i
Negative -> 2*i + 1
allDarts :: [Dart s]
allDarts = concatMap (\a -> [Dart a Positive, Dart a Negative]) [Arc 0..]
data World = Primal | Dual deriving (Show,Eq)
type family DualOf (sp :: World) where
DualOf Primal = Dual
DualOf Dual = Primal
dualDualIdentity :: forall w. DualOf (DualOf w) :~: w
dualDualIdentity = unsafeCoerce Refl
newtype VertexId s (w :: World) = VertexId { _unVertexId :: Int }
deriving (Eq,Ord,Enum,ToJSON,FromJSON,Generic,NFData)
type VertexId' s = VertexId s Primal
unVertexId :: Getter (VertexId s w) Int
unVertexId = to _unVertexId
instance Show (VertexId s w) where
show (VertexId i) = "VertexId " ++ show i
data PlanarGraph s (w :: World) v e f = PlanarGraph { _embedding :: Permutation (Dart s)
, _vertexData :: V.Vector v
, _rawDartData :: V.Vector e
, _faceData :: V.Vector f
, _dual :: PlanarGraph s (DualOf w) f e v
} deriving (Generic)
instance (Show v, Show e, Show f) => Show (PlanarGraph s w v e f) where
show (PlanarGraph e v r f _) = unwords [ "PlanarGraph"
, "embedding =", show e
, ", vertexData =", show v
, ", rawDartData =", show r
, ", faceData =", show f
]
instance (Eq v, Eq e, Eq f) => Eq (PlanarGraph s w v e f) where
(PlanarGraph e v r f _) == (PlanarGraph e' v' r' f' _) = e == e' && v == v'
&& r == r' && f == f'
instance (ToJSON v, ToJSON e, ToJSON f)
=> ToJSON (PlanarGraph s w v e f) where
toJSON = object . encodeJSON
toEncoding = pairs . mconcat . encodeJSON
encodeJSON :: (ToJSON f, ToJSON e, ToJSON v, KeyValue t)
=> PlanarGraph s w v e f -> [t]
encodeJSON g = [ "vertices" .= ((\(v,x) -> v :+ x) <$> vertices g)
, "darts" .= ((\(e,x) -> endPoints e g :+ x) <$> darts g)
, "faces" .= ((\(f,x) -> f :+ x) <$> faces g)
, "adjacencies" .= toAdjacencyLists g
]
instance (FromJSON v, FromJSON e, FromJSON f)
=> FromJSON (PlanarGraph s Primal v e f) where
parseJSON = withObject "" $ \v -> buildFromJSON <$> v .: "vertices"
<*> v .: "darts"
<*> v .: "faces"
<*> v .: "adjacencies"
buildFromJSON :: V.Vector (VertexId' s :+ v)
-> V.Vector ((VertexId' s, VertexId' s) :+ e)
-> V.Vector (FaceId' s :+ f)
-> [(VertexId' s, V.Vector (VertexId' s))]
-> PlanarGraph s Primal v e f
buildFromJSON vs es fs as = g&vertexData .~ reorder vs _unVertexId
&dartData .~ ds
&faceData .~ reorder fs (_unVertexId._unFaceId)
where
g = fromAdjacencyLists as
oracle = edgeOracle g
findEdge' (u,v) = fromJust $ findDart u v oracle
ds = es&traverse %~ \(e:+x) -> (findEdge' e,x)
reorder v f = V.create $ do
v' <- MV.new (V.length v)
forM_ v $ \(i :+ x) ->
MV.write v' (f i) x
pure v'
embedding :: Getter (PlanarGraph s w v e f) (Permutation (Dart s))
embedding = to _embedding
vertexData :: Lens (PlanarGraph s w v e f) (PlanarGraph s w v' e f)
(V.Vector v) (V.Vector v')
vertexData = lens _vertexData (\g vD -> updateData (const vD) id id g)
rawDartData :: Lens (PlanarGraph s w v e f) (PlanarGraph s w v e' f)
(V.Vector e) (V.Vector e')
rawDartData = lens _rawDartData (\g dD -> updateData id (const dD) id g)
faceData :: Lens (PlanarGraph s w v e f) (PlanarGraph s w v e f')
(V.Vector f) (V.Vector f')
faceData = lens _faceData (\g fD -> updateData id id (const fD) g)
dual :: Getter (PlanarGraph s w v e f) (PlanarGraph s (DualOf w) f e v)
dual = to _dual
dartData :: Lens (PlanarGraph s w v e f) (PlanarGraph s w v e' f)
(V.Vector (Dart s, e)) (V.Vector (Dart s, e'))
dartData = lens darts (\g dD -> updateData id (const $ reorderEdgeData dD) id g)
edgeData :: Lens (PlanarGraph s w v e f) (PlanarGraph s w v e' f)
(V.Vector (Dart s, e)) (V.Vector (Dart s, e'))
edgeData = dartData
updateData :: forall s w v e f v' e' f'
. (V.Vector v -> V.Vector v')
-> (V.Vector e -> V.Vector e')
-> (V.Vector f -> V.Vector f')
-> PlanarGraph s w v e f
-> PlanarGraph s w v' e' f'
updateData = gcastWith proof updateData'
where
proof :: DualOf (DualOf w) :~: w
proof = dualDualIdentity
updateData' :: (DualOf (DualOf w) ~ w)
=> (V.Vector v -> V.Vector v')
-> (V.Vector e -> V.Vector e')
-> (V.Vector f -> V.Vector f')
-> PlanarGraph s w v e f
-> PlanarGraph s w v' e' f'
updateData' fv fe ff (PlanarGraph em vtxData dData fData dg) = g'
where
vtxData' = fv vtxData
dData' = fe dData
fData' = ff fData
g' = PlanarGraph em vtxData' dData' fData' dg'
dg' = PlanarGraph (dg^.embedding) fData' dData' vtxData' g'
reorderEdgeData :: Foldable f => f (Dart s, e) -> V.Vector e
reorderEdgeData ds = V.create $ do
v <- MV.new (F.length ds)
forM_ (F.toList ds) $ \(d,x) ->
MV.write v (fromEnum d) x
pure v
traverseVertices :: Applicative m
=> (VertexId s w -> v -> m v')
-> PlanarGraph s w v e f
-> m (PlanarGraph s w v' e f)
traverseVertices f = itraverseOf (vertexData.itraversed) (\i -> f (VertexId i))
traverseDarts :: Applicative m
=> (Dart s -> e -> m e')
-> PlanarGraph s w v e f
-> m (PlanarGraph s w v e' f)
traverseDarts f = itraverseOf (rawDartData.itraversed) (\i -> f (toEnum i))
traverseFaces :: Applicative m
=> (FaceId s w -> f -> m f')
-> PlanarGraph s w v e f
-> m (PlanarGraph s w v e f')
traverseFaces f = itraverseOf (faceData.itraversed) (\i -> f (FaceId $ VertexId i))
planarGraph' :: Permutation (Dart s) -> PlanarGraph s w () () ()
planarGraph' perm = pg
where
pg = PlanarGraph perm vData eData fData (computeDual pg)
d = size perm
e = d `div` 2
v = V.length (perm^.orbits)
f = e - v + 2
vData = V.replicate v ()
eData = V.replicate d ()
fData = V.replicate f ()
planarGraph :: [[(Dart s,e)]] -> PlanarGraph s Primal () e ()
planarGraph ds = (planarGraph' perm)&dartData .~ (V.fromList . concat $ ds)
where
n = sum . map length $ ds
perm = toCycleRep n $ map (map fst) ds
fromAdjacencyLists :: forall s w h. (Foldable h, Functor h)
=> [(VertexId s w, h (VertexId s w))]
-> PlanarGraph s w () () ()
fromAdjacencyLists adjM = planarGraph' . toCycleRep n $ perm
where
n = sum . fmap length $ perm
perm = map toOrbit $ adjM'
adjM' = fmap (second F.toList) adjM
oracle :: EdgeOracle s w Int
oracle = fmap (^.core) . assignArcs . buildEdgeOracle
. map (second $ map ext) $ adjM'
toOrbit (u,adjU) = concatMap (toDart u) adjU
toDart u v = let Just a = findEdge u v oracle
in case u `compare` v of
LT -> [Dart (Arc a) Positive]
EQ -> [Dart (Arc a) Positive, Dart (Arc a) Negative]
GT -> [Dart (Arc a) Negative]
assignArcs :: EdgeOracle s w e -> EdgeOracle s w (Int :+ e)
assignArcs o = evalState (traverse f o) 0
where
f :: e -> State Int (Int :+ e)
f e = do i <- get ; put (i+1) ; pure (i :+ e)
toAdjacencyLists :: PlanarGraph s w v e f -> [(VertexId s w, V.Vector (VertexId s w))]
toAdjacencyLists pg = map (\u -> (u,neighboursOf u pg)) . V.toList . vertices' $ pg
numVertices :: PlanarGraph s w v e f -> Int
numVertices g = V.length (g^.embedding.orbits)
numDarts :: PlanarGraph s w v e f -> Int
numDarts g = size (g^.embedding)
numEdges :: PlanarGraph s w v e f -> Int
numEdges g = numDarts g `div` 2
numFaces :: PlanarGraph s w v e f -> Int
numFaces g = numEdges g - numVertices g + 2
vertices' :: PlanarGraph s w v e f -> V.Vector (VertexId s w)
vertices' g = VertexId <$> V.enumFromN 0 (V.length (g^.embedding.orbits))
vertices :: PlanarGraph s w v e f -> V.Vector (VertexId s w, v)
vertices g = V.zip (vertices' g) (g^.vertexData)
darts' :: PlanarGraph s w v e f -> V.Vector (Dart s)
darts' = elems . _embedding
darts :: PlanarGraph s w v e f -> V.Vector (Dart s, e)
darts g = (\d -> (d,g^.dataOf d)) <$> darts' g
edges' :: PlanarGraph s w v e f -> V.Vector (Dart s)
edges' = V.filter isPositive . darts'
edges :: PlanarGraph s w v e f -> V.Vector (Dart s, e)
edges = V.filter (isPositive . fst) . darts
tailOf :: Dart s -> PlanarGraph s w v e f -> VertexId s w
tailOf d g = VertexId . fst $ lookupIdx (g^.embedding) d
headOf :: Dart s -> PlanarGraph s w v e f -> VertexId s w
headOf d = tailOf (twin d)
endPoints :: Dart s -> PlanarGraph s w v e f -> (VertexId s w, VertexId s w)
endPoints d g = (tailOf d g, headOf d g)
incidentEdges :: VertexId s w -> PlanarGraph s w v e f
-> V.Vector (Dart s)
incidentEdges (VertexId v) g = g^?!embedding.orbits.ix v
incomingEdges :: VertexId s w -> PlanarGraph s w v e f -> V.Vector (Dart s)
incomingEdges v g = V.filter (not . isPositive) $ incidentEdges v g
outgoingEdges :: VertexId s w -> PlanarGraph s w v e f -> V.Vector (Dart s)
outgoingEdges v g = V.filter isPositive $ incidentEdges v g
neighboursOf :: VertexId s w -> PlanarGraph s w v e f -> V.Vector (VertexId s w)
neighboursOf v g = otherVtx <$> incidentEdges v g
where
otherVtx d = let u = tailOf d g in if u == v then headOf d g else u
nextIncidentEdge :: Dart s -> PlanarGraph s w v e f -> Dart s
nextIncidentEdge d g = let perm = g^.embedding
(i,j) = lookupIdx perm d
in next (perm^?!orbits.ix i) j
prevIncidentEdge :: Dart s -> PlanarGraph s w v e f -> Dart s
prevIncidentEdge d g = let perm = g^.embedding
(i,j) = lookupIdx perm d
in previous (perm^?!orbits.ix i) j
class HasDataOf g i where
type DataOf g i
dataOf :: i -> Lens' g (DataOf g i)
instance HasDataOf (PlanarGraph s w v e f) (VertexId s w) where
type DataOf (PlanarGraph s w v e f) (VertexId s w) = v
dataOf (VertexId i) = vertexData.singular (ix i)
instance HasDataOf (PlanarGraph s w v e f) (Dart s) where
type DataOf (PlanarGraph s w v e f) (Dart s) = e
dataOf d = rawDartData.singular (ix $ fromEnum d)
instance HasDataOf (PlanarGraph s w v e f) (FaceId s w) where
type DataOf (PlanarGraph s w v e f) (FaceId s w) = f
dataOf (FaceId (VertexId i)) = faceData.singular (ix i)
endPointDataOf :: Dart s -> Getter (PlanarGraph s w v e f) (v,v)
endPointDataOf d = to $ endPointData d
endPointData :: Dart s -> PlanarGraph s w v e f -> (v,v)
endPointData d g = let (u,v) = endPoints d g in (g^.dataOf u, g^.dataOf v)
computeDual :: forall s w v e f. PlanarGraph s w v e f -> PlanarGraph s (DualOf w) f e v
computeDual = gcastWith proof computeDual'
where
proof :: DualOf (DualOf w) :~: w
proof = dualDualIdentity
computeDual' :: (DualOf (DualOf w) ~ w)
=> PlanarGraph s w v e f -> PlanarGraph s (DualOf w) f e v
computeDual' g = dualG
where
perm = g^.embedding
dualG = PlanarGraph (cycleRep (elems perm) (apply perm . twin))
(g^.faceData)
(g^.rawDartData)
(g^.vertexData)
g
newtype FaceId s w = FaceId { _unFaceId :: VertexId s (DualOf w) }
deriving (Eq,Ord,Enum,ToJSON,FromJSON)
type FaceId' s = FaceId s Primal
instance Show (FaceId s w) where
show (FaceId (VertexId i)) = "FaceId " ++ show i
faces' :: PlanarGraph s w v e f -> V.Vector (FaceId s w)
faces' = fmap FaceId . vertices' . _dual
faces :: PlanarGraph s w v e f -> V.Vector (FaceId s w, f)
faces g = V.zip (faces' g) (g^.faceData)
leftFace :: Dart s -> PlanarGraph s w v e f -> FaceId s w
leftFace d g = FaceId . headOf d $ _dual g
rightFace :: Dart s -> PlanarGraph s w v e f -> FaceId s w
rightFace d g = FaceId . tailOf d $ _dual g
nextEdge :: Dart s -> PlanarGraph s w v e f -> Dart s
nextEdge d = nextIncidentEdge d . _dual
prevEdge :: Dart s -> PlanarGraph s w v e f -> Dart s
prevEdge d = prevIncidentEdge d . _dual
boundaryDart :: FaceId s w -> PlanarGraph s w v e f -> Dart s
boundaryDart f = V.head . boundary f
boundary :: FaceId s w -> PlanarGraph s w v e f -> V.Vector (Dart s)
boundary (FaceId v) g = incidentEdges v $ _dual g
boundary' :: Dart s -> PlanarGraph s w v e f -> V.Vector (Dart s)
boundary' d g = fromMaybe (error "boundary'") . rotateTo d $ boundary (rightFace d g) g
where
rotateTo :: Eq a => a -> V.Vector a -> Maybe (V.Vector a)
rotateTo x v = f <$> V.elemIndex x v
where
f i = let (a,b) = V.splitAt i v in b <> a
boundaryVertices :: FaceId s w -> PlanarGraph s w v e f -> V.Vector (VertexId s w)
boundaryVertices f g = fmap (flip tailOf g) $ boundary f g
newtype EdgeOracle s w a =
EdgeOracle { _unEdgeOracle :: V.Vector (V.Vector (VertexId s w :+ a)) }
deriving (Show,Eq)
instance Functor (EdgeOracle s w) where
fmap = fmapDefault
instance Foldable (EdgeOracle s w) where
foldMap = foldMapDefault
instance Traversable (EdgeOracle s w) where
traverse f (EdgeOracle v) = EdgeOracle <$> traverse g v
where
g = traverse (bitraverse pure f)
edgeOracle :: PlanarGraph s w v e f -> EdgeOracle s w (Dart s)
edgeOracle g = buildEdgeOracle [ (v, mkAdjacency v <$> incidentEdges v g)
| v <- F.toList $ vertices' g
]
where
mkAdjacency v d = otherVtx v d :+ d
otherVtx v d = let u = tailOf d g in if u == v then headOf d g else u
buildEdgeOracle :: forall f s w e. (Foldable f)
=> [(VertexId s w, f (VertexId s w :+ e))] -> EdgeOracle s w e
buildEdgeOracle inAdj' = EdgeOracle $ V.create $ do
counts <- UV.thaw initCounts
marks <- UMV.replicate (UMV.length counts) False
outV <- MV.new (UMV.length counts)
build counts marks outV initQ
pure outV
where
inAdj = V.create $ do
mv <- MV.new (length inAdj')
forM_ inAdj' $ \(VertexId i,adjI) ->
MV.write mv i (V.fromList . F.toList $ adjI)
pure mv
initCounts = V.convert . fmap GV.length $ inAdj
initQ = GV.ifoldr (\i k q -> if k <= 6 then i : q else q) [] initCounts
extractAdj :: UMV.MVector s' Bool -> Int
-> ST s' (V.Vector (VertexId s w :+ e))
extractAdj marks i = let p = fmap not . UMV.read marks . (^.core.unVertexId)
in GV.filterM p $ inAdj V.! i
decrease :: UMV.MVector s' Int -> (VertexId s w :+ e')
-> ST s' (Maybe Int)
decrease counts (VertexId j :+ _) = do k <- UMV.read counts j
let k' = k - 1
UMV.write counts j k'
pure $ if k' <= 6 then Just j else Nothing
build :: UMV.MVector s' Int -> UMV.MVector s' Bool
-> MV.MVector s' (V.Vector (VertexId s w :+ e)) -> [Int] -> ST s' ()
build _ _ _ [] = pure ()
build counts marks outV (i:q) = do
b <- UMV.read marks i
nq <- if b then pure []
else do
adjI <- extractAdj marks i
MV.write outV i adjI
UMV.write marks i True
V.toList <$> mapM (decrease counts) adjI
build counts marks outV (catMaybes nq <> q)
hasEdge :: VertexId s w -> VertexId s w -> EdgeOracle s w a -> Bool
hasEdge u v = isJust . findEdge u v
findEdge :: VertexId s w -> VertexId s w -> EdgeOracle s w a -> Maybe a
findEdge (VertexId u) (VertexId v) (EdgeOracle os) = find' u v <|> find' v u
where
find' j i = fmap (^.extra) . F.find (\(VertexId k :+ _) -> j == k) $ os V.! i
findDart :: VertexId s w -> VertexId s w -> EdgeOracle s w (Dart s) -> Maybe (Dart s)
findDart (VertexId u) (VertexId v) (EdgeOracle os) = find' twin u v <|> find' id v u
where
find' f j i = fmap (f . (^.extra)) . F.find (\(VertexId k :+ _) -> j == k) $ os V.! i