packages feed

cubical-0.2.0: Eval.hs

{-# LANGUAGE TupleSections, GeneralizedNewtypeDeriving #-}
module Eval ( eval
            , evals
            , app
            , conv
            , fstSVal
            , Eval
            , runEval
            ) where

import Control.Applicative
import Control.Arrow (second)
import Control.Monad
import Control.Monad.Reader
import Data.Functor.Identity
import Data.List
import Data.Maybe (fromMaybe)

import CTT

trace :: String -> Eval ()
trace s = do
  debug <- ask
  when debug $ liftIO (putStrLn s)

-- For now only store the debugging boolean
type EState = Bool

newtype Eval a = Eval { unEval :: ReaderT Bool IO a }
  deriving (Functor, Applicative, Monad, MonadIO, MonadReader Bool)

runEval :: Bool -> Eval a -> IO a
runEval debug e = runReaderT (unEval e) debug

look :: Ident -> OEnv -> Eval (Binder, Val)
look x (OEnv (Pair rho (n@(y,l),u)) opaques)
  | x == y    = return (n, u)
  | otherwise = look x (OEnv rho opaques)
look x r@(OEnv (PDef es r1) o) = case lookupIdent x es of
  Just (y,t)  -> (y,) <$> eval r t
  Nothing     -> look x (OEnv r1 o)

eval :: OEnv -> Ter -> Eval Val
eval e U                 = return VU
eval e (PN pn)           = evalAppPN e pn []
eval e t@(App r s)       = case unApps t of
  (PN pn,us) -> evalAppPN e pn us
  _          -> appM (eval e r) (eval e s)
eval e (Var i)           = do
  (x,v) <- look i e
  return $ if x `elem` opaques e then VVar ("opaque_" ++ show x) $ support v else v
eval e (Pi a b)          = VPi <$> eval e a <*> eval e b
eval e (Lam x t)         = return $ Ter (Lam x t) e -- stop at lambdas
eval e (Sigma a b)       = VSigma <$> eval e a <*> eval e b
eval e (SPair a b)       = VSPair <$> eval e a <*> eval e b
eval e (Fst a)           = fstSVal <$> eval e a
eval e (Snd a)           = sndSVal <$> eval e a
eval e (Where t decls)   = eval (oPDef False decls e) t
eval e (Con name ts)     = VCon name <$> mapM (eval e) ts
eval e (Split pr alts)   = return $ Ter (Split pr alts) e
eval e (Sum pr ntss)     = return $ Ter (Sum pr ntss) e

evals :: OEnv -> [(Binder,Ter)] -> Eval [(Binder,Val)]
evals env = sequenceSnd . map (second (eval env))

fstSVal, sndSVal :: Val -> Val
fstSVal (VSPair a b)    = a
fstSVal u | isNeutral u = VFst u
          | otherwise   = error $ show u ++ " should be neutral"
sndSVal (VSPair a b)    = b
sndSVal u | isNeutral u = VSnd u
          | otherwise   = error $ show u ++ " should be neutral"

-- Application
app :: Val -> Val -> Eval Val
app (Ter (Lam x t) e) u                         = eval (oPair e (x,u)) t
app (Kan Com (VPi a b) box@(Box dir x v nvs)) u = do
  trace "Pi Com"
  ufill <- fill a (Box (mirror dir) x u [])
  bcu   <- cubeToBox ufill (shapeOfBox box)
  comM (app b ufill) (appBox box bcu)
app kf@(Kan Fill (VPi a b) box@(Box dir i w nws)) v = do
  trace "Pi fill"
  let x = fresh (kf, v)
  u     <- v `face` (i,dir)
  ufill <- fill a (Box (mirror dir) i u [])
  bcu   <- cubeToBox ufill (shapeOfBox box)
  vfill <- fill a (Box (mirror dir) i u [((x,down),ufill),((x,up),v)])
  vx    <- fillM (app b ufill) (appBox box bcu)
  vi0   <- appM (return w) (vfill `face` (i,mirror dir))
  vi1   <- comM (app b ufill) (appBox box bcu)
  nvs   <- sequenceSnd [ ((n,d),appM (return ws) (vfill `face` (n,d)))
                       | ((n,d),ws) <- nws ]
  comM (app b vfill) (return (Box up x vx (((i,mirror dir),vi0) : ((i,dir),vi1):nvs)))
-- app vext@(VExt x bv fv gv pv) w = do
--   -- NB: there are various choices how to construct this
--   let y = fresh (vext, w)
--   w0    <- w `face` (x,down)
--   left  <- app fv w0
--   right <- app gv (swap w x y)
--   pvxw  <- appNameM (app pv w0) x
--   comM (app bv w) (return (Box up y pvxw [((x,down),left),((x,up),right)]))
app vhext@(VHExt x bv fv gv pv) w = do
  a0    <- w `face` (x,down)
  a1    <- w `face` (x,up)
  appNameM (apps pv [a0, a1, Path x w]) x
app (Ter (Split _ nvs) e) (VCon name us) = case lookup name nvs of
    Just (xs,t)  -> eval (upds e (zip xs us)) t
    Nothing -> error $ "app: Split with insufficient arguments; " ++
                        "missing case for " ++ name
app u@(Ter (Split _ _) _) v
  | isNeutral v = return $ VSplit u v -- v should be neutral
  | otherwise   = error $ "app: (VSplit) " ++ show v ++ " is not neutral"
app r s
  | isNeutral r = return $ VApp r s -- r should be neutral
  | otherwise   = error $ "app: (VApp) " ++ show r ++ " is not neutral"

-- Monadic version of app
appM :: Eval Val -> Eval Val -> Eval Val
appM t1 t2 = do
  u <- t1
  v <- t2
  app u v

apps :: Val -> [Val] -> Eval Val
apps = foldM app

appBox :: Box Val -> Box Val -> Eval (Box Val)
appBox (Box dir x v nvs) (Box _ _ u nus) = do
  let lookup' x = fromMaybe (error "appBox") . lookup x
  sequenceBox $ Box dir x (app v u) [ (nnd,app v (lookup' nnd nus))
                                    | (nnd,v) <- nvs ]

appName :: Val -> Name -> Eval Val
appName (Path x u) y | y `elem` [0,1] = u `face` (x,y)
appName p y          | y `elem` [0,1] = return $ VAppName p y
                                        -- p has to be neutral
appName (Path x u) y | x == y             = return u
                     | y `elem` support u = error ("appName " ++ "\nu = " ++
                                                   show u ++ "\ny = " ++ show y)
                     | otherwise          = return $ swap u x y
appName v y          = return $ VAppName v y

appNameM :: Eval Val -> Name -> Eval Val
appNameM v n = do
  v' <- v
  appName v' n

-- Apply a primitive notion
evalAppPN :: OEnv -> PN -> [Ter] -> Eval Val
evalAppPN e pn ts
  | length ts < arity pn =
      -- Eta expand primitive notions
      let r       = arity pn - length ts
          binders = map (\n -> '_' : show n) [1..r]
          vars    = map Var binders
      in return $ Ter (mkLams binders $ mkApps (PN pn) (ts ++ vars)) e
  | otherwise = do
      let (args,rest) = splitAt (arity pn) ts
      vas <- mapM (eval e) args
      p   <- evalPN (freshs e) pn vas
      r   <- mapM (eval e) rest
      apps p r

-- Evaluate primitive notions
evalPN :: [Name] -> PN -> [Val] -> Eval Val
evalPN (x:_) Id            [a,a0,a1]     = return $ VId (Path x a) a0 a1
evalPN (x:_) IdP           [_,_,p,a0,a1] = return $ VId p a0 a1
evalPN (x:_) Refl          [_,a]         = return $ Path x a
evalPN (x:_) TransU        [_,_,p,t]     =
  comM (appName p x) (return (Box up x t []))
evalPN (x:_) TransInvU     [_,_,p,t]     =
  comM (appName p x) (return (Box down x t []))
evalPN (x:_) TransURef     [a,t]         = Path x <$> fill a (Box up x t [])
evalPN (x:_) TransUEquivEq [_,b,f,_,_,u] = do
  fu <- app f u
  Path x <$> fill b (Box up x fu [])   -- TODO: Check this!
evalPN (x:y:_) CSingl [a,u,v,p] = do
  pv    <- appName p y
  theta <- fill a (Box up y u [((x,down),u),((x,up),pv)])
  omega <- theta `face` (y,up)
  return $ Path x (VSPair omega (Path y theta))
-- evalPN (x:_)   Ext        [_,b,f,g,p]   = return $ Path x $ VExt x b f g p
evalPN (x:_)   HExt       [_,b,f,g,p]   = return $ Path x $ VHExt x b f g p
evalPN _       Inh        [a]           = return $ VInh a
evalPN _       Inc        [_,t]         = return $ VInc t
evalPN (x:_)   Squash     [_,r,s]       = return $ Path x $ VSquash x r s
evalPN _       InhRec     [_,b,p,phi,a] = inhrec b p phi a
evalPN (x:_)   EquivEq    [a,b,f,s,t]   = return $ Path x $ VEquivEq x a b f s t
evalPN (x:y:_) EquivEqRef [a,s,t]       =
  return $ Path y $ Path x $ VEquivSquare x y a s t
evalPN (x:_)   MapOnPath  [_,_,f,_,_,p]    =
  Path x <$> appM (return f) (appName p x)
evalPN (x:_)   MapOnPathD [_,_,f,_,_,p]    =
  Path x <$> appM (return f) (appName p x)
evalPN (x:_)   AppOnPath [_,_,_,_,_,_,p,q] =
  Path x <$> appM (appName p x) (appName q x)
evalPN (x:_)   MapOnPathS [_,_,_,f,_,_,p,_,_,q] =
  Path x <$> appM (appM (pure f) (appName p x)) (appName q x)
evalPN _       Circle     []               = return VCircle
evalPN _       Base       []               = return VBase
evalPN (x:_)   Loop       []               = return $ Path x $ VLoop x
evalPN _       CircleRec  [f,b,l,s]        = circlerec f b l s
evalPN _       I          []               = return VI
evalPN _       I0         []               = return VI0
evalPN _       I1         []               = return VI1
evalPN (x:_)   Line       []               = return $ Path x $ VLine x
evalPN _       IntRec     [f,s,e,l,u]      = intrec f s e l u
evalPN _       u          _                = error ("evalPN " ++ show u)


appS1 :: Val -> Val -> Name -> Eval Val
appS1 f p x | x `elem` [0,1] = appName p x
appS1 f p x = do
  let y = fresh (p,(f,x))
  q <- appName p y
  a <- appName p 0
  b <- appName p 1
  newBox <- Box down y b <$>
            sequenceSnd  [ ((x,down),q `face` (x,down))
                         , ((x,up),b `face` (x,up))]
  fb <- app f VBase
  fl <- app f (VLoop y)
  tu <- fillM (return VU) (Box down y fb <$>
                           sequenceSnd [ ((x,down),fl `face` (x,down))
                                       , ((x,up),fb `face` (x,up))])
  com tu newBox

-- Compute the face of an environment
faceEnv :: OEnv -> Side -> Eval OEnv
faceEnv e xd = mapOEnvM (`face` xd) e

faceName :: Name -> Side -> Name
faceName 0 _                 = 0
faceName 1 _                 = 1
faceName x (y,d) | x == y    = d
                 | otherwise = x

-- Compute the face of a value
face :: Val -> Side -> Eval Val
face u xdir@(x,dir) =
  let fc v = v `face` xdir in case u of
  VU          -> return VU
  Ter t e -> do e' <- e `faceEnv` xdir
                eval e' t
  VId a v0 v1 -> VId <$> fc a <*> fc v0 <*> fc v1
  Path y v | x == y    -> return u
           | otherwise -> Path y <$> fc v
  -- VExt y b f g p | x == y && dir == down -> return f
  --                | x == y && dir == up   -> return g
  --                | otherwise             ->
  --                  VExt y <$> fc b <*> fc f <*> fc g <*> fc p
  VHExt y b f g p | x == y && dir == down -> return f
                  | x == y && dir == up   -> return g
                  | otherwise             ->
                    VHExt y <$> fc b <*> fc f <*> fc g <*> fc p
  VPi a f    -> VPi <$> fc a <*> fc f
  VSigma a f -> VSigma <$> fc a <*> fc f
  VSPair a b -> VSPair <$> fc a <*> fc b
  VInh v     -> VInh <$> fc v
  VInc v     -> VInc <$> fc v
  VSquash y v0 v1 | x == y && dir == down -> return v0
                  | x == y && dir == up   -> return v1
                  | otherwise             -> VSquash y <$> fc v0 <*> fc v1
  VCon c us -> VCon c <$> mapM fc us
  VEquivEq y a b f s t | x == y && dir == down -> return a
                       | x == y && dir == up   -> return b
                       | otherwise             ->
                         VEquivEq y <$> fc a <*> fc b <*> fc f <*> fc s <*> fc t
  VPair y a v | x == y && dir == down -> return a
              | x == y && dir == up   -> fc v
              | otherwise             -> VPair y <$> fc a <*> fc v
  VEquivSquare y z a s t | x == y                -> return a
                         | x == z && dir == down -> return a
                         | x == z && dir == up   -> do
                           let idV = Ter (Lam (noLoc "x") (Var "x")) oEmpty
                           return $ VEquivEq y a a idV s t
                         | otherwise             ->
                          VEquivSquare y z <$> fc a <*> fc s <*> fc t
  VSquare y z v | x == y                -> fc v
                | x == z && dir == down -> fc v
                | x == z && dir == up   -> do
                  v' <- fc v
                  VPair y <$> v' `face` (y,down) <*> pure v'
                | otherwise             -> VSquare y z <$> fc v
  Kan Fill a b@(Box dir' y v nvs)
    | x /= y && x `notElem` nonPrincipal b -> fillM (fc a) (mapBoxM fc b)
    | x `elem` nonPrincipal b              -> return $ lookBox (x,dir) b
    | x == y && dir == mirror dir'         -> return v
    | otherwise                            -> com a b
  VFillN a b@(Box dir' y v nvs)
    | x /= y && x `notElem` nonPrincipal b -> fillM (fc a) (mapBoxM fc b)
    | x `elem` nonPrincipal b              -> return $ lookBox (x,dir) b
    | x == y && dir == mirror dir'         -> return v
    | otherwise                            -> com a b
  Kan Com a b@(Box dir' y v nvs)
    | x == y                     -> return u
    | x `notElem` nonPrincipal b -> comM (fc a) (mapBoxM fc b)
    | x `elem` nonPrincipal b    -> lookBox (x,dir) b `face` (y,dir')
  VComN a b@(Box dir' y v nvs)
    | x == y                     -> return u
    | x `notElem` nonPrincipal b -> comM (fc a) (mapBoxM fc b)
    | x `elem` nonPrincipal b    -> lookBox (x,dir) b `face` (y,dir')
  VComp b@(Box dir' y _ _)
    | x == y                     -> return u
    | x `notElem` nonPrincipal b -> VComp <$> mapBoxM fc b
    | x `elem` nonPrincipal b    -> lookBox (x,dir) b `face` (y,dir')
  VFill z b@(Box dir' y v nvs)
    | x == z                               -> return u
    | x /= y && x `notElem` nonPrincipal b -> VFill z <$> mapBoxM fc b
    | (x,dir) `elem` defBox b              ->
      lookBox (x,dir) <$> mapBoxM (`face` (z,down)) b
    | x == y && dir == dir'                ->
        VComp <$> mapBoxM (`face` (z,up)) b
  VInhRec b p h a     -> join $ inhrec <$> fc b <*> fc p <*> fc h <*> fc a
  VApp u v            -> appM (fc u) (fc v)
  VAppName u n        -> do
   trace ("face " ++ "\nxdir " ++ show xdir ++
          "\nu " ++ show u ++ "\nn " ++ show n)
   appNameM (fc u) (faceName n xdir)
  VSplit u v          -> appM (fc u) (fc v)
  VVar s d            -> return $ VVar s [ faceName n xdir | n <- d ]
  VFst p              -> fstSVal <$> fc p
  VSnd p              -> sndSVal <$> fc p
  VCircle             -> return VCircle
  VBase               -> return VBase
  VLoop y | x == y    -> return VBase
          | otherwise -> return $ VLoop y
  VCircleRec f b l s  -> join $ circlerec <$> fc f <*> fc b <*> fc l <*> fc s
  VI  -> return VI
  VI0 -> return VI0
  VI1 -> return VI1
  VLine y
    | x == y && dir == down -> return VI0
    | x == y && dir == up   -> return VI1
    | otherwise             -> return $ VLine y
  VIntRec f s e l u -> join $ intrec <$> fc f <*> fc s <*> fc e <*> fc l <*> fc u

faceM :: Eval Val -> Side -> Eval Val
faceM t xdir = do
  v <- t
  v `face` xdir

unCompAs :: Val -> Name -> Box Val
unCompAs (VComp box) y = swap box (pname box) y
unCompAs v           _ = error $ "unCompAs: " ++ show v ++ " is not a VComp"

unFillAs :: Val -> Name -> Box Val
unFillAs (VFill x box) y = swap box x y
unFillAs v             _ = error $ "unFillAs: " ++ show v ++ " is not a VFill"

-- p(x) = <z>q(x,z)
-- a(x) = q(x,0)     b(x) = q(x,1)
-- q(0,y) connects a(0) and b(0)
-- we connect q(0,0) to q(1,1)
-- appDiag :: Val -> Val -> Name -> Val
-- appDiag tu p x | x `elem` [0,1] = appName p x
-- appDiag tu p x =
-- traceb ("appDiag " ++ "\ntu = " ++ show tu ++ "\np = " ++ show p ++ "\nx = "
-- --                       ++ show x ++ " " ++ show y
-- --                       ++ "\nq = " ++ show q) -- "\nnewBox =" ++ show newBox)
--  com tu newBox
--    where y = fresh (p,(tu,x))
--          q = appName p y
--          a = appName p 0
--          b = appName p 1
--          newBox = Box down y b [((x,down),q `face` (x,down)),((x,up),b `face` (x,up))]

cubeToBox :: Val -> Box () -> Eval (Box Val)
cubeToBox v = modBoxM (\nd _ -> v `face` nd)

inhrec :: Val -> Val -> Val -> Val -> Eval Val
inhrec _ _ phi (VInc a)          = app phi a
inhrec b p phi (VSquash x a0 a1) = do
  let fc w d = w `face` (x,d)
  b0 <- join $ inhrec <$> fc b down <*> fc p down <*> fc phi down <*> pure a0
  b1 <- join $ inhrec <$> fc b up   <*> fc p up   <*> fc phi up   <*> pure a1
  let z = fresh [b,p,phi,b0,b1]
  b0fill   <- fill b (Box up x b0 [])
  b0fillx1 <- b0fill `face` (x, up)
  right    <- appNameM (appM (appM (fc p up) (return b0fillx1)) (return b1)) z
  com b (Box up z b0fill [((x,down),b0),((x,up),right)])
inhrec b p phi (Kan ktype (VInh a) box) = do
  let irec (j,dir) v = let fc v = v `face` (j,dir)
                       in join $ inhrec <$> fc b <*> fc p <*> fc phi <*> pure v
  box' <- modBoxM irec box
  kan ktype b box'
inhrec b p phi v = return $ VInhRec b p phi v -- v should be neutral

circlerec :: Val -> Val -> Val -> Val -> Eval Val
circlerec _ b _ VBase       = return b
circlerec f b l v@(VLoop x) = do
  let y = fresh [f,b,l,v]
  pxy   <- appName l y
  theta <- connection VCircle x y v
  a     <- app f theta
  px1   <- pxy `face` (y,up)
  p11   <- px1 `face` (x,up)
  p0y   <- pxy `face` (x,down)
  trace ("circlerec " ++ "\nf = " ++ show f ++ "\nl = " ++
         show l ++ "\nx = " ++ show x)
  com a (Box down y px1 [((x,down),p0y),((x,up),p11)])
circlerec f b l v@(Kan ktype VCircle box) = do
  let crec side u = let fc w = w `face` side
                    in join $ circlerec <$> fc f <*> fc b <*> fc l <*> pure u
  fv   <- app f v
  box' <- modBoxM crec box
  kan ktype fv box'
circlerec f b l v = return $ VCircleRec f b l v -- v should be neutral

-- Assumes y is fresh and x fresh for a; constructs a connection
-- square with faces u (x), u (y), u (1), u (1).
connection :: Val -> Name -> Name -> Val -> Eval Val
connection a x y u = do
  u1    <- u `face` (x,up)
  ufill <- fill a (Box down y u1 [((x,down), swap u x y), ((x,up),u1)])
  let z       = fresh ([x,y], [a,u])
      ufillzy = swap ufill x z
      ufillzx = swap ufillzy y x
  com a (Box down z u1 [ ((x,down),ufillzy), ((x,up),u1)
                       , ((y,down),ufillzx), ((y,up),u1)])

intrec :: Val -> Val -> Val -> Val -> Val -> Eval Val
intrec _ s _ _ VI0         = return s
intrec _ _ e _ VI1         = return e
intrec f s e l v@(VLine x) = do
  let y = fresh [f,s,e,l,v]
  pxy   <- appName l y
  theta <- connection VI x y v
  a     <- app f theta
  px1   <- pxy `face` (y,up)
  p11   <- px1 `face` (x,up)
  p0y   <- pxy `face` (x,down)
  com a (Box down y px1 [((x,down),p0y),((x,up),p11)])
intrec f s e l v@(Kan ktype VCircle box) = do
  let irec side u = let fc w = w `face` side
                    in join $ intrec <$> fc f <*> fc s <*>
                                         fc e <*> fc l <*> pure u
  fv   <- app f v
  box' <- modBoxM irec box
  kan ktype fv box'
intrec f s e l v = return $ VIntRec f s e l v -- v should be neutral

kan :: KanType -> Val -> Box Val -> Eval Val
kan Fill = fill
kan Com  = com

isNeutralFill :: Val -> Box Val -> Eval Bool
isNeutralFill v box | isNeutral v               = return True
isNeutralFill v@(Ter (PN (Undef _)) _) box      = return True
isNeutralFill (Ter (Sum _ _) _) (Box _ _ v nvs) =
 return $ isNeutral v || or [ isNeutral u | (_,u) <- nvs ]
isNeutralFill v@(Kan Com VU tbox') box@(Box d x _ _) = do
  let nK  = nonPrincipal tbox'
      nJ  = nonPrincipal box
      nL  = nJ \\ nK
      aDs = if x `elem` nK then allDirs nL else (x,mirror d):allDirs nL
  return $ or [ isNeutral (lookBox yc box) | yc <- aDs ]
isNeutralFill v@(Kan Fill VU tbox) box =
  return $ or [ isNeutral (lookBox yc box) | yc <- defBox box \\ defBox tbox ]
isNeutralFill v@(VEquivSquare y z _ _ _) box@(Box d x _ _) = do
  let nJ  = nonPrincipal box
      nL  = nJ \\ [y,z]
      aDs = if x `elem` [y,z] then allDirs nL else (x,mirror d) : allDirs nL
  return $ or [ isNeutral (lookBox yc box) | yc <- aDs ]
isNeutralFill v@(VEquivEq z a b f s t) box@(Box d x vx nxs)
  | d == down && z == x = isNeutral <$> app s vx
  | otherwise           = do -- TODO: check
    let nJ  = nonPrincipal box
        nL  = nJ \\ [z]
        aDs = if x == z then allDirs nL else (x,mirror d) : allDirs nL
    return $ or [ isNeutral (lookBox yc box) | yc <- aDs ]
isNeutralFill v box = return False

-- Monadic version of fill
fillM :: Eval Val -> Eval (Box Val) -> Eval Val
fillM v b = do
  v' <- v
  b' <- b
  fill v' b'

fills :: [(Binder,Ter)] -> OEnv -> [Box Val] -> Eval [Val]
fills []         _ []          = return []
fills ((x,a):as) e (box:boxes) = do
  v  <- fillM (eval e a) (return box)
  vs <- fills as (oPair e (x,v)) boxes
  return $ v : vs
fills _ _ _ = error "fills: different lengths of types and values"

unPack :: Name -> Name -> (Name,Dir) -> Val -> Val
unPack x y (z,c) v | z /= x && z /= y  = unSquare v
                   | z == y && c == up = sndVal v
                   | otherwise         = v

-- Kan filling
fill :: Val -> Box Val -> Eval Val
fill v box = do
  b <- isNeutralFill v box
  if b then return $ VFillN v box else fill' v box
fill' vid@(VId a v0 v1) box@(Box dir i v nvs) = do
  let x = fresh (vid, box)
  box' <- consBox (x,(v0,v1)) <$> mapBoxM (`appName` x) box
  Path x <$> fillM (a `appName` x) (return box')
fill' (VSigma a f) box@(Box dir x v nvs) = do
  u <- fill a (mapBox fstSVal box)
  VSPair u <$> fillM (app f u) (return (mapBox sndSVal box))
-- assumes cvs are constructor vals
fill' v@(Ter (Sum _ nass) env) box@(Box _ _ (VCon n _) _) = case getIdent n nass of
  Just as -> do
    let boxes = transposeBox $ mapBox unCon box
    -- fill boxes for each argument position of the constructor
    VCon n <$> fills as env boxes
  Nothing -> error $ "fill: missing constructor in labelled sum " ++ n
fill' (VEquivSquare x y a s t) box@(Box dir x' vx' nvs) =
  VSquare x y <$> fill a (modBox (unPack x y) box)
fill' veq@(VEquivEq x a b f s t) box@(Box dir z vz nvs)
  | x /= z && x `notElem` nonPrincipal box = do
    trace "VEquivEq case 1"
    ax0 <- fill a (mapBox fstVal box)
    bx0 <- app f ax0
    let bx = mapBox sndVal box
    bx' <- mapBoxM (`face` (x,up)) bx
    bx1 <- fill b bx'        --- independent of x
    v   <- fill b $ (x,(bx0,bx1)) `consBox` bx
    return $ VPair x ax0 v
  | x /= z && x `elem` nonPrincipal box = do
    trace "VEquivEq case 2"
    let ax0 = lookBox (x,down) box

        -- modification function
        mf (ny,dy) vy | x /= ny    = return (sndVal vy)
                      | dy == down = app f ax0
                      | otherwise  = return vy

    bx  <- sequenceBox $ modBox mf box
    VPair x ax0 <$> fill b bx
  | x == z && dir == up = do
    trace "VEquivEq case 3"
    let ax0 = vz
    bx0 <- app f ax0
    v   <- fill b $ Box dir z bx0 [ (nnd,sndVal v) | (nnd,v) <- nvs ]
    return $ VPair x ax0 v
  | x == z && dir == down = do
    trace "VEquivEq case 4"
    gbsb <- app s vz
    let (gb,sb) = (fstSVal gbsb, sndSVal gbsb)
        y       = fresh (veq, box)
    vy <- appName sb x

    let vpTSq :: Name -> Dir -> Val -> Eval (Val,Val)
        vpTSq nz dz (VPair z a0 v0) = do
          let vp = VSPair a0 (Path z v0)
          t0 <- t `face` (nz,dz)
          b0 <- vz `face` (nz,dz)
          l0sq0 <- appNameM (appM (app t0 b0) (return vp)) y
          let (l0,sq0) = (fstSVal l0sq0, sndSVal l0sq0)
          sq0x <- appName sq0 x
          return (l0,sq0x)  -- TODO: check the correctness of the square s0

    -- TODO: Use modBox!
    vsqs <- sequenceSnd [ ((n,d),vpTSq n d v) | ((n,d),v) <- nvs]
    let box1   = Box up y gb [ (nnd,v) | (nnd,(v,_)) <- vsqs ]
    afill <- fill a box1

    acom <- afill `face` (y,up)
    fafill <- app f afill

    let box2 = Box up y vy (((x,down),fafill) : ((x,up),vz) :
                            [ (nnd,v) | (nnd,(_,v)) <- vsqs ])
    bcom <- com b box2
    return $ VPair x acom bcom
  | otherwise = error "fill EqEquiv"
fill' v@(Kan Com VU tbox') box@(Box dir x' vx' nvs')
  | toAdd /= [] = do  -- W.l.o.g. assume that box contains faces for
                      -- the non-principal sides of tbox.

    trace "Kan Com 1"

    let -- TODO: Is this correct? Do we have to consider the auxsides?
        add :: Side -> Eval Val
        add yc = do box' <- mapBoxM (`face` yc) box
                    fillM (lookBox yc tbox `face` (x,tdir)) (return box')

    -- Note: This could be done nicer by providing a monad instance for (,)
    sides' <- sequence [ do m1 <- add (n,down)
                            m2 <- add (n,up)
                            return (n,(m1,m2)) | n <- toAdd ]

    fill v (sides' `appendBox` box)
  | x' `notElem` nK = do
    trace "Kan Com 2"

    principal <- fill tx (mapBox (pickout (x,tdir')) boxL)
    nonprincipal <-
      sequence [ do pyc <- principal `face` yc
                    let side = [((x,tdir),lookBox yc box),((x,tdir'),pyc)]
                    v' <- fill (lookBox yc tbox)
                               (side `appendSides` mapBox (pickout yc) boxL)
                    return (yc,v')
               | yc <- allDirs nK ]

    return $ VComp (Box tdir x principal nonprincipal)
  | x' `elem` nK = do
    trace "Kan Com 3"

    let -- assumes zc in defBox tbox
        auxsides zc = [ (yd,pickout zc (lookBox yd box)) | yd <- allDirs nL ]

    -- extend input box along x with orientation tdir'; results
    -- in the non-principal faces on the intersection of defBox
    -- box and defBox tbox; note, that the intersection contains
    -- (x',dir'), but not (x',dir) (and (x,_))
    npintbox <- modBoxM (\yc boxside -> fill (lookBox yc tbox)
                                             (Box tdir' x boxside (auxsides yc)))
                        (subBox (nK `intersect` nJ) box)

    npintfacebox <- mapBoxM (`face` (x,tdir')) npintbox
    principal    <- fill tx (auxsides (x,tdir') `appendSides` npintfacebox)
    nplp         <- principal `face` (x',dir)
    fnpintboxs   <- sequence [ do fv <- v `face` (x',dir)
                                  return (yc,fv)
                             | (yc,v) <- sides npintbox ]

    let nplnp = auxsides (x',dir) ++ fnpintboxs
    -- the missing non-principal face on side (x',dir)
    v' <- fill (lookBox (x',dir) tbox) (Box tdir x nplp nplnp)
    let nplast = ((x',dir),v')

    return $ VComp (Box tdir x principal (nplast:fromBox npintbox))
  where nK    = nonPrincipal tbox
        nJ    = nonPrincipal box
        z     = fresh (tbox', box)
        -- x is z
        tbox@(Box tdir x tx nvs) = swap tbox' (pname tbox') z
        toAdd = nK \\ (x' : nJ)
        nL    = nJ \\ nK
        boxL  = subBox nL box
        dir'  = mirror dir
        tdir' = mirror tdir
        -- asumes zd is in the sides of tbox
        pickout zd vcomp = lookBox zd (unCompAs vcomp z)

fill' v@(Kan Fill VU tbox@(Box tdir x tx nvs)) box@(Box dir x' vx' nvs')
  -- the cases should be (in order):
  -- 1) W.l.o.g. K subset x', J
  -- 2) x' = x &  dir = tdir
  -- 3) x' = x &  dir = mirror tdir
  -- 4) x `notElem` J (maybe combine with 1?)
  -- 5) x' `notElem` K
  -- 6) x' `elem` K
  | toAdd /= [] = do
    trace "Kan Fill VU Case 1"  -- W.l.o.g. nK subset x':nJ
    let add :: Side -> Eval Val
        add zc = fillM (return (lookBox zc tbox)) (mapBoxM (`face` zc) box)
    newSides <- sequenceSnd [ (zc,add zc) | zc <- allDirs toAdd ]
    fill v (newSides `appendSides` box)
  | x == x' && dir == tdir = do -- assumes K subset x',J
    trace "Kan Fill VU Case 2"
    let boxp = lookBox (x,dir') box  -- is vx'
    principal <- fill (lookBox (x',tdir') tbox)
                      (Box up z boxp (auxsides (x',tdir')))
    nonprincipal <-
      sequenceSnd [ (zc,do let principzc = lookBox zc box
                           fpzc <- principal `face` zc
                           -- "degenerate" along z!
                           ppzc <- principzc `face` (x,tdir)
                           let sides = [((x,tdir'),fpzc),((x,tdir),ppzc)]
                           fill (lookBox zc tbox)
                                (Box up z principzc (sides ++ auxsides zc)))
                  | zc <- allDirs nK ]
    return $ VFill z (Box tdir x principal nonprincipal)

  | x == x' && dir == mirror tdir = do -- assumes K subset x',J
    trace "Kan Fill VU Case 3"
    let -- the principal side of box must be a VComp
        -- should be safe given the neutral test at the beginning
        upperbox = unCompAs (lookBox (x,dir') box) x
    nonprincipal <- sequenceSnd
      [ (zc,do let top    = lookBox zc upperbox
                   bottom = lookBox zc box
               princ <- top `face` (x,tdir) -- same as: bottom `face` (x,tdir)
               let sides  = [((z,down),bottom),((z,up),top)]
               fill (lookBox zc tbox) (Box tdir' x princ -- "degenerate" along z!
                                       (sides ++ auxsides zc)))
      | zc <- allDirs nK ]
    nonprincipalfaces <- sequenceSnd [ (zc,u `face` (x,dir))
                                     | (zc,u) <- nonprincipal ]
    principal <- fill (lookBox (x,tdir') tbox)
                      (Box up z (lookBox (x,tdir') upperbox)
                       (nonprincipalfaces ++ auxsides (x,tdir')))
    return $ VFill z (Box tdir x principal nonprincipal)
  | x `notElem` nJ = do  -- assume x /= x' and K subset x', J
    trace "Kan Fill VU Case 4"
    comU <- v `face` (x,tdir) -- Kan Com VU (tbox (z=up))
    let fcbox = mapBoxM (`face` (x,tdir)) box
    xsides <- sequenceSnd [ ((x,tdir), fillM (return comU) fcbox)
                          , ((x,tdir'),
                             fillM (return (lookBox (x,tdir') tbox)) fcbox) ]

    fill v (xsides `appendSides` box)
  | x' `notElem` nK = do -- assumes x,K subset x',J
    trace "Kan Fill VU Case 5"
    let -- TODO: Do we need a fresh name? (Probably not: doesn't depend on x!)
        xaux      = unCompAs (lookBox (x,tdir) box) x
        boxprinc  = unFillAs (lookBox (x',dir') box) z
        princnp   = [((z,up),lookBox (x,tdir') xaux),((z,down),lookBox (x,tdir') box)]
                    ++ auxsides (x,tdir')
    principal <- fill (lookBox (x,tdir') tbox) -- tx
                      (Box dir x' (lookBox (x,tdir') boxprinc) princnp)
    nonprincipal <- sequence
      [ do let yup = lookBox yc xaux
           fyup <- yup `face` (x,tdir)
           fpyc <- principal `face` yc
           let np  = [ ((z,up),yup), ((z,down),lookBox yc box)
                     , ((y,c), fyup) -- deg along z!
                     , ((y,mirror c), fpyc) ] ++ auxsides yc
           fb <- fill (lookBox yc tbox) (Box dir x' (lookBox yc boxprinc) np)
           return (yc, fb)
      | yc@(y,c) <- allDirs nK]
    return $ VFill z (Box tdir x principal nonprincipal)
  | x' `elem` nK = do -- assumes x,K subset x',J
    trace "Kan Fill VU Case 6"
    -- surprisingly close to the last case of the Kan-Com-VU filling
    let upperbox = unCompAs (lookBox (x,dir') box) x
    npintbox <- modBoxM (\zc downside ->
                     let bottom = lookBox zc box
                         top    = lookBox zc upperbox
                         princ  = downside -- same as bottom `face` (x',tdir) and
                                           -- top `face` (x',tdir)
                         sides  = [((z,down),bottom),((z,up),top)]
                     in fill (lookBox zc tbox) (Box tdir' x princ -- deg along z!
                                                (sides ++ auxsides zc)))
                        (subBox (nK `intersect` nJ) box)

    let npint = fromBox npintbox
    npintfacebox <- mapBoxM (`face` (x,tdir)) npintbox
    let principalbox = ([ ((z,down),lookBox (x,tdir') box)
                        , ((z,up)  ,lookBox (x,tdir') upperbox)]
                        ++ auxsides (x,tdir'))
                       `appendSides` npintfacebox
    principal <- fill tx principalbox
    let nplp = lookBox (x',dir) upperbox
    nplnp <- sequenceSnd $
     [ ((x',dir), nplp `face` (x',dir)) -- deg along z!
     , ((x', dir'),principal `face` (x',dir)) ]
     ++  map (second return) (auxsides (x',dir))
      ++ [ (zc,u `face` (x',dir)) | (zc,u) <- sides npintbox ]
    fb <- fill (lookBox (x',dir) tbox) (Box down z nplp nplnp)

    return $ VFill z (Box tdir x principal (((x',dir),fb) : npint))
    where z     = fresh (v, box)
          nK    = nonPrincipal tbox
          nJ    = nonPrincipal box
          toAdd = nK \\ (x' : nJ)
          nL    = nJ \\ (x : nK)
          dir'  = mirror dir
          tdir' = mirror tdir
          -- asumes zc is in the sides of tbox
          pickout zc vfill = lookBox zc (unFillAs vfill z)
          -- asumes zc is in the sides of tbox
          auxsides zc = [ (yd,pickout zc (lookBox yd box)) | yd <- allDirs nL ]
fill' v b = return $ Kan Fill v b

-- Composition (ie., the face of fill which is created)
com :: Val -> Box Val -> Eval Val
com u box = do
  b <- isNeutralFill u box
  if b then return $ VComN u box else com' u box
com' vid@VId{} box@(Box dir i _ _)         = fill vid box `faceM` (i,dir)
com' vsigma@VSigma{} box@(Box dir i _ _)   = fill vsigma box `faceM` (i,dir)
com' veq@VEquivEq{} box@(Box dir i _ _)    = fill veq box `faceM` (i,dir)
com' u@(Kan Com VU _) box@(Box dir i _ _)  = fill u box `faceM` (i,dir)
com' u@(Kan Fill VU _) box@(Box dir i _ _) = fill u box `faceM` (i,dir)
com' ter@Ter{} box@(Box dir i _ _)         = fill ter box `faceM` (i,dir)
com' v box                                 = return $ Kan Com v box

-- Monadic version of com
comM :: Eval Val -> Eval (Box Val) -> Eval Val
comM t b = do
  v  <- t
  b' <- b
  com v b'

-- Conversion functions
(<&&>) :: Monad m => m Bool -> m Bool -> m Bool
(<&&>) = liftM2 (&&)

(<==>) :: (Monad m, Eq a) => a -> a -> m Bool
a <==> b = return (a == b)

andM :: [Eval Bool] -> Eval Bool
andM = liftM and . sequence

conv :: Int -> Val -> Val -> Eval Bool
conv k VU VU                                  = return True
conv k (Ter (Lam x u) e) (Ter (Lam x' u') e') = do
  let v = mkVar k $ support (e, e')
  convM (k+1) (eval (oPair e (x,v)) u) (eval (oPair e' (x',v)) u')
conv k (Ter (Lam x u) e) u' = do
  let v = mkVar k $ support e
  convM (k+1) (eval (oPair e (x,v)) u) (app u' v)
conv k u' (Ter (Lam x u) e) = do
  let v = mkVar k $ support e
  convM (k+1) (app u' v) (eval (oPair e (x,v)) u)
conv k (Ter (Split p _) e) (Ter (Split p' _) e') =
  liftM ((p == p') &&) $ convEnv k e e'
conv k (Ter (Sum p _) e)   (Ter (Sum p' _) e') =
  ((p == p') &&) <$> convEnv k e e'
conv k (Ter (PN (Undef p)) e) (Ter (PN (Undef p')) e') =
  liftM ((p == p') &&) $ convEnv k e e'
conv k (VPi u v) (VPi u' v') = do
  let w = mkVar k $ support [u,u',v,v']
  conv k u u' <&&> convM (k+1) (app v w) (app v' w)
conv k (VSigma u v) (VSigma u' v') = do
  let w = mkVar k $ support [u,u',v,v']
  conv k u u' <&&> convM (k+1) (app v w) (app v' w)
conv k (VId a u v) (VId a' u' v') = andM [conv k a a', conv k u u', conv k v v']
conv k (Path x u) (Path x' u')    = conv k (swap u x z) (swap u' x' z)
  where z = fresh (u,u')
conv k (Path x u) p'              = convM k (return (swap u x z)) (appName p' z)
  where z = fresh u
conv k p (Path x' u')             = convM k (appName p z) (return (swap u' x' z))
  where z = fresh u'
-- conv k (VExt x b f g p) (VExt x' b' f' g' p') =
--   andM [x <==> x', conv k b b', conv k f f', conv k g g', conv k p p']
conv k (VHExt x b f g p) (VHExt x' b' f' g' p') =
  andM [x <==> x', conv k b b', conv k f f', conv k g g', conv k p p']
conv k (VFst u) (VFst u')                     = conv k u u'
conv k (VSnd u) (VSnd u')                     = conv k u u'
conv k (VInh u) (VInh u')                     = conv k u u'
conv k (VInc u) (VInc u')                     = conv k u u'
conv k (VSquash x u v) (VSquash x' u' v')     =
  andM [x <==> x', conv k u u', conv k v v']
conv k (VCon c us) (VCon c' us') =
  liftM (\bs -> (c == c') && and bs) (zipWithM (conv k) us us')
conv k (Kan Fill v box) (Kan Fill v' box')    =
  conv k v v' <&&> convBox k box box'
conv k (Kan Com v box) (Kan Com v' box')      =
  andM [conv k v v', convBox k (swap box x y) (swap box' x' y)]
  where y      = fresh ((v,v'),(box,box'))
        (x,x') = (pname box, pname box')
conv k (VComN v box) (VComN v' box')      =
  andM [conv k v v', convBox k (swap box x y) (swap box' x' y)]
  where y      = fresh ((v,v'),(box,box'))
        (x,x') = (pname box, pname box')
conv k (VFillN v box) (VFillN v' box')      =
  andM [conv k v v', convBox k (swap box x y) (swap box' x' y)]
  where y      = fresh ((v,v'),(box,box'))
        (x,x') = (pname box, pname box')
conv k (VEquivEq x a b f s t) (VEquivEq x' a' b' f' s' t') =
  andM [x <==> x', conv k a a', conv k b b',
       conv k f f', conv k s s', conv k t t']
conv k (VEquivSquare x y a s t) (VEquivSquare x' y' a' s' t') =
  andM [x <==> x', y <==> y', conv k a a', conv k s s', conv k t t']
conv k (VPair x u v) (VPair x' u' v')     =
  andM [x <==> x', conv k u u', conv k v v']
conv k (VSquare x y u) (VSquare x' y' u') =
  andM [x <==> x', y <==> y', conv k u u']
conv k (VComp box) (VComp box')           =
  convBox k (swap box x y) (swap box' x' y)
  where y      = fresh (box,box')
        (x,x') = (pname box, pname box')
conv k (VFill x box) (VFill x' box')      =
  convBox k (swap box x y) (swap box' x' y)
  where y      = fresh (box,box')
conv k (VSPair u v)   (VSPair u' v')   = conv k u u' <&&> conv k v v'
conv k (VSPair u v)   w                =
  conv k u (fstSVal w) <&&> conv k v (sndSVal w)
conv k w              (VSPair u v)     =
  conv k (fstSVal w) u <&&> conv k (sndSVal w) v
conv k (VApp u v)     (VApp u' v')     = conv k u u' <&&> conv k v v'
conv k (VAppName u x) (VAppName u' x') = conv k u u' <&&> (x <==> x')
conv k (VSplit u v)   (VSplit u' v')   = conv k u u' <&&> conv k v v'
conv k (VVar x d)     (VVar x' d')     = return $ (x == x')   && (d == d')
conv k (VInhRec b p phi v) (VInhRec b' p' phi' v') =
  andM [conv k b b', conv k p p', conv k phi phi', conv k v v']
conv k VCircle        VCircle          = return True
conv k VBase          VBase            = return True
conv k (VLoop x)      (VLoop y)        = x <==> y
conv k (VCircleRec f b l v) (VCircleRec f' b' l' v') =
  andM [conv k f f', conv k b b', conv k l l', conv k v v']
conv k VI             VI               = return True
conv k VI0            VI0              = return True
conv k VI1            VI1              = return True
conv k (VLine x)      (VLine y)        = x <==> y
conv k (VIntRec f s e l u) (VIntRec f' s' e' l' u') =
  andM [conv k f f', conv k s s', conv k e e', conv k l l', conv k u u']
conv k _              _                = return False

-- Monadic version of conv
convM :: Int -> Eval Val -> Eval Val -> Eval Bool
convM k v1 v2 = do
  v1' <- v1
  v2' <- v2
  conv k v1' v2'

convBox :: Int -> Box Val -> Box Val -> Eval Bool
convBox k box@(Box d pn _ ss) box'@(Box d' pn' _ ss') =
  if (d == d') && (pn == pn') && (sort np == sort np')
     then and <$> sequence [ conv k (lookBox s box) (lookBox s box')
                           | s <- defBox box ]
     else return False
  where (np, np') = (nonPrincipal box, nonPrincipal box')

convEnv :: Int -> OEnv -> OEnv -> Eval Bool
convEnv k e e' = liftM and $ zipWithM (conv k) (valOfOEnv e) (valOfOEnv e')