module Lang.LamIf.Val where

import FP
import Lang.LamIf.StateSpace
import Lang.LamIf.Syntax

-- Concrete
data CVal   = LitC Lit | CloC (Clo  ) | TupC (PicoVal  , PicoVal  ) | BotC
  deriving (Eq, Ord)
makePrisms ''CVal

instance (Eq , Eq ) => PartialOrder (CVal  ) where
  pcompare BotC _ = PLT
  pcompare _ BotC = PGT
  pcompare v1 v2 | v1 == v2  = PEQ
                 | otherwise = PUN

instance (Ord , Ord ) => Val   (CVal  ) where
  lit :: Lit -> CVal  
  lit = LitC
  clo :: Clo   -> CVal  
  clo = CloC
  binop :: BinOp -> CVal   -> CVal   -> CVal  
  binop Add (LitC (I n1)) (LitC (I n2)) = LitC $ I $ n1 + n2
  binop Sub (LitC (I n1)) (LitC (I n2)) = LitC $ I $ n1 - n2
  binop GTE (LitC (I n1)) (LitC (I n2)) = LitC $ B $ n1 >= n2
  binop _ _ _ = BotC
  tup :: (PicoVal  , PicoVal  ) -> CVal  
  tup = TupC
  elimBool :: CVal   -> Set Bool
  elimBool (LitC (B b)) = single b
  elimBool _ = empty
  elimClo :: CVal   -> Set (Clo  )
  elimClo (CloC c) = single c
  elimClo _ = empty
  elimTup :: CVal   -> Set (PicoVal  , PicoVal  )
  elimTup (TupC t) = single t
  elimTup _ = empty

-- Abstract
data AVal   = LitA Lit | IA | BA | CloA (Clo  ) | TupA (PicoVal  , PicoVal  ) | BotA
  deriving (Eq, Ord)
makePrisms ''AVal

instance (Eq , Eq ) => PartialOrder (AVal  ) where
  pcompare BotA _ = PLT
  pcompare _ BotA = PGT
  pcompare (LitA (I _)) IA = PLT
  pcompare IA (LitA (I _)) = PGT
  pcompare (LitA (B _)) BA = PLT
  pcompare BA (LitA (B _)) = PGT
  pcompare v1 v2 | v1 == v2 = PEQ
                 | otherwise = PUN

instance (Ord , Ord ) =>  Val   (AVal  ) where
  lit :: Lit -> AVal  
  lit = LitA
  clo :: Clo   -> AVal  
  clo = CloA
  binop :: BinOp -> AVal   -> AVal   -> AVal  
  binop Add v1 v2 | v1  IA && v2  IA = IA
  binop Sub v1 v2 | v1  IA && v2  IA = IA
  binop GTE v1 v2 | v1  IA && v2  IA = BA
  binop _ _ _ = BotA
  tup :: (PicoVal  , PicoVal  ) -> AVal  
  tup = TupA
  elimBool :: AVal   -> Set Bool
  elimBool (LitA (B b)) = single b
  elimBool BA = fromList [True, False]
  elimBool _ = empty
  elimClo :: AVal   -> Set (Clo  )
  elimClo (CloA c) = single c
  elimClo _ = empty
  elimTup :: AVal   -> Set (PicoVal  , PicoVal  )
  elimTup (TupA t) = single t
  elimTup _ = empty

-- Lifting to Powerset
newtype Power val   = Power { runPower :: Set (val  ) }
  deriving 
    ( Eq, Ord, PartialOrder, Bot, Join, JoinLattice, Difference
    , Iterable (val  ), Container (val  ), Buildable (val  )
    )

instance (Ord , Ord ) => Val   (Power CVal  ) where
  lit = Power . single . lit
  clo = Power . single . clo
  binop o vP1 vP2 = Power $ do
    v1 <- runPower vP1
    v2 <- runPower vP2
    single $ binop o v1 v2
  tup = Power . single . tup
  elimBool = extend elimBool . runPower
  elimClo = extend elimClo . runPower
  elimTup = extend elimTup . runPower

instance (Ord , Ord ) => Val   (Power AVal  ) where
  lit = Power . single . lit
  clo = Power . single . clo
  binop o vP1 vP2 = Power $ do
    v1 <- runPower vP1
    v2 <- runPower vP2
    single $ binop o v1 v2
  tup = Power . single . tup
  elimBool = extend elimBool . runPower
  elimClo = extend elimClo . runPower
  elimTup = extend elimTup . runPower