{-# LANGUAGE ScopedTypeVariables, MultiParamTypeClasses, BangPatterns, CPP #-}
{-# OPTIONS  -w #-}     -- TODO: enable warnings
#include "fusion-phases.h"

-- | Wrappers for primitives defined in @Data.Vector@.
--
--   * This is an internal API and shouldn't need to be used directly.
--     Client programs should use "Data.Array.Parallel.Unlifted".
--

--  This module doesn't have docs because the bindings are mostly just 
--  forwards for the Data.Vector functions. See there for details.
module Data.Array.Parallel.Unlifted.Sequential.Vector (

  -- * Array classes
  Unbox,

  -- * Array types
  Vector, MVector,

  -- * Streaming
  stream, unstream,

  -- * Basic operations
  length, null, empty, singleton, cons, units,
  replicate,
  (++), index,
  interleave, indexed, repeat, repeatS,

  -- * Subarrays
  slice,   unsafeSlice,
  extract, unsafeExtract,
  tail,
  take, drop, splitAt,

  -- * Permutations
  permute,
  bpermute,
  mbpermute,
  bpermuteDft,
  reverse,
  update,

  -- * Higher-order operations
  map, zipWith, zipWith3,
  filter, pack, 
  combine, combine2ByTag,
  foldl, foldl1, foldl1Maybe,
  fold, fold1, fold1Maybe,
  scanl, scanl1,
  scan, scan1,
  scanRes,

  -- * Searching
  elem, notElem,

  -- * Logical operations
  and, or, any, all,

  -- * Arithmetic operations
  sum,        product,
  maximum,    minimum,
  maximumBy,  minimumBy,
  maxIndex,   minIndex,
  maxIndexBy, minIndexBy,

  -- * Arrays of pairs
  zip,  unzip, fsts, snds,
  zip3, unzip3,

  -- * Enumerations
  enumFromTo,
  enumFromThenTo,
  enumFromStepLen,
  enumFromToEach,
  enumFromStepLenEach,

  -- * Searching
  find, findIndex,

  -- * Conversions to\/from lists
  toList, fromList,

  -- * Random arrays
  random, randomR,

  -- * Mutating operations
  new, copy,

  -- * Mutable vectors
  newM, unsafeFreeze, M.write, M.read, mpermute, mupdate,
  mdrop, mslice,

  -- * I\/O
  UIO(..)
) 
where
import Data.Array.Parallel.Unlifted.Stream.Segmented
import Data.Array.Parallel.Base ( Tag, checkEq, ST )
import qualified Data.Array.Parallel.Base       as B
import qualified Data.Vector.Unboxed            as V
import qualified Data.Vector.Unboxed.Mutable    as M
import qualified Data.Vector.Unboxed.Base       as VBase
import qualified Data.Vector.Generic            as G
import qualified Data.Vector.Generic.Mutable    as MG
import qualified Data.Vector.Storable           as Storable
import qualified Data.Vector.Storable.Mutable   as MStorable
import qualified Data.Vector.Generic.New        as New
import qualified Data.Vector.Fusion.Stream      as S
import Data.Vector.Fusion.Stream.Monadic        ( Stream(..), Step(..) )
import Data.Vector.Fusion.Stream.Size           ( Size(..) )
import Data.Vector.Generic                      ( stream, unstream )

import Data.Vector.Unboxed 
        hiding ( slice, zip, unzip, zip3, unzip3, foldl, foldl1, scanl, scanl1,
                 unsafeSlice )

import Prelude 
        hiding ( length, null,
                replicate, (++), repeat,
                tail, take, drop, splitAt,
                reverse,
                map, zipWith, zipWith3, filter,
                foldl, foldl1, scanl, scanl1,
                elem, notElem,
                and, or, any, all,
                sum, product,
                maximum, minimum,
                zip, unzip, zip3, unzip3,
                enumFromTo, enumFromThenTo )

import qualified Prelude
import qualified System.Random as R
import Foreign hiding ( new )
import System.IO
import Control.Monad

here s = "Data.Array.Parallel.Unlifted.Sequential.Flat." Prelude.++ s

-------------------------------------------------------------------------------
new :: Unbox a => Int -> (forall s. MVector s a -> ST s ()) -> Vector a
new n p 
 = V.create 
 $ do   v <- M.new n
        p v
        return v
{-# INLINE new #-}


newM :: Unbox a => Int -> ST s (MVector s a)
newM = M.new
{-# INLINE newM #-}


-- Yield an array of units 
units :: Int -> Vector ()
units n = replicate n ()
{-# INLINE units #-}
                        

-- Interleave the elements of two arrays
interleave :: Unbox e => Vector e -> Vector e -> Vector e
interleave xs ys = unstream (interleaveS (stream xs) (stream ys))
{-# INLINE_U interleave #-}


-- Repeat an array @n@ times
repeat :: Unbox e => Int -> Vector e -> Vector e
repeat n xs = unstream (repeatS n xs)
{-# INLINE_U repeat #-}


repeatS :: Unbox e => Int -> Vector e -> S.Stream e
{-# INLINE_STREAM repeatS #-}
repeatS k !xs = Stream next (0,k) (Exact (k*n))
  where
    !n = length xs

    {-# INLINE next #-}
    next (i,0) = return Done
    next (i,k) | i == n    = return $ Skip                     (0,k-1)
               | otherwise = return $ Yield (unsafeIndex xs i) (i+1,k)


-- Take a sub-range of a vector, avoiding copying.
slice :: Unbox a => String -> Vector a -> Int -> Int -> Vector a
slice here xs i n 
        = B.checkSlice here (V.length xs) i n
        $ V.slice i n xs
{-# INLINE_U slice #-}


-- Take a sub-range of a vector, avoiding copying, without bounds checks.
unsafeSlice :: Unbox a => Vector a -> Int -> Int -> Vector a
unsafeSlice xs i n = V.unsafeSlice i n xs
{-# INLINE_U unsafeSlice #-}


index :: Unbox a => String -> Vector a -> Int -> a
index here vec ix
        = B.check here (V.length vec) ix
        $ V.unsafeIndex vec ix
{-# INLINE_U index #-}


-- Copy out a subrange of a vector.
extract :: Unbox a => Vector a -> Int -> Int -> Vector a
{-# INLINE_U extract #-}
extract xs i n 
        = force (V.slice i n xs)


-- Copy out a subrange of a vector, without bounds checks.
unsafeExtract :: Unbox a => Vector a -> Int -> Int -> Vector a
unsafeExtract xs i n = force (V.unsafeSlice i n xs)
{-# INLINE_U unsafeExtract #-}


mupdate :: Unbox e => MVector s e -> Vector (Int,e) -> ST s ()
mupdate marr xs 
        = MG.update marr (stream xs)
{-# INLINE_U mupdate #-}


mpermute :: Unbox e => MVector s e -> Vector e -> Vector Int -> ST s ()
mpermute marr xs is 
        = MG.update marr (stream (zip is xs))
{-# INLINE_U mpermute #-}


permute :: Unbox e => Vector e -> Vector Int -> Vector e
{-# INLINE_U permute #-}
permute xs is 
 = create 
 $ do   v <- M.new (length xs)
        mpermute v xs is
        return v


bpermute :: Unbox e => Vector e -> Vector Int -> Vector e
bpermute = backpermute
{-# INLINE_U bpermute #-}


mbpermute :: (Unbox e, Unbox d) => (e -> d) -> Vector e -> Vector Int -> Vector d
mbpermute f es is
        = unstream (mbpermuteS f es (stream is))
{-# INLINE_STREAM mbpermute #-}


bpermuteS :: Unbox e => Vector e -> S.Stream Int -> S.Stream e
bpermuteS !a s 
        = S.map (a!) s
{-# INLINE_STREAM bpermuteS #-}


mbpermuteS:: Unbox e => (e -> d) -> Vector e -> S.Stream Int -> S.Stream d
mbpermuteS f !a 
        = S.map (f . (a!))
{-# INLINE_STREAM mbpermuteS #-}


-- Default back permute
--
-- * The values of the index-value pairs are written into the position in the
--   result array that is indicated by the corresponding index.
--
-- * All positions not covered by the index-value pairs will have the value
--   determined by the initialiser function for that index position.
--
bpermuteDft :: Unbox e
	    => Int			        -- length of result array
	    -> (Int -> e)		        -- initialiser function
	    -> Vector (Int,e)	        	-- index-value pairs
	    -> Vector e
bpermuteDft n init
        = update (map init (enumFromN 0 n))
{-# INLINE_U bpermuteDft #-}


-- Extract all elements from an array according to a given flag array
pack:: Unbox e => Vector e -> Vector Bool -> Vector e
pack xs = map fst . filter snd . zip xs
{-# INLINE_U pack #-}


combine :: Unbox a
	 => Vector Bool -> Vector a -> Vector a -> Vector a
combine bs
        = combine2ByTag (map (\b -> if b then 0 else 1) bs)
{-# INLINE combine #-}


combine2ByTag :: Unbox a => Vector Tag -> Vector a -> Vector a -> Vector a
combine2ByTag ts xs ys
  = checkEq (here "combine2ByTag")
            ("tags length /= sum of args length")
            (length ts) (length xs + length ys)
  $ unstream (combine2ByTagS (stream ts) (stream xs) (stream ys))
{-# INLINE_U combine2ByTag #-}


-- Array reduction proceeding from the left
foldl :: Unbox a => (b -> a -> b) -> b -> Vector a -> b
foldl = foldl'
{-# INLINE_U foldl #-}


-- Array reduction proceeding from the left for non-empty arrays
foldl1 :: Unbox a => (a -> a -> a) -> Vector a -> a
foldl1 = foldl1'
{-# INLINE_U foldl1 #-}


-- Array reduction that requires an associative combination function with its
-- unit
fold :: Unbox a => (a -> a -> a) -> a -> Vector a -> a
fold = foldl
{-# INLINE_U fold #-}


-- Reduction of a non-empty array which requires an associative combination
-- function
fold1 :: Unbox a => (a -> a -> a) -> Vector a -> a
fold1 = foldl1
{-# INLINE_U fold1 #-}


foldl1Maybe :: Unbox a => (a -> a -> a) -> Vector a -> Maybe a
foldl1Maybe f xs = foldl' join Nothing xs
  where
    {-# INLINE join #-}
    join Nothing  y = Just $! y
    join (Just x) y = Just $! f x y
{-# INLINE_U foldl1Maybe #-}


fold1Maybe :: Unbox a => (a -> a -> a) -> Vector a -> Maybe a
fold1Maybe = foldl1Maybe
{-# INLINE_U fold1Maybe #-}


-- Prefix scan proceedings from left to right
scanl :: (Unbox a, Unbox b) => (b -> a -> b) -> b -> Vector a -> Vector b
scanl = prescanl'
{-# INLINE_U scanl #-}


-- Prefix scan of a non-empty array proceeding from left to right
scanl1 :: Unbox a => (a -> a -> a) -> Vector a -> Vector a
scanl1 = scanl1'
{-# INLINE_U scanl1 #-}


-- Prefix scan proceeding from left to right that needs an associative
-- combination function with its unit
scan :: Unbox a => (a -> a -> a) -> a -> Vector a -> Vector a
scan = scanl
{-# INLINE_U scan #-}


-- Prefix scan of a non-empty array proceeding from left to right that needs
-- an associative combination function
scan1 :: Unbox a => (a -> a -> a) -> Vector a -> Vector a
scan1 = scanl1
{-# INLINE_U scan1 #-}


scanRes :: Unbox a => (a -> a -> a) -> a -> Vector a -> (Vector a,a)
scanRes f z xs 
 = let  ys = scanl' f z xs
   in   (unsafeInit ys, unsafeLast ys)
{-# INLINE_U scanRes #-}


fsts :: (Unbox a, Unbox b) => Vector (a,b) -> Vector a
fsts (VBase.V_2 _ xs ys) = xs
{-# INLINE_STREAM fsts #-}


snds :: (Unbox a, Unbox b) => Vector (a,b) -> Vector b
snds (VBase.V_2 _ xs ys) = ys
{-# INLINE_STREAM snds #-}


zip :: (Unbox a, Unbox b) => Vector a -> Vector b -> Vector (a,b)
zip !xs !ys = V.zip xs ys
{-# INLINE_STREAM zip #-}


unzip :: (Unbox a, Unbox b) => Vector (a,b) -> (Vector a, Vector b)
unzip ps = V.unzip ps
{-# INLINE_STREAM unzip #-}


{-# RULES

"fsts/new/unstream [dph-prim-seq]" forall xs.
  fsts (G.new (New.unstream xs)) = V.map fst (G.new (New.unstream xs))

"snds/new/unstream [dph-prim-seq]" forall xs.
  snds (G.new (New.unstream xs)) = V.map snd (G.new (New.unstream xs))

"stream/zip [dph-prim-seq]" forall xs ys.
  G.stream (zip xs ys) = S.zip (G.stream xs) (G.stream ys)

  #-}


zip3    :: (Unbox a, Unbox b, Unbox c)
        => Vector a -> Vector b -> Vector c -> Vector (a,b,c)
zip3 !xs !ys !zs 
        = V.zip3 xs ys zs
{-# INLINE_STREAM zip3 #-}


unzip3  :: (Unbox a, Unbox b, Unbox c)
        => Vector (a,b,c) -> (Vector a, Vector b, Vector c)
unzip3 ps = V.unzip3 ps
{-# INLINE_STREAM unzip3 #-}


enumFromStepLen :: Int -> Int -> Int -> Vector Int
enumFromStepLen = enumFromStepN
{-# INLINE_U enumFromStepLen #-}


enumFromToEach :: Int -> Vector (Int,Int) -> Vector Int
enumFromToEach n 
        = unstream . enumFromToEachS n . stream
{-# INLINE_U enumFromToEach #-}


enumFromStepLenEach :: Int -> Vector Int -> Vector Int -> Vector Int -> Vector Int
enumFromStepLenEach len starts steps lens
        = unstream 
        $ enumFromStepLenEachS len 
        $ stream 
        $ V.zip3 starts steps lens
{-# INLINE_U enumFromStepLenEach #-}


random :: (Unbox a, R.Random a, R.RandomGen g) => Int -> g -> Vector a
random n = unstream . randomS n
{-# INLINE_U random #-}


randomR :: (Unbox a, R.Random a, R.RandomGen g) => Int -> (a,a) -> g -> Vector a
randomR n r = unstream . randomRS n r
{-# INLINE_U randomR #-}


randomS :: (R.RandomGen g, R.Random a) => Int -> g -> S.Stream a
randomS n g 
  = Stream step (g,n) (Exact n)
  where
    {-# INLINE step #-}
    step (g,0) = return Done
    step (g,n) = let (x,g') = R.random g
                 in return $ Yield x (g',n-1)
{-# INLINE_STREAM randomS #-}


randomRS :: (R.RandomGen g, R.Random a) => Int -> (a,a) -> g -> S.Stream a
randomRS n r g 
  = Stream step (g,n) (Exact n)
  where
    {-# INLINE step #-}
    step (g,0) = return Done
    step (g,n) = let (x,g') = R.randomR r g
                 in return $ Yield x (g',n-1)
{-# INLINE_STREAM randomRS #-}


mdrop :: Unbox a => Int -> MVector s a -> MVector s a
mdrop = M.drop
{-# INLINE mdrop #-}


mslice :: Unbox a => Int -> Int -> MVector s a -> MVector s a
mslice = M.slice
{-# INLINE mslice #-}


-- IO Functions ---------------------------------------------------------------
hGetStorable 
        :: forall a. Storable a 
        => Handle -> IO (Storable.Vector a)
hGetStorable h =
  alloca $ \iptr ->
  do
    hGetBuf h iptr (sizeOf (undefined :: Int))
    n <- peek iptr
    v <- MStorable.unsafeNew n
    let bytes = sizeOf (undefined :: a) * MStorable.length v
    r <- MStorable.unsafeWith v $ \ptr -> hGetBuf h ptr bytes
    Storable.unsafeFreeze (MStorable.take r v)


hPutStorable 
        :: forall a. Storable a
        => Handle -> Storable.Vector a -> IO ()
hPutStorable h xs =
  alloca $ \iptr ->
  do
    poke iptr n 
    hPutBuf h iptr (sizeOf n)
    Storable.unsafeWith xs $ \ptr ->
      do
        hPutBuf h ptr (sizeOf (undefined :: a) * n)
        return ()
  where
    !n = Storable.length xs


class Unbox a => UIO a where
  hPut :: Handle -> Vector a -> IO ()
  hGet :: Handle -> IO (Vector a)


primPut :: (Unbox a, Storable a) => Handle -> Vector a -> IO ()
primPut h = hPutStorable h . Storable.convert
{-# INLINE primPut #-}


primGet :: (Unbox a, Storable a) => Handle -> IO (Vector a)
primGet = fmap convert . hGetStorable
{-# INLINE primGet #-}


instance UIO Int where
  {-# INLINE hPut #-}
  hPut = primPut

  {-# INLINE hGet #-}
  hGet = primGet


instance UIO Double where
  {-# INLINE hPut #-}
  hPut = primPut

  {-# INLINE hGet #-}
  hGet = primGet


instance (UIO a, UIO b) => UIO (a,b) where
  {-# INLINE hPut #-}
  hPut h xs = case V.unzip xs of
                (ys,zs) -> do hPut h ys
                              hPut h zs

  {-# INLINE hGet #-}
  hGet h = do xs <- hGet h
              ys <- hGet h
              return (V.zip xs ys)


-- Additional Unbox instances -------------------------------------------------
newtype instance MVector s Ordering = MV_Ordering (M.MVector s Word8)
newtype instance Vector    Ordering = V_Ordering  (V.Vector    Word8)

instance Unbox Ordering

instance MG.MVector MVector Ordering where
  {-# INLINE basicLength #-}
  {-# INLINE basicUnsafeSlice #-}
  {-# INLINE basicOverlaps #-}
  {-# INLINE basicUnsafeNew #-}
  {-# INLINE basicUnsafeReplicate #-}
  {-# INLINE basicUnsafeRead #-}
  {-# INLINE basicUnsafeWrite #-}
  {-# INLINE basicClear #-}
  {-# INLINE basicSet #-}
  {-# INLINE basicUnsafeCopy #-}
  {-# INLINE basicUnsafeGrow #-}
  basicLength (MV_Ordering v)           
        = MG.basicLength v

  basicUnsafeSlice i n (MV_Ordering v)  
        = MV_Ordering $ MG.basicUnsafeSlice i n v

  basicOverlaps (MV_Ordering v1) (MV_Ordering v2) 
        = MG.basicOverlaps v1 v2

  basicUnsafeNew n                      
        = MV_Ordering `liftM` MG.basicUnsafeNew n

  basicUnsafeReplicate n x              
        = MV_Ordering `liftM` MG.basicUnsafeReplicate n (fromOrdering x)

  basicUnsafeRead (MV_Ordering v) i     
        = toOrdering `liftM` MG.basicUnsafeRead v i

  basicUnsafeWrite (MV_Ordering v) i x  
        = MG.basicUnsafeWrite v i (fromOrdering x)

  basicClear (MV_Ordering v) 
        = MG.basicClear v

  basicSet (MV_Ordering v) x
        = MG.basicSet v (fromOrdering x)

  basicUnsafeCopy (MV_Ordering v1) (MV_Ordering v2) 
        = MG.basicUnsafeCopy v1 v2

  basicUnsafeMove (MV_Ordering v1) (MV_Ordering v2) 
        = MG.basicUnsafeMove v1 v2

  basicUnsafeGrow (MV_Ordering v) n 
        = MV_Ordering `liftM` MG.basicUnsafeGrow v n


instance G.Vector Vector Ordering where
  {-# INLINE basicUnsafeFreeze #-}
  {-# INLINE basicUnsafeThaw #-}
  {-# INLINE basicLength #-}
  {-# INLINE basicUnsafeSlice #-}
  {-# INLINE basicUnsafeIndexM #-}
  {-# INLINE elemseq #-}
  basicUnsafeFreeze (MV_Ordering v) 
        = V_Ordering `liftM` G.basicUnsafeFreeze v

  basicUnsafeThaw (V_Ordering v) 
        = MV_Ordering `liftM` G.basicUnsafeThaw v

  basicLength (V_Ordering v) 
        = G.basicLength v

  basicUnsafeSlice i n (V_Ordering v) 
        = V_Ordering $ G.basicUnsafeSlice i n v

  basicUnsafeIndexM (V_Ordering v) i 
        = toOrdering `liftM` G.basicUnsafeIndexM v i

  basicUnsafeCopy (MV_Ordering mv) (V_Ordering v) 
        = G.basicUnsafeCopy mv v

  elemseq _ = seq


fromOrdering :: Ordering -> Word8
{-# INLINE fromOrdering #-}
fromOrdering LT = 0
fromOrdering EQ = 1
fromOrdering GT = 2

toOrdering :: Word8 -> Ordering
{-# INLINE toOrdering #-}
toOrdering 0 = LT
toOrdering 1 = EQ
toOrdering _ = GT


instance Unbox Integer
instance MG.MVector MVector Integer
instance G.Vector Vector Integer