packages feed

lhae-0.0.3: src/Util/DynArray.hs

module Util.DynArray (DynArray,new,newMatrix,newLinearArray,read
                     ,write,writes
                     ,resizeTo,updateIO,update,bounds,index,indexBy
                     ,elements,showDynArray
                     ,deleteInLinearArray
                     ,deleteColumnsInMatrix,deleteRowsInMatrix
                     ,insertEmptyIntoLinearArrayBefore
                     ,insertEmptyRowIntoMatrixBefore
                     ,insertEmptyColumnIntoMatrixBefore)
    where

import Prelude hiding (init,read)
import Control.Applicative ((<$>))
import Control.Monad (forM_,when,foldM)
import Control.Exception (assert)
import Data.Array.IO (IOArray,readArray,writeArray,getBounds,getElems)
import qualified Data.Array.IO as ArrayIO
import Data.Ix (Ix,range,inRange)
import Data.IORef
import Data.List (sort)

data DynArray i e = DynArray { ref :: IORef (IOArray i e)
                             , grow :: (i,i) -> i -> (i,i)
                             , init :: e
                             }

defaultGrowingSize = 10

showDynArray :: (Ix i,Show e) => DynArray i e -> IO String
showDynArray dynArray = show <$> (readIORef (ref dynArray) >>= getElems)

new :: Ix i => (i,i) -> ((i,i) -> i -> (i,i)) -> e -> IO (DynArray i e)
new ix grow e = do
  array <- ArrayIO.newArray ix e
  ref <- newIORef array
  return $ DynArray ref grow e

newMatrix :: (Int,Int) -> e -> IO (DynArray (Int,Int) e)
newMatrix (initRows,initColumns) = 
    let grow (_,(r,c)) (i,j) = ( (0,0)
                               , ( max r $ i + defaultGrowingSize - 1
                                 , max c $ j + defaultGrowingSize - 1))
    in
      new ((0,0),(initRows-1,initColumns-1)) grow

newLinearArray :: Int -> e -> IO (DynArray Int e)
newLinearArray numElements =
    let grow _ i = (0,i + defaultGrowingSize - 1)
    in
      new (0,numElements-1) grow

read :: Ix i => i -> DynArray i e -> IO e
read i dynArray = do
  array <- readIORef $ ref dynArray
  bounds <- getBounds array
  if inRange bounds i then readArray array i
                      else return $ init dynArray

growArray :: Ix i => i -> DynArray i e -> IO ()
growArray i dynArray = do
  array <- readIORef $ ref dynArray
  bounds <- getBounds array
  array' <- ArrayIO.newArray (grow dynArray bounds i) $ init dynArray
  copyUnsafe array array'
  writeIORef (ref dynArray) array'

write :: Ix i => i -> e -> DynArray i e -> IO ()
write i e dynArray = do
  array <- readIORef $ ref dynArray
  bounds <- getBounds array
  if inRange bounds i 
   then writeArray array i e
   else growArray i dynArray >> write i e dynArray

writes :: (Ix i,Enum i) => i -> [e] -> DynArray i e -> IO ()
writes i es dynArray = 
    forM_ (zip [i..] es) $ \(pos,e) -> write pos e dynArray

copyUnsafe :: Ix i => IOArray i e -> IOArray i e -> IO ()
copyUnsafe from to = do
  r <- getBounds from
  forM_ (range r) $ \i -> readArray from i >>= writeArray to i

resizeTo :: Ix i => (i,i) -> DynArray i e -> IO ()
resizeTo range dynArray = 
    ArrayIO.newArray range (init dynArray) >>= writeIORef (ref dynArray)

updateIO :: Ix i => i -> (e -> IO e) -> DynArray i e -> IO ()
updateIO i f dynArray = 
    read i dynArray >>= f >>= \e -> write i e dynArray

update ::  Ix i => i -> (e -> e) -> DynArray i e -> IO ()
update i f = updateIO i (return . f)
  
bounds :: Ix i => DynArray i e -> IO (i,i)
bounds dynArray = readIORef (ref dynArray) >>= getBounds

indexBy :: (Enum i, Ix i) => (e -> Bool) -> DynArray i e -> IO (Maybe i)
indexBy equals dynArray = do
  array <- readIORef $ ref dynArray
  bounds <- getBounds array
  let indexRec i = 
          if inRange bounds i 
          then do value <- readArray array i
                  if equals value then return $ Just i
                                  else indexRec $ succ i
          else return Nothing
  indexRec $ fst bounds

index :: (Enum i, Ix i, Eq e) => e -> DynArray i e -> IO (Maybe i)
index e = indexBy ((==) e)

elements :: Ix i => DynArray i e -> IO [e]
elements dynArray = readIORef (ref dynArray) >>= getElems

insertEmptyIntoLinearArrayBefore :: Int -> Int -> DynArray Int e -> IO ()
insertEmptyIntoLinearArrayBefore i maxCopy dynArray = do
  maxBound <- snd <$> bounds dynArray
  when (maxCopy >= maxBound) $ growArray maxCopy dynArray
  array <- readIORef $ ref dynArray
  forM_ [maxCopy,maxCopy-1..i] $ \n -> 
      readArray array n >>= writeArray array (n+1)
  writeArray array i $ init dynArray

insertEmptyRowIntoMatrixBefore :: Int -> Int -> DynArray (Int,Int) e -> IO ()
insertEmptyRowIntoMatrixBefore i maxCopyRow dynArray = do
  (maxRow,_) <- snd <$> bounds dynArray
  when (maxCopyRow >= maxRow) $ growArray (maxCopyRow,0) dynArray

  forM_ [maxCopyRow,maxCopyRow-1..i] $ \n -> 
      copyRowUnsafe n (n+1) dynArray
  writeEmptyRow i dynArray

insertEmptyColumnIntoMatrixBefore :: Int -> Int -> DynArray (Int,Int) e -> IO ()
insertEmptyColumnIntoMatrixBefore i maxCopyCol dynArray = do
  (_,maxCol) <- snd <$> bounds dynArray
  when (maxCopyCol >= maxCol) $ growArray (0,maxCopyCol) dynArray

  forM_ [maxCopyCol,maxCopyCol-1..i] $ \n -> 
      copyColumnUnsafe n (n+1) dynArray
  writeEmptyColumn i dynArray

deleteInLinearArray :: [Int] -> DynArray Int e -> IO ()
deleteInLinearArray is dynArray = do
  assert (is == sort is) $ return ()
  array <- readIORef $ ref dynArray
  (minB,maxB) <- getBounds array

  let deleteElement (x:xs,deleted) index | x == index = 
          return (xs,deleted+1)

      deleteElement (xs,deleted) index = do
        when (deleted > 0) $ 
          readArray array index >>= writeArray array (index - deleted)
        return (xs,deleted)

  (_,deleted) <- foldM deleteElement (is,0) [minB..maxB] 
  forM_ [maxB-deleted+1..maxB] $ \x -> writeArray array x $ init dynArray

deleteColumnsInMatrix :: [Int] -> DynArray (Int,Int) e -> IO ()
deleteColumnsInMatrix columns dynArray = do
  assert (columns == sort columns) $ return ()
  array <- readIORef $ ref dynArray
  ((_,minCol),(_,maxCol)) <- getBounds array
                                       
  let deleteColumn (col:cols,deleted) index | col == index = 
          return (cols,deleted+1)

      deleteColumn (cols,deleted) index = do
        when (deleted > 0) $ 
             copyColumnUnsafe index (index - deleted) dynArray
        return (cols,deleted)

  (_,deleted) <- foldM deleteColumn (columns,0) [minCol..maxCol] 
  forM_ [maxCol-deleted+1..maxCol] $ writeEmptyColumn `flip` dynArray

deleteRowsInMatrix :: [Int] -> DynArray (Int,Int) e -> IO ()
deleteRowsInMatrix rowsToDelete dynArray = do
  assert (rowsToDelete == sort rowsToDelete) $ return ()
  array <- readIORef $ ref dynArray
  ((minRow,_),(maxRow,_)) <- getBounds array
                                       
  let deleteRow (row:rows,deleted) index | row == index = 
          return (rows,deleted+1)

      deleteRow (rows,deleted) index = do
        when (deleted > 0) $ 
             copyRowUnsafe index (index - deleted) dynArray
        return (rows,deleted)

  (_,deleted) <- foldM deleteRow (rowsToDelete,0) [minRow..maxRow] 
  forM_ [maxRow-deleted+1..maxRow] $ writeEmptyRow `flip` dynArray

copyColumnUnsafe :: Int -> Int -> DynArray (Int,Int) e -> IO ()
copyColumnUnsafe from to dynArray = do
  array <- readIORef $ ref dynArray
  ((minRow,_),(maxRow,_)) <- getBounds array
  forM_ [minRow..maxRow] $ \r -> 
      readArray array (r,from) >>= writeArray array (r,to)

copyRowUnsafe :: Int -> Int -> DynArray (Int,Int) e -> IO ()
copyRowUnsafe from to dynArray = do
  array <- readIORef $ ref dynArray
  ((_,minCol),(_,maxCol)) <- getBounds array
  forM_ [minCol..maxCol] $ \c -> 
      readArray array (from,c) >>= writeArray array (to,c)

writeEmptyColumn :: Int -> DynArray (Int,Int) e -> IO ()
writeEmptyColumn i dynArray = do
  array <- readIORef $ ref dynArray
  ((minRow,_),(maxRow,_)) <- getBounds array
  forM_ [minRow..maxRow] $ \r -> 
      writeArray array (r,i) $ init dynArray

writeEmptyRow :: Int -> DynArray (Int,Int) e -> IO ()
writeEmptyRow i dynArray = do
  array <- readIORef $ ref dynArray
  ((_,minCol),(_,maxCol)) <- getBounds array
  forM_ [minCol..maxCol] $ \c -> 
      writeArray array (i,c) $ init dynArray