module Top.Implementation.TypeGraph.Path where
import Data.Function
import Data.List
import Data.Maybe
import qualified Data.Map as M
import Utils (internalError)
data Path a = Path a :|: Path a
| Path a :+: Path a
| Step a
| Fail
| Empty
seqList, seqList1 :: [Path a] -> Path a
seqList = foldr (:+:) Empty
seqList1 = foldr1 (:+:)
altList, altList1 :: [Path a] -> Path a
altList = foldr (:|:) Fail
altList1 = foldr1 (:|:)
instance Show a => Show (Path a) where
show path =
case path of
x :|: y -> show x ++ "|" ++ show y
x :+: y -> parIf (pathPrio x < 1) (show x) ++ "+" ++ parIf (pathPrio y < 1) (show y)
Step a -> show a
Fail -> "Fail"
Empty -> "Empty"
where pathPrio :: Path a -> Int
pathPrio (_ :|: _) = 0
pathPrio (_ :+: _) = 1
pathPrio _ = 2
parIf b s = if b then "("++s++")" else s
mCombine :: Monad m => (a -> b -> c) -> m a -> m b -> m c
mCombine op mp1 mp2 =
do p1 <- mp1
p2 <- mp2
return (p1 `op` p2)
(<+>), (<|>) :: Monad m => m (Path a) -> m (Path a) -> m (Path a)
(<+>) = mCombine (:+:)
(<|>) = mCombine (:|:)
(<++>) :: Monad m => m [Path a] -> m [Path a] -> m [Path a]
(<++>) = mCombine (++)
steps :: Path a -> [a]
steps = ($ []) . rec where
rec path =
case path of
x :|: y -> rec x . rec y
x :+: y -> rec x . rec y
Step a -> (a:)
Fail -> id
Empty -> id
mapPath :: (a -> b) -> Path a -> Path b
mapPath f = changeStep (Step . f)
changeStep :: (a -> Path b) -> Path a -> Path b
changeStep f = rec
where
rec path =
case path of
Step a -> f a
x :|: y -> rec x :|: rec y
x :+: y -> rec x :+: rec y
Fail -> Fail
Empty -> Empty
changeStepM :: Monad m => (a -> m (Path b)) -> Path a -> m (Path b)
changeStepM f path =
case path of
Step a -> f a
x :|: y -> do x' <- changeStepM f x; y' <- changeStepM f y; return (x' :|: y')
x :+: y -> do x' <- changeStepM f x; y' <- changeStepM f y; return (x' :+: y')
Fail -> return Fail
Empty -> return Empty
minCompleteInPath :: (a -> a -> Ordering) -> Path a -> Maybe a
minCompleteInPath f = rec . simplifyPath
where
rec path =
case path of
x :|: y -> do v1 <- rec x; v2 <- rec y; return (minimumBy f [v1, v2])
x :+: y -> do v1 <- rec x; v2 <- rec y; return (maximumBy f [v1, v2])
Step a -> Just a
Fail -> Nothing
Empty -> Nothing
simplifyPath :: Path a -> Path a
simplifyPath path =
case path of
x :|: y ->
case (simplifyPath x, simplifyPath y) of
(Empty, _ ) -> Empty
(_ , Empty) -> Empty
(Fail , p2 ) -> p2
(p1 , Fail ) -> p1
(p1 , p2 ) -> p1 :|: p2
x :+: y ->
case (simplifyPath x, simplifyPath y) of
(Fail , _ ) -> Fail
(_ , Fail ) -> Fail
(Empty, p1 ) -> p1
(p2 , Empty) -> p2
(p1 , p2 ) -> p1 :+: p2
_ -> path
tailSharingBy :: (a -> a -> Ordering) -> Path a -> Path a
tailSharingBy compf thePath =
case simplifyPath thePath of
Empty -> Empty
Fail -> Fail
p -> rec p
where
eqf x y = compf x y == EQ
eqfM x y = compfM x y == EQ
compfM Nothing Nothing = EQ
compfM (Just x) (Just y) = compf x y
compfM m1 _ = if isJust m1 then GT else LT
rec (Step a) = Step a
rec (p1 :+: p2) = p1 :+: rec p2
rec path =
let sharedTail = map (\((p, tl):rest) -> combine (p:map fst rest) tl)
. groupBy (eqfM `on` snd)
. sortBy (compfM `on` snd)
$ [ (p, lastStep p) | p <- altPath path ]
combine paths Nothing = altList1 paths
combine paths (Just tl) =
case tailSharingBy compf (altList1 (map removeTail paths)) of
Fail -> Fail
Empty -> Step tl
p -> p :+: Step tl
in altList1 sharedTail
altPath :: Path a -> [Path a]
altPath (p1 :|: p2) = altPath p1 ++ altPath p2
altPath path = [path]
lastStep (Step a) = Just a
lastStep (_ :+: p2) = lastStep p2
lastStep (p1 :|: p2) = do a <- lastStep p1
b <- lastStep p2
if a `eqf` b
then Just a
else Nothing
lastStep _ = internalError "Top.TypeGraph.Paths" "lastStep" "unexpected path"
removeTail (Step _) = Empty
removeTail (p1 :+: p2) = p1 :+: removeTail p2
removeTail (p1 :|: p2) = removeTail p1 :|: removeTail p2
removeTail _ = internalError "Top.TypeGraph.Paths" "removeTail" "unexpected path"
flattenPath :: Path a -> [[a]]
flattenPath path =
case path of
Empty -> [[]]
Fail -> []
Step a -> [[a]]
p1 :+: p2 -> [ as ++ bs | as <- flattenPath p1, bs <- flattenPath p2]
p1 :|: p2 -> flattenPath p1 ++ flattenPath p2
minimalSets :: (a -> a -> Bool) -> Path a -> [[a]]
minimalSets eqF = rec where
rec path =
case simplifyPath path of
Empty -> []
Fail -> [[]]
p ->
let a = head (steps p)
sol1 = rec (changeStep (\b -> if a `eqF` b then Empty else Step b) p)
sol2 = [ a : set
| set <- rec (changeStep (\b -> if a `eqF` b then Fail else Step b) p)
]
in case (sol1, sol2) of
(x:_, y:_) ->
case length x `compare` length y of
LT -> sol1
EQ -> sol1 ++ sol2
GT -> sol2
_ -> sol1 ++ sol2
removeSomeDuplicates :: Ord b => (a -> b) -> Path a -> Path a
removeSomeDuplicates toOrd = simplifyPath . rec M.empty where
rec fm path =
case path of
left :+: right ->
case left of
Step a -> let int = toOrd a
fm' = M.insert int Empty fm
in case M.lookup int fm of
Just left' -> left' :+: rec fm right
Nothing -> left :+: rec fm' right
p1 :+: p2 -> rec fm (p1 :+: (p2 :+: right))
_ -> rec fm left :+: rec fm right
left :|: right ->
case left of
Step a -> let int = toOrd a
fm' = M.insert int Fail fm
in case M.lookup int fm of
Just left' -> left' :|: rec fm right
Nothing -> left :|: rec fm' right
p1 :|: p2 -> rec fm (p1 :|: (p2 :|: right))
_ -> rec fm left :|: rec fm right
Step a ->
M.findWithDefault path (toOrd a) fm
_ -> path
participationMap :: Ord a => Path a -> (Integer, M.Map a Integer)
participationMap path =
case path of
Empty -> (1, M.empty)
Fail -> (0, M.empty)
Step a -> (1, M.singleton a 1)
p1 :+: p2 -> let (i1, fm1) = participationMap p1
(i2, fm2) = participationMap p2
fm1' = M.map (*i2) fm1
fm2' = M.map (*i1) fm2
in (i1 * i2, M.unionWith (\j1 j2 -> j1 + j2 ((j1*j2) `div` (i1*i2))) fm1' fm2')
p1 :|: p2 -> let (i1, fm1) = participationMap p1
(i2, fm2) = participationMap p2
in (i1 + i2, M.unionWith (+) fm1 fm2)
pathSize :: Path a -> Int
pathSize (p1 :|: p2) = pathSize p1 + pathSize p2
pathSize (p1 :+: p2) = pathSize p1 * pathSize p2
pathSize (Step _) = 1
pathSize _ = 0
maxNumberOfEqualPaths :: Maybe Int
maxNumberOfEqualPaths = Just 50
reduceNumberOfPaths :: Path a -> Path a
reduceNumberOfPaths = maybe id limitNumberOfPaths maxNumberOfEqualPaths
limitNumberOfPaths :: Int -> Path a -> Path a
limitNumberOfPaths size = fst . rec size
where
fromInt :: Num a => Int -> a
fromInt = fromInteger . toInteger
rec sz path =
case path of
Empty -> (path, 1)
Fail -> (path, 0)
Step _ -> (path, 1)
p1 :+: p2 -> let (p1', n1) = rec sz p1
newSize
| n1 == 0 = sz
| otherwise = ceiling ((fromInt sz / fromInt n1) :: Double)
(p2', n2) = rec newSize p2
in (p1' :+: p2', n1*n2)
p1 :|: p2 -> let both@(p1' , n1) = rec sz p1
(p2', n2) = rec (sz n1) p2
in if n1 >= sz
then both
else (p1' :|: p2', n1 + n2)