{-# LANGUAGE FlexibleContexts      #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables   #-}

-- | Module that provides functions for identifying graph/subgraph isomorphism.
--
module Math.Grads.Algo.Isomorphism
  ( EComparator, VComparator
  , GComparable(..)
  , VertexIndex
  , getIso
  , getMultiIso
  , isIso
  , isIsoSub
  ) where

import           Data.Map                          (Map)
import           Data.Maybe                        (isJust, listToMaybe)

import qualified Math.Grads.Algo.Isomorphism.RI    as RI
import           Math.Grads.Algo.Isomorphism.Types (EComparator,
                                                    GComparable (..),
                                                    VComparator, VertexIndex)
import           Math.Grads.GenericGraph           (GenericGraph (..))
import           Math.Grads.Graph                  (toList)


-- | Checks whether two graphs are isomorphic.
--
isIso :: (Ord v1, Ord v2, GComparable GenericGraph v1 e1 GenericGraph v2 e2, Eq e1, Eq e2)
      => GenericGraph v1 e1
      -> GenericGraph v2 e2
      -> Bool
isIso queryGraph targetGraph = res
  where
    (v1, e1) = toList queryGraph
    (v2, e2) = toList targetGraph
    isoSub = isIsoSub queryGraph targetGraph

    res = length v1 == length v2 && length e1 == length e2 && isoSub

-- | Check for queryGraph \( \subseteq \) targetGraph.
--
isIsoSub :: (Ord v1, Ord v2, GComparable GenericGraph v1 e1 GenericGraph v2 e2, Eq e1, Eq e2)
         => GenericGraph v1 e1 -- ^ queryGraph
         -> GenericGraph v2 e2 -- ^ targetGraph
         -> Bool
isIsoSub queryGraph targetGraph = isJust $ getIso queryGraph targetGraph

-- | Get one vertices matching (if exists) from queryGraph to targetGraph.
--
getIso :: (Ord v1, Ord v2, GComparable GenericGraph v1 e1 GenericGraph v2 e2, Eq e1, Eq e2)
       => GenericGraph v1 e1 -- ^ queryGraph
       -> GenericGraph v2 e2 -- ^ targetGraph
       -> Maybe (Map Int Int)
getIso queryGraph targetGraph = listToMaybe $ getMultiIso queryGraph targetGraph

-- | Get all possible vertices matchings from queryGraph to targetGraph.
--
getMultiIso :: (Ord v1, Ord v2, GComparable GenericGraph v1 e1 GenericGraph v2 e2, Eq e1, Eq e2)
            => GenericGraph v1 e1 -- ^ queryGraph
            -> GenericGraph v2 e2 -- ^ targetGraph
            -> [Map Int Int]
getMultiIso = RI.getMultiIso