{-# OPTIONS -fno-warn-orphans #-}
-- | Defines the class of scalar element types, as well as the 
--   PData instances for these types.
--
module Data.Array.Parallel.PArray.Scalar (
  Scalar(..),

  -- These functions have corresponding members in the PR class
  -- from Data.Array.Parallel.PArray.PData.
  emptyPRScalar,
  replicatePRScalar,
  replicatelPRScalar,
  repeatPRScalar, 
  indexPRScalar,
  extractPRScalar,
  bpermutePRScalar,
  appPRScalar,
  applPRScalar,
  packByTagPRScalar,
  combine2PRScalar,
  updatePRScalar,
  fromListPRScalar,
  nfPRScalar
)
where
import Data.Array.Parallel.PArray.PData
import Data.Array.Parallel.Base
import Data.Array.Parallel.Base.DTrace
import qualified Data.Array.Parallel.Unlifted   as U
import GHC.Exts                                 (Int(..))


-- | Class of scalar types.
--   Scalar types are the ones that we can store in our underlying U.Arrays
--   (which are currently implemented as Data.Vectors).
--
--   To perform an operation on a PData array of scalar elements, we coerce
--   it to the underling U.Array and use the corresponding U.Array operators.
--
class U.Elt a => Scalar a where
  fromScalarPData :: PData a -> U.Array a
  toScalarPData   :: U.Array a -> PData a


-- Scalar Wrappers ------------------------------------------------------------
--  These wrappers work on (PData a) arrays when we know the element type 'a'
--  is scalar. For most of them we can just coerce the PData to the underling 
--  U.Array and use the corresponding U.Array operator.
--
--  The underlying U.Array may be processed in parallel or sequentially,
--  depending on what U.Array primitive library has been linked in.
--
emptyPRScalar :: Scalar a => T_emptyPR a
{-# INLINE emptyPRScalar #-}
emptyPRScalar 
  = toScalarPData U.empty

replicatePRScalar :: Scalar a => T_replicatePR a
{-# INLINE replicatePRScalar #-}
replicatePRScalar n# x
  = traceF "replicatePRScalar"
  $ toScalarPData (U.replicate (I# n#) x)

replicatelPRScalar :: Scalar a => T_replicatelPR a
{-# INLINE replicatelPRScalar #-}
replicatelPRScalar segd xs 
  = traceF "replicatelPRScalar"
  $ toScalarPData
  $ U.replicate_s segd 
  $ fromScalarPData xs

repeatPRScalar :: Scalar a => T_repeatPR a
{-# INLINE repeatPRScalar #-}
repeatPRScalar n# len# xs
  = traceF "repeatPRScalar"
  $ toScalarPData
  $ U.repeat (I# n#) (I# len#)
  $ fromScalarPData xs

indexPRScalar :: Scalar a => T_indexPR a
{-# INLINE indexPRScalar #-}
indexPRScalar xs i#
  = U.index "indexPRScalar" (fromScalarPData xs) (I# i#)

extractPRScalar :: Scalar a => T_extractPR a
{-# INLINE extractPRScalar #-}
extractPRScalar xs i# n#
  = traceF "extractPRScalar"
  $ toScalarPData
  $ U.extract (fromScalarPData xs) (I# i#) (I# n#)

bpermutePRScalar :: Scalar a => T_bpermutePR a
{-# INLINE bpermutePRScalar #-}
bpermutePRScalar xs _ is
  = traceF "bpermutePRScalar"
  $ toScalarPData
  $ U.bpermute (fromScalarPData xs) is

appPRScalar :: Scalar a => T_appPR a
{-# INLINE appPRScalar #-}
appPRScalar xs ys
  = traceF "appPRScalar"
  $ toScalarPData
  $ fromScalarPData xs U.+:+ fromScalarPData ys

applPRScalar :: Scalar a => T_applPR a
{-# INLINE applPRScalar #-}
applPRScalar segd xsegd xs ysegd ys
  = traceF "applPRScalar"
  $ toScalarPData
  $ U.append_s segd xsegd (fromScalarPData xs)
                    ysegd (fromScalarPData ys)
                        
packByTagPRScalar :: Scalar a => T_packByTagPR a
{-# INLINE packByTagPRScalar #-}
packByTagPRScalar xs _ tags t#
  = traceF "packByTagPRScalar"
  $ toScalarPData
  $ U.packByTag (fromScalarPData xs)
                tags
                (intToTag (I# t#))

combine2PRScalar :: Scalar a => T_combine2PR a
{-# INLINE combine2PRScalar #-}
combine2PRScalar _ sel xs ys 
  = traceF "combine2PRScalar"
  $ toScalarPData
  $ U.combine2 (U.tagsSel2 sel)
               (U.repSel2 sel)
               (fromScalarPData xs)
               (fromScalarPData ys)

updatePRScalar :: Scalar a => T_updatePR a
{-# INLINE updatePRScalar #-}
updatePRScalar xs is ys 
  = traceF "updatePRScalar"
  $ toScalarPData
  $ U.update (fromScalarPData xs)
             (U.zip is (fromScalarPData ys))

fromListPRScalar :: Scalar a => T_fromListPR a
{-# INLINE fromListPRScalar #-}
fromListPRScalar _ xs
  = toScalarPData (U.fromList xs)

nfPRScalar :: Scalar a => T_nfPR a
{-# INLINE nfPRScalar #-}
nfPRScalar xs
  = fromScalarPData xs `seq` ()