-- | Vectorise variables and literals.
module Vectorise.Var (
	vectBndr,
	vectBndrNew,
	vectBndrIn,
	vectBndrNewIn,
	vectBndrsIn,
	vectVar,
	vectPolyVar,
	vectLiteral
) where
import Vectorise.Utils
import Vectorise.Monad
import Vectorise.Env
import Vectorise.Vect
import Vectorise.Type.Type
import CoreSyn
import Type
import Var
import VarEnv
import Literal
import Id
import FastString
import Control.Monad


-- Binders ----------------------------------------------------------------------------------------
-- | Vectorise a binder variable, along with its attached type.
vectBndr :: Var -> VM VVar
vectBndr v
 = do (vty, lty) <- vectAndLiftType (idType v)
      let vv = v `Id.setIdType` vty
          lv = v `Id.setIdType` lty

      updLEnv (mapTo vv lv)

      return  (vv, lv)
  where
    mapTo vv lv env = env { local_vars = extendVarEnv (local_vars env) v (vv, lv) }


-- | Vectorise a binder variable, along with its attached type, 
--   but give the result a new name.
vectBndrNew :: Var -> FastString -> VM VVar
vectBndrNew v fs
 = do vty <- vectType (idType v)
      vv  <- newLocalVVar fs vty
      updLEnv (upd vv)
      return vv
  where
    upd vv env = env { local_vars = extendVarEnv (local_vars env) v vv }


-- | Vectorise a binder then run a computation with that binder in scope.
vectBndrIn :: Var -> VM a -> VM (VVar, a)
vectBndrIn v p
 = localV
 $ do vv <- vectBndr v
      x <- p
      return (vv, x)


-- | Vectorise a binder, give it a new name, then run a computation with that binder in scope.
vectBndrNewIn :: Var -> FastString -> VM a -> VM (VVar, a)
vectBndrNewIn v fs p
 = localV
 $ do vv <- vectBndrNew v fs
      x  <- p
      return (vv, x)


-- | Vectorise some binders, then run a computation with them in scope.
vectBndrsIn :: [Var] -> VM a -> VM ([VVar], a)
vectBndrsIn vs p
 = localV
 $ do vvs <- mapM vectBndr vs
      x	  <- p
      return (vvs, x)


-- Variables --------------------------------------------------------------------------------------
-- | Vectorise a variable, producing the vectorised and lifted versions.
vectVar :: Var -> VM VExpr
vectVar v
 = do 
      -- lookup the variable from the environment.
      r	<- lookupVar v

      case r of
        -- If it's been locally bound then we'll already have both versions available.
        Local (vv,lv) 
         -> return (Var vv, Var lv)

        -- To create the lifted version of a global variable we replicate it
	-- using the integer context in the VM state for the number of elements.
        Global vv     
         -> do let vexpr = Var vv
               lexpr <- liftPD vexpr
               return (vexpr, lexpr)


-- | Like `vectVar` but also add type applications to the variables.
vectPolyVar :: Var -> [Type] -> VM VExpr
vectPolyVar v tys
 = do vtys	<- mapM vectType tys
      r		<- lookupVar v
      case r of
        Local (vv, lv) 
         -> liftM2 (,) (polyApply (Var vv) vtys)
                       (polyApply (Var lv) vtys)

        Global poly    
         -> do vexpr <- polyApply (Var poly) vtys
               lexpr <- liftPD vexpr
               return (vexpr, lexpr)


-- Literals ---------------------------------------------------------------------------------------
-- | Lifted literals are created by replicating them
--   We use the the integer context in the `VM` state for the number
--   of elements in the output array.
vectLiteral :: Literal -> VM VExpr
vectLiteral lit
 = do lexpr <- liftPD (Lit lit)
      return (Lit lit, lexpr)