{-# OPTIONS_HADDOCK hide #-}
{-# LANGUAGE ExplicitForAll, TypeOperators #-}

module Data.Array.Repa.Operators.Reduction
	( fold, foldAll
	, sum,  sumAll)
where
import Data.Array.Repa.Index
import Data.Array.Repa.Internals.Elt
import Data.Array.Repa.Internals.Base
import Data.Array.Repa.Shape		as S
import qualified Data.Vector.Unboxed	as V
import Prelude				hiding (sum)


-- | Sequentially fold the innermost dimension of an array.
--	Combine this with `transpose` to fold any other dimension.
fold 	:: (Shape sh, Elt a)
	=> (a -> a -> a)
	-> a 
	-> Array (sh :. Int) a
	-> Array sh a

{-# INLINE fold #-}
fold f x arr
 = x `seq` arr `deepSeqArray` 
   let	sh' :. n	= extent arr
	elemFn i 	= V.foldl' f x
			$ V.map	(\ix -> arr ! (i :. ix)) 
				(V.enumFromTo 0 (n - 1))
   in	fromFunction sh' elemFn


-- | Sequentially fold all the elements of an array.
foldAll :: (Shape sh, Elt a)
	=> (a -> a -> a)
	-> a
	-> Array sh a
	-> a
	
{-# INLINE foldAll #-}
foldAll f x arr
	= V.foldl' f x
	$ V.map ((arr !) . (S.fromIndex (extent arr)))
	$ V.enumFromTo
		0
		((S.size $ extent arr) - 1)



-- | Sum the innermost dimension of an array.
sum	:: (Shape sh, Elt a, Num a)
	=> Array (sh :. Int) a
	-> Array sh a

{-# INLINE sum #-}
sum arr	= fold (+) 0 arr


-- | Sum all the elements of an array.
sumAll	:: (Shape sh, Elt a, Num a)
	=> Array sh a
	-> a

{-# INLINE sumAll #-}
sumAll arr
	= V.foldl' (+) 0
	$ V.map ((arr !) . (S.fromIndex (extent arr)))
	$ V.enumFromTo
		0
		((S.size $ extent arr) - 1)