module Lang.LamIf.Domains where

import FP
import Lang.LamIf.Values
import Lang.LamIf.Syntax

data ConcreteVal = 
    CInt  
  | CClo Closure 
  | CFrame (Frame,Maybe ExpAddr)
  | CBot
  deriving (Eq,Ord)
makePrisms ''ConcreteVal
makePrettySum ''ConcreteVal

instance Val ConcreteVal 
  where
    intI = CInt
    isZeroE = elimMaybe bot (single  () 0)  view cIntL
    delZero (CInt 0) = CBot
    delZero v = v
    cloI = CClo
    cloE = elimMaybe bot single  view cCloL
    frameI = CFrame
    frameE = elimMaybe bot single  view cFrameL
    δ o v₁ v₂ = elimMaybe CBot CInt $ do
      i₁  view cIntL v₁
      i₂  view cIntL v₂
      return $ case o of
        Plus  i₁ + i₂
        Minus  i₁ - i₂

data AbstractVal = 
    AInt 
  | APos
  | AZero
  | ANeg
  | ANotZero
  | AAnyInt
  | AClo Closure
  | AFrame (Frame,Maybe ExpAddr)
  | ABot
  deriving (Eq, Ord)
makePrisms ''AbstractVal
makePrettySum ''AbstractVal

negateAbstractVal  AbstractVal  AbstractVal
negateAbstractVal (AInt i) = AInt (negate i)
negateAbstractVal APos = ANeg
negateAbstractVal AZero = AZero
negateAbstractVal ANeg = APos
negateAbstractVal AAnyInt = AAnyInt
negateAbstractVal v = v

instance Val AbstractVal
  where
    intI = AInt
    isZeroE (AInt i) = single $ i  0
    isZeroE APos = single False
    isZeroE AZero = single True
    isZeroE ANeg = single False
    isZeroE AAnyInt = set [True,False]
    isZeroE _ = bot
    delZero (AInt 0) = ABot
    delZero AZero = ABot
    delZero AAnyInt = ANotZero
    delZero v = v
    cloI = AClo
    cloE = elimMaybe bot single  view aCloL
    frameI = AFrame
    frameE = elimMaybe bot single  view aFrameL
    -- TODO: handle NotZero
    δ Plus (AInt i₁) (AInt i₂) = case (i₁ + i₂)  0 of
      LT  ANeg
      EQ  AZero
      GT  APos
    δ Plus (AInt i) APos = case i  0 of
      LT  AAnyInt
      EQ  APos
      GT  APos
    δ Plus (AInt i) AZero = AInt i
    δ Plus (AInt i) ANeg = case i  0 of
      LT  ANeg
      EQ  ANeg
      GT  AAnyInt
    δ Plus (AInt i) ANotZero = case i  0 of
      LT  AAnyInt
      EQ  ANotZero
      GT  AAnyInt
    δ Plus (AInt _) AAnyInt = AAnyInt
    δ Plus APos APos = APos
    δ Plus APos AZero = APos
    δ Plus APos ANeg = AAnyInt
    δ Plus APos ANotZero = AAnyInt
    δ Plus APos AAnyInt = AAnyInt
    δ Plus AZero AZero = AZero
    δ Plus AZero ANeg = ANeg
    δ Plus AZero ANotZero = ANotZero
    δ Plus AZero AAnyInt = AAnyInt
    δ Plus ANeg ANeg = ANeg
    δ Plus ANeg ANotZero = AAnyInt
    δ Plus ANeg AAnyInt = AAnyInt
    δ Plus APos (AInt i) = δ Plus (AInt i) APos
    δ Plus AZero (AInt i) = δ Plus (AInt i) AZero
    δ Plus ANeg (AInt i) = δ Plus (AInt i) ANeg
    δ Plus ANotZero (AInt i) = δ Plus (AInt i) ANotZero
    δ Plus AAnyInt (AInt i) = δ Plus (AInt i) AAnyInt
    δ Plus AZero APos = δ Plus APos AZero
    δ Plus ANeg APos = δ Plus APos ANeg
    δ Plus ANotZero APos = δ Plus APos ANotZero
    δ Plus AAnyInt APos = δ Plus APos AAnyInt
    δ Plus ANeg AZero = δ Plus AZero ANeg
    δ Plus ANotZero AZero = δ Plus AZero ANotZero
    δ Plus AAnyInt AZero = δ Plus AZero AAnyInt
    δ Plus ANotZero ANeg = δ Plus ANeg ANotZero
    δ Plus AAnyInt ANeg = δ Plus ANeg AAnyInt
    δ Minus i₁ i₂ = δ Plus i₁ (negateAbstractVal i₂)
    δ _ _ _ = ABot

instance (Ord val,Val val)  Val (𝒫 val) where
  intI = single  intI
  isZeroE = extendSet isZeroE
  delZero = mapSet delZero
  cloI = single  cloI
  cloE = extendSet cloE
  frameI = single  frameI
  frameE = extendSet frameE
  δ o vM₁ vM₂ = 
    vM₁ =* \ v₁ 
    vM₂ =* \ v₂ 
    single $ δ o v₁ v₂

data DomainParam where
  DomainParam   val. P val  W (Ord val,POrd val,JoinLattice val,Val val,Difference val,Pretty val)  DomainParam

concrete  DomainParam
concrete = DomainParam (P  P (𝒫 ConcreteVal)) W

abstract  DomainParam
abstract = DomainParam (P  P (𝒫 AbstractVal)) W