module Logic where 
import Control.Monad ( liftM
                     , liftM2)
import Data.List     ( union
                     , subsequences)
import Test.QuickCheck ( Arbitrary
                       , arbitrary
                       , oneof
                       , elements
                       , sized
                       , quickCheck)
import qualified Data.Set as S
type VarProp = String
data FProp = T
           | F
           | Atom VarProp
           | Neg FProp 
           | Conj FProp FProp 
           | Disj FProp FProp 
           | Impl FProp FProp 
           | Equi FProp FProp 
  deriving (Eq,Ord)
instance Show FProp where
    show (T)        = "⊤"
    show (F)        = "⊥"
    show (Atom x)   = x
    show (Neg x)    = "¬" ++ show x
    show (Conj x y) = "(" ++ show x ++ " ∧ " ++ show y ++ ")"
    show (Disj x y) = "(" ++ show x ++ " ∨ " ++ show y ++ ")"
    show (Impl x y) = "(" ++ show x ++ " → " ++ show y ++ ")"
    show (Equi x y) = "(" ++ show x ++ " ↔ " ++ show y ++ ")"
p, q, r :: FProp
p  = Atom "p"
q  = Atom "q"
r  = Atom "r"
no :: FProp -> FProp
no = Neg
(∨) :: FProp -> FProp -> FProp
(∨)   = Disj
infixr 5 ∨
(∧) :: FProp -> FProp -> FProp
(∧)   = Conj
infixr 4 ∧
(→) :: FProp -> FProp -> FProp
(→)  = Impl
infixr 3 →
(↔) :: FProp -> FProp -> FProp
(↔) = Equi
infixr 2 ↔
instance Arbitrary FProp where
  arbitrary = sized prop
    where
      prop n  | n <= 0     = atom
              | otherwise  = oneof [ 
                    atom
                    , liftM Neg subform
                    , liftM2 Conj subform  subform
                    , liftM2 Disj subform  subform
                    , liftM2 Impl subform  subform
                    , liftM2 Equi subform' subform' ]
        where
          atom     = oneof [liftM Atom (elements ["p","q","r","s"]),
                            elements [F,T]]
          subform  = prop ( n `div` 2)
          subform' = prop ( n `div` 4)
substitute :: FProp -> VarProp -> FProp -> FProp
substitute T            _ _ = T
substitute F            _ _ = F
substitute (Atom f)     p g | f == p = g
                           | otherwise = Atom f
substitute (Neg f)      p g = Neg (substitute f p g)
substitute (Conj f1 f2) p g = Conj (substitute f1 p g) (substitute f2 p g)
substitute (Disj f1 f2) p g = Disj (substitute f1 p g) (substitute f2 p g)
substitute (Impl f1 f2) p g = Impl (substitute f1 p g) (substitute f2 p g)
substitute (Equi f1 f2) p g = Equi (substitute f1 p g) (substitute f2 p g)
type Interpretation = [FProp]
signify :: FProp -> Interpretation -> Bool
signify T          _ = True
signify F          _ = False
signify (Atom f)   i = (Atom f) `elem` i
signify (Neg f)    i = not (signify f i)
signify (Conj f g) i = (signify f i) && (signify g i)
signify (Disj f g) i = (signify f i) || (signify g i)
signify (Impl f g) i = signify (Disj (Neg f) g) i
signify (Equi f g) i = signify (Conj (Impl f g) (Impl g f)) i
isModelForm :: Interpretation -> FProp -> Bool
isModelForm i f = signify f i
propSymbolsForm :: FProp -> [FProp]
propSymbolsForm T          = []
propSymbolsForm F          = []
propSymbolsForm (Atom f)   = [(Atom f)]
propSymbolsForm (Neg f)    = propSymbolsForm f
propSymbolsForm (Conj f g) = propSymbolsForm f `union` propSymbolsForm g
propSymbolsForm (Disj f g) = propSymbolsForm f `union` propSymbolsForm g
propSymbolsForm (Impl f g) = propSymbolsForm f `union` propSymbolsForm g
propSymbolsForm (Equi f g) = propSymbolsForm f `union` propSymbolsForm g
interpretationsForm :: FProp -> [Interpretation]
interpretationsForm = subsequences . propSymbolsForm
modelsForm :: FProp -> [Interpretation]
modelsForm f = [i | i <- interpretationsForm f, isModelForm i f]
isValid :: FProp -> Bool
isValid f = modelsForm f == interpretationsForm f
isUnsatisfiable :: FProp -> Bool
isUnsatisfiable = null . modelsForm
isSatisfiable :: FProp -> Bool
isSatisfiable = not . null . modelsForm
type KB = S.Set FProp
propSymbolsKB :: KB -> [FProp]
propSymbolsKB = foldl (\acc f -> union acc (propSymbolsForm f)) []
interpretationsKB :: KB -> [Interpretation]
interpretationsKB = subsequences . propSymbolsKB
isModelKB :: Interpretation -> KB -> Bool
isModelKB i = all (isModelForm i)
modelsKB :: KB -> [Interpretation]
modelsKB s = [i | i <- interpretationsKB s, isModelKB i s]
isConsistent :: KB -> Bool
isConsistent = not . null . modelsKB
isInconsistent :: KB -> Bool
isInconsistent = null . modelsKB
isConsequence :: KB -> FProp -> Bool
isConsequence k f =
  null [i | i <- interpretationsKB (S.insert f k)
          , isModelKB i k
          , not (isModelForm i f)]
prop_isValid :: FProp -> Bool
prop_isValid f =
   isValid f == isConsequence S.empty f
prop_isConsequence :: KB -> FProp -> Bool
prop_isConsequence k f =
   isConsequence k f == isInconsistent (S.insert (Neg f) k)
isConsequenceKB :: KB -> KB -> Bool
isConsequenceKB k = all (isConsequence k)
equivalent :: FProp -> FProp -> Bool
equivalent f g = isValid (f ↔ g)
equivalentKB :: KB -> KB -> Bool
equivalentKB k k' = isConsequenceKB k k' && isConsequenceKB k' k
prop_equivalent :: FProp -> FProp -> Bool
prop_equivalent f g =
  equivalent f g == equivalentKB (S.singleton f) (S.singleton g)