-- Doubly-ended queue (deque) with size and max size
--
-- Author: Patrick Maier
-------------------------------------------------------------------------------

module Control.Parallel.HdpH.Internal.Data.Deque
  ( -- * functional deque
    Deque,        -- no instances
    empty,        -- :: Deque a
    fromList,     -- :: [a] -> Deque a
    pushFront,    -- :: Deque a -> a -> Deque a
    pushBack,     -- :: Deque a -> a -> Deque a
    popFront,     -- :: Deque a -> (Maybe a, Deque a)
    popBack,      -- :: Deque a -> (Maybe a, Deque a)
    first,        -- :: Deque a -> Maybe a
    last,         -- :: Deque a -> Maybe a
    null,         -- :: Deque a -> Bool
    length,       -- :: Deque a -> Int
    maxLength,    -- :: Deque a -> Int

    -- * stateful, concurrently accessible deque
    DequeIO,      -- no instances
    emptyIO,      -- :: IO (DequeIO a)
    fromListIO,   -- :: [a] -> IO (DequeIO a)
    pushFrontIO,  -- :: DequeIO a -> a -> IO ()
    pushBackIO,   -- :: DequeIO a -> a -> IO ()
    popFrontIO,   -- :: DequeIO a -> IO (Maybe a)
    popBackIO,    -- :: DequeIO a -> IO (Maybe a)
    firstIO,      -- :: DequeIO a -> IO (Maybe a)
    lastIO,       -- :: DequeIO a -> IO (Maybe a)
    nullIO,       -- :: DequeIO a -> IO Bool
    lengthIO,     -- :: DequeIO a -> IO Int
    maxLengthIO   -- :: DequeIO a -> IO Int
  ) where

import Prelude hiding (error, last, length, null)
import Data.Functor ((<$>))
import Data.IORef (IORef, newIORef, readIORef, atomicModifyIORef)
import qualified Data.List as List (length)
import Data.Sequence (Seq, (|>), (<|), ViewR((:>)), viewr, ViewL((:<)), viewl)
import qualified Data.Sequence as Seq (empty, null, length, fromList)


-----------------------------------------------------------------------------
-- functional deque with size and max size (with amortised O(1) operations)

data Deque a = Deque { q  :: Seq a,  -- sequence of elements
                       mx :: !Int }  -- maximal length of above sequence

empty :: Deque a
empty = Deque { q = Seq.empty, mx = 0 }

fromList :: [a] -> Deque a
fromList xs = Deque { q = Seq.fromList xs, mx = List.length xs }

pushFront :: Deque a -> a -> Deque a
pushFront dq x = dq { q = x <| q dq, mx = max (length dq + 1) (maxLength dq) }

pushBack :: Deque a -> a -> Deque a
pushBack dq x = dq { q = q dq |> x, mx = max (length dq + 1) (maxLength dq) }

popFront :: Deque a -> (Maybe a, Deque a)
popFront dq = case viewl (q dq) of
                hd :< rest -> (Just hd, dq { q = rest })
                _          -> (Nothing, dq)

popBack :: Deque a -> (Maybe a, Deque a)
popBack dq = case viewr (q dq) of
                rest :> tl -> (Just tl, dq { q = rest })
                _          -> (Nothing, dq)

first :: Deque a -> Maybe a
first dq = case viewl (q dq) of
             x :< _ -> Just x
             _      -> Nothing

last :: Deque a -> Maybe a
last dq = case viewr (q dq) of
            _ :> x -> Just x
            _      -> Nothing

null :: Deque a -> Bool
null = Seq.null . q

length :: Deque a -> Int
length = Seq.length . q

maxLength :: Deque a -> Int
maxLength = mx


-----------------------------------------------------------------------------
-- concurrently accessible deque (in the IO monad) with size and max size;
-- concurrent access is via a global lock on the deque.

newtype DequeIO a = DequeIO (IORef (Deque a))

emptyIO :: IO (DequeIO a)
emptyIO = DequeIO <$> newIORef empty

fromListIO :: [a] -> IO (DequeIO a)
fromListIO xs = DequeIO <$> newIORef (fromList xs)

pushFrontIO :: DequeIO a -> a -> IO ()
pushFrontIO (DequeIO dqRef) x =
  atomicModifyIORef dqRef $ \ dq -> (pushFront dq x, ())

pushBackIO :: DequeIO a -> a -> IO ()
pushBackIO (DequeIO dqRef) x =
  atomicModifyIORef dqRef $ \ dq -> (pushBack dq x, ())

popFrontIO :: DequeIO a -> IO (Maybe a)
popFrontIO (DequeIO dqRef) =
  atomicModifyIORef dqRef $ swap . popFront

popBackIO :: DequeIO a -> IO (Maybe a)
popBackIO (DequeIO dqRef) =
  atomicModifyIORef dqRef $ swap . popBack

firstIO :: DequeIO a -> IO (Maybe a)
firstIO (DequeIO dqRef) = first <$> readIORef dqRef

lastIO :: DequeIO a -> IO (Maybe a)
lastIO (DequeIO dqRef) = last <$> readIORef dqRef

nullIO :: DequeIO a -> IO Bool
nullIO (DequeIO dqRef) = null <$> readIORef dqRef

lengthIO :: DequeIO a -> IO Int
lengthIO (DequeIO dqRef) = length <$> readIORef dqRef

maxLengthIO :: DequeIO a -> IO Int
maxLengthIO (DequeIO dqRef) = maxLength <$> readIORef dqRef


-----------------------------------------------------------------------------
-- auxiliary functions

-- NOTE: Should be exported by Data.Tuple.
swap :: (a,b) -> (b,a)
swap (a,b) = (b,a)