    -----------------------------------------------------------------------------
    -- |
    -- Module      : Data.Array.Parallel.Unlifted.Sequential.Flat.Sums
    -- Copyright   : (c) [2001..2002] Manuel M T Chakravarty & Gabriele Keller
    --		 (c) 2006         Manuel M T Chakravarty & Roman Leshchinskiy
    -- License     : see libraries/rl/LICENSE
    --
    -- Maintainer  : Roman Leshchinskiy <rl@cse.unsw.edu.au>
    -- Stability   : internal
    -- Portability : portable
    --
    -- Description ---------------------------------------------------------------
    --
    --  Various sum-like combinators for flat unlifted arrays.
    --
    -- Todo ----------------------------------------------------------------------
    --

    module Data.Array.Parallel.Unlifted.Sequential.Flat.Sums (
      andU, orU, anyU, allU,
      elemU, notElemU,
      sumU, productU,
      maximumU, minimumU, maximumByU, minimumByU,
      maximumIndexU, minimumIndexU,
      maximumIndexByU, minimumIndexByU,

      -- FIXME
      lengthU'
    ) where

    import Data.Array.Parallel.Base (
      (:*:)(..), fstS)
    import Data.Array.Parallel.Unlifted.Sequential.Flat.UArr (
      UA, UArr)
    import Data.Array.Parallel.Unlifted.Sequential.Flat.Basics (
      indexedU)
    import Data.Array.Parallel.Unlifted.Sequential.Flat.Combinators (
      mapU, foldU, fold1U, foldlU)

    infix  4 `elemU`, `notElemU`

    -- |
    andU :: UArr Bool -> Bool
    {-# INLINE andU #-}
    andU = foldU (&&) True

    -- |
    orU :: UArr Bool -> Bool
    {-# INLINE orU #-}
    orU = foldU (||) False

    -- |
    allU :: UA e => (e -> Bool) -> UArr e -> Bool
    {-# INLINE allU #-}
    allU p = andU . mapU p

    -- |
    anyU :: UA e => (e -> Bool) -> UArr e -> Bool
    {-# INLINE anyU #-}
    anyU p =  orU . mapU p

    -- |Compute the sum of an array of numerals
    --
    sumU :: (Num e, UA e) => UArr e -> e
    {-# INLINE sumU #-}
    sumU = foldU (+) 0

    -- |Compute the product of an array of numerals
    --
    productU :: (Num e, UA e) => UArr e -> e
    {-# INLINE productU #-}
    productU = foldU (*) 1

    -- |Determine the maximum element in an array
    --
    maximumU :: (Ord e, UA e) => UArr e -> e
    {-# INLINE maximumU #-}
    maximumU = fold1U max

    -- |Determine the maximum element in an array under the given ordering
    --
    maximumByU :: UA e => (e -> e -> Ordering) -> UArr e -> e
    {-# INLINE maximumByU #-}
    maximumByU = fold1U . maxBy
      where
        maxBy compare x y = case x `compare` y of
                              LT -> y
                              _  -> x

    -- |Determine the index of the maximum element in an array
    --
    maximumIndexU :: (Ord e, UA e) => UArr e -> Int
    {-# INLINE maximumIndexU #-}
    maximumIndexU = maximumIndexByU compare

    -- |Determine the index of the maximum element in an array under the given
    -- ordering
    --
    maximumIndexByU :: UA e => (e -> e -> Ordering) -> UArr e -> Int
    {-# INLINE maximumIndexByU #-}
    maximumIndexByU cmp = fstS . maximumByU cmp' . indexedU
      where
        cmp' (_ :*: x) (_ :*: y) = cmp x y

    -- |Determine the minimum element in an array
    --
    minimumU :: (Ord e, UA e) => UArr e -> e
    {-# INLINE minimumU #-}
    minimumU = fold1U min

    -- |Determine the minimum element in an array under the given ordering
    --
    minimumByU :: UA e => (e -> e -> Ordering) -> UArr e -> e
    {-# INLINE minimumByU #-}
    minimumByU = fold1U . minBy
      where
        minBy compare x y = case x `compare` y of
                              GT -> y
                              _  -> x

    -- |Determine the index of the minimum element in an array
    --
    minimumIndexU :: (Ord e, UA e) => UArr e -> Int
    {-# INLINE minimumIndexU #-}
    minimumIndexU = minimumIndexByU compare

    -- |Determine the index of the minimum element in an array under the given
    -- ordering
    --
    minimumIndexByU :: UA e => (e -> e -> Ordering) -> UArr e -> Int
    {-# INLINE minimumIndexByU #-}
    minimumIndexByU cmp = fstS . minimumByU cmp' . indexedU
      where
        cmp' (_ :*: x) (_ :*: y) = cmp x y

    -- |Determine whether the given element is in an array
    --
    elemU :: (Eq e, UA e) => e -> UArr e -> Bool
    elemU e = anyU (== e)

    -- |Negation of `elemU'
    --
    notElemU :: (Eq e, UA e) => e -> UArr e -> Bool
    notElemU e = allU (/= e)

    -- |FIXME: A fuseable version of 'lengthU', should go away
    --
    lengthU' :: UA e => UArr e -> Int
    {-# INLINE lengthU' #-}
    lengthU' = foldlU (const . (+1)) 0