module Test.QuickSpec.Reasoning.PartialEquationalReasoning where
#include "errors.h"
import Test.QuickSpec.Equation
import Test.QuickSpec.Term hiding (Variable, vars)
import qualified Test.QuickSpec.Term as Term
import Test.QuickSpec.Utils.Typed
import qualified Test.QuickSpec.Reasoning.NaiveEquationalReasoning as EQ
import Test.QuickSpec.Reasoning.NaiveEquationalReasoning(EQ, evalEQ, runEQ)
import Data.IntMap(IntMap)
import qualified Data.IntMap as IntMap
import Control.Monad.Trans.State
import qualified Control.Monad.Trans.State as S
import Data.List
import Data.Ord
import Test.QuickSpec.Utils
import Test.QuickSpec.Signature hiding (vars)
import Data.Monoid
import Control.Monad
data PEquation = Precondition :\/: Equation
type Precondition = [Symbol]
data Totality = Partial | Total [Int] | Variable deriving (Eq, Ord, Show)
instance Eq PEquation where
e1 == e2 = e1 `compare` e2 == EQ
instance Ord PEquation where
compare = comparing stamp
where stamp (pre :\/: eq) = (eq, length pre, usort pre)
instance Show PEquation where
show = showPEquation mempty
showPEquation :: Sig -> PEquation -> String
showPEquation sig (pre :\/: t :=: u) =
show (mapVars f t) ++ " == " ++ show (mapVars f u) ++
showPre (map f pre)
where f = disambiguate sig (Term.vars t ++ Term.vars u ++ pre)
showPre [] = ""
showPre xs = " when " ++ conjunction (map show xs) ++ " " ++ plural xs "is" "are" ++ " partial"
plural xs x y
| length xs == 1 = x
| otherwise = y
conjunction [x] = x
conjunction xs =
intercalate ", " (init xs) ++ " and " ++ last xs
infix 5 :\/:
data Context = Context {
total :: EQ.Context,
partial :: IntMap EQ.Context,
vars :: IntMap Symbol
}
type PEQ = State Context
initial :: Int -> [(Symbol, Totality)] -> [Tagged Term] -> Context
initial d syms univ
| ok syms = Context total partial vars
| otherwise = __
where
ok syms = and (zipWith (==) [0..] (map (index . fst) syms))
total = EQ.initial d (map fst syms) (filter (isTotal Nothing [] . erase) univ)
partial = IntMap.fromList [
(i, EQ.initial d (map fst syms) (filter (isTotal (Just i) [] . erase) univ))
| (i, (sym, Variable)) <- zip [0..] syms
]
totality = IntMap.fromList [(index sym, tot) | (sym, tot) <- syms]
isTotal ctx args (Var x) = ctx /= Just (index x) && all (isTotal ctx []) args
isTotal ctx args (App f x) = isTotal ctx (x:args) f
isTotal ctx args (Const x) =
case IntMap.findWithDefault
(ERROR "type not found")
(index x) totality of
Partial -> False
Total pre -> and [ isTotal ctx [] arg || i `elem` pre | (i, arg) <- zip [0..] args ]
Variable -> __
vars = IntMap.fromList [(index s, s) | (s, Variable) <- syms]
runPEQ :: Context -> PEQ a -> (a, Context)
runPEQ = flip runState
evalPEQ :: Context -> PEQ a -> a
evalPEQ ctx x = fst (runPEQ ctx x)
execPEQ :: Context -> PEQ a -> Context
execPEQ ctx x = snd (runPEQ ctx x)
liftEQ :: [Int] -> (Maybe Int -> EQ a) -> PEQ [a]
liftEQ pre x = do
Context total partial vars <- S.get
let (totalRes, total') = runEQ total (x Nothing)
(partialRes, partial') = IntMap.mapAccumWithKey f [] partial
f rs i ctx
| i `elem` pre = runEQ ctx (fmap (:rs) (x (Just i)))
| otherwise = (rs, ctx)
S.put (Context total' partial' vars)
return (totalRes:partialRes)
equal :: PEquation -> PEQ Bool
equal (pre :\/: t :=: u) = liftM2 (==) (rep pre t) (rep pre u)
irrelevant :: Equation -> PEQ Precondition
irrelevant (t :=: u) = do
vs <- gets (IntMap.elems . vars)
return (vs \\ (Term.vars t `intersect` Term.vars u))
unify :: PEquation -> PEQ Bool
unify (pre :\/: eq) = do
irr <- irrelevant eq
fmap and . liftEQ (map index (pre ++ irr)) $ \n ->
case n of
Just i | i `notElem` map index pre -> return True
_ -> EQ.unify eq
precondition :: Equation -> PEQ Precondition
precondition eq = do
Context _ partial vars <- S.get
fmap concat . liftEQ (IntMap.keys partial) $ \n ->
case n of
Nothing -> return []
Just i -> do
r <- EQ.equal eq
if r then
return [IntMap.findWithDefault (ERROR "precondition: var not found") i vars]
else return []
get :: PEQ Context
get = S.get
put :: Context -> PEQ ()
put = S.put
rep :: Precondition -> Term -> PEQ [Int]
rep pre t = liftEQ (map index pre) (const (EQ.rep t))