[project @ 2005-10-13 11:09:50 by ross]
[packages/containers.git] / Data / Array / Diff.hs
1 -----------------------------------------------------------------------------
2 -- |
3 -- Module : Data.Array.Diff
4 -- Copyright : (c) The University of Glasgow 2001
5 -- License : BSD-style (see the file libraries/base/LICENSE)
6 --
7 -- Maintainer : libraries@haskell.org
8 -- Stability : experimental
9 -- Portability : non-portable (uses Data.Array.IArray)
10 --
11 -- Functional arrays with constant-time update.
12 --
13 -----------------------------------------------------------------------------
14
15 module Data.Array.Diff (
16
17 -- * Diff array types
18
19 -- | Diff arrays have an immutable interface, but rely on internal
20 -- updates in place to provide fast functional update operator
21 -- '//'.
22 --
23 -- When the '//' operator is applied to a diff array, its contents
24 -- are physically updated in place. The old array silently changes
25 -- its representation without changing the visible behavior:
26 -- it stores a link to the new current array along with the
27 -- difference to be applied to get the old contents.
28 --
29 -- So if a diff array is used in a single-threaded style,
30 -- i.e. after '//' application the old version is no longer used,
31 -- @a'!'i@ takes O(1) time and @a '//' d@ takes O(@length d@).
32 -- Accessing elements of older versions gradually becomes slower.
33 --
34 -- Updating an array which is not current makes a physical copy.
35 -- The resulting array is unlinked from the old family. So you
36 -- can obtain a version which is guaranteed to be current and
37 -- thus have fast element access by @a '//' []@.
38
39 -- Possible improvement for the future (not implemented now):
40 -- make it possible to say "I will make an update now, but when
41 -- I later return to the old version, I want it to mutate back
42 -- instead of being copied".
43
44 IOToDiffArray, -- data IOToDiffArray
45 -- (a :: * -> * -> *) -- internal mutable array
46 -- (i :: *) -- indices
47 -- (e :: *) -- elements
48
49 -- | Type synonyms for the two most important IO array types.
50
51 -- Two most important diff array types are fully polymorphic
52 -- lazy boxed DiffArray:
53 DiffArray, -- = IOToDiffArray IOArray
54 -- ...and strict unboxed DiffUArray, working only for elements
55 -- of primitive types but more compact and usually faster:
56 DiffUArray, -- = IOToDiffArray IOUArray
57
58 -- * Overloaded immutable array interface
59
60 -- | Module "Data.Array.IArray" provides the interface of diff arrays.
61 -- They are instances of class 'IArray'.
62 module Data.Array.IArray,
63
64 -- * Low-level interface
65
66 -- | These are really internal functions, but you will need them
67 -- to make further 'IArray' instances of various diff array types
68 -- (for either more 'MArray' types or more unboxed element types).
69 newDiffArray, readDiffArray, replaceDiffArray
70 )
71 where
72
73 ------------------------------------------------------------------------
74 -- Imports.
75
76 import Prelude
77
78 import Data.Ix
79 import Data.Array.Base
80 import Data.Array.IArray
81 import Data.Array.IO
82
83 import Foreign.Ptr ( Ptr, FunPtr )
84 import Foreign.StablePtr ( StablePtr )
85 import Data.Int ( Int8, Int16, Int32, Int64 )
86 import Data.Word ( Word, Word8, Word16, Word32, Word64 )
87
88 import System.IO.Unsafe ( unsafePerformIO )
89 import Control.Exception ( evaluate )
90 import Control.Concurrent.MVar ( MVar, newMVar, takeMVar, putMVar, readMVar )
91
92 ------------------------------------------------------------------------
93 -- Diff array types.
94
95 -- | An arbitrary 'MArray' type living in the 'IO' monad can be converted
96 -- to a diff array.
97
98 newtype IOToDiffArray a i e =
99 DiffArray {varDiffArray :: MVar (DiffArrayData a i e)}
100
101 -- Internal representation: either a mutable array, or a link to
102 -- another diff array patched with a list of index+element pairs.
103 data DiffArrayData a i e = Current (a i e)
104 | Diff (IOToDiffArray a i e) [(Int, e)]
105
106 -- | Fully polymorphic lazy boxed diff array.
107 type DiffArray = IOToDiffArray IOArray
108
109 -- | Strict unboxed diff array, working only for elements
110 -- of primitive types but more compact and usually faster than 'DiffArray'.
111 type DiffUArray = IOToDiffArray IOUArray
112
113 -- Having 'MArray a e IO' in instance context would require
114 -- -fallow-undecidable-instances, so each instance is separate here.
115
116 ------------------------------------------------------------------------
117 -- Showing DiffArrays
118
119 instance (Ix ix, Show ix, Show e) => Show (DiffArray ix e) where
120 showsPrec = showsIArray
121
122 instance (Ix ix, Show ix) => Show (DiffUArray ix Char) where
123 showsPrec = showsIArray
124
125 instance (Ix ix, Show ix) => Show (DiffUArray ix Int) where
126 showsPrec = showsIArray
127
128 instance (Ix ix, Show ix) => Show (DiffUArray ix Word) where
129 showsPrec = showsIArray
130
131 instance (Ix ix, Show ix) => Show (DiffUArray ix Float) where
132 showsPrec = showsIArray
133
134 instance (Ix ix, Show ix) => Show (DiffUArray ix Double) where
135 showsPrec = showsIArray
136
137 instance (Ix ix, Show ix) => Show (DiffUArray ix Int8) where
138 showsPrec = showsIArray
139
140 instance (Ix ix, Show ix) => Show (DiffUArray ix Int16) where
141 showsPrec = showsIArray
142
143 instance (Ix ix, Show ix) => Show (DiffUArray ix Int32) where
144 showsPrec = showsIArray
145
146 instance (Ix ix, Show ix) => Show (DiffUArray ix Int64) where
147 showsPrec = showsIArray
148
149 instance (Ix ix, Show ix) => Show (DiffUArray ix Word8) where
150 showsPrec = showsIArray
151
152 instance (Ix ix, Show ix) => Show (DiffUArray ix Word16) where
153 showsPrec = showsIArray
154
155 instance (Ix ix, Show ix) => Show (DiffUArray ix Word32) where
156 showsPrec = showsIArray
157
158 instance (Ix ix, Show ix) => Show (DiffUArray ix Word64) where
159 showsPrec = showsIArray
160
161 ------------------------------------------------------------------------
162 -- Boring instances.
163
164 instance HasBounds a => HasBounds (IOToDiffArray a) where
165 bounds a = unsafePerformIO $ boundsDiffArray a
166
167 instance IArray (IOToDiffArray IOArray) e where
168 unsafeArray lu ies = unsafePerformIO $ newDiffArray lu ies
169 unsafeAt a i = unsafePerformIO $ a `readDiffArray` i
170 unsafeReplace a ies = unsafePerformIO $ a `replaceDiffArray1` ies
171
172 instance IArray (IOToDiffArray IOUArray) Char where
173 unsafeArray lu ies = unsafePerformIO $ newDiffArray lu ies
174 unsafeAt a i = unsafePerformIO $ a `readDiffArray` i
175 unsafeReplace a ies = unsafePerformIO $ a `replaceDiffArray2` ies
176
177 instance IArray (IOToDiffArray IOUArray) Int where
178 unsafeArray lu ies = unsafePerformIO $ newDiffArray lu ies
179 unsafeAt a i = unsafePerformIO $ a `readDiffArray` i
180 unsafeReplace a ies = unsafePerformIO $ a `replaceDiffArray2` ies
181
182 instance IArray (IOToDiffArray IOUArray) Word where
183 unsafeArray lu ies = unsafePerformIO $ newDiffArray lu ies
184 unsafeAt a i = unsafePerformIO $ a `readDiffArray` i
185 unsafeReplace a ies = unsafePerformIO $ a `replaceDiffArray2` ies
186
187 instance IArray (IOToDiffArray IOUArray) (Ptr a) where
188 unsafeArray lu ies = unsafePerformIO $ newDiffArray lu ies
189 unsafeAt a i = unsafePerformIO $ a `readDiffArray` i
190 unsafeReplace a ies = unsafePerformIO $ a `replaceDiffArray2` ies
191
192 instance IArray (IOToDiffArray IOUArray) (FunPtr a) where
193 unsafeArray lu ies = unsafePerformIO $ newDiffArray lu ies
194 unsafeAt a i = unsafePerformIO $ a `readDiffArray` i
195 unsafeReplace a ies = unsafePerformIO $ a `replaceDiffArray2` ies
196
197 instance IArray (IOToDiffArray IOUArray) Float where
198 unsafeArray lu ies = unsafePerformIO $ newDiffArray lu ies
199 unsafeAt a i = unsafePerformIO $ a `readDiffArray` i
200 unsafeReplace a ies = unsafePerformIO $ a `replaceDiffArray2` ies
201
202 instance IArray (IOToDiffArray IOUArray) Double where
203 unsafeArray lu ies = unsafePerformIO $ newDiffArray lu ies
204 unsafeAt a i = unsafePerformIO $ a `readDiffArray` i
205 unsafeReplace a ies = unsafePerformIO $ a `replaceDiffArray2` ies
206
207 instance IArray (IOToDiffArray IOUArray) (StablePtr a) where
208 unsafeArray lu ies = unsafePerformIO $ newDiffArray lu ies
209 unsafeAt a i = unsafePerformIO $ a `readDiffArray` i
210 unsafeReplace a ies = unsafePerformIO $ a `replaceDiffArray2` ies
211
212 instance IArray (IOToDiffArray IOUArray) Int8 where
213 unsafeArray lu ies = unsafePerformIO $ newDiffArray lu ies
214 unsafeAt a i = unsafePerformIO $ a `readDiffArray` i
215 unsafeReplace a ies = unsafePerformIO $ a `replaceDiffArray2` ies
216
217 instance IArray (IOToDiffArray IOUArray) Int16 where
218 unsafeArray lu ies = unsafePerformIO $ newDiffArray lu ies
219 unsafeAt a i = unsafePerformIO $ a `readDiffArray` i
220 unsafeReplace a ies = unsafePerformIO $ a `replaceDiffArray2` ies
221
222 instance IArray (IOToDiffArray IOUArray) Int32 where
223 unsafeArray lu ies = unsafePerformIO $ newDiffArray lu ies
224 unsafeAt a i = unsafePerformIO $ a `readDiffArray` i
225 unsafeReplace a ies = unsafePerformIO $ a `replaceDiffArray2` ies
226
227 instance IArray (IOToDiffArray IOUArray) Int64 where
228 unsafeArray lu ies = unsafePerformIO $ newDiffArray lu ies
229 unsafeAt a i = unsafePerformIO $ a `readDiffArray` i
230 unsafeReplace a ies = unsafePerformIO $ a `replaceDiffArray2` ies
231
232 instance IArray (IOToDiffArray IOUArray) Word8 where
233 unsafeArray lu ies = unsafePerformIO $ newDiffArray lu ies
234 unsafeAt a i = unsafePerformIO $ a `readDiffArray` i
235 unsafeReplace a ies = unsafePerformIO $ a `replaceDiffArray2` ies
236
237 instance IArray (IOToDiffArray IOUArray) Word16 where
238 unsafeArray lu ies = unsafePerformIO $ newDiffArray lu ies
239 unsafeAt a i = unsafePerformIO $ a `readDiffArray` i
240 unsafeReplace a ies = unsafePerformIO $ a `replaceDiffArray2` ies
241
242 instance IArray (IOToDiffArray IOUArray) Word32 where
243 unsafeArray lu ies = unsafePerformIO $ newDiffArray lu ies
244 unsafeAt a i = unsafePerformIO $ a `readDiffArray` i
245 unsafeReplace a ies = unsafePerformIO $ a `replaceDiffArray2` ies
246
247 instance IArray (IOToDiffArray IOUArray) Word64 where
248 unsafeArray lu ies = unsafePerformIO $ newDiffArray lu ies
249 unsafeAt a i = unsafePerformIO $ a `readDiffArray` i
250 unsafeReplace a ies = unsafePerformIO $ a `replaceDiffArray2` ies
251
252
253
254 ------------------------------------------------------------------------
255 -- The important stuff.
256
257 newDiffArray :: (MArray a e IO, Ix i)
258 => (i,i)
259 -> [(Int, e)]
260 -> IO (IOToDiffArray a i e)
261 newDiffArray (l,u) ies = do
262 a <- newArray_ (l,u)
263 sequence_ [unsafeWrite a i e | (i, e) <- ies]
264 var <- newMVar (Current a)
265 return (DiffArray var)
266
267 readDiffArray :: (MArray a e IO, Ix i)
268 => IOToDiffArray a i e
269 -> Int
270 -> IO e
271 a `readDiffArray` i = do
272 d <- readMVar (varDiffArray a)
273 case d of
274 Current a' -> unsafeRead a' i
275 Diff a' ies -> maybe (readDiffArray a' i) return (lookup i ies)
276
277 replaceDiffArray :: (MArray a e IO, Ix i)
278 => IOToDiffArray a i e
279 -> [(Int, e)]
280 -> IO (IOToDiffArray a i e)
281 a `replaceDiffArray` ies = do
282 d <- takeMVar (varDiffArray a)
283 case d of
284 Current a' -> case ies of
285 [] -> do
286 -- We don't do the copy when there is nothing to change
287 -- and this is the current version. But see below.
288 putMVar (varDiffArray a) d
289 return a
290 _:_ -> do
291 diff <- sequence [do e <- unsafeRead a' i; return (i, e)
292 | (i, _) <- ies]
293 sequence_ [unsafeWrite a' i e | (i, e) <- ies]
294 var' <- newMVar (Current a')
295 putMVar (varDiffArray a) (Diff (DiffArray var') diff)
296 return (DiffArray var')
297 Diff _ _ -> do
298 -- We still do the copy when there is nothing to change
299 -- but this is not the current version. So you can use
300 -- 'a // []' to make sure that the resulting array has
301 -- fast element access.
302 putMVar (varDiffArray a) d
303 a' <- thawDiffArray a
304 -- thawDiffArray gives a fresh array which we can
305 -- safely mutate.
306 sequence_ [unsafeWrite a' i e | (i, e) <- ies]
307 var' <- newMVar (Current a')
308 return (DiffArray var')
309
310 -- The elements of the diff list might recursively reference the
311 -- array, so we must seq them before taking the MVar to avoid
312 -- deadlock.
313 replaceDiffArray1 :: (MArray a e IO, Ix i)
314 => IOToDiffArray a i e
315 -> [(Int, e)]
316 -> IO (IOToDiffArray a i e)
317 a `replaceDiffArray1` ies = do
318 mapM_ (evaluate . fst) ies
319 a `replaceDiffArray` ies
320
321 -- If the array contains unboxed elements, then the elements of the
322 -- diff list may also recursively reference the array from inside
323 -- replaceDiffArray, so we must seq them too.
324 replaceDiffArray2 :: (MArray a e IO, Ix i)
325 => IOToDiffArray a i e
326 -> [(Int, e)]
327 -> IO (IOToDiffArray a i e)
328 a `replaceDiffArray2` ies = do
329 mapM_ (\(a,b) -> do evaluate a; evaluate b) ies
330 a `replaceDiffArray` ies
331
332
333 boundsDiffArray :: (HasBounds a, Ix ix)
334 => IOToDiffArray a ix e
335 -> IO (ix,ix)
336 boundsDiffArray a = do
337 d <- readMVar (varDiffArray a)
338 case d of
339 Current a' -> return (bounds a')
340 Diff a' _ -> boundsDiffArray a'
341
342 freezeDiffArray :: (MArray a e IO, Ix ix)
343 => a ix e
344 -> IO (IOToDiffArray a ix e)
345 freezeDiffArray a = case bounds a of
346 (l,u) -> do
347 a' <- newArray_ (l,u)
348 sequence_ [unsafeRead a i >>= unsafeWrite a' i | i <- [0 .. rangeSize (l,u) - 1]]
349 var <- newMVar (Current a')
350 return (DiffArray var)
351
352 {-# RULES
353 "freeze/DiffArray" freeze = freezeDiffArray
354 #-}
355
356 -- unsafeFreezeDiffArray is really unsafe. Better don't use the old
357 -- array at all after freezing. The contents of the source array will
358 -- be changed when '//' is applied to the resulting array.
359
360 unsafeFreezeDiffArray :: (MArray a e IO, Ix ix)
361 => a ix e
362 -> IO (IOToDiffArray a ix e)
363 unsafeFreezeDiffArray a = do
364 var <- newMVar (Current a)
365 return (DiffArray var)
366
367 {-# RULES
368 "unsafeFreeze/DiffArray" unsafeFreeze = unsafeFreezeDiffArray
369 #-}
370
371 thawDiffArray :: (MArray a e IO, Ix ix)
372 => IOToDiffArray a ix e
373 -> IO (a ix e)
374 thawDiffArray a = do
375 d <- readMVar (varDiffArray a)
376 case d of
377 Current a' -> case bounds a' of
378 (l,u) -> do
379 a'' <- newArray_ (l,u)
380 sequence_ [unsafeRead a' i >>= unsafeWrite a'' i | i <- [0 .. rangeSize (l,u) - 1]]
381 return a''
382 Diff a' ies -> do
383 a'' <- thawDiffArray a'
384 sequence_ [unsafeWrite a'' i e | (i, e) <- ies]
385 return a''
386
387 {-# RULES
388 "thaw/DiffArray" thaw = thawDiffArray
389 #-}
390
391 -- unsafeThawDiffArray is really unsafe. Better don't use the old
392 -- array at all after thawing. The contents of the resulting array
393 -- will be changed when '//' is applied to the source array.
394
395 unsafeThawDiffArray :: (MArray a e IO, Ix ix)
396 => IOToDiffArray a ix e
397 -> IO (a ix e)
398 unsafeThawDiffArray a = do
399 d <- readMVar (varDiffArray a)
400 case d of
401 Current a' -> return a'
402 Diff a' ies -> do
403 a'' <- unsafeThawDiffArray a'
404 sequence_ [unsafeWrite a'' i e | (i, e) <- ies]
405 return a''
406
407 {-# RULES
408 "unsafeThaw/DiffArray" unsafeThaw = unsafeThawDiffArray
409 #-}