-- |
-- Module: NetSpider.SeqID
-- Description: Sequential node IDs
-- Maintainer: Toshio Ito <debug.ito@gmail.com>
--
-- @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
  { SeqIDMaker n i -> HashMap n i
toSeqID :: HashMap n i,
    SeqIDMaker n i -> HashMap i n
fromSeqID :: HashMap i n,
    SeqIDMaker n i -> i
nextID :: i
  }
  deriving (Int -> SeqIDMaker n i -> ShowS
[SeqIDMaker n i] -> ShowS
SeqIDMaker n i -> String
(Int -> SeqIDMaker n i -> ShowS)
-> (SeqIDMaker n i -> String)
-> ([SeqIDMaker n i] -> ShowS)
-> Show (SeqIDMaker n i)
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
forall n i. (Show n, Show i) => Int -> SeqIDMaker n i -> ShowS
forall n i. (Show n, Show i) => [SeqIDMaker n i] -> ShowS
forall n i. (Show n, Show i) => SeqIDMaker n i -> String
showList :: [SeqIDMaker n i] -> ShowS
$cshowList :: forall n i. (Show n, Show i) => [SeqIDMaker n i] -> ShowS
show :: SeqIDMaker n i -> String
$cshow :: forall n i. (Show n, Show i) => SeqIDMaker n i -> String
showsPrec :: Int -> SeqIDMaker n i -> ShowS
$cshowsPrec :: forall n i. (Show n, Show i) => Int -> SeqIDMaker n i -> ShowS
Show,SeqIDMaker n i -> SeqIDMaker n i -> Bool
(SeqIDMaker n i -> SeqIDMaker n i -> Bool)
-> (SeqIDMaker n i -> SeqIDMaker n i -> Bool)
-> Eq (SeqIDMaker n i)
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
forall n i.
(Eq n, Eq i) =>
SeqIDMaker n i -> SeqIDMaker n i -> Bool
/= :: SeqIDMaker n i -> SeqIDMaker n i -> Bool
$c/= :: forall n i.
(Eq n, Eq i) =>
SeqIDMaker n i -> SeqIDMaker n i -> Bool
== :: SeqIDMaker n i -> SeqIDMaker n i -> Bool
$c== :: forall n i.
(Eq n, Eq i) =>
SeqIDMaker n i -> SeqIDMaker n i -> Bool
Eq)

-- | Make a 'SeqIDMaker' with the given initial ID.
newSeqIDMaker :: i -- ^ Initial ID
              -> SeqIDMaker n i
newSeqIDMaker :: i -> SeqIDMaker n i
newSeqIDMaker i
init_id = HashMap n i -> HashMap i n -> i -> SeqIDMaker n i
forall n i. HashMap n i -> HashMap i n -> i -> SeqIDMaker n i
SeqIDMaker HashMap n i
forall k v. HashMap k v
HM.empty HashMap i n
forall k v. HashMap k v
HM.empty i
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 :: SeqIDMaker n i -> n -> (SeqIDMaker n i, i)
convertNodeID SeqIDMaker n i
maker n
nid =
  case n -> HashMap n i -> Maybe i
forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
HM.lookup n
nid (HashMap n i -> Maybe i) -> HashMap n i -> Maybe i
forall a b. (a -> b) -> a -> b
$ SeqIDMaker n i -> HashMap n i
forall n i. SeqIDMaker n i -> HashMap n i
toSeqID SeqIDMaker n i
maker of
    Just i
existing_id -> (SeqIDMaker n i
maker, i
existing_id)
    Maybe i
Nothing -> (SeqIDMaker n i
new_maker, i
new_id)
  where
    new_id :: i
new_id = SeqIDMaker n i -> i
forall n i. SeqIDMaker n i -> i
nextID SeqIDMaker n i
maker
    new_next_id :: i
new_next_id = i -> i
forall a. Enum a => a -> a
succ (i -> i) -> i -> i
forall a b. (a -> b) -> a -> b
$ i
new_id
    new_maker :: SeqIDMaker n i
new_maker = SeqIDMaker n i
maker { toSeqID :: HashMap n i
toSeqID = HashMap n i
new_to,
                        fromSeqID :: HashMap i n
fromSeqID = HashMap i n
new_from,
                        nextID :: i
nextID = i
new_next_id
                      }
    new_to :: HashMap n i
new_to = n -> i -> HashMap n i -> HashMap n i
forall k v.
(Eq k, Hashable k) =>
k -> v -> HashMap k v -> HashMap k v
HM.insert n
nid i
new_id (HashMap n i -> HashMap n i) -> HashMap n i -> HashMap n i
forall a b. (a -> b) -> a -> b
$ SeqIDMaker n i -> HashMap n i
forall n i. SeqIDMaker n i -> HashMap n i
toSeqID SeqIDMaker n i
maker
    new_from :: HashMap i n
new_from = i -> n -> HashMap i n -> HashMap i n
forall k v.
(Eq k, Hashable k) =>
k -> v -> HashMap k v -> HashMap k v
HM.insert i
new_id n
nid (HashMap i n -> HashMap i n) -> HashMap i n -> HashMap i n
forall a b. (a -> b) -> a -> b
$ SeqIDMaker n i -> HashMap i n
forall n i. SeqIDMaker n i -> HashMap i n
fromSeqID SeqIDMaker n i
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 :: SeqIDMaker n i
-> SnapshotGraph n na la -> (SeqIDMaker n i, SnapshotGraph i na la)
convertGraph SeqIDMaker n i
maker ([SnapshotNode n na]
nodes, [SnapshotLink n la]
links) = (SeqIDMaker n i
new_maker, ([SnapshotNode i na]
new_nodes, [SnapshotLink i la]
new_links))
  where
    (SeqIDMaker n i
inter_maker, [SnapshotNode i na]
new_nodes_rev) = ((SeqIDMaker n i, [SnapshotNode i na])
 -> SnapshotNode n na -> (SeqIDMaker n i, [SnapshotNode i na]))
-> (SeqIDMaker n i, [SnapshotNode i na])
-> [SnapshotNode n na]
-> (SeqIDMaker n i, [SnapshotNode i na])
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' (SeqIDMaker n i, [SnapshotNode i na])
-> SnapshotNode n na -> (SeqIDMaker n i, [SnapshotNode i na])
forall i n na.
(Enum i, Eq n, Eq i, Hashable n, Hashable i) =>
(SeqIDMaker n i, [SnapshotNode i na])
-> SnapshotNode n na -> (SeqIDMaker n i, [SnapshotNode i na])
nodeFolder (SeqIDMaker n i
maker, []) [SnapshotNode n na]
nodes
    nodeFolder :: (SeqIDMaker n i, [SnapshotNode i na])
-> SnapshotNode n na -> (SeqIDMaker n i, [SnapshotNode i na])
nodeFolder (SeqIDMaker n i
m, [SnapshotNode i na]
ns) SnapshotNode n na
node = let (SeqIDMaker n i
step_m, SnapshotNode i na
new_node) = SeqIDMaker n i
-> SnapshotNode n na -> (SeqIDMaker n i, SnapshotNode i na)
forall n i na.
(Eq n, Hashable n, Enum i, Eq i, Hashable i) =>
SeqIDMaker n i
-> SnapshotNode n na -> (SeqIDMaker n i, SnapshotNode i na)
convertNode SeqIDMaker n i
m SnapshotNode n na
node
                              in (SeqIDMaker n i
step_m, SnapshotNode i na
new_node SnapshotNode i na -> [SnapshotNode i na] -> [SnapshotNode i na]
forall a. a -> [a] -> [a]
: [SnapshotNode i na]
ns)
    new_nodes :: [SnapshotNode i na]
new_nodes = [SnapshotNode i na] -> [SnapshotNode i na]
forall a. [a] -> [a]
reverse [SnapshotNode i na]
new_nodes_rev
    (SeqIDMaker n i
new_maker, [SnapshotLink i la]
new_links_rev) = ((SeqIDMaker n i, [SnapshotLink i la])
 -> SnapshotLink n la -> (SeqIDMaker n i, [SnapshotLink i la]))
-> (SeqIDMaker n i, [SnapshotLink i la])
-> [SnapshotLink n la]
-> (SeqIDMaker n i, [SnapshotLink i la])
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' (SeqIDMaker n i, [SnapshotLink i la])
-> SnapshotLink n la -> (SeqIDMaker n i, [SnapshotLink i la])
forall i n la.
(Enum i, Eq n, Eq i, Hashable n, Hashable i) =>
(SeqIDMaker n i, [SnapshotLink i la])
-> SnapshotLink n la -> (SeqIDMaker n i, [SnapshotLink i la])
linkFolder (SeqIDMaker n i
inter_maker, []) [SnapshotLink n la]
links
    linkFolder :: (SeqIDMaker n i, [SnapshotLink i la])
-> SnapshotLink n la -> (SeqIDMaker n i, [SnapshotLink i la])
linkFolder (SeqIDMaker n i
m, [SnapshotLink i la]
ls) SnapshotLink n la
link = let (SeqIDMaker n i
step_m, SnapshotLink i la
new_link) = SeqIDMaker n i
-> SnapshotLink n la -> (SeqIDMaker n i, SnapshotLink i la)
forall n i la.
(Eq n, Hashable n, Enum i, Eq i, Hashable i) =>
SeqIDMaker n i
-> SnapshotLink n la -> (SeqIDMaker n i, SnapshotLink i la)
convertLink SeqIDMaker n i
m SnapshotLink n la
link
                              in (SeqIDMaker n i
step_m, SnapshotLink i la
new_link SnapshotLink i la -> [SnapshotLink i la] -> [SnapshotLink i la]
forall a. a -> [a] -> [a]
: [SnapshotLink i la]
ls)
    new_links :: [SnapshotLink i la]
new_links = [SnapshotLink i la] -> [SnapshotLink i la]
forall a. [a] -> [a]
reverse [SnapshotLink i la]
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 :: SeqIDMaker n i
-> SnapshotLink n la -> (SeqIDMaker n i, SnapshotLink i la)
convertLink SeqIDMaker n i
maker SnapshotLink n la
link = (SeqIDMaker n i
new_maker, SnapshotLink i la
new_link)
  where
    (SeqIDMaker n i
inter_maker, i
seq_source) = SeqIDMaker n i -> n -> (SeqIDMaker n i, i)
forall n i.
(Eq n, Hashable n, Enum i, Eq i, Hashable i) =>
SeqIDMaker n i -> n -> (SeqIDMaker n i, i)
convertNodeID SeqIDMaker n i
maker (n -> (SeqIDMaker n i, i)) -> n -> (SeqIDMaker n i, i)
forall a b. (a -> b) -> a -> b
$ SnapshotLink n la -> n
forall n la. SnapshotLink n la -> n
_sourceNode SnapshotLink n la
link
    (SeqIDMaker n i
new_maker, i
seq_dest) = SeqIDMaker n i -> n -> (SeqIDMaker n i, i)
forall n i.
(Eq n, Hashable n, Enum i, Eq i, Hashable i) =>
SeqIDMaker n i -> n -> (SeqIDMaker n i, i)
convertNodeID SeqIDMaker n i
inter_maker (n -> (SeqIDMaker n i, i)) -> n -> (SeqIDMaker n i, i)
forall a b. (a -> b) -> a -> b
$ SnapshotLink n la -> n
forall n la. SnapshotLink n la -> n
_destinationNode SnapshotLink n la
link
    new_link :: SnapshotLink i la
new_link = SnapshotLink n la
link { _sourceNode :: i
_sourceNode = i
seq_source, _destinationNode :: i
_destinationNode = i
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 :: SeqIDMaker n i
-> SnapshotNode n na -> (SeqIDMaker n i, SnapshotNode i na)
convertNode SeqIDMaker n i
maker SnapshotNode n na
node = (SeqIDMaker n i
new_maker, SnapshotNode i na
new_node)
  where
    (SeqIDMaker n i
new_maker, i
seq_id) = SeqIDMaker n i -> n -> (SeqIDMaker n i, i)
forall n i.
(Eq n, Hashable n, Enum i, Eq i, Hashable i) =>
SeqIDMaker n i -> n -> (SeqIDMaker n i, i)
convertNodeID SeqIDMaker n i
maker (n -> (SeqIDMaker n i, i)) -> n -> (SeqIDMaker n i, i)
forall a b. (a -> b) -> a -> b
$ SnapshotNode n na -> n
forall n na. SnapshotNode n na -> n
_nodeId SnapshotNode n na
node
    new_node :: SnapshotNode i na
new_node = SnapshotNode n na
node { _nodeId :: i
_nodeId = i
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 :: SeqIDMaker n i -> i -> Maybe n
originalIDFor SeqIDMaker n i
maker i
seq_id = i -> HashMap i n -> Maybe n
forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
HM.lookup i
seq_id (HashMap i n -> Maybe n) -> HashMap i n -> Maybe n
forall a b. (a -> b) -> a -> b
$ SeqIDMaker n i -> HashMap i n
forall n i. SeqIDMaker n i -> HashMap i n
fromSeqID SeqIDMaker n i
maker