{- |
Module      :  Sample.Memo
Copyright   :  (c) Eduard Sergeev 2013
License     :  BSD-style (see the file LICENSE)

Maintainer  :  eduard.sergeev@gmail.com
Stability   :  experimental
Portability :  non-portable

More advanced examples
-}

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances,
  FlexibleContexts, UndecidableInstances, TypeSynonymInstances #-}

module Example.Customisation.Array
(

  -- * Custom `ArrayMemo`
  -- $UnboxedInt16TupleArray
  Int16Sum,
  evalFibSTUA,
  runFibSTUA,
  evalFibIOUA,
  runFibIOUA

) where

import Data.Ix
import Data.Int
import Data.Array.MArray (MArray, freeze)
import qualified Data.Array.Unboxed as UA
import Control.Monad.ST
import Control.Monad.Writer

import Data.MaybeLike
import Control.Monad.Memo.Class
import Control.Monad.Memo.Array


fibmw 0 = return 0
fibmw 1 = return 1
fibmw n = do
  f1 <- memo fibmw (n-1)
  f2 <- memo fibmw (n-2)
  tell $ Sum 1
  return (f1+f2)

{- $UnboxedInt16TupleArray
The way to memoize a tuple of Int16 values using unboxed `UArrayCache`
-}

-- | A tuple of unboxed `Int16` and `Sum` of it
type Int16Sum = (Int16,Sum Int16)

-- | `MaybeLike` instance for our tuple
instance MaybeLike Int32 Int16Sum where
    nothing = minBound
    isNothing v = v == minBound
    just (a,Sum b) = fromIntegral a * 2^16 + fromIntegral b
    fromJust v =
        let (a,b) = v `divMod` (2^16)
        in (fromIntegral a, Sum (fromIntegral b))

-- | `UArrayMemo` instance for our tuple
-- Now we can use `evalUArrayMemo` and `runUArrayMemo` methods
instance UArrayMemo Int16Sum Int32


evalFibSTUA :: Int -> Int16Sum
evalFibSTUA n = runST $ evalUArrayMemo (runWriterT (fibmw n)) (0,n)

runFibSTUA :: Int -> (Int16Sum, UA.UArray Int Int32)
runFibSTUA n = runST $ do 
   (a,arr) <- runUArrayMemo (runWriterT (fibmw n)) (0,n)
   iarr <- freeze arr
   return (a, iarr)


evalFibIOUA :: Int -> IO Int16Sum
evalFibIOUA n = (`evalUArrayMemo`(0,n)) . runWriterT . fibmw $ n 

runFibIOUA :: Int -> IO (Int16Sum, UA.UArray Int Int32)
runFibIOUA n = do
   (a,arr) <- runUArrayMemo (runWriterT (fibmw n)) (0,n)
   iarr <- freeze arr
   return (a, iarr)

