-- This file is part of Intricacy -- Copyright (C) 2013 Martin Bays -- -- This program is free software: you can redistribute it and/or modify -- it under the terms of version 3 of the GNU General Public License as -- published by the Free Software Foundation, or any later version. -- -- You should have received a copy of the GNU General Public License -- along with this program. If not, see http://www.gnu.org/licenses/. module GraphColouring (fiveColour) where import qualified Data.Map as Map import Data.Map (Map) import qualified Data.Set as Set import Data.Set (Set) import qualified Data.Vector as Vector import Data.List import Data.Maybe type Colouring a = Map a Int type Graph a = (Set a, Set (Set a)) type PlanarGraph a = Map a [a] fiveColour :: Ord a => PlanarGraph a -> Colouring a -> Colouring a -- ^algorithm based on that presented in -- http://people.math.gatech.edu/~thomas/PAP/fcstoc.pdf -- Key point: a planar graph can't have all vertices of degree >= 6 -- (Proof: suppose it does, so |E| >= 3|V|; WLOG the graph is triangulated, -- so then |F| <= 2/3 |E|. So \xi = |V|-|E|+|F| <= (1/3 - 1 + 2/3)|E| = 0. -- But a planar graph has Euler characteristic 1.) -- Aims to minimise changes from given (partial) colouring lastCol. fiveColour g lastCol = if Map.keysSet lastCol == Map.keysSet g && isColouring g lastCol then lastCol else fiveColour' lastCol g isColouring :: Ord a => PlanarGraph a -> Colouring a -> Bool isColouring g mapping = and [ Map.lookup s mapping /= Map.lookup e mapping | s <- Map.keys g , e <- g Map.! s ] fiveColour' :: Ord a => Colouring a -> PlanarGraph a -> Colouring a fiveColour' pref g | g == Map.empty = Map.empty fiveColour' pref g = let adjsOf v = (nub $ g Map.! v) \\ [v] v0 = head $ filter ((<=5) . length . adjsOf) $ Map.keys g adjs = adjsOf v0 addTo c = let vc = head $ possCols pref v0 \\ map (c Map.!) adjs in Map.insert v0 vc c in if length adjs < 5 then addTo $ fiveColour' pref $ deleteNode v0 g else let (v',v'') = if adjs!!2 `elem` (g Map.! (adjs!!0)) then (adjs!!1,adjs!!3) else (adjs!!0,adjs!!2) in addTo $ demerge v' v'' $ fiveColour' pref $ merge v0 v' v'' g possCols :: Ord a => Colouring a -> a -> [Int] possCols pref v = maybe [0..4] (\lvc -> lvc:([0..4] \\ [lvc])) $ Map.lookup v pref demerge :: Ord a => a -> a -> Colouring a -> Colouring a demerge v v' c = Map.insert v' (c Map.! v) c merge :: Ord a => a -> a -> a -> PlanarGraph a -> PlanarGraph a merge v v' v'' g = deleteNode v $ contractNodes v' v'' $ Map.adjust (concatAdjsOver v $ g Map.! v'') v' g concatAdjsOver :: Ord a => a -> [a] -> [a] -> [a] concatAdjsOver v adjs adjs' = let (s,_:e) = splitAt (fromJust $ elemIndex v adjs) adjs in s ++ adjs' ++ e deleteNode :: Ord a => a -> PlanarGraph a -> PlanarGraph a deleteNode v = fmap (filter (/= v)) . Map.delete v contractNodes :: Ord a => a -> a -> PlanarGraph a -> PlanarGraph a contractNodes v v' = fmap (map (\v'' -> if v'' == v' then v else v'')) . Map.delete v'