module Data.Git.Diff.Patience
( Item(..)
, diff
) where
import Data.List
import Data.Function (on)
import qualified Data.Map as M
import qualified Data.IntMap as IM
data Card a = Card !Int a !(Maybe (Card a))
longestIncreasing :: [(Int,a)] -> [(Int,a)]
longestIncreasing =
maybe [] (flatten . head . fst)
. IM.maxView
. foldl' ins IM.empty
where
ins :: IM.IntMap [Card a] -> (Int, a) -> IM.IntMap [Card a]
ins m (x,a) =
case IM.minViewWithKey gt of
Nothing -> IM.insert x [new] m
Just ((k,_),_) ->
case IM.updateLookupWithKey (\_ _ -> Nothing) k m of
(Just v, mm) -> IM.insert x (new : v) mm
(Nothing, _) -> m
where
(lt, gt) = IM.split x m
prev = (head . fst) `fmap` IM.maxView lt
new = Card x a prev
flatten :: Card a -> [(Int, a)]
flatten (Card x a c) = (x,a) : maybe [] flatten c
data Piece a =
Match !a !a
| Diff [a] [a]
deriving (Show)
lcs :: Ord t => [t] -> [t] -> [Piece t]
lcs ma mb =
chop ma mb
$ longestIncreasing
$ sortBy (compare `on` snd)
$ M.elems
$ M.intersectionWith (,) (unique ma) (unique mb)
where
unique = M.mapMaybe id . foldr ins M.empty . zip [0..]
where
ins (a,x) = M.insertWith (\_ _ -> Nothing) x (Just a)
chop :: [t] -> [t] -> [(Int,Int)] -> [Piece t]
chop xs ys []
| null xs && null ys = []
| otherwise = [Diff xs ys]
chop xs ys ((nx,ny):ns) =
let (xsr, (x : xse)) = splitAt nx xs
(ysr, (y : yse)) = splitAt ny ys
in Diff xse yse : Match x y : chop xsr ysr ns
data Item t =
Old !t
| New !t
| Both !t !t
deriving (Show,Eq)
instance Functor Item where
fmap f (Old x) = Old (f x)
fmap f (New x) = New (f x)
fmap f (Both x y) = Both (f x) (f y)
diff :: Ord t => [t] -> [t] -> [Item t]
diff = matchPrefix []
where
matchPrefix acc (x:xs) (y:ys)
| x == y = Both x y : matchPrefix acc xs ys
matchPrefix acc l r = matchSuffix acc (reverse l) (reverse r)
matchSuffix acc (x:xs) (y:ys)
| x == y = matchSuffix (Both x y : acc) xs ys
matchSuffix acc l r = diffInner (reverse acc) (reverse l) (reverse r)
diffInner acc l r =
case lcs l r of
[Diff _ _] -> fmap Old l ++ fmap New r ++ acc
ps -> recur acc ps
recur acc [] = acc
recur acc (Match x y : ps) = recur (Both x y : acc) ps
recur acc (Diff xs ys : ps) = recur [] ps ++ matchPrefix acc xs ys