-- | Repa Array API that automatically choses an array layout based -- on the element type. -- -- This is a re-export of the module "Data.Repa.Array". -- module Data.Repa.Array.Auto.Operator ( Array , Elem, Build -- * Basics , index , (!) , length , first, last , head, tail, init -- * Construction , empty , singleton , generateMaybeS, mapMaybeS , generateEitherS, mapEitherS -- * Conversion , fromList , fromLists , fromListss , toList , toLists , toListss -- * Operators -- ** Replicating , replicates -- ** Mapping , map , map2 , mapElems -- ** Folding , foldl , sum, product , mean, std -- *** Special Folds , correlate , folds , foldsWith -- ** Filtering , filter , slices , trims , trimEnds , trimStarts -- ** Zipping , zip , unzip -- ** Sloshing , reverse , concat , concats , concatWith , unlines , intercalate , ragspose3 -- ** Slicing , slice -- ** Inserting , insert -- ** Searching , findIndex -- ** Merging , merge , mergeMaybe -- ** Compacting , compact , compactIn -- ** Processing , process -- ** Grouping , groups , groupsWith -- ** Splitting , segment , segmentOn , dice , diceSep) where import Data.Repa.Array.Auto.Base import Data.Repa.Array.Material.Auto (Name(..)) import Data.Repa.Array.Generic.Convert as A import Control.Monad import GHC.Exts hiding (fromList, toList) import qualified Data.Repa.Array.Generic as G import qualified Data.Repa.Array.Material.Auto as A import qualified Data.Repa.Array.Material.Nested as N import qualified Data.Repa.Array.Meta.Tuple as A import qualified Data.Repa.Array.Meta.Window as A import qualified Data.Repa.Array.Meta.Delayed as A import qualified Data.Repa.Array.Meta.Delayed2 as A import qualified Data.Repa.Array.Internals.Bulk as G import qualified Data.Repa.Chain as C import qualified Data.Vector.Unboxed as U import Prelude hiding ( map, length, reverse, filter, concat, unlines, foldl , sum, product, zip, unzip , head, tail, init, last) -- Basic ------------------------------------------------------------------------------------------ -- | O(1). Get an element from an array. -- -- If the provided index is outside the extent of the array then the -- result depends on the layout. index :: Elem a => Array a -> Int -> a index = (G.!) {-# INLINE index #-} -- | O(1). Alias for `index` (!) :: Elem a => Array a -> Int -> a (!) = index {-# INLINE (!) #-} -- | O(1). Get the number of elements in an array. length :: Elem a => Array a -> Int length = G.length {-# INLINE length #-} -- | O(1). Take the first element of an array, or `Nothing` if it's empty. first :: Elem a => Array a -> Maybe a first = G.first {-# INLINE first #-} -- | O(1). Take the last element of an array, or `Nothing` if it's empty. last :: Elem a => Array a -> Maybe a last = G.last {-# INLINE last #-} -- | O(1). alias for `first`. head :: Elem a => Array a -> Maybe a head = G.first {-# INLINE head #-} -- | O(1). Take the tail of an array, or `Nothing` if it's empty. tail :: Elem a => Array a -> Maybe (Array a) tail = A.tail {-# INLINE tail #-} -- | O(1). Take the initial elements of an array, or `Nothing` if it's empty. init :: Elem a => Array a -> Maybe (Array a) init = A.init {-# INLINE init #-} -- Construction ----------------------------------------------------------------------------------- -- | O(1). An empty array of the given layout. empty :: Build a => Array a empty = G.empty A {-# INLINE empty #-} -- | O(1). Create a new empty array containing a single element. singleton :: Build a => a -> Array a singleton = G.singleton A {-# INLINE singleton #-} -- | Like `generateS` but use a function that produces Maybe an element. -- If any element returns `Nothing`, then `Nothing` for the whole array. generateMaybeS :: Build a => Int -> (Int -> Maybe a) -> Maybe (Array a) generateMaybeS = G.generateMaybeS A {-# INLINE generateMaybeS #-} -- | Apply a function to every element of an array, -- if any application returns `Nothing`, then `Nothing` for the whole result. mapMaybeS :: (Elem a, Build b) => (a -> Maybe b) -> Array a -> Maybe (Array b) mapMaybeS = G.mapMaybeS A {-# INLINE mapMaybeS #-} -- | Like `generateS` but use a function that produces Either some error -- or an element. If any element returns `Nothing`, then `Nothing` for -- the whole array. generateEitherS :: Build a => Int -> (Int -> Either err a) -> Either err (Array a) generateEitherS = G.generateEitherS A {-# INLINE generateEitherS #-} -- | Apply a function to every element of an array, -- if any application returns `Left`, then `Left` for the whole result. mapEitherS :: (Elem a, Build b) => (a -> Either err b) -> Array a -> Either err (Array b) mapEitherS = G.mapEitherS A {-# INLINE mapEitherS #-} -- Conversion ------------------------------------------------------------------------------------- -- | Convert a list to an array. fromList :: Build a => [a] -> Array a fromList = G.fromList A {-# INLINE fromList #-} -- | Convert a nested list to an array. fromLists :: Build a => [[a]] -> Array (Array a) fromLists xs = convert $! N.fromLists A xs {-# INLINE fromLists #-} -- | Convert a triply nested list to a triply nested array. fromListss :: Build a => [[[a]]] -> Array (Array (Array a)) fromListss xs = convert $! N.fromListss A xs {-# INLINE fromListss #-} -- | Convert an array to a list. toList :: Elem a => Array a -> [a] toList = G.toList {-# INLINE toList #-} -- | Convert a nested array to some lists. toLists :: (Elem a, Elem (Array a)) => Array (Array a) -> [[a]] toLists = G.toLists {-# INLINE toLists #-} -- | Convert a triply nested array to a triply nested list. toListss :: (Elem a, Elem (Array a), Elem (Array (Array a))) => Array (Array (Array a)) -> [[[a]]] toListss = G.toListss {-# INLINE toListss #-} -- Index space ------------------------------------------------------------------------------------ -- | O(n). Reverse the elements of a list. -- -- @ -- > toList $ reverse $ fromList [0 .. 10 :: Int] -- [10,9,8,7,6,5,4,3,2,1,0] -- @ -- reverse :: Build a => Array a -> Array a reverse arr = G.computeS A $! A.reverse arr {-# INLINE reverse #-} -- Replicating ------------------------------------------------------------------------------------ -- | Segmented replicate. replicates :: (Elem a, Build a) => Array (Int, a) -> Array a replicates arr = G.replicates A arr {-# INLINE replicates #-} -- Mapping ---------------------------------------------------------------------------------------- -- | Apply a function to all the elements of a list. map :: (Elem a, Build b) => (a -> b) -> Array a -> Array b map f arr = G.computeS A $! A.map f arr {-# INLINE map #-} -- | Combine two arrays of the same length element-wise. -- -- If the arrays don't have the same length then `Nothing`. -- map2 :: (Elem a, Elem b, Build c) => (a -> b -> c) -> Array a -> Array b -> Maybe (Array c) map2 f xs ys = liftM (G.computeS A) $! A.map2 f xs ys {-# INLINE map2 #-} -- | Apply a function to all the elements of a doubly nested -- array, preserving the nesting structure. -- -- * This function has a non-standard time complexity. -- As nested arrays use a segment descriptor based representation, -- detatching and reattaching the nesting structure is a constant time -- operation. However, the array passed to the worker function will -- also contain any elements in the array representation that are -- not reachable from the segment descriptor. This matters if the -- source array was produced by a function that filters the segments -- directly, like `slices`. -- mapElems :: (Array a -> Array b) -> Array (Array a) -> (Array (Array b)) mapElems f (A.AArray_Array arr) = A.AArray_Array (N.mapElems f arr) {-# INLINE mapElems #-} -- Folding ---------------------------------------------------------------------------------------- -- | Left fold of all elements in an array. foldl :: Elem b => (a -> b -> a) -> a -> Array b -> a foldl = G.foldl {-# INLINE foldl #-} -- | Yield the sum of the elements of an array. sum :: (Elem a, Num a) => Array a -> a sum = G.sum {-# INLINE sum #-} -- | Yield the product of the elements of an array. product :: (Elem a, Num a) => Array a -> a product = G.product {-# INLINE product #-} -- | Yield the mean value of the elements of an array. mean :: (Elem a, Fractional a) => Array a -> a mean = G.mean {-# INLINE mean #-} -- | Yield the standard deviation of the elements of an array std :: (Elem a, Floating a) => Array a -> a std = G.std {-# INLINE std #-} -- | Compute the Pearson correlation of two arrays. -- -- If the arrays differ in length then only the common -- prefix is correlated. -- correlate :: (Elem a, Floating a) => Array a -> Array a -> a correlate = G.correlate {-# INLINE correlate #-} -- | Segmented fold over vectors of segment lengths and input values. -- -- * The total lengths of all segments need not match the length of the -- input elements vector. The returned `C.Folds` state can be inspected -- to determine whether all segments were completely folded, or the -- vector of segment lengths or elements was too short relative to the -- other. -- folds :: (Elem a, Build n, Build b) => (a -> b -> b) -- ^ Worker function. -> b -- ^ Initial state when folding segments. -> Array (n, Int) -- ^ Segment names and lengths. -> Array a -- ^ Elements. -> (Array (n, b), C.Folds Int Int n a b) folds f z lens vals = let (arr', result) = G.folds A A f z lens vals in (A.AArray_T2 arr', result) {-# INLINE folds #-} -- | Like `folds`, but take an initial state for the first segment. -- foldsWith :: (Elem a, Build n, Build b) => (a -> b -> b) -- ^ Worker function. -> b -- ^ Initial state when folding segments. -> Maybe (n, Int, b) -- ^ Name, length and initial state for first segment. -> Array (n, Int) -- ^ Segment names and lengths. -> Array a -- ^ Elements. -> (Array (n, b), C.Folds Int Int n a b) foldsWith f z start lens vals = let (arr', result) = G.foldsWith A A f z start lens vals in (A.AArray_T2 arr', result) {-# INLINE foldsWith #-} -- Filtering -------------------------------------------------------------------------------------- -- | O(len src) Keep the elements of an array that match the given predicate. filter :: Build a => (a -> Bool) -> Array a -> Array a filter = G.filter A {-# INLINE filter #-} -- | O(1). Produce a nested array by taking slices from some array of elements. -- -- * This is a constant time operation, as the representation for nested -- vectors just wraps the starts, lengths and elements vectors. -- slices :: Array Int -- ^ Segment starting positions. -> Array Int -- ^ Segment lengths. -> Array a -- ^ Array elements. -> Array (Array a) slices (A.AArray_Int starts) (A.AArray_Int lens) elems = A.AArray_Array $! N.slices starts lens elems {-# INLINE slices #-} -- | For each segment of a nested vector, trim elements off the start -- and end of the segment that match the given predicate. trims :: Elem a => (a -> Bool) -> Array (Array a) -> Array (Array a) trims f (A.AArray_Array arr) = A.AArray_Array $! N.trims f arr {-# INLINE trims #-} -- | For each segment of a nested array, trim elements off the end of -- the segment that match the given predicate. trimEnds :: Elem a => (a -> Bool) -> Array (Array a) -> Array (Array a) trimEnds f (A.AArray_Array arr) = A.AArray_Array $! N.trimEnds f arr {-# INLINE trimEnds #-} -- | For each segment of a nested array, trim elements off the start of -- the segment that match the given predicate. trimStarts :: Elem a => (a -> Bool) -> Array (Array a) -> Array (Array a) trimStarts f (A.AArray_Array arr) = A.AArray_Array $! N.trimStarts f arr {-# INLINE trimStarts #-} -- Zipping ---------------------------------------------------------------------------------------- -- | O(1). Pack a pair of arrays to an array of pairs. zip :: (Elem a, Elem b) => Array a -> Array b -> Array (a, b) zip arr1 arr2 = let len = max (length arr1) (length arr2) arr1' = A.window 0 len arr1 arr2' = A.window 0 len arr2 in A.AArray_T2 (A.tup2 arr1' arr2') {-# INLINE zip #-} -- | O(1). Unpack an array of pairs to a pair of arrays. unzip :: (Elem a, Elem b) => Array (a, b) -> (Array a, Array b) unzip arr@(A.AArray_T2 arr') = let len = length arr (arr1, arr2) = A.untup2 arr' arr1' = A.window 0 len arr1 arr2' = A.window 0 len arr2 in (arr1', arr2') -- Sloshing --------------------------------------------------------------------------------------- -- | Concatenate nested arrays. concat :: (Elem a, Build a) => Array (Array a) -- ^ Arrays to concatenate. -> Array a concat arr = (inline G.concat) A arr {-# INLINABLE concat #-} -- | O(len result) Concatenate the elements of some nested vector, -- inserting a copy of the provided separator array between each element. concatWith :: (Elem a, Build a) => Array a -- ^ Separator array. -> Array (Array a) -- ^ Arrays to concatenate. -> Array a concatWith = G.concatWith A {-# INLINE concatWith #-} -- | O(len result) Concatenate the outer two layers of a triply nested array. -- (Segmented concatenation). -- -- * The operation is performed entirely on the segment descriptors of the -- array, and does not require the inner array elements to be copied. -- * This version is faster than plain `concat` on triply nested arrays. -- concats :: Array (Array (Array a)) -> Array (Array a) concats (A.AArray_Array (N.NArray starts1 lens1 (A.AArray_Array arr))) = A.AArray_Array $ N.concats (N.NArray starts1 lens1 arr) {-# INLINE concats #-} -- | O(len result) Perform a `concatWith`, adding a newline character to -- the end of each inner array. unlines :: Array (Array Char) -> Array Char unlines = G.unlines A {-# INLINE unlines #-} -- | O(len result) Insert a copy of the separator array between the elements of -- the second and concatenate the result. intercalate :: (Elem a, Build a) => Array a -- ^ Separator array. -> Array (Array a) -- ^ Arrays to concatenate. -> Array a intercalate = G.intercalate A {-# INLINE intercalate #-} -- | Ragged transpose of a triply nested array. -- -- * This operation is performed entirely on the segment descriptors -- of the nested arrays, and does not require the inner array elements -- to be copied. -- ragspose3 :: Array (Array (Array a)) -> Array (Array (Array a)) ragspose3 (A.AArray_Array (N.NArray starts0 lens0 (A.AArray_Array arr))) = let N.NArray starts1 elems1 (N.NArray starts2 elems2 arr') = N.ragspose3 (N.NArray starts0 lens0 arr) in A.AArray_Array $! N.NArray starts1 elems1 $! A.AArray_Array $! N.NArray starts2 elems2 arr' {-# INLINE ragspose3 #-} -- Slicing ---------------------------------------------------------------------------------------- -- | Take a slice out of an array, given a starting position and length. slice :: Elem a => Int -> Int -> Array a -> Maybe (Array a) slice from len arr | from >= 0, len >= 0 , len <= G.length arr - from = Just $ A.window from len arr | otherwise = Nothing {-# INLINE slice #-} -- Merging ---------------------------------------------------------------------------------------- -- | Merge two sorted key-value streams. merge :: (Ord k, Elem (k, a), Elem (k, b), Build (k, c)) => (k -> a -> b -> c) -- ^ Combine two values with the same key. -> (k -> a -> c) -- ^ Handle a left value without a right value. -> (k -> b -> c) -- ^ Handle a right value without a left value. -> Array (k, a) -- ^ Array of keys and left values. -> Array (k, b) -- ^ Array of keys and right values. -> Array (k, c) -- ^ Array of keys and results. merge = G.merge A {-# INLINE merge #-} -- | Like `merge`, but only produce the elements where the worker functions -- return `Just`. mergeMaybe :: (Ord k, Elem (k, a), Elem (k, b), Build (k, c)) => (k -> a -> b -> Maybe c) -- ^ Combine two values with the same key. -> (k -> a -> Maybe c) -- ^ Handle a left value without a right value. -> (k -> b -> Maybe c) -- ^ Handle a right value without a left value. -> Array (k, a) -- ^ Array of keys and left values. -> Array (k, b) -- ^ Array of keys and right values. -> Array (k, c) -- ^ Array of keys and results. mergeMaybe = G.mergeMaybe A {-# INLINE mergeMaybe #-} -- Splitting -------------------------------------------------------------------------------------- -- | Combination of `fold` and `filter`. -- -- We walk over the stream front to back, maintaining an accumulator. -- At each point we can chose to emit an element (or not) -- compact :: (Elem a, Build b) => (s -> a -> (s, Maybe b)) -> s -> Array a -> Array b compact = G.compact A {-# INLINE compact #-} -- | Like `compact` but use the first value of the stream as the -- initial state, and add the final state to the end of the output. compactIn :: Build a => (a -> a -> (a, Maybe a)) -> Array a -> Array a compactIn = G.compactIn A {-# INLINE compactIn #-} -- | Apply a generic stream process to an array. process :: ( Build a, Build b, Elem b) => (s -> a -> (s, Array b)) -- ^ Worker function -> s -- ^ Initial state. -> Array a -- ^ Input array. -> (s, Array b) -- ^ Result state and array. process = G.process A {-# INLINE process #-} -- Inserting -------------------------------------------------------------------------------------- -- | Insert elements produced by the given function in to an array. insert :: Build a => (Int -> Maybe a) -> Array a -> Array a insert = G.insert A {-# INLINE insert #-} -- Searching -------------------------------------------------------------------------------------- -- | O(len src) Yield `Just` the index of the first element matching the predicate -- or `Nothing` if no such element exists. findIndex :: Elem a => (a -> Bool) -> Array a -> Maybe Int findIndex = G.findIndex {-# INLINE findIndex #-} -- Splitting -------------------------------------------------------------------------------------- -- | O(len src). Given predicates which detect the start and end of a segment, -- split an vector into the indicated segments. segment :: (Elem a, U.Unbox a) => (a -> Bool) -- ^ Detect the start of a segment. -> (a -> Bool) -- ^ Detect the end of a segment. -> Array a -- ^ Array to segment. -> Array (Array a) segment pStart pEnd elems = A.AArray_Array $! N.segment pStart pEnd elems {-# INLINE segment #-} -- | O(len src). Given a terminating value, split an vector into segments. -- -- The result segments do not include the terminator. segmentOn :: (Elem a, U.Unbox a) => (a -> Bool) -- ^ Detect the end of a segment. -> Array a -- ^ Array to segment. -> Array (Array a) segmentOn pEnd arr = A.AArray_Array $! N.segmentOn pEnd arr {-# INLINE segmentOn #-} -- | O(len src). Like `segment`, but cut the source array twice. dice :: (Elem a, U.Unbox a) => (a -> Bool) -- ^ Detect the start of an inner segment. -> (a -> Bool) -- ^ Detect the end of an inner segment. -> (a -> Bool) -- ^ Detect the start of an outer segment. -> (a -> Bool) -- ^ Detect the end of an outer segment. -> Array a -- ^ Array to dice. -> Array (Array (Array a)) dice pStart1 pEnd1 pStart2 pEnd2 arr = let N.NArray starts1 elems1 (N.NArray starts2 elems2 arr') = N.dice pStart1 pEnd1 pStart2 pEnd2 arr in A.AArray_Array $! N.NArray starts1 elems1 $! A.AArray_Array $! N.NArray starts2 elems2 arr' {-# INLINE dice #-} -- | O(len src). Given field and row terminating values, -- split an array into rows and fields. -- diceSep :: (Elem a, Eq a) => a -- ^ Terminating element for inner segments. -> a -- ^ Terminating element for outer segments. -> Array a -- ^ Vector to dice. -> Array (Array (Array a)) diceSep xEndCol xEndRow arr = let N.NArray starts1 elems1 (N.NArray starts2 elems2 arr') = N.diceSep xEndCol xEndRow arr in A.AArray_Array $! N.NArray starts1 elems1 $! A.AArray_Array $! N.NArray starts2 elems2 arr' {-# INLINE diceSep #-} -- Grouping --------------------------------------------------------------------------------------- -- | From a stream of values which has consecutive runs of idential values, -- produce a stream of the lengths of these runs. groups :: (Eq a, Build a) => Array a -- ^ Input elements. -> (Array (a, Int), Maybe (a, Int)) -- ^ Completed and final segment lengths. groups arr = let (arr', result) = G.groups A A arr in (A.AArray_T2 arr', result) {-# INLINE groups #-} -- | Like `groups`, but use the given function to determine whether two -- consecutive elements should be in the same group. -- Also take an initial starting group and count. groupsWith :: Build a => (a -> a -> Bool) -- ^ Comparison function. -> Maybe (a, Int) -- ^ Starting element and count. -> Array a -- ^ Input elements. -> (Array (a, Int), Maybe (a, Int)) -- ^ Completed and final segment lengths. groupsWith f start arr = let (arr', result) = G.groupsWith A A f start arr in (A.AArray_T2 arr', result) {-# INLINE groupsWith #-}