content: module AnswerBase where
  
  import GSyntax
  
  -- interpretation of Base
  
  type Prop = Bool
  type Ent = Int
  domain = [0 .. 100]
  
  iS :: GS -> Prop
  iS s = case s of
    GPredAP np ap -> iNP np (iAP ap)
  
  iNP :: GNP -> (Ent -> Prop) -> Prop
  iNP np p = case np of
    GEvery cn -> all (\x -> not (iCN cn x) || p x) domain
    GSome  cn -> any (\x ->      iCN cn x  && p x) domain
    GNone     -> not (any (\x -> p x) domain)
    GMany pns -> and (map p (iListPN pns))
    GConjNP c np1 np2 -> iConj c (iNP np1 p) (iNP np2 p)
    GUsePN a -> p (iPN a)
  
  iPN :: GPN -> Ent
  iPN pn = case pn of  
    GUseInt i -> iInt i
    GSum pns -> sum (iListPN pns)
    GProduct pns -> product (iListPN pns)
    GGCD pns -> foldl1 gcd (iListPN pns)
  
  iAP :: GAP -> Ent -> Prop
  iAP ap e = case ap of
    GComplA2 a2 np    -> iNP np (iA2 a2 e)
    GConjAP c ap1 ap2 -> iConj c (iAP ap1 e) (iAP ap2 e)
    GEven  -> even e
    GOdd   -> odd e
    GPrime -> prime e
  
  iCN :: GCN -> Ent -> Prop
  iCN cn e = case cn of
    GModCN ap cn0 -> (iCN cn0 e) && (iAP ap e)
    GNumber -> True
  
  iConj :: GConj -> Prop -> Prop -> Prop
  iConj c = case c of
    GAnd -> (&&)
    GOr  -> (||)
  
  iA2 :: GA2 -> Ent -> Ent -> Prop
  iA2 a2 e1 e2 = case a2 of
    GGreater -> e1 > e2
    GSmaller -> e1 < e2 
    GEqual   -> e1 == e2
    GDivisible -> e2 /= 0 && mod e1 e2 == 0
  
  iListPN :: GListPN -> [Ent]
  iListPN gls = case gls of
    GListPN pns -> map iPN pns
  
  iInt :: GInt -> Ent
  iInt gi = case gi of
    GInt i -> fromInteger i
  
  -- questions and answers
  
  iQuestion :: GQuestion -> Either Bool [Ent]
  iQuestion q = case q of
    GWhatIs pn      -> Right [iPN pn]  -- computes the value
    GWhichAre cn ap -> Right [e | e <- domain, iCN cn e, iAP ap e]
    GQuestS s       -> Left  (iS s)
  
  question2answer :: GQuestion -> GAnswer
  question2answer q = case iQuestion q of
    Left True  -> GYes
    Left False -> GNo
    Right []   -> GValue GNone
    Right [v]  -> GValue (GUsePN (ent2pn v))
    Right vs   -> GValue (GMany (GListPN (map ent2pn vs)))
  
  ent2pn :: Ent -> GPN
  ent2pn e = GUseInt (GInt (toInteger e))
  
  
  -- auxiliary
  
  prime :: Int -> Bool
  prime x = elem x primes where
    primes = sieve [2 .. x]
    sieve (p:xs) = p : sieve [ n | n <- xs, n `mod` p > 0 ]
    sieve [] = []
  