-- 
-- (c) Susumu Katayama 2009
--
module MagicHaskeller.T10 where
import Control.Monad
import MagicHaskeller.CoreLang
-- import PriorSubsts
import Data.List(partition, sortBy)
import Data.Monoid

import MagicHaskeller.Types

liftList :: MonadPlus m => [a] -> m a
liftList = msum . map return


-- Was:  scan10 = mapDepth tokoro10 . scanl1 (++)
scan10 (xs:xss) = scanl (\x y -> tokoro10 (x ++ y)) (tokoro10 xs) xss

-- mergesortWithBy const cmp = map head . groupBy (\x y -> cmp x y == EQ) . sortBy cmp, except that mergesortWithBy is quicker when the domain includes lots of duplicates.
-- (If there are no duplicates, implementation with sortBy should be quicker.)
-- Implementation of mergesort by Ian Lynagh which is found in GHC distributions was useful for implementing this.
mergesortWithBy :: (k->k->k) -> (k->k->Ordering) -> [k] -> [k]
mergesortWithBy op cmp = mergesortWithByBot op (\x y -> Just (cmp x y))

mergesortWithByBot :: (k->k->k) -> (k -> k -> Maybe Ordering) -> [k] -> [k]
mergesortWithByBot op cmp = mergesortWithByBot' op cmp . map return

mergesortWithByBot' :: (k->k->k) -> (k -> k -> Maybe Ordering) -> [[k]] -> [k]
mergesortWithByBot' op cmp [] = []
mergesortWithByBot' op cmp [xs] = xs
mergesortWithByBot' op cmp xss = mergesortWithByBot' op cmp (merge_pairsBot op cmp xss)

merge_pairsBot :: (k->k->k) -> (k -> k -> Maybe Ordering) -> [[k]] -> [[k]]
merge_pairsBot op cmp []          = []
merge_pairsBot op cmp [xs]        = [xs]
merge_pairsBot op cmp (xs:ys:xss) = mergeWithByBot op cmp xs ys : merge_pairsBot op cmp xss


mergeWithBy :: (k->k->k) -> (k->k->Ordering) -> [k] -> [k] -> [k]
mergeWithBy op cmp = mergeWithByBot op (\x y -> Just (cmp x y))

-- cmp returns Nothing when the comparison causes time-out.
mergeWithByBot :: (k->k->k) -> (k -> k -> Maybe Ordering) -> [k] -> [k] -> [k]
mergeWithByBot op cmp xs     []     = xs
mergeWithByBot op cmp []     ys     = ys
mergeWithByBot op cmp (x:xs) (y:ys)
    = case x `cmp` y of
        Just GT ->        y : mergeWithByBot op cmp (x:xs)   ys
        Just EQ -> x `op` y : mergeWithByBot op cmp    xs    ys
        Just LT -> x        : mergeWithByBot op cmp    xs (y:ys)
	Nothing -> mergeWithByBot op cmp xs ys
-- Actually it is questionable if we may remove both of the expressions compared just because comparison between them fails, because only one of them might be responsible.

-- filters only emptySubst
tokoro10nil    :: Eq k => [([a],[k],i)] -> [([a],[k],i)]
tokoro10nil xs =  case partition (\ (_,k,_) -> k==[] ) xs of
                                        ([], diff) -> diff
					(same@((_,_,i):_), diff) -> (concat (map (\ (a,_,_) -> a) same), [], i) : diff
{-
-- ちゃんと計算してないけど，O(n^2)くらい？ ただ，要素数nは少ないっぽいのでこっちの方が速いかも
tokoro10             :: Eq k => [([a],k,i)] -> [([a],k,i)]
tokoro10 []          =  []
tokoro10 ((es,k,i):xs) =  case partition (\ (_,k',_) -> k==k' ) xs of
					(same, diff) -> (es ++ concat (map (\ (a,_,_) -> a) same), k, i) : tokoro10 diff
-}
{- quicksortの変形
tokoro10             :: (Eq k, Ord k) => [([a],k,i)] -> [([a],k,i)]
tokoro10 []             = []
tokoro10 ((t@(x,k,i)):ts) = case partition3 k ts of (ls,es,gs) -> tokoro10 ls ++ (x ++ concat es, k, i) : tokoro10 gs
partition3     :: (Eq k, Ord k) => k -> [(a,k,i)] -> ([(a,k,i)],[a],[(a,k,i)])
-- {-# INLINE partition3 #-}
partition3 k ts = foldr (select3 k) ([],[],[]) ts
select3 k t@(x,k',_) (ls,es,gs) = case k' `compare` k of LT -> (t:ls,   es,   gs)
						         EQ -> (  ls, x:es,   gs)
						         GT -> (  ls,   es, t:gs)
-}

-- merge sort could be much faster.
tokoro10             :: (Monoid a, Eq k, Ord k) => [(a,k,i)] -> [(a,k,i)]
tokoro10 = mergesortWithBy (\(xs,k,i) (ys,_,_) -> (xs `mappend` ys, k, i)) (\ (_,k,_) (_,l,_) -> k `compare` l)
{- sortはしないけど，実はこっちの方が効率が悪いのでは? 長さに対して2乗のオーダーになりそう．ソートすれば，O(n log n)（後半はO(n)ですみそう）しかも，(\\\)を使うにはソートしてないとだめ．
tokoro10 :: (Eq k, Ord k) => [([a],k,i)] -> [([a],k,i)]
tokoro10 ((t@(xs,k,i)):ts) = case partition (\ (_,k',_) -> k'==k) ts of (es,ns) -> (xs ++ concat (map (\ (a,_,_) -> a) es),  k,  i) : tokoro10 ns
-}

tkr10 :: [(Type,Int)] -> [(Type,[Int])]
tkr10 = mergesortWithBy (\ (k,is) (_,js) -> (k,is++js)) (\ (k,_) (l,_) -> k `compare` l) . map (\(k,i)->(k,[i]))

-- Moved from DebMT.lhs
-- ? means Maybe
[]     !? n = Nothing
(x:xs) !? 0 = Just x
(x:xs) !? n = xs !? (n-1)


-- nlambda n e = iterate Lambda e !! nの方が美しい？効率は？
nlambda 0 e = e
nlambda n e = Lambda $ nlambda (n-1) e
