packages feed

creditmonad-1.0.0: src/Test/Credit/Sortable/MergeSort.hs

{-# LANGUAGE GADTs #-}

module Test.Credit.Sortable.MergeSort where

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

data MergeSort a m = MergeSort Size (Thunk m (MLazyCon m) [[a]])

mrg :: MonadCredit m => Ord a => [a] -> [a] -> m [a]
mrg [] ys = pure ys
mrg xs [] = pure xs
mrg (x:xs) (y:ys) = tick >>
  if x <= y
    then (x:) <$> mrg xs (y:ys)
    else (y:) <$> mrg (x:xs) ys

addSeg :: MonadCredit m => Ord a => [a] -> [[a]] -> Size -> m [[a]]
addSeg seg segs size =
  if size `mod` 2 == 0 then pure $ seg : segs
  else do -- technically we should have a tick here, but Okasaki doesn't and we follow his presentation
    let (seg1:segs') = segs
    seg' <- mrg seg seg1 
    addSeg seg' segs' (size `div` 2)

psi :: Size -> Credit
psi n = 2 * linear n - 2 * sum [ linear $ b i * (n `mod` 2^i + 1) | i <- [0..(log2 n + 1)] ]
  where
    b i = (n `div` 2^i) `mod` 2

data MLazyCon m a where
  Empty :: MLazyCon m [[a]]
  AddSeg :: Ord a => [a] -> Thunk m (MLazyCon m) [[a]] -> Size -> MLazyCon m [[a]]

instance MonadCredit m => HasStep (MLazyCon m) m where
  step Empty = pure []
  step (AddSeg seg segs size) = do
    creditWith segs (psi size)
    segs' <- force segs
    addSeg seg segs' size

mrgAll :: MonadCredit m => Ord a => [a] -> [[a]] -> m [a]
mrgAll xs [] = pure xs
mrgAll xs (seg:segs) = tick >> do
  seg' <- mrg xs seg
  mrgAll seg' segs

instance Sortable MergeSort where
  empty = do
    segs <- delay Empty
    pure $ MergeSort 0 segs
  add x (MergeSort size segs) = do
    segs' <- delay $ AddSeg [x] segs size
    creditWith segs' (2 * log2 size + 1)
    pure $ MergeSort (size + 1) segs'
  sort (MergeSort size segs) = do
    creditWith segs (psi size)
    segs' <- force segs
    mrgAll [] segs'

instance BoundedSortable MergeSort where
  scost n (Add _) = 2 * log2 n + 1
  scost n Sort = psi n +  2 * linear n

instance (MonadMemory m, MemoryCell m a) => MemoryCell m (MLazyCon m a) where
  prettyCell Empty = pure $ mkMCell "Empty" []
  prettyCell (AddSeg seg segs size) = do
    -- seg' <- prettyCell seg
    segs' <- prettyCell segs
    size' <- prettyCell size
    pure $ mkMCell "AddSeg" [segs', size']

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

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