packages feed

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

{-# LANGUAGE TypeFamilies #-}

module Test.Credit.RandomAccess.Zeroless 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 = One (Tree a) | Two (Tree a) (Tree a) | Three (Tree a) (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 t1 ts = smatch ts
  (pure $ SCons (One t1) SNil)
  (\d ds -> case d of
    One t2 -> pure $ SCons (Two t1 t2) ds
    Two t2 t3 -> credit 2 ds >> pure (SCons (Three t1 t2 t3) ds)
    Three t2 t3 t4 -> do
      ds' <- indirect $ consTree (link t3 t4) ds
      credit 2 ds'
      pure $ SCons (Two t1 t2) 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 -> smatch ds
        (pure $ Just (t, SNil))
        (\_ _ -> do
          ds' <- indirect $ do
            ds' <- unconsTree ds
            case ds' of
              Just (Node _ t1 t2, ds'') -> do
                pure $ SCons (Two t1 t2) ds''
              Nothing -> fail "unconsTree: malformed tree"
          credit 2 ds'
          pure $ Just (t, ds'))
      Two t1 t2 -> credit 2 ds >> pure (Just (t1, SCons (One t2) ds))
      Three t1 t2 t3 -> pure $ Just (t1, SCons (Two t2 t3) ds))

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 ZerolessRA a m = ZerolessRA { unZerolessRA :: Stream m (Digit a) }

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

instance BoundedRandomAccess ZerolessRA where
  qcost n (Cons _) = 5
  qcost n Uncons = 6
  qcost _ (Lookup i) = 3 + 5 * log2 (fromIntegral i * 2)
  qcost _ (Update i _) = 3 + 5 * log2 (fromIntegral i * 2)

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 (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']
  prettyCell (Three t1 t2 t3) = do
    t1' <- prettyCell t1
    t2' <- prettyCell t2
    t3' <- prettyCell t3
    pure $ mkMCell "Three" [t1', t2', t3']

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

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