{- |
Module      :  ./DFOL/Colimit.hs
Description :  Signature colimits for first-order logic with dependent
               types (DFOL)
Copyright   :  (c) Kristina Sojakova, DFKI Bremen 2009
License     :  GPLv2 or higher, see LICENSE.txt

Maintainer  :  k.sojakova@jacobs-university.de
Stability   :  experimental
Portability :  portable

Algorithm description:

Input : graph where nodes are signatures and edges are morphisms
Output : a signature "sig" and for each input signature "sig1" a morphism
         m : sig1 -> sig

1 : Compute the transitive and symmetric closure of the relation R induced
    by the morphisms in the graph

2 : Initialize the output signature "sig" and collection of morphisms "M" to
    empty
    Initialize the set of analyzed symbols to empty

3 : For each input signature sig1 :
    3.1 : Initialize the output map "m" to empty
    3.2 : For each symbol "s" in sig1 (keeping the order in which they are
              defined) :
          3.1.1 Check if s R s' for any already analyzed symbol "s'"
          3.1.2 If yes:
                3.1.2.1 Recover from M the value "c" that s' maps to
                3.1.2.2 Update m by adding the pair (s,c)
          3.1.3 If no:
                3.1.3.1 Get the type "t" of s in sig1
                3.1.3.2 Translate t along m; call this new type "t1"
                3.1.3.3 Update sig by adding the declaration s : t1
                        (in case the name s is already contained in sig,
                        we generate a fresh name "s1" and add the declaration
                        s1 : t and pair (s,s1) to sig and m respectively.
    3.3 : Add m to M
-}

module DFOL.Colimit (
    sigColimit
  ) where

import DFOL.AS_DFOL
import DFOL.Sign
import DFOL.Morphism
import Common.Result
import Common.Id
import Common.Lib.Graph
import qualified Data.Graph.Inductive.Graph as Graph
import qualified Common.Lib.Rel as Rel
import qualified Data.Map as Map
import qualified Data.Set as Set
import qualified Data.IntMap as IntMap

-- main functions
sigColimit :: Gr Sign (Int, Morphism) -> Result (Sign, Map.Map Int Morphism)
sigColimit gr =
  let sigs = Graph.labNodes gr
      rel = computeRel gr
      (sig, maps) = addSig emptySig IntMap.empty Set.empty rel sigs
      maps1 = IntMap.mapWithKey (\ i m -> let Just sig1 = Graph.lab gr i
                                              in Morphism sig1 sig m
                                )
                                maps
      maps2 = Map.fromList $ IntMap.toList maps1
      in Result [] $ Just (sig, maps2)

-- preparation for computing the colimit
addSig :: Sign -> IntMap.IntMap (Map.Map NAME NAME) -> Set.Set (Int, NAME) ->
          Rel.Rel (Int, NAME) -> [(Int, Sign)] ->
          (Sign, IntMap.IntMap (Map.Map NAME NAME))
addSig sig maps _ _ [] = (sig, maps)
addSig sig maps doneSyms rel ((i, Sign ds) : sigs) =
  processSig sig maps Map.empty i (expandDecls ds) doneSyms rel sigs

-- main function for computing the colimit
processSig :: Sign ->                        -- the signature determined so far
       IntMap.IntMap (Map.Map NAME NAME) ->  -- the morphisms determined so far
       Map.Map NAME NAME ->                  -- the current map
       Int ->                                -- the number or the current sig
       [SDECL] ->                            -- the declarations to be processed
       Set.Set (Int, NAME) ->                -- the symbols done so far
       Rel.Rel (Int, NAME) ->                -- the equality relation
       [(Int, Sign)] ->                      -- the signatures to be processed
       (Sign, IntMap.IntMap (Map.Map NAME NAME))     -- the determined colimit

processSig sig maps m i [] doneSyms rel sigs =
  let maps1 = IntMap.insert i m maps
      in addSig sig maps1 doneSyms rel sigs

processSig sig maps m i ((n, t) : ds) doneSyms rel sigs =
  let n1 = (i, n)
      eqSyms = Set.filter (\ k -> Rel.member n1 k rel) doneSyms
      in if Set.null eqSyms
            then let mt = toTermMap m
                     syms = getSymbols sig
                     t1 = translate mt syms t
                     n2 = toName n syms
                     sig1 = addSymbolDecl ([n2], t1) sig
                     m1 = Map.insert n n2 m
                     doneSyms1 = Set.insert n1 doneSyms
                     in processSig sig1 maps m1 i ds doneSyms1 rel sigs
            else let k = Set.findMin eqSyms
                     c = findValue k $ IntMap.insert i m maps
                     m1 = Map.insert n c m
                     doneSyms1 = Set.insert n1 doneSyms
                     in processSig sig maps m1 i ds doneSyms1 rel sigs

-- helper functions
findValue :: (Int, NAME) -> IntMap.IntMap (Map.Map NAME NAME) -> NAME
findValue (i, k) maps = let Just m = IntMap.lookup i maps
                           in Map.findWithDefault k k m

toName :: NAME -> Set.Set NAME -> NAME
toName n names =
  if Set.notMember n names
     then n
     else let s = tokStr n
              n1 = Token ("gn_" ++ s) nullRange
              in getNewName n1 names

computeRel :: Gr Sign (Int, Morphism) -> Rel.Rel (Int, NAME)
computeRel gr =
  let morphs = Graph.labEdges gr
      rel = foldl (\ r1 (i, j, (_, m)) ->
                     let syms = Set.toList $ getSymbols $ source m
                         in foldl (\ r2 s ->
                                   let t = mapSymbol m s
                                   in Rel.insertPair (i, s) (j, t)
                                          $ Rel.insertPair (j, t) (i, s) r2
                                  )
                                  r1
                                  syms
                  )
                  Rel.empty
                  morphs
     in Rel.transClosure rel
