-- | Generic array API.
--
--  A Repa array is a wrapper around an underlying container structure that
--  holds the array elements.
--
--  In the type (`Array` @l@ @a@), the @l@ specifies the `Layout` of data,
--  which includes the type of the underlying container, as well as how 
--  the elements should be arranged in that container. The @a@ specifies 
--  the element type.
--
--   The operators provided by this module do not depend on any particular
--   array representation.
--
module Data.Repa.Array.Generic
        ( Name

          -- * Array Access
        , Bulk  (..),   BulkI
        , (!)
        , length
        , first,        last

          -- * Array Computation
        , Load
        , Target,       TargetI
        , computeS,     computeIntoS

         -- * Operators
         -- ** Construction
        , empty
        , singleton
        , generateMaybeS,  mapMaybeS
        , generateEitherS, mapEitherS

         -- ** Conversion
        , fromList,     fromListInto
        , toList
        , convert,      copy

          -- ** Replicating
        , replicates

          -- ** Mapping
        , mapS, map2S

          -- ** Merging
        , merge
        , mergeMaybe

          -- ** Splitting
        , compact
        , compactIn

          -- ** Processing
        , process

          -- ** Unfolding
        , unfolds
        , StepUnfold (..)

          -- ** Filtering
        , filter

          -- ** Inserting
        , insert

          -- ** Searching
        , findIndex

          -- ** Sloshing
          -- | Sloshing operators copy array elements into a different arrangement, 
          --   but do not create new element values.
        , concat
        , concatWith,   unlines
        , intercalate
        , ConcatDict

          -- ** Grouping
        , groups
        , groupsWith
        , GroupsDict

          -- ** Folding
          -- *** Complete fold
        , foldl, sum, product, mean, std
        , correlate

          -- *** Segmented fold
        , folds
        , foldsWith
        , Folds(..)
        , FoldsDict)
where
import Data.Repa.Array.Generic.Load                     as A
import Data.Repa.Array.Generic.Index                    as A
import Data.Repa.Array.Meta                             as A
import Data.Repa.Array.Internals.Bulk                   as A
import Data.Repa.Array.Internals.Target                 as A
import Data.Repa.Array.Internals.Operator.Concat        as A
import Data.Repa.Array.Internals.Operator.Compact       as A
import Data.Repa.Array.Internals.Operator.Filter        as A
import Data.Repa.Array.Internals.Operator.Fold          as A
import Data.Repa.Array.Internals.Operator.Group         as A
import Data.Repa.Array.Internals.Operator.Insert        as A
import Data.Repa.Array.Internals.Operator.Merge         as A
import Data.Repa.Array.Internals.Operator.Process       as A
import Data.Repa.Array.Internals.Operator.Reduce        as A
import Data.Repa.Array.Internals.Operator.Replicate     as A
import qualified Data.Repa.Array.Generic.Convert        as A
import qualified Data.Vector.Fusion.Stream.Monadic      as V
import Control.Monad
import Prelude  
       hiding   ( reverse, length, map, zipWith, concat, unlines
                , foldl, sum, product, last
                , filter)
#include "repa-array.h"


-- | O(len src) Yield `Just` the index of the first element matching the predicate
--   or `Nothing` if no such element exists.
findIndex :: BulkI l a
          => (a -> Bool) -> Array l a -> Maybe Int

findIndex p !arr
 = loop_findIndex V.SPEC 0
 where  
        !len    = size (extent $ layout arr)

        loop_findIndex !sPEC !ix
         | ix >= len    = Nothing
         | otherwise    
         = let  !x      = arr `index` ix
           in   if p x  then Just ix
                        else loop_findIndex sPEC (ix + 1)
        {-# INLINE_INNER loop_findIndex #-}
{-# INLINE_ARRAY findIndex #-}


-- | Like `A.map`, but immediately `computeS` the result.
mapS    :: (Bulk lSrc a, Target lDst b, Index lSrc ~ Index lDst) 
        => Name lDst    -- ^ Name of destination layout.
        -> (a -> b)     -- ^ Worker function.
        -> Array lSrc a -- ^ Source array.
        -> Array lDst b
mapS l f !xs = computeS l $! map f xs
{-# INLINE mapS #-}


-- | Like `A.map2`, but immediately `computeS` the result.
map2S   :: (Bulk   lSrc1 a, Bulk lSrc2 b, Target lDst c
           , Index lSrc1 ~ Index lDst
           , Index lSrc2 ~ Index lDst)
        => Name lDst            -- ^ Name of destination layout.
        -> (a -> b -> c )       -- ^ Worker function.
        -> Array lSrc1 a        -- ^ Source array.
        -> Array lSrc2 b        -- ^ Source array
        -> Maybe (Array lDst  c)
map2S l f xs ys
 = liftM (computeS l) $! map2 f xs ys
{-# INLINE map2S #-}


-- | O(1). Constant time conversion of one array representation to another.
convert :: A.Convert l1 a1 l2 a2 
        => Name l2 -> Array l1 a1 -> Array l2 a2
convert _ = A.convert
{-# INLINE convert #-}


-- | O(n). Linear time copy of one array representation to another.
-- 
--   This function must be used instead of `convert` when the bit-wise 
--   layout of the two array representations are different.
--
copy    :: (Bulk l1 a, Target l2 a, Index l1 ~ Index l2)
        => Name l2 -> Array l1 a -> Array l2 a
copy n2 arr  = computeS n2 $! delay arr
{-# INLINE copy #-}