-- | -- Module: NetSpider.SeqID -- Description: Sequential node IDs -- Maintainer: Toshio Ito -- -- @since 0.4.3.0 module NetSpider.SeqID ( -- * Type SeqIDMaker, -- * Construction newSeqIDMaker, -- * Conversion convertGraph, convertLink, convertNode, convertNodeID, originalIDFor ) where import Data.Foldable (foldl') import Data.List (reverse) import Data.Hashable (Hashable) import Data.HashMap.Strict (HashMap) import qualified Data.HashMap.Strict as HM import NetSpider.Snapshot.Internal ( SnapshotNode(_nodeId), SnapshotLink(_sourceNode, _destinationNode), SnapshotGraph ) -- | 'SeqIDMaker' converts node ID type @n@ into the new node ID type -- @i@. The type @i@ is supposed to be an 'Enum', and it generates the -- node ID of type @i@ sequentially for each node ID of type @n@. -- -- 'SeqIDMaker' is useful to convert the 'SnapshotGraph' into a graph -- representation of another graph library such as -- [fgl](https://hackage.haskell.org/package/fgl). Note that the -- target graph library can provide a better way for conversion. For -- example, fgl has @NodeMap@ type to do basically the same thing. data SeqIDMaker n i = SeqIDMaker { toSeqID :: HashMap n i, fromSeqID :: HashMap i n, nextID :: i } deriving (Show,Eq) -- | Make a 'SeqIDMaker' with the given initial ID. newSeqIDMaker :: i -- ^ Initial ID -> SeqIDMaker n i newSeqIDMaker init_id = SeqIDMaker HM.empty HM.empty init_id -- | Convert the given node ID of type @n@ into the sequential ID of -- type @i@. convertNodeID :: (Eq n, Hashable n, Enum i, Eq i, Hashable i) => SeqIDMaker n i -> n -- ^ Old ID -> (SeqIDMaker n i, i) -- ^ Updated 'SeqIDMaker' and the new ID. convertNodeID maker nid = case HM.lookup nid $ toSeqID maker of Just existing_id -> (maker, existing_id) Nothing -> (new_maker, new_id) where new_id = nextID maker new_next_id = succ $ new_id new_maker = maker { toSeqID = new_to, fromSeqID = new_from, nextID = new_next_id } new_to = HM.insert nid new_id $ toSeqID maker new_from = HM.insert new_id nid $ fromSeqID maker -- | Convert node IDs in the 'SnapshotGraph'. convertGraph :: (Eq n, Hashable n, Enum i, Eq i, Hashable i) => SeqIDMaker n i -> SnapshotGraph n na la -> (SeqIDMaker n i, SnapshotGraph i na la) convertGraph maker (nodes, links) = (new_maker, (new_nodes, new_links)) where (inter_maker, new_nodes_rev) = foldl' nodeFolder (maker, []) nodes nodeFolder (m, ns) node = let (step_m, new_node) = convertNode m node in (step_m, new_node : ns) new_nodes = reverse new_nodes_rev (new_maker, new_links_rev) = foldl' linkFolder (inter_maker, []) links linkFolder (m, ls) link = let (step_m, new_link) = convertLink m link in (step_m, new_link : ls) new_links = reverse new_links_rev -- | Convert node IDs in the 'SnapshotLink'. convertLink :: (Eq n, Hashable n, Enum i, Eq i, Hashable i) => SeqIDMaker n i -> SnapshotLink n la -> (SeqIDMaker n i, SnapshotLink i la) convertLink maker link = (new_maker, new_link) where (inter_maker, seq_source) = convertNodeID maker $ _sourceNode link (new_maker, seq_dest) = convertNodeID inter_maker $ _destinationNode link new_link = link { _sourceNode = seq_source, _destinationNode = seq_dest } -- | Convert node ID in the 'SnapshotNode'. convertNode :: (Eq n, Hashable n, Enum i, Eq i, Hashable i) => SeqIDMaker n i -> SnapshotNode n na -> (SeqIDMaker n i, SnapshotNode i na) convertNode maker node = (new_maker, new_node) where (new_maker, seq_id) = convertNodeID maker $ _nodeId node new_node = node { _nodeId = seq_id } -- | Get the original ID of type @n@ for the new ID of type @i@. originalIDFor :: (Eq i, Hashable i) => SeqIDMaker n i -> i -> Maybe n originalIDFor maker seq_id = HM.lookup seq_id $ fromSeqID maker