packages feed

creditmonad-1.1.0: src/Test/Credit/Queue/Bankers.hs

{-# LANGUAGE LambdaCase, GADTs #-}

module Test.Credit.Queue.Bankers where

import Control.Monad.Credit
import Data.Maybe (fromMaybe)
import Prettyprinter (Pretty)
import Test.Credit
import Test.Credit.Queue.Base
import Test.Credit.Queue.Streams

data BQueue a m = BQueue
  { flen :: !Int
  , rlen :: !Int
  , front :: Stream m a
  , rear  :: Stream m a
  }

allEvaluated :: MonadInherit m => StreamCell m a -> m ()
allEvaluated SNil = pure ()
allEvaluated (SCons _ xs) = isEvaluated xs

isEvaluated :: MonadInherit m => Stream m a -> m ()
isEvaluated s = lazymatch s allEvaluated (error "Stream should be pure")

allInvariant :: MonadInherit m => Maybe Int -> StreamCell m a -> m ()
allInvariant _ SNil = pure ()
allInvariant rlen (SCons x xs) = invariant xs (fmap (subtract 2) rlen)

invariant :: MonadInherit m => Stream m a -> Maybe Int -> m ()
invariant front rlen = 
  lazymatch front (allInvariant rlen) $ \case
    SAppend xs ys -> do
      lxs <- slength xs
      lys <- slength ys
      front `hasAtLeast` (fromIntegral $ fromMaybe (2 * lxs) rlen)
      invariant xs Nothing
      ys `hasAtLeast` (fromIntegral $ lys - lxs)
    SReverse xs ys -> do
      lxs <- slength xs
      front `hasAtLeast` (fromIntegral lxs)
      isEvaluated xs
      isEvaluated ys

bqueue :: MonadInherit m => Int -> Int -> Stream m a -> Stream m a -> m (BQueue a m)
bqueue fl rl f r = do
  isEvaluated r
  invariant f (Just rl)
  if fl >= rl 
    then pure $ BQueue fl rl f r
    else do
      r' <- delay . SReverse r =<< nil
      r' `creditWith` 1
      BQueue (fl + rl) 0 <$> (delay $ SAppend f r') <*> nil

instance Queue BQueue where
  empty = BQueue 0 0 <$> nil <*> nil
  snoc (BQueue fl rl f r) x = do
    f `creditWith` 1
    bqueue fl (rl + 1) f =<< cons x r
  uncons (BQueue fl rl f r) = do
    f `creditWith` 2
    force f >>= \case
      SCons x f' -> do
        q <- bqueue (fl - 1) rl f' r
        pure $ Just (x, q)
      SNil -> pure Nothing

isEmpty :: BQueue a m -> Bool
isEmpty (BQueue flen rlen _ _) = flen == 0 && rlen == 0

lazyqueue :: MonadInherit m => BQueue a m -> m [a]
lazyqueue (BQueue fl rl f r) = do
  f' <- toList f
  r' <- toList r
  pure $ f' ++ reverse r'

instance BoundedQueue BQueue where
  qcost _ (Snoc _) = 2
  qcost _ Uncons = 4

instance (MonadMemory m, MemoryCell m a) => MemoryCell m (BQueue a m) where
  prettyCell (BQueue fl rl f r) = do
    fl' <- prettyCell fl
    rl' <- prettyCell rl
    f' <- prettyCell f
    r' <- prettyCell r
    pure $ mkMCell "Queue" [fl', rl', f', r']

instance Pretty a => MemoryStructure (BQueue (PrettyCell a)) where
  prettyStructure = prettyCell