packages feed

sdp-0.2: src/SDP/Prim/TArray.hs

{-# LANGUAGE Trustworthy, MagicHash, MultiParamTypeClasses, FlexibleInstances #-}

{- |
    Module      :  SDP.Prim.TArray
    Copyright   :  (c) Andrey Mulik 2020
    License     :  BSD-style
    Maintainer  :  work.a.mulik@gmail.com
    Portability :  non-portable
    
    "SDP.Prim.TArray" provides lazy boxed array of @stm@ 'TVar's.
    Note that 'TArray#' stores each element in 'TVar'.
-}
module SDP.Prim.TArray
(
  -- TArray
  TArray# (..), STM, TVar
)
where

import Prelude ()
import SDP.SafePrelude
import SDP.Prim.SArray

import GHC.Conc

import Control.Exception.SDP

default ()

--------------------------------------------------------------------------------

-- | 'SArray#' of @stm@ 'TVar's.
newtype TArray# e = TArray# (SArray# (TVar e)) deriving ( Eq )

--------------------------------------------------------------------------------

{- Nullable, Estimate, Bordered and BorderedM instances. -}

instance Nullable (TArray# e)
  where
    isNull = \ (TArray# es) -> isNull es
    lzero  = TArray# Z

instance Estimate (TArray# e)
  where
    (<==>) = on (<=>) sizeOf
    (.<=.) = on (<=)  sizeOf
    (.>=.) = on (>=)  sizeOf
    (.>.)  = on (>)   sizeOf
    (.<.)  = on (<)   sizeOf
    
    (<.=>) = (<=>) . sizeOf
    (.>=)  = (>=)  . sizeOf
    (.<=)  = (<=)  . sizeOf
    (.>)   = (>)   . sizeOf
    (.<)   = (<)   . sizeOf

instance Bordered (TArray# e) Int
  where
    lower _ = 0
    
    upper    (TArray# arr) = upper arr
    sizeOf   (TArray# arr) = sizeOf arr
    bounds   (TArray# arr) = (0, upper arr)
    indices  (TArray# arr) = [0 .. upper arr]
    indexOf  (TArray# arr) = index (0, upper arr)
    offsetOf (TArray# arr) = offset (0, upper arr)
    indexIn  (TArray# arr) = \ i -> i >= 0 && i < sizeOf arr

instance BorderedM STM (TArray# e) Int
  where
    getIndexOf = return ... indexOf
    getIndices = return . indices
    getBounds  = return . bounds
    getSizeOf  = return . sizeOf
    getUpper   = return . upper
    getLower _ = return 0

--------------------------------------------------------------------------------

{- LinearM and SplitM instances. -}

instance LinearM STM (TArray# e) e
  where
    newNull = return (TArray# Z)
    nowNull = return . isNull . unpack
    getHead = readTVar . head . unpack
    getLast = readTVar . last . unpack
    singleM = fmap (TArray# . single) . newTVar
    
    prepend e es = TArray# . (:> unpack es) <$> newTVar e
    append  es e = TArray# . (unpack es :<) <$> newTVar e
    
    newLinear     = fmap (TArray# . fromList) . mapM newTVar
    newLinearN  n = fmap (TArray# . fromListN n) . mapM newTVar
    fromFoldableM = fmap (TArray# . fromList) . foldr (liftA2 (:) . newTVar) (return [])
    
    (!#>)  = readTVar ... (!^) . unpack
    writeM = writeM'
    
    getLeft  = mapM readTVar . listL . unpack
    getRight = mapM readTVar . listR . unpack
    merged   = return . TArray# . concatMap unpack
    reversed = return . TArray# . reverse . unpack
    filled n = fmap (TArray# . fromList) . replicateM n . newTVar
    
    copyTo src so trg to n = when (n > 0) $ do
        when      (so < 0 || to < 0)      $ underEx "copyTo"
        when (so + n > n1 || to + n > n2) $ overEx  "copyTo"
        go so to n
      where
        go _ _ 0 = return ()
        go i j c = do e <- src !#> i; writeM trg j e; go (i + 1) (j + 1) (c - 1)
        
        n1 = sizeOf src
        n2 = sizeOf trg
    
    ofoldlM f base = ofoldl (\ i es -> ($ f i) . (es >>=<<) . readTVar) (return base) . unpack
    ofoldrM f base = ofoldr (\ i -> ($ f i) ... (>>=<<) . readTVar) (return base) . unpack
    
    foldlM f base = foldl (\ es -> ($ f) . (es >>=<<) . readTVar) (return base) . unpack
    foldrM f base = foldr (($ f) ... (>>=<<) . readTVar) (return base) . unpack

instance SplitM STM (TArray# e) e
  where
    takeM n = return . TArray# . take n . unpack
    dropM n = return . TArray# . drop n . unpack
    keepM n = return . TArray# . keep n . unpack
    sansM n = return . TArray# . sans n . unpack
    
    prefixM p es =
      let
          go i = i >= c ? return c $ do e <- es !#> i; p e ? go (succ 1) $ return i
          c = sizeOf es
      in  go 0
    
    suffixM p es =
      let
          go i = i < 0 ? return c $ do e <- es !#> i; p e ? go (pred i) $ return (c - i - 1)
          c = sizeOf es
      in  go (c - 1)
    
    mprefix p es =
      let
          go i = i >= c ? return c $ do e <- es !#> i; p e ?^ go (succ 1) $ return i
          c = sizeOf es
      in  go 0
    
    msuffix p es =
      let
          go i = i < 0 ? return c $ do e <- es !#> i; p e ?^ go (pred i) $ return (c - i - 1)
          c = sizeOf es
      in  go (c - 1)

--------------------------------------------------------------------------------

{- MapM and IndexedM instances. -}

instance MapM STM (TArray# e) Int e
  where
    newMap' defvalue ascs = fromAssocs' (ascsBounds ascs) defvalue ascs
    
    (>!) = (!#>)
    
    overwrite es ascs = do
      mapM_ (uncurry $ writeM es) (filter (indexIn es . fst) ascs)
      return es
    
    kfoldrM = ofoldrM
    kfoldlM = ofoldlM

instance IndexedM STM (TArray# e) Int e
  where
    fromAssocs' bnds defvalue ascs = size bnds `filled` defvalue >>= (`overwrite` ascs)
    
    {-# INLINE writeM' #-}
    writeM' = writeTVar ... (!^) . unpack
    
    fromIndexed' es = do
      let n = sizeOf es
      copy <- filled n (unreachEx "fromIndexed'")
      forM_ [0 .. n - 1] $ \ i -> writeM copy i (es !^ i)
      return copy
    
    fromIndexedM es = do
      n    <- getSizeOf es
      copy <- filled n (unreachEx "fromIndexedM")
      forM_ [0 .. n - 1] $ \ i -> es !#> i >>= writeM copy i
      return copy

--------------------------------------------------------------------------------

{- Freeze and Thaw instances. -}

instance Thaw STM (SArray# e) (TArray# e) where thaw = fmap TArray# . mapM newTVar

instance Freeze STM (TArray# e) (SArray# e) where freeze = mapM readTVar . unpack

--------------------------------------------------------------------------------

ascsBounds :: (Ord a) => [(a, b)] -> (a, a)
ascsBounds =  \ ((x, _) : xs) -> foldr (\ (e, _) (mn, mx) -> (min mn e, max mx e)) (x, x) xs

unpack :: TArray# e -> SArray# (TVar e)
unpack =  \ (TArray# arr) -> arr

overEx :: String -> a
overEx =  throw . IndexOverflow . showString "in SDP.Prim.TArray."

underEx :: String -> a
underEx =  throw . IndexUnderflow . showString "in SDP.Prim.TArray."

unreachEx :: String -> a
unreachEx =  throw . UnreachableException . showString "in SDP.Prim.TArray."