-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathList.hs
156 lines (124 loc) · 4.52 KB
/
List.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE ScopedTypeVariables #-}
-- | A list diff.
module Data.TreeDiff.List (
diffBy,
Edit (..),
) where
import Control.DeepSeq (NFData (..))
import Control.Monad.ST (ST, runST)
import qualified Data.Primitive as P
-- import Debug.Trace
-- | List edit operations
--
-- The 'Swp' constructor is redundant, but it let us spot
-- a recursion point when performing tree diffs.
data Edit a
= Ins a -- ^ insert
| Del a -- ^ delete
| Cpy a -- ^ copy unchanged
| Swp a a -- ^ swap, i.e. delete + insert
deriving (Eq, Show)
instance NFData a => NFData (Edit a) where
rnf (Ins x) = rnf x
rnf (Del x) = rnf x
rnf (Cpy x) = rnf x
rnf (Swp x y) = rnf x `seq` rnf y
-- | List difference.
--
-- >>> diffBy (==) "hello" "world"
-- [Swp 'h' 'w',Swp 'e' 'o',Swp 'l' 'r',Cpy 'l',Swp 'o' 'd']
--
-- >>> diffBy (==) "kitten" "sitting"
-- [Swp 'k' 's',Cpy 'i',Cpy 't',Cpy 't',Swp 'e' 'i',Cpy 'n',Ins 'g']
--
-- prop> \xs ys -> length (diffBy (==) xs ys) >= max (length xs) (length (ys :: String))
-- prop> \xs ys -> length (diffBy (==) xs ys) <= length xs + length (ys :: String)
--
diffBy :: forall a. Show a => (a -> a -> Bool) -> [a] -> [a] -> [Edit a]
diffBy _ [] [] = []
diffBy _ [] ys' = map Ins ys'
diffBy _ xs' [] = map Del xs'
diffBy eq xs' ys'
| otherwise = reverse (getCell lcs)
where
xn = length xs'
yn = length ys'
xs = P.arrayFromListN xn xs'
ys = P.arrayFromListN yn ys'
lcs :: Cell [Edit a]
lcs = runST $ do
-- traceShowM ("sizes", xn, yn)
-- create two buffers.
buf1 <- P.newArray yn (Cell 0 [])
buf2 <- P.newArray yn (Cell 0 [])
-- fill the first row
-- 0,0 case is filled already
yLoop (Cell 0 []) $ \m (Cell w edit) -> do
let cell = Cell (w + 1) (Ins (P.indexArray ys m) : edit)
P.writeArray buf1 m cell
P.writeArray buf2 m cell
-- traceShowM ("init", m, cell)
return cell
-- following rows
--
-- cellC cellT
-- cellL cellX
(buf1final, _, _) <- xLoop (buf1, buf2, Cell 0 []) $ \n (prev, curr, cellC) -> do
-- prevZ <- P.unsafeFreezeArray prev
-- currZ <- P.unsafeFreezeArray prev
-- traceShowM ("prev", n, prevZ)
-- traceShowM ("curr", n, currZ)
let cellL :: Cell [Edit a]
cellL = case cellC of (Cell w edit) -> Cell (w + 1) (Del (P.indexArray xs n) : edit)
-- traceShowM ("cellC, cellL", n, cellC, cellL)
yLoop (cellC, cellL) $ \m (cellC', cellL') -> do
-- traceShowM ("inner loop", n, m)
cellT <- P.readArray prev m
-- traceShowM ("cellT", n, m, cellT)
let x, y :: a
x = P.indexArray xs n
y = P.indexArray ys m
-- from diagonal
let cellX1 :: Cell [Edit a]
cellX1
| eq x y = bimap id (Cpy x :) cellC'
| otherwise = bimap (+1) (Swp x y :) cellC'
-- from left
let cellX2 :: Cell [Edit a]
cellX2 = bimap (+1) (Ins y :) cellL'
-- from top
let cellX3 :: Cell [Edit a]
cellX3 = bimap (+1) (Del x :) cellT
-- the actual cell is best of three
let cellX :: Cell [Edit a]
cellX = bestOfThree cellX1 cellX2 cellX3
-- traceShowM ("cellX", n, m, cellX)
-- memoize
P.writeArray curr m cellX
return (cellT, cellX)
return (curr, prev, cellL)
P.readArray buf1final (yn - 1)
xLoop :: acc -> (Int -> acc -> ST s acc) -> ST s acc
xLoop !acc0 f = go acc0 0 where
go !acc !n | n < xn = do
acc' <- f n acc
go acc' (n + 1)
go !acc _ = return acc
yLoop :: acc -> (Int -> acc -> ST s acc) -> ST s ()
yLoop !acc0 f = go acc0 0 where
go !acc !m | m < yn = do
acc' <- f m acc
go acc' (m + 1)
go _ _ = return ()
data Cell a = Cell !Int !a deriving Show
getCell :: Cell a -> a
getCell (Cell _ x) = x
bestOfThree :: Cell a -> Cell a -> Cell a -> Cell a
bestOfThree a@(Cell i _x) b@(Cell j _y) c@(Cell k _z)
| i <= j
= if i <= k then a else c
| otherwise
= if j <= k then b else c
bimap :: (Int -> Int) -> (a -> b) -> Cell a -> Cell b
bimap f g (Cell i x) = Cell (f i) (g x)