module Data.TrieMap.Representation.TH.Factorized (unify) where
import Control.Exception
import Data.List
import Data.Maybe
import Data.Ord
import Language.Haskell.TH
import Data.TrieMap.Representation.TH.Representation
import Data.TrieMap.Representation.TH.Utils
data FactorCase = FCase {fInput :: [Pat], fFactor :: Exp, fOutput :: Exp}
data Factored = Factored {factorType :: Type, fRestType :: Type, fCases :: [FactorCase]}
factorRepr, otherRepr :: Factored -> Representation
factorRepr Factored{..} =
Repr {reprType = factorType, cases = map factorCase fCases}
otherRepr Factored{..} =
Repr {reprType = fRestType, cases = map otherCase fCases}
factorCase, otherCase :: FactorCase -> Case
factorCase FCase{..} = Case{input = fInput, output = fFactor}
otherCase FCase{..} = Case{input = fInput, output = fOutput}
caseFactor :: Case -> FactorCase
caseFactor Case{..} = FCase{fInput = input, fFactor = output, fOutput = TupE []}
combFCase :: Case -> FactorCase -> FactorCase
combFCase Case{..} FCase{..} =
assert (input == fInput) $ FCase{fOutput = TupE [output, fOutput], ..}
combFactor :: Representation -> Factored -> Factored
combFactor Repr{..} Factored{fRestType = TupleT 0,..} =
Factored{factorType, fRestType = reprType, fCases = [FCase{fOutput = output,..} | (FCase{..}, Case{output}) <- zip fCases cases]}
combFactor Repr{..} Factored{..} =
Factored{factorType, fRestType = reprType `tyProd` fRestType, fCases = zipWith combFCase cases fCases}
factors :: Representation -> [Factored]
factors repr@Repr{..} = case reprType of
TupleT 2 `AppT` _ `AppT` _
-> let fs1 = map (combFactor (sndRepr repr)) (factors (fstRepr repr))
fs2 = map (combFactor (fstRepr repr)) (factors (sndRepr repr))
in baseFactor:fs1 ++ fs2
_ -> [baseFactor]
where baseFactor = Factored {
factorType = reprType,
fRestType = TupleT 0,
fCases = map caseFactor cases}
distinctFactors :: [Representation] -> [Type]
distinctFactors reprs = nub [factorType | repr <- reprs, Factored{factorType} <- factors repr, factorType /= TupleT 0]
factorWith :: Type -> Representation -> Maybe Factored
factorWith fTy repr = listToMaybe [factor | factor@Factored{factorType} <- factors repr, factorType == fTy]
factorOut :: Type -> [Representation] -> ([Factored], [Representation])
factorOut _ [] = ([], [])
factorOut fTy (repr:reprs) = case (factorWith fTy repr, factorOut fTy reprs) of
(Nothing, (factors, others)) -> (factors, repr:others)
(Just f, (factors, others)) -> (f:factors, others)
unify :: [Representation] -> Representation
unify reprs = case (allFactors, bestOption) of
([], _) -> checkEnumRepr (mergeWith sumRepr reprs)
(_, ([_], _)) -> checkEnumRepr (mergeWith sumRepr reprs)
(_, (factors, [])) -> distributeMany factors
(_, (factors, others)) -> distributeMany factors `sumRepr` unify others
where allFactors = distinctFactors reprs
options = map (`factorOut` reprs) (distinctFactors reprs)
bestOption = maximumBy (comparing (length . fst)) options
distributeMany :: [Factored] -> Representation
distributeMany factors =
foldr1 unifySumRepr (map factorRepr factors) `unifyProdRepr` unify (map otherRepr factors)