packages feed

creditmonad-1.1.0: src/Test/Credit/RandomAccess/Binary.hs

{-# LANGUAGE TypeFamilies #-}

module Test.Credit.RandomAccess.Binary where

import Prelude hiding (lookup)
import Prettyprinter (Pretty)
import Control.Monad.Credit
import Test.Credit
import Test.Credit.RandomAccess.Base

data Stream m a
  = SCons a (Stream m a)
  | SNil
  | SIndirect (Thunk m (Lazy m) (Stream m a))

indirect :: MonadCredit m => m (Stream m a) -> m (Stream m a)
indirect = fmap SIndirect . delay . Lazy

credit :: MonadCredit m => Credit -> Stream m a -> m ()
credit cr (SIndirect i) = creditWith i cr
credit _ _ = pure ()

-- | Smart destructor for streams, consuming one credit
smatch :: MonadCredit m => Stream m a -- ^ Scrutinee
       -> m b -- ^ Nil case
       -> (a -> Stream m a -> m b) -- ^ Cons case
       -> m b
smatch x nil cons = tick >> eval x
  where
    eval x = case x of
      SCons a as -> cons a as
      SNil -> nil
      SIndirect i -> force i >>= eval

data Tree a = Leaf a | Node Int (Tree a) (Tree a)
  deriving (Eq, Ord, Show)

data Digit a = Zero | One (Tree a) | Two (Tree a) (Tree a)
  deriving (Eq, Ord, Show)

size :: Tree a -> Int
size (Leaf _) = 1
size (Node w _ _) = w

link :: Tree a -> Tree a -> Tree a
link t1 t2 = Node (size t1 + size t2) t1 t2

consTree :: MonadCredit m => Tree a -> Stream m (Digit a) -> m (Stream m (Digit a))
consTree t ts = smatch ts
  (pure $ SCons (One t) SNil)
  (\d ds -> case d of
    Zero -> pure $ SCons (One t) ds
    One t' -> credit 1 ds >> pure (SCons (Two t t') ds)
    Two t2 t3 -> do
      ds' <- indirect $ consTree (link t2 t3) ds
      credit 1 ds'
      pure $ SCons (One t) ds')

unconsTree :: MonadCredit m => Stream m (Digit a) -> m (Maybe (Tree a, Stream m (Digit a)))
unconsTree ts = smatch ts
  (pure Nothing)
  (\d ds -> case d of
    One t -> credit 1 ds >> smatch ds
      (pure $ Just (t, SNil))
      (\_ _ -> pure $ Just (t, SCons Zero ds))
    Two t t' -> pure $ Just (t, SCons (One t') ds)
    Zero -> do
      ds' <- unconsTree ds
      case ds' of
        Just (Node _ t1 t2, ds'') -> pure $ Just (t1, SCons (One t2) ds'')
        _ -> pure Nothing)

lookupTree :: MonadCredit m => Int -> Tree a -> m (Maybe a)
lookupTree 0 (Leaf x) = pure $ Just x
lookupTree i (Leaf _) = pure Nothing
lookupTree i (Node w t1 t2)
  | i < w `div` 2 = tick >> lookupTree i t1
  | otherwise = tick >> lookupTree (i - w `div` 2) t2

updateTree :: MonadCredit m => Int -> a -> Tree a -> m (Tree a)
updateTree 0 y (Leaf _) = pure $ Leaf y
updateTree i _ (Leaf x) = pure $ Leaf x
updateTree i y (Node w t1 t2)
  | i < w `div` 2 = tick >> do
      t1' <- updateTree i y t1
      pure $ Node w t1' t2
  | otherwise = tick >> do
      t2' <- updateTree (i - w `div` 2) y t2
      pure $ Node w t1 t2'

newtype BinaryRA a m = BinaryRA { unBinaryRA :: Stream m (Digit a) }

instance RandomAccess BinaryRA where
  empty = pure $ BinaryRA SNil
  cons x (BinaryRA ts) = BinaryRA <$> consTree (Leaf x) ts
  uncons (BinaryRA ts) = do
    m <- unconsTree ts
    case m of
      Just (Leaf x, ts') -> pure $ Just (x, BinaryRA ts')
      _ -> pure Nothing
  lookup i (BinaryRA ts) = smatch ts
    (pure Nothing)
    (\d ds -> case d of
      Zero -> lookup i (BinaryRA ds)
      One t ->
        if i < size t
          then lookupTree i t
          else credit 1 ds >> lookup (i - size t) (BinaryRA ds)
      Two t1 t2 ->
        if i < size t1
          then lookupTree i t1
          else let j = i - size t1 in
            if j < size t2
              then lookupTree j t2
              else lookup (j - size t2) (BinaryRA ds))
  update i y (BinaryRA ts) = smatch ts
    (pure $ BinaryRA SNil)
    (\d ds -> case d of
      Zero -> BinaryRA . (SCons Zero) . unBinaryRA <$> update i y (BinaryRA ds)
      One t ->
        if i < size t
          then BinaryRA . (flip SCons ds) . One <$> updateTree i y t
          else credit 1 ds >> BinaryRA . (SCons (One t)) . unBinaryRA <$> update (i - size t) y (BinaryRA ds)
      Two t1 t2 ->
        if i < size t1
          then BinaryRA . (flip SCons ds) . flip Two t2 <$> updateTree i y t1
          else let j = i - size t1 in
            if j < size t2
              then BinaryRA . (flip SCons ds) . Two t1 <$> updateTree j y t2
              else BinaryRA . (SCons (Two t1 t2)) . unBinaryRA <$> update (j - size t2) y (BinaryRA ds))

instance BoundedRandomAccess BinaryRA where
  qcost n (Cons _) = 2
  qcost n Uncons = 3 + log2 n
  qcost n (Lookup _) = 1 + 3 * log2 n
  qcost n (Update _ _) = 1 + 3 * log2 n

instance (MonadMemory m, MemoryCell m a) => MemoryCell m (Stream m a) where
  prettyCell xs = mkMList <$> toList xs <*> toHole xs
    where
      toList SNil = pure $ []
      toList (SCons x xs) = (:) <$> prettyCell x <*> toList xs
      toList (SIndirect t) = pure $ []

      toHole SNil = pure $ Nothing
      toHole (SCons x xs) = toHole xs
      toHole (SIndirect t) = Just <$> prettyCell t

instance MemoryCell m a => MemoryCell m (Tree a) where
  prettyCell (Leaf x) = do
    x' <- prettyCell x
    pure $ mkMCell "Leaf" [x']
  prettyCell (Node w t1 t2) = do
    t1' <- prettyCell t1
    t2' <- prettyCell t2
    pure $ mkMCell "Node" [t1', t2']

instance MemoryCell m a => MemoryCell m (Digit a) where
  prettyCell Zero = pure $ mkMCell "Zero" []
  prettyCell (One t) = do
    t' <- prettyCell t
    pure $ mkMCell "One" [t']
  prettyCell (Two t1 t2) = do
    t1' <- prettyCell t1
    t2' <- prettyCell t2
    pure $ mkMCell "Two" [t1', t2']

instance (MonadMemory m, MemoryCell m a) => MemoryCell m (BinaryRA a m) where
  prettyCell (BinaryRA ts) = do
    ts' <- prettyCell ts
    pure $ mkMCell "BinaryRA" [ts']

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