-- | A fast(est) mutable data
{-# LANGUAGE
    BangPatterns
  #-}
{-# LANGUAGE Trustworthy #-}

module SAT.Solver.Mios.Data.Singleton
       (
         -- * Bool
         BoolSingleton
       , newBool
       , getBool
       , setBool
       , modifyBool
         -- * Int
       , IntSingleton
       , newInt
       , getInt
       , setInt
       , modifyInt
         -- * Double
       , DoubleSingleton
       , newDouble
       , getDouble
       , setDouble
       , modifyDouble
       )
       where
{-
----------------------------------------
-- Implementation 1. :: IORef
----------------------------------------

import Data.IORef

type BoolSingleton = IORef Bool

newBool :: Bool -> IO BoolSingleton
newBool = newIORef

{-# INLINE getBool #-}
getBool :: BoolSingleton -> IO Bool
getBool = readIORef

{-# INLINE setBool #-}
setBool :: BoolSingleton -> Bool -> IO ()
setBool = writeIORef

{-# INLINE modifyBool #-}
modifyBool :: BoolSingleton -> (Bool -> Bool) -> IO ()
modifyBool = modifyIORef'

type IntSingleton = IORef Int

newInt :: Int -> IO IntSingleton
newInt = newIORef

{-# INLINE getInt #-}
getInt :: IntSingleton -> IO Int
getInt = readIORef

{-# INLINE setInt #-}
setInt :: IntSingleton -> Int -> IO ()
setInt = writeIORef

{-# INLINE modifyInt #-}
modifyInt :: IntSingleton -> (Int -> Int) -> IO ()
modifyInt = modifyIORef'

type DoubleSingleton = IORef Double

newDouble :: Double -> IO DoubleSingleton
newDouble = newIORef

{-# INLINE getDouble #-}
getDouble :: DoubleSingleton -> IO Double
getDouble = readIORef

{-# INLINE setDouble #-}
setDouble :: DoubleSingleton -> Double -> IO ()
setDouble = writeIORef

{-# INLINE modifyDouble #-}
modifyDouble :: DoubleSingleton -> (Double -> Double) -> IO ()
modifyDouble = modifyIORef'
-}
{-
----------------------------------------
-- Implementation 2. :: Data.Mutable.IOURef
----------------------------------------

import qualified Data.Mutable as M

newtype IntSingleton = IntSingleton
                       {
                         mutableInt :: M.IOURef Int
                       }

newInt :: IO IntSingleton
newInt = IntSingleton <$> M.newRef 0

{-# INLINE getInt #-}
getInt :: IntSingleton -> IO Int
getInt !(IntSingleton val) = M.readRef val

{-# INLINE setInt #-}
setInt :: IntSingleton -> Int -> IO ()
setInt !(IntSingleton val) !x = M.writeRef val x

{-# INLINE modifyInt #-}
modifyInt :: IntSingleton -> (Int -> Int) -> IO ()
modifyInt !(IntSingleton val) !f = M.modifyRef' val f
-}

-- {-
----------------------------------------
-- Implementation 3. :: Data.Vector.Unboxed.Mutable
----------------------------------------

import qualified Data.Vector.Unboxed.Mutable as UV

-- | mutable Int
type IntSingleton = UV.IOVector Int

-- | returns a new 'IntSingleton'
newInt :: Int -> IO IntSingleton
newInt k = do
  s <- UV.new 1
  UV.unsafeWrite s 0 k
  return s

-- | returns the value
{-# INLINE getInt #-}
getInt :: IntSingleton -> IO Int
getInt val = UV.unsafeRead val 0

-- | sets the value
{-# INLINE setInt #-}
setInt :: IntSingleton -> Int -> IO ()
setInt val !x = UV.unsafeWrite val 0 x

-- | modifies the value
{-# INLINE modifyInt #-}
modifyInt :: IntSingleton -> (Int -> Int) -> IO ()
modifyInt val !f = UV.unsafeModify val f 0

-- | mutable Bool
type BoolSingleton = UV.IOVector Bool

-- | returns a new 'BoolSingleton'
newBool :: Bool -> IO BoolSingleton
newBool b = do
  s <- UV.new 1
  UV.unsafeWrite s 0 b
  return s

-- | returns the value
{-# INLINE getBool #-}
getBool :: BoolSingleton -> IO Bool
getBool val = UV.unsafeRead val 0

-- | sets the value
{-# INLINE setBool #-}
setBool :: BoolSingleton -> Bool -> IO ()
setBool val !x = UV.unsafeWrite val 0 x

-- | modifies the value
{-# INLINE modifyBool #-}
modifyBool :: BoolSingleton -> (Bool -> Bool) -> IO ()
modifyBool val !f = UV.unsafeModify val f 0

-- | mutable Double
type DoubleSingleton = UV.IOVector Double

-- | returns a new 'DoubleSingleton'
newDouble :: Double -> IO DoubleSingleton
newDouble d = do
  s <- UV.new 1
  UV.unsafeWrite s 0 d
  return s

-- | returns the value
{-# INLINE getDouble #-}
getDouble :: DoubleSingleton -> IO Double
getDouble val = UV.unsafeRead val 0

-- | sets the value
{-# INLINE setDouble #-}
setDouble :: DoubleSingleton -> Double -> IO ()
setDouble val !x = UV.unsafeWrite val 0 x

-- | modifies the value
{-# INLINE modifyDouble #-}
modifyDouble :: DoubleSingleton -> (Double -> Double) -> IO ()
modifyDouble val !f = UV.unsafeModify val f 0
-- -}