{-# LANGUAGE Rank2Types, TypeOperators #-}

-- ---------------------------------------------------------------------------
-- |
-- Module      : Data.Vector.Algorithms.Combinators
-- Copyright   : (c) 2008-2010 Dan Doel
-- Maintainer  : Dan Doel <dan.doel@gmail.com>
-- Stability   : Experimental
-- Portability : Non-portable (rank-2 types)
--
-- The purpose of this module is to supply various combinators for commonly
-- used idioms for the algorithms in this package. Examples at the time of
-- this writing include running an algorithm keyed on some function of the
-- elements (but only computing said function once per element), and safely
-- applying the algorithms on mutable arrays to immutable arrays.

module Data.Vector.Algorithms.Combinators
       ( apply
--       , usingKeys
--       , usingIxKeys
       ) where

import Prelude hiding (length)

import Control.Monad.ST

import Data.Ord

import Data.Vector.Generic

import qualified Data.Vector.Generic.Mutable as M
import qualified Data.Vector.Generic.New     as N

-- | Safely applies a mutable array algorithm to an immutable array.
apply :: (Vector v e) => (forall s mv. M.MVector mv e => mv s e -> ST s ()) -> v e -> v e
apply algo v = new . N.New $
                 M.unstream (stream v) >>= \arr -> algo arr >> return arr

{-
-- | Uses a function to compute a key for each element which the
-- algorithm should use in lieu of the actual element. For instance:
--
-- > usingKeys sortBy f arr
--
-- should produce the same results as:
--
-- > sortBy (comparing f) arr
--
-- the difference being that usingKeys computes each key only once
-- which can be more efficient for expensive key functions.
usingKeys :: (UA e, UA k, Ord k)
          => (forall e'. (UA e') => Comparison e' -> MUArr e' s -> ST s ())
          -> (e -> k)
          -> MUArr e s
          -> ST s ()
usingKeys algo f arr = usingIxKeys algo (const f) arr
{-# INLINE usingKeys #-}

-- | As usingKeys, only the key function has access to the array index
-- at which each element is stored.
usingIxKeys :: (UA e, UA k, Ord k)
            => (forall e'. (UA e') => Comparison e' -> MUArr e' s -> ST s ())
            -> (Int -> e -> k)
            -> MUArr e s
            -> ST s ()
usingIxKeys algo f arr = do
  keys <- newMU (lengthMU arr)
  fill len keys
  algo (comparing fstS) (unsafeZipMU keys arr)
 where
 len = lengthMU arr
 fill k keys
   | k < 0     = return ()
   | otherwise = readMU arr k >>= writeMU keys k . f k >> fill (k-1) keys
{-# INLINE usingIxKeys #-}
-}