    {-# LANGUAGE TupleSections #-}

    -- |Vectorise variables and literals.

    module Vectorise.Var
      ( vectBndr
      , vectBndrNew
      , vectBndrIn
      , vectBndrNewIn
      , vectBndrsIn
      , vectVar
      , vectConst
      )
    where

    import Vectorise.Utils
    import Vectorise.Monad
    import Vectorise.Env
    import Vectorise.Vect
    import Vectorise.Type.Type
    import CoreSyn
    import Type
    import VarEnv
    import Id
    import FastString
    import Control.Applicative
    import Prelude -- avoid redundant import warning due to AMP

    -- 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 var
      = do { vVar <- lookupVar var
           ; case vVar of
               Local (vv, lv) -> return (Var vv, Var lv) -- local variables have a vect & lifted version
               Global vv      -> vectConst (Var vv)      -- global variables get replicated
           }

    -- Constants --------------------------------------------------------------------------------------

    -- |Constants are lifted by replication along the integer context in the `VM` state for the number
    -- of elements in the result array.
    --
    vectConst :: CoreExpr -> VM VExpr
    vectConst c = (c,) <$> liftPD c