module Lang.CPS.Val where

import FP
import Lang.Common
import Lang.CPS.StateSpace

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

instance (Ord ( ψ), Ord ( ψ)) => Val   ψ (CVal   ψ) where
  lit :: Lit -> CVal   ψ
  lit = LitC
  clo :: Clo   ψ -> CVal   ψ
  clo = CloC
  op :: Op -> CVal   ψ -> CVal   ψ
  op Add1 (LitC (I n)) = LitC $ I $ n+1
  op Sub1 (LitC (I n)) = LitC $ I $ n-1
  op IsNonNeg (LitC (I n)) = LitC $ B $ n >= 0
  op _ _ = BotC
  elimBool :: CVal   ψ -> Set Bool
  elimBool (LitC (B b)) = singleton b
  elimBool _ = empty
  elimClo :: CVal   ψ -> Set (Clo   ψ)
  elimClo (CloC c) = singleton c
  elimClo _ = empty

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

instance (Ord ( ψ), Ord ( ψ)) =>  Val   ψ (AVal   ψ) where
  lit :: Lit -> AVal   ψ
  lit = LitA
  clo :: Clo   ψ -> AVal   ψ
  clo = CloA
  op :: Op -> AVal   ψ -> AVal   ψ
  op Add1 (LitA (I _)) = IA
  op Add1 IA = IA
  op Sub1 (LitA (I _)) = IA
  op Sub1 IA = IA
  op IsNonNeg (LitA (I _)) = BA
  op IsNonNeg IA = BA
  op _ _ = BotA
  elimBool :: AVal   ψ -> Set Bool
  elimBool (LitA (B b)) = singleton b
  elimBool BA = fromList [True, False]
  elimBool _ = empty
  elimClo :: AVal   ψ -> Set (Clo   ψ)
  elimClo (CloA c) = singleton c
  elimClo _ = empty

-- Lifting to Powerset
newtype Power val   ψ = Power { runPower :: Set (val   ψ) }
  deriving 
    ( Eq, Ord, PartialOrder, JoinLattice
    , Iterable (val   ψ), Container (val   ψ), SetLike (val   ψ)
    )

instance (Ord ( ψ), Ord ( ψ)) => Val   ψ (Power CVal   ψ) where
  lit = Power . singleton . lit
  clo = Power . singleton . clo
  op o = Power . setMap (op o) . runPower
  elimBool = extend elimBool . runPower
  elimClo = extend elimClo . runPower

instance (Ord ( ψ), Ord ( ψ)) => Val   ψ (Power AVal   ψ) where
  lit = Power . singleton . lit
  clo = Power . singleton . clo
  op o = Power . setMap (op o) . runPower
  elimBool = extend elimBool . runPower
  elimClo = extend elimClo . runPower