ddc-core-flow-0.3.2.1: DDC/Core/Flow/Transform/Schedule/SeriesEnv.hs
module DDC.Core.Flow.Transform.Schedule.SeriesEnv
( SeriesEnv (..)
, emptySeriesEnv
, insertElemForSeries
, bindNextElem
, bindNextElems
, elemBindOfSeriesBind
, elemBoundOfSeriesBound
, elemTypeOfSeriesType
, rateTypeOfSeriesType )
where
import DDC.Core.Flow.Transform.Schedule.Nest
import DDC.Core.Flow.Procedure
import DDC.Core.Flow.Compounds
import DDC.Core.Flow.Prim
import DDC.Core.Flow.Exp
import qualified Data.Map as Map
import Data.Map (Map)
data SeriesEnv
= SeriesEnv
{ -- | Maps the bound for a whole series to the bound for
-- a single element in the series context.
envSeriesElems :: Map Name (Bound Name)
}
-- | An empty series environment.
emptySeriesEnv :: SeriesEnv
emptySeriesEnv
= SeriesEnv Map.empty
-- | Insert an entry into the series environment.
insertElemForSeries
:: Name -> BoundF -> SeriesEnv -> SeriesEnv
insertElemForSeries n u (SeriesEnv env)
= SeriesEnv (Map.insert n u env)
-- | Produce the `Bound` that holds the next element for the given series,
-- which exists in the series's context.
--
-- We first try to look up the required bound from the series environment,
-- if it's not already available then insert a statement into the loop nest
-- to get actually get the next element from the series.
bindNextElem
:: Name -- ^ Name of series.
-> TypeF -- ^ Rate of series
-> TypeF -- ^ Series element type.
-> SeriesEnv -- ^ Current series environment.
-> Nest -- ^ Current loop nest.
-> (BoundF, SeriesEnv, Nest)
bindNextElem nSeries tRate tElem env nest0
-- There is already a mapping in the environment.
| Just uElem <- Map.lookup nSeries (envSeriesElems env)
= (uElem, env, nest0)
-- Insert a statement into the loop nest to get the next element
-- from the series.
| otherwise
= let -- bound for the single element
nElem = NameVarMod nSeries "elem"
uElem = UName nElem
-- Expression to get the next element from the series.
uSeries = UName nSeries
uIndex = UIx 0
xGet = xNext tRate tElem (XVar uSeries) (XVar uIndex)
-- Insert the statement into the loop nest.
Just nest1
= (insertBody nest0 (ContextRate tRate)
[ BodyStmt (BName nElem tElem) xGet ])
env' = env { envSeriesElems
= Map.insert nSeries uElem
(envSeriesElems env) }
in (uElem, env', nest1)
-- | Like `bindNextElem`, but handle several series at once.
bindNextElems
:: [(Name, TypeF, TypeF)]
-- ^ Names, rates, and element types.
-> SeriesEnv -- ^ Current series environment.
-> Nest -- ^ Current loop nest.
-> ([BoundF], SeriesEnv, Nest)
bindNextElems junk env nest0
= case junk of
[]
-> ([], env, nest0)
(nSeries, tRate, tElem) : junk'
-> let (uElem1, env1, nest1)
= bindNextElem nSeries tRate tElem env nest0
(uElems', env', nest')
= bindNextElems junk' env1 nest1
in (uElem1 : uElems', env', nest')
-- | Given the bind of a series, produce the bound that refers to the
-- next element of the series in its context.
elemBindOfSeriesBind :: BindF -> Maybe BindF
elemBindOfSeriesBind bSeries
| BName nSeries tSeries' <- bSeries
, nElem <- NameVarMod nSeries "elem"
, Just tElem <- elemTypeOfSeriesType tSeries'
= Just $ BName nElem tElem
| otherwise
= Nothing
-- | Given the bound of a series, produce the bound that refers to the
-- next element of the series in its context.
elemBoundOfSeriesBound :: BoundF -> Maybe BoundF
elemBoundOfSeriesBound uSeries
| UName nSeries <- uSeries
, nElem <- NameVarMod nSeries "elem"
= Just $ UName nElem
| otherwise
= Nothing
-- | Given the type of a series like @Series k e@, produce the type
-- of a single element, namely the @e@.
elemTypeOfSeriesType :: TypeF -> Maybe TypeF
elemTypeOfSeriesType tSeries'
| Just (_tcSeries, [_tK, tE]) <- takeTyConApps tSeries'
= Just tE
| otherwise
= Nothing
-- | Given the type of a series like @Series k e@, produce the type
-- of the rate, namely the @k@.
rateTypeOfSeriesType :: TypeF -> Maybe TypeF
rateTypeOfSeriesType tSeries'
| Just (_tcSeries, [tK, _tE]) <- takeTyConApps tSeries'
= Just tK
| otherwise
= Nothing