module ProjectM36.TransactionGraph.Merge where
import ProjectM36.Base
import ProjectM36.Error
import qualified Data.Set as S
import qualified Data.Map as M
import qualified ProjectM36.TypeConstructorDef as TCD
import ProjectM36.Relation
import Control.Monad (foldM)
import qualified Data.HashSet as HS
data MergePreference = PreferFirst | PreferSecond | PreferNeither
unionMergeMaps :: (Ord k, Eq a) => MergePreference -> M.Map k a -> M.Map k a -> Either MergeError (M.Map k a)
unionMergeMaps prefer mapA mapB = case prefer of
PreferFirst -> pure $ M.union mapA mapB
PreferSecond -> pure $ M.union mapB mapA
PreferNeither -> if M.intersection mapA mapB == M.intersection mapA mapB then
pure $ M.union mapA mapB
else
Left StrategyViolatesComponentMergeError
unionMergeRelation :: MergePreference -> Relation -> Relation -> Either MergeError Relation
unionMergeRelation prefer relA relB = case relA `union` relB of
Right unionRel -> pure unionRel
Left (AttributeNamesMismatchError _) -> preferredRelVar
Left _ -> Left StrategyViolatesRelationVariableMergeError
where
preferredRelVar = case prefer of
PreferFirst -> Right relA
PreferSecond -> Right relB
PreferNeither -> Left StrategyViolatesRelationVariableMergeError
unionMergeRelVars :: MergePreference -> RelationVariables -> RelationVariables -> Either MergeError RelationVariables
unionMergeRelVars prefer relvarsA relvarsB = do
let allNames = S.toList (S.union (M.keysSet relvarsA) (M.keysSet relvarsB))
foldM (\acc name -> do
mergedRel <- do
let findRel = M.lookup name
lookupA = findRel relvarsA
lookupB = findRel relvarsB
case (lookupA, lookupB) of
(Just relA, Just relB) ->
unionMergeRelation prefer relA relB
(Nothing, Just relB) -> pure relB
(Just relA, Nothing) -> pure relA
(Nothing, Nothing) -> error "impossible relvar naming lookup"
pure $ M.insert name mergedRel acc
) M.empty allNames
unionMergeAtomFunctions :: MergePreference -> AtomFunctions -> AtomFunctions -> Either MergeError AtomFunctions
unionMergeAtomFunctions prefer funcsA funcsB = case prefer of
PreferFirst -> pure $ HS.union funcsA funcsB
PreferSecond -> pure $ HS.union funcsB funcsA
PreferNeither -> pure $ HS.union funcsA funcsB
unionMergeTypeConstructorMapping :: MergePreference -> TypeConstructorMapping -> TypeConstructorMapping -> Either MergeError TypeConstructorMapping
unionMergeTypeConstructorMapping prefer typesA typesB = do
let allFuncNames = S.fromList $ map (\(tc,_) -> TCD.name tc) (typesA ++ typesB)
foldM (\acc name -> do
let findType tcm = case filter (\(t,_) -> TCD.name t == name) tcm of
[] -> Nothing
[x] -> Just x
_ -> error "multiple names matching in TypeConstructorMapping"
lookupA = findType typesA
lookupB = findType typesB
cat t = pure (t : acc)
case (lookupA, lookupB) of
(Nothing, Nothing) -> error "type name lookup failure"
(Just typeA, Nothing) -> cat typeA
(Nothing, Just typeB) -> cat typeB
(Just typeA, Just typeB) -> if typeA == typeB then
cat typeA
else
case prefer of
PreferFirst -> cat typeA
PreferSecond -> cat typeB
PreferNeither -> Left StrategyViolatesTypeConstructorMergeError
) [] (S.toList allFuncNames)
unionMergeDatabaseContextFunctions :: MergePreference -> DatabaseContextFunctions -> DatabaseContextFunctions -> Either MergeError DatabaseContextFunctions
unionMergeDatabaseContextFunctions prefer funcsA funcsB = case prefer of
PreferFirst -> pure $ HS.union funcsA funcsB
PreferSecond -> pure $ HS.union funcsB funcsA
PreferNeither -> pure $ HS.union funcsA funcsB