-- | -- Module : $Header$ -- Copyright : (c) 2014-2016 Galois, Inc. -- License : BSD3 -- Maintainer : cryptol@galois.com -- Stability : provisional -- Portability : portable -- -- TODO: -- - Putting in a normal form to spot "prove by assumption" -- - Additional simplification rules, namely various cancelation. -- - Things like: lg2 e(x) = x, where we know thate is increasing. {-# LANGUAGE Safe, PatternGuards, BangPatterns #-} {-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-} module Cryptol.TypeCheck.Solver.Numeric.Simplify ( -- * Simplify a property crySimplify, crySimplifyMaybe -- * Simplify expressions in a prop , crySimpPropExpr, crySimpPropExprMaybe ) where import Cryptol.TypeCheck.Solver.Numeric.AST import Cryptol.TypeCheck.Solver.Numeric.SimplifyExpr import Cryptol.TypeCheck.Solver.InfNat(genLog,genRoot,rootExact) import Cryptol.Utils.Misc ( anyJust ) import Control.Monad ( mplus ) import Data.List ( sortBy ) import Data.Maybe ( fromMaybe ) import qualified Data.Set as Set -- | Simplify a property, if possible. crySimplify :: Prop -> Prop crySimplify p = fromMaybe p (crySimplifyMaybe p) -- | Simplify a property, if possible. crySimplifyMaybe :: Prop -> Maybe Prop crySimplifyMaybe p = let mbSimpExprs = simpSubs p exprsSimped = fromMaybe p mbSimpExprs mbRearrange = tryRearrange exprsSimped rearranged = fromMaybe exprsSimped mbRearrange in crySimplify `fmap` (crySimpStep rearranged `mplus` mbRearrange `mplus` mbSimpExprs) where tryRearrange q = case q of _ :&& _ -> cryRearrangeAnd q _ :|| _ -> cryRearrangeOr q _ -> Nothing simpSubs q = case q of Not a -> Not `fmap` crySimplifyMaybe a a :&& b -> do [a',b'] <- anyJust crySimplifyMaybe [a,b] return (a' :&& b') a :|| b -> do [a',b'] <- anyJust crySimplifyMaybe [a,b] return (a' :|| b') _ -> crySimpPropExprMaybe q -- | A single simplification step. crySimpStep :: Prop -> Maybe Prop crySimpStep prop = case prop of Fin x -> cryIsFin x -- Fin only on variables. x :== y -> Just (cryIsEq x y) x :> y -> Just (cryIsGt x y) x :>= y -> case (x,y) of -- XXX: DUPLICTION (K (Nat 0), _) -> Just (y :== zero) (K (Nat a), Width b) -> Just (K (Nat (2 ^ a)) :>= b) (_, K (Nat 0)) -> Just PTrue (Width e, K (Nat b)) -> Just (e :>= K (Nat (2^(b-1)))) (K Inf, _) -> Just PTrue (_, K Inf) -> Just (x :== inf) _ -> Just (x :== inf :|| one :+ x :> y) x :==: y -> case (x,y) of (K a, K b) -> Just (if a == b then PTrue else PFalse) (K (Nat n), _) | Just p <- cryIsNat True n y -> Just p (_, K (Nat n)) | Just p <- cryIsNat True n x -> Just p _ | x == y -> Just PTrue | otherwise -> case (x,y) of (Var _, _) -> Nothing (_, Var _) -> Just (y :==: x) _ -> Nothing x :>: y -> case (x,y) of (K (Nat n),_) | Just p <- cryNatGt True n y -> Just p (_, K (Nat n)) | Just p <- cryGtNat True n x -> Just p _ | x == y -> Just PFalse | otherwise -> Nothing -- For :&& and :|| we assume that the props have been rearrnaged p :&& q -> cryAnd p q p :|| q -> cryOr p q Not p -> cryNot p PFalse -> Nothing PTrue -> Nothing -- | Rebalance parens, and arrange conjucts so that we can transfer -- information left-to-right. cryRearrangeAnd :: Prop -> Maybe Prop cryRearrangeAnd prop = case rebalance prop of Just p -> Just p Nothing -> cryAnds `fmap` cryRearrange cmpAnd (split prop) where rebalance (a :&& b) = case a of PFalse -> Just PFalse PTrue -> Just b a1 :&& a2 -> Just (a1 :&& (a2 :&& b)) _ -> fmap (a :&&) (rebalance b) rebalance _ = Nothing split (a :&& b) = a : split b split a = [a] -- | Rebalance parens, and arrange disjuncts so that we can transfer -- information left-to-right. cryRearrangeOr :: Prop -> Maybe Prop cryRearrangeOr prop = case rebalance prop of Just p -> Just p Nothing -> cryOrs `fmap` cryRearrange cmpOr (split prop) where rebalance (a :|| b) = case a of PFalse -> Just b PTrue -> Just PTrue a1 :|| a2 -> Just (a1 :|| (a2 :|| b)) _ -> fmap (a :||) (rebalance b) rebalance _ = Nothing split (a :|| b) = a : split b split a = [a] -- | Identify propositions that are suiatable for inlining. cryIsDefn :: Prop -> Maybe (Name, Expr) cryIsDefn (Var x :==: e) = if (x `Set.member` cryExprFVS e) then Nothing else Just (x,e) cryIsDefn _ = Nothing type PropOrdering = (Int,Prop) -> (Int,Prop) -> Ordering {- | Rearrange proposition for conjuctions and disjunctions. information left-to-right, so we put proposition with information content on the left. -} cryRearrange :: PropOrdering -> [Prop] -> Maybe [Prop] cryRearrange cmp ps = if ascending keys then Nothing else Just sortedProps where -- We tag each proposition with a number, so that later we can tell easily -- if the propositions got rearranged. (keys, sortedProps) = unzip (sortBy cmp (zip [ 0 :: Int .. ] ps)) ascending (x : y : zs) = x < y && ascending (y : zs) ascending _ = True cmpAnd :: PropOrdering cmpAnd (k1,prop1) (k2,prop2) = case (prop1, prop2) of -- First comes PFalse, maybe we don't need to do anything (PFalse, PFalse) -> compare k1 k2 (PFalse, _) -> LT (_,PFalse) -> GT -- Next comes PTrue (PTrue, PTrue) -> compare k1 k2 (PTrue, _) -> LT (_,PTrue) -> GT -- Next come `not (fin a)` (i.e, a = inf) (Not (Fin (Var x)), Not (Fin (Var y))) -> cmpVars x y (Not (Fin (Var _)), _) -> LT (_, Not (Fin (Var _))) -> GT -- Next come defintions: `x = e` (with `x` not in `fvs e`) -- XXX: Inefficient, because we keep recomputing free variables -- (here, and then when actually applying the substitution) _ | Just (x,_) <- mbL , Just (y,_) <- mbR -> cmpVars x y | Just _ <- mbL -> LT | Just _ <- mbR -> GT where mbL = cryIsDefn prop1 mbR = cryIsDefn prop2 -- Next come `fin a` (Fin (Var x), Fin (Var y)) -> cmpVars x y (Fin (Var _), _) -> LT (_, Fin (Var _)) -> GT -- Everything else stays as is _ -> compare k1 k2 where cmpVars x y | x < y = LT | x > y = GT | otherwise = compare k1 k2 cmpOr :: PropOrdering cmpOr (k1,prop1) (k2,prop2) = case (prop1, prop2) of -- First comes PTrue, maybe we don't need to do anything (PTrue, PTrue) -> compare k1 k2 (PTrue, _) -> LT (_,PTrue) -> GT -- Next comes PFalse (PFalse, PFalse) -> compare k1 k2 (PFalse, _) -> LT (_,PFalse) -> GT -- Next comes `fin a` (because we propagete `a = inf`) (Fin (Var x), Fin (Var y)) -> cmpVars x y (Fin (Var _), _) -> LT (_, Fin (Var _)) -> GT -- Next come `not (fin a)` (i.e, propagete (fin a)) (Not (Fin (Var x)), Not (Fin (Var y))) -> cmpVars x y (Not (Fin (Var _)), _) -> LT (_, Not (Fin (Var _))) -> GT -- we don't propagete (x /= e) for now. -- Everything else stays as is _ -> compare k1 k2 where cmpVars x y | x < y = LT | x > y = GT | otherwise = compare k1 k2 -- | Simplification of ':&&'. -- Assumes arranged conjucntions. -- See 'cryRearrangeAnd'. cryAnd :: Prop -> Prop -> Maybe Prop cryAnd p q = case p of PTrue -> Just q PFalse -> Just PFalse Not (Fin (Var x)) | Just q' <- cryKnownFin x False q -> Just (p :&& q') Fin (Var x) | Just q' <- cryKnownFin x True q -> Just (p :&& q') _ | Just (x,e) <- cryIsDefn p , Just q' <- cryLet x e q -> Just (p :&& q') _ -> Nothing -- | Simplification of ':||'. -- Assumes arranged disjunctions. -- See 'cryRearrangeOr' cryOr :: Prop -> Prop -> Maybe Prop cryOr p q = case p of PTrue -> Just PTrue PFalse -> Just q Fin (Var x) | Just q' <- cryKnownFin x False q -> Just (p :|| q') Not (Fin (Var x)) | Just q' <- cryKnownFin x True q -> Just (p :|| q') _ -> Nothing -- | Propagate the fact that the variable is known to be finite ('True') -- or not-finite ('False'). -- Note that this may introduce new expression redexes. cryKnownFin :: Name -> Bool -> Prop -> Maybe Prop cryKnownFin a isFin prop = case prop of Fin (Var a') | a == a' -> Just (if isFin then PTrue else PFalse) p :&& q -> do [p',q'] <- anyJust (cryKnownFin a isFin) [p,q] return (p' :&& q') p :|| q -> do [p',q'] <- anyJust (cryKnownFin a isFin) [p,q] return (p' :|| q') Not p -> Not `fmap` cryKnownFin a isFin p x :==: y | not isFin, Just [x',y'] <- anyJust (cryLet a inf) [x,y] -> Just (cryNatOp (:==:) x' y') x :>: y | not isFin, Just [x',y'] <- anyJust (cryLet a inf) [x,y] -> Just (cryNatOp (:>:) x' y') -- All the other cases should be simplified, eventually. _ -> Nothing -- | Negation. cryNot :: Prop -> Maybe Prop cryNot prop = case prop of Fin _ -> Nothing x :== y -> Just (x :> y :|| y :> x) x :>= y -> Just (y :> x) x :> y -> Just (y :>= x) x :==: y -> Just (x :>: y :|| y :>: x) _ :>: _ -> Nothing p :&& q -> Just (Not p :|| Not q) p :|| q -> Just (Not p :&& Not q) Not p -> Just p PFalse -> Just PTrue PTrue -> Just PFalse -- | Simplificaiton for @:==@ cryIsEq :: Expr -> Expr -> Prop cryIsEq l r = case (l,r) of (K m, K n) -> if m == n then PTrue else PFalse (K Inf, _) -> Not (Fin r) (_, K Inf) -> Not (Fin l) (Div x y, z) -> x :>= z :* y :&& (one :+ z) :* y :> x (K (Nat n),_) | Just p <- cryIsNat False n r -> p (_,K (Nat n)) | Just p <- cryIsNat False n l -> p _ -> Not (Fin l) :&& Not (Fin r) :|| Fin l :&& Fin r :&& cryNatOp (:==:) l r -- | Simplificatoin for @:>@ cryIsGt :: Expr -> Expr -> Prop cryIsGt (K m) (K n) = if m > n then PTrue else PFalse cryIsGt (K (Nat n)) e | Just p <- cryNatGt False n e = p cryIsGt e (K (Nat n)) | Just p <- cryGtNat False n e = p cryIsGt x y = Fin y :&& (x :== inf :|| Fin x :&& cryNatOp (:>:) x y) -- | Attempt to simplify a @fin@ constraint. -- Assumes a defined input. cryIsFin :: Expr -> Maybe Prop cryIsFin expr = case expr of K Inf -> Just PFalse K (Nat _) -> Just PTrue Var _ -> Nothing t1 :+ t2 -> Just (Fin t1 :&& Fin t2) t1 :- _ -> Just (Fin t1) t1 :* t2 -> Just ( Fin t1 :&& Fin t2 :|| t1 :== zero :&& t2 :== inf :|| t2 :== zero :&& t1 :== inf ) Div t1 _ -> Just (Fin t1) Mod _ _ -> Just PTrue t1 :^^ t2 -> Just ( Fin t1 :&& Fin t2 :|| t1 :== inf :&& t2 :== zero -- inf ^^ 0 :|| t2 :== inf :&& (t1 :== zero :|| t1 :== one) -- 0 ^^ inf, 1 ^^ inf ) Min t1 t2 -> Just (Fin t1 :|| Fin t2) Max t1 t2 -> Just (Fin t1 :&& Fin t2) Width t1 -> Just (Fin t1) LenFromThen _ _ _ -> Just PTrue LenFromThenTo _ _ _ -> Just PTrue cryIsNat :: Bool -> Integer -> Expr -> Maybe Prop cryIsNat useFinite n expr = case expr of K Inf -> Just PFalse K (Nat m) -> Just (if m == n then PTrue else PFalse) Var _ | useFinite -> Nothing | otherwise -> Just (Fin expr :&& expr :==: K (Nat n)) K (Nat m) :+ e2 -> Just $ if m > n then PFalse else eq e2 $ K $ Nat $ n - m x :+ y | n == 0 -> Just (eq x zero :&& eq y zero) | n == 1 -> Just (eq x zero :&& eq y one :|| eq x one :&& eq y zero) | otherwise -> Nothing e1 :- e2 -> Just $ eq (K (Nat n) :+ e1) e2 K (Nat m) :* e2 -> Just $ if m == 0 then if n == 0 then PTrue else PFalse else case divMod n m of (q,r) -> if r == 0 then eq e2 (K (Nat q)) else PFalse e1 :* e2 | n == 0 -> Just (eq e1 zero :|| eq e2 zero) | n == 1 -> Just (eq e1 one :&& eq e2 one) | otherwise -> Nothing -- (x >= n * y) /\ ((n+1) * y > x) Div x y -> Just (gt (one :+ x) (K (Nat n) :* y) :&& gt (K (Nat (n + 1)) :* y) x) Mod _ _ | useFinite -> Nothing | otherwise -> Just (cryNatOp (:==:) expr (K (Nat n))) K (Nat m) :^^ y -> Just $ case genLog n m of Just (a, exact) | exact -> eq y (K (Nat a)) _ -> PFalse x :^^ K (Nat m) -> Just $ case rootExact n m of Just a -> eq x (K (Nat a)) Nothing -> PFalse x :^^ y | n == 0 -> Just (eq x zero :&& gt y zero) | n == 1 -> Just (eq x one :|| eq y zero) | otherwise -> Nothing Min x y | n == 0 -> Just (eq x zero :|| eq y zero) | otherwise -> Just ( eq x (K (Nat n)) :&& gt y (K (Nat (n - 1))) :|| eq y (K (Nat n)) :&& gt x (K (Nat (n - 1))) ) Max x y -> Just ( eq x (K (Nat n)) :&& gt (K (Nat (n + 1))) y :|| eq y (K (Nat n)) :&& gt (K (Nat (n + 1))) y ) Width x | n == 0 -> Just (eq x zero) | otherwise -> Just (gt x (K (Nat (2^(n-1) - 1))) :&& gt (K (Nat (2 ^ n))) x) LenFromThen x y w | n == 0 -> Just PFalse -- See Note [Sequences of Length 1] in 'Cryptol.TypeCheck.Solver.InfNat' | n == 1 -> Just (gt y x :&& gt (y :+ one) (two :^^ w)) | otherwise -> Nothing -- XXX: maybe some more? -- See `nLenFromThenTo` in 'Cryptol.TypeCheck.Solver.InfNat' LenFromThenTo x y z | n == 0 -> Just ( gt x y :&& gt z x :|| gt y x :&& gt x z ) -- See Note [Sequences of Length 1] in 'Cryptol.TypeCheck.Solver.InfNat' | n == 1 -> Just (gt z y :&& gt (x :+ one) z :|| gt y z :&& gt (z :+ one) x) | otherwise -> Nothing -- XXX: maybe some more? where eq x y = if useFinite then x :==: y else x :== y gt x y = if useFinite then x :>: y else x :> y -- | Constant > expression cryNatGt :: Bool -> Integer -> Expr -> Maybe Prop cryNatGt useFinite n expr | n == 0 = Just PFalse | n == 1 = Just (eq expr zero) | otherwise = case expr of K x -> Just $ if Nat n > x then PTrue else PFalse Var _ -> Nothing K (Nat m) :+ y -> Just $ if n >= m then gt (k (n - m)) y else PFalse _ :+ _ -> Nothing x :- y -> Just $ gt (k n :+ y) x K (Nat m) :* y | m == 0 -> Just PTrue -- because we know that n > 1 | otherwise -> Just $ case divMod n m of (q,0) -> gt (k q) y (0,_) -> eq y zero (q,_) -> gt (k (q + 1)) y _ :* _ -> Nothing Div x y -> Just $ gt (k n :* y) x Mod _ (K (Nat m)) | m <= n -> Just PTrue Mod (K (Nat m)) _ | m < n -> Just PTrue Mod _ _ -> Nothing K (Nat m) :^^ y | m == 0 -> Just PTrue -- because n > 1 | m == 1 -> Just PTrue -- ditto | otherwise -> do (a,exact) <- genLog n m return $ if exact then gt (k a) y else gt (k (a + 1)) y x :^^ K (Nat m) | m == 0 -> Just PTrue | m == 1 -> Just (gt (k n) x) | otherwise -> do (a,exact) <- genRoot n m return $ if exact then gt (k a) x else gt (k (a + 1)) x _ :^^ _ -> Nothing Min x y -> Just $ gt (k n) x :|| gt (k n) y Max x y -> Just $ gt (k n) x :&& gt (k n) y Width x -> Just $ gt (k (2 ^ n)) x LenFromThen _ _ _ -> Nothing -- Are there some rules? LenFromThenTo _ _ _ -> Nothing -- Are there some rulesj where k x = K (Nat x) eq x y = if useFinite then x :==: y else x :== y gt x y = if useFinite then x :>: y else x :> y -- | Expression > constant cryGtNat :: Bool -> Integer -> Expr -> Maybe Prop cryGtNat useFinite n expr = case expr of K x -> Just $ if x > Nat n then PTrue else PFalse Var _ -> Nothing K (Nat m) :+ y | m > n -> Just PTrue | otherwise -> Just (gt y (K (Nat (n - m)))) x :+ y | n == 0 -> Just (gt x zero :|| gt y zero) | otherwise -> Nothing x :- y -> Just $ gt x (K (Nat n) :+ y) K (Nat m) :* y | m > 0 -> Just $ case divMod n m of (a,_) -> gt y $ K $ Nat a x :* y | n == 0 -> Just (gt x zero :&& gt y zero) | otherwise -> Nothing Div x y -> Just $ gt (one :+ x) (K (Nat (n+1)) :* y) Mod _ (K (Nat m)) | m <= n -> Just PFalse Mod (K (Nat m)) _ | m < n -> Just PFalse Mod _ _ -> Nothing K (Nat m) :^^ y | m == 0 -> Just $ if n == 0 then eq y zero else PFalse | m == 1 -> Just $ if n == 0 then PTrue else PFalse | otherwise -> do (a,_exact) <- genLog n m Just (gt y (K (Nat a))) x :^^ K (Nat m) | m == 0 -> Just $ if n == 0 then PTrue else PFalse | m == 1 -> Just $ gt x (K (Nat n)) | otherwise -> do (a,exact) <- genRoot n m Just $ if exact then gt x (K (Nat a)) else gt (one :+ x) (K (Nat (a+1))) x :^^ y | n == 0 -> Just (gt x zero :|| eq y zero) | otherwise -> Nothing Min x y -> Just $ gt x (K (Nat n)) :&& gt y (K (Nat n)) Max x y -> Just $ gt x (K (Nat n)) :|| gt y (K (Nat n)) Width x -> Just $ gt (one :+ x) (K (Nat (2 ^ n))) LenFromThen _ _ _ | n == 0 -> Just PTrue | otherwise -> Nothing -- Are there some rules? LenFromThenTo x y z | n == 0 -> Just (gt x y :&& gt z x :|| gt y x :&& gt x z) | otherwise -> Nothing where eq x y = if useFinite then x :==: y else x :== y gt x y = if useFinite then x :>: y else x :> y -- | Simplify only the Expr parts of a Prop. crySimpPropExpr :: Prop -> Prop crySimpPropExpr p = fromMaybe p (crySimpPropExprMaybe p) -- | Simplify only the Expr parts of a Prop. -- Returns `Nothing` if there were no changes. crySimpPropExprMaybe :: Prop -> Maybe Prop crySimpPropExprMaybe prop = case prop of Fin e -> Fin `fmap` crySimpExprMaybe e a :== b -> binop crySimpExprMaybe (:== ) a b a :>= b -> binop crySimpExprMaybe (:>= ) a b a :> b -> binop crySimpExprMaybe (:> ) a b a :==: b -> binop crySimpExprMaybe (:==:) a b a :>: b -> binop crySimpExprMaybe (:>: ) a b a :&& b -> binop crySimpPropExprMaybe (:&&) a b a :|| b -> binop crySimpPropExprMaybe (:||) a b Not p -> Not `fmap` crySimpPropExprMaybe p PFalse -> Nothing PTrue -> Nothing where binop simp f l r = case (simp l, simp r) of (Nothing,Nothing) -> Nothing (l',r') -> Just (f (fromMaybe l l') (fromMaybe r r')) -- | Our goal is to bubble @inf@ terms to the top of @Return@. cryNoInf :: Expr -> IfExpr Expr cryNoInf expr = case expr of -- These are the interesting cases where we have to branch x :* y -> do x' <- cryNoInf x y' <- cryNoInf y case (x', y') of (K Inf, K Inf) -> return inf (K Inf, _) -> mkIf (y' :==: zero) (return zero) (return inf) (_, K Inf) -> mkIf (x' :==: zero) (return zero) (return inf) _ -> return (x' :* y') x :^^ y -> do x' <- cryNoInf x y' <- cryNoInf y case (x', y') of (K Inf, K Inf) -> return inf (K Inf, _) -> mkIf (y' :==: zero) (return one) (return inf) (_, K Inf) -> mkIf (x' :==: zero) (return zero) $ mkIf (x' :==: one) (return one) $ return inf _ -> return (x' :^^ y') -- The rest just propagates K _ -> return expr Var _ -> return expr x :+ y -> do x' <- cryNoInf x y' <- cryNoInf y case (x', y') of (K Inf, _) -> return inf (_, K Inf) -> return inf _ -> return (x' :+ y') x :- y -> do x' <- cryNoInf x y' <- cryNoInf y case (x', y') of (_, K Inf) -> Impossible (K Inf, _) -> return inf _ -> mkIf (x' :==: y) (return zero) (mkIf (x' :>: y) (return (x' :- y')) Impossible) Div x y -> do x' <- cryNoInf x y' <- cryNoInf y case (x', y') of (K Inf, _) -> Impossible (_, K Inf) -> return zero _ -> mkIf (y' :>: zero) (return (Div x' y')) Impossible Mod x y -> do x' <- cryNoInf x -- `Mod x y` is finite, even if `y` is `inf`, so first check -- for finiteness. mkIf (Fin y) (do y' <- cryNoInf y case (x',y') of (K Inf, _) -> Impossible (_, K Inf) -> Impossible _ -> mkIf (y' :>: zero) (return (Mod x' y')) Impossible ) (return x') Min x y -> do x' <- cryNoInf x y' <- cryNoInf y case (x',y') of (K Inf, _) -> return y' (_, K Inf) -> return x' _ -> return (Min x' y') Max x y -> do x' <- cryNoInf x y' <- cryNoInf y case (x', y') of (K Inf, _) -> return inf (_, K Inf) -> return inf _ -> return (Max x' y') Width x -> do x' <- cryNoInf x case x' of K Inf -> return inf _ -> return (Width x') LenFromThen x y w -> fun3 LenFromThen x y w LenFromThenTo x y z -> fun3 LenFromThenTo x y z where fun3 f x y z = do x' <- cryNoInf x y' <- cryNoInf y z' <- cryNoInf z case (x',y',z') of (K Inf, _, _) -> Impossible (_, K Inf, _) -> Impossible (_, _, K Inf) -> Impossible _ -> mkIf (x' :==: y') Impossible (return (f x' y' z')) mkIf p t e = case crySimplify p of PTrue -> t PFalse -> e p' -> If p' t e -- | Make an expression that should work ONLY on natural nubers. -- Eliminates occurances of @inf@. -- Assumes that the two input expressions are well-formed and finite. -- The expression is constructed by the given function. cryNatOp :: (Expr -> Expr -> Prop) -> Expr -> Expr -> Prop cryNatOp op x y = toProp $ do x' <- noInf x y' <- noInf y return (op x' y') where noInf a = do a' <- cryNoInf a case a' of K Inf -> Impossible _ -> return a' toProp ite = case ite of Impossible -> PFalse -- It doesn't matter, but @false@ might anihilate. Return p -> p If p t e -> p :&& toProp t :|| Not p :&& toProp e