packages feed

creditmonad-1.0.0: src/Test/Credit/Queue/Physicists.hs

{-# LANGUAGE TypeFamilies #-}

module Test.Credit.Queue.Physicists where

import Prettyprinter (Pretty)
import Control.Monad.Credit
import Test.Credit
import Test.Credit.Queue.Base

app :: MonadCredit m => [a] -> [a] -> m [a]
app [] ys = pure ys
app (x : xs) ys = tick >> app xs (x : ys)

rev :: MonadCredit m => [a] -> [a] -> m [a]
rev [] acc = pure acc
rev (x : xs) acc = tick >> rev xs (x : acc)

data PLazyCon m a where
  Empty :: PLazyCon m [a]
  AppRev :: [a] -> [a] -> PLazyCon m [a]
  Tail :: Thunk m (PLazyCon m) [a] -> PLazyCon m [a]

instance MonadCredit m => HasStep (PLazyCon m) m where
  step Empty = pure []
  step (AppRev xs ys) = app xs =<< rev ys []
  step (Tail xs) = tick >> drop 1 <$> force xs

type PThunk m = Thunk m (PLazyCon m)

data Physicists a m = Queue [a] Int (PThunk m [a]) (PThunk m [a]) Int [a]

checkw :: MonadCredit m => Physicists a m -> m (Physicists a m)
checkw (Queue working lenf front ghost lenr rear) = case working of
  [] -> do
    front' <- force front
    pure $ Queue front' lenf front ghost lenr rear
  _ -> pure $ Queue working lenf front ghost lenr rear

check :: MonadCredit m => Physicists a m -> m (Physicists a m)
check q@(Queue _ lenf front ghost lenr rear) =
  if lenr <= lenf
    then do
      creditWith ghost 1
      checkw q
    else do
      working <- force front
      front' <- delay $ AppRev working rear
      creditWith front' 1
      checkw $ Queue working (lenf + lenr) front' front' 0 []

instance Queue Physicists where
  empty = do
    front <- delay Empty
    pure $ Queue [] 0 front front 0 []
  snoc (Queue working lenf front ghost lenr rear) x = tick >> do
    creditWith ghost 1
    check (Queue working lenf front ghost (lenr + 1) (x : rear))
  uncons (Queue [] lenf front ghost lenr rear) = tick >> pure Nothing
  uncons (Queue (x : working) lenf front ghost lenr rear) = tick >> do
    front' <- delay $ Tail front
    creditWith front' 1
    creditWith ghost 1
    q' <- check $ Queue working (lenf - 1) front' ghost lenr rear
    pure $ Just (x, q')

instance BoundedQueue Physicists where
  qcost _ (Snoc _) = 3
  qcost _ Uncons = 4

instance (MonadMemory m, MemoryCell m a) => MemoryCell m (PLazyCon m a) where
  prettyCell Empty = pure $ mkMCell "Empty" []
  prettyCell (AppRev xs ys) = do
    xs' <- prettyCell xs
    ys' <- prettyCell ys
    pure $ mkMCell "AppRev" [xs', ys']
  prettyCell (Tail xs) = do
    xs' <- prettyCell xs
    pure $ mkMCell "Tail" [xs']

instance (MonadMemory m, MemoryCell m a) => MemoryCell m (Physicists a m) where
  prettyCell (Queue working lenf front _ lenr rear) = do
    working' <- prettyCell working
    lenf' <- prettyCell lenf
    front' <- prettyCell front
    lenr' <- prettyCell lenr
    rear' <- prettyCell rear
    pure $ mkMCell "Queue" [working', lenf', front', lenr', rear']

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