-- | -- Module : $Header$ -- Copyright : (c) 2015-2016 Galois, Inc. -- License : BSD3 -- Maintainer : cryptol@galois.com -- Stability : provisional -- Portability : portable -- -- Simplification of expressions. -- The result of simplifying an expression `e`, is a new expression `e'`, -- which satisfies the property: -- -- if e is well-defined then e and e' will evaluate to the same type. -- {-# LANGUAGE Safe, BangPatterns #-} module Cryptol.TypeCheck.Solver.Numeric.SimplifyExpr where import Cryptol.TypeCheck.Solver.Numeric.AST import qualified Cryptol.TypeCheck.Solver.InfNat as IN import Cryptol.Utils.Misc ( anyJust ) import Control.Monad ( guard ) import Data.Maybe ( fromMaybe, maybeToList ) import qualified Data.Map as Map -- | Simplify an expression, if possible. crySimpExpr :: Expr -> Expr crySimpExpr expr = fromMaybe expr (crySimpExprMaybe expr) -- | Perform simplification from the leaves up. -- Returns `Nothing` if there were no changes. crySimpExprMaybe :: Expr -> Maybe Expr crySimpExprMaybe expr = case crySimpExprStep (fromMaybe expr mbE1) of Nothing -> mbE1 Just e2 -> Just (fromMaybe e2 (crySimpExprMaybe e2)) where mbE1 = cryRebuildExpr expr `fmap` anyJust crySimpExprMaybe (cryExprExprs expr) -- XXX: Add rules to group together occurances of variables data Sign = Pos | Neg deriving Show otherSign :: Sign -> Sign otherSign s = case s of Pos -> Neg Neg -> Pos signed :: Sign -> Integer -> Integer signed s = case s of Pos -> id Neg -> negate splitSum :: Expr -> [(Sign,Expr)] splitSum e0 = go Pos e0 [] where go s (e1 :+ e2) es = go s e1 (go s e2 es) go s (e1 :- e2) es = go s e1 (go (otherSign s) e2 es) go s e es = (s,e) : es normSum :: Expr -> Expr normSum = posTerm . go 0 Map.empty Nothing . splitSum where -- constants, variables, other terms go !_ !_ !_ ((Pos,K Inf) : _) = (Pos, K Inf) go k xs t ((s, K (Nat n)) : es) = go (k + signed s n) xs t es go k xs t ((s, Var x) : es) = go k (Map.insertWith (+) x (signed s 1) xs) t es go k xs t ((s, K (Nat n) :* Var x) : es) | n == 0 = go k xs t es | otherwise = go k (Map.insertWith (+) x (signed s n) xs) t es go k xs Nothing (e : es) = go k xs (Just e) es go k xs (Just e1) (e2 : es) = go k xs (Just (add e1 e2)) es go k xs t [] = let terms = constTerm k ++ concatMap varTerm (Map.toList xs) ++ maybeToList t in case terms of [] -> (Pos, K (Nat 0)) ts -> foldr1 add ts constTerm k | k == 0 = [] | k > 0 = [ (Pos, K (Nat k)) ] | otherwise = [ (Neg, K (Nat (negate k))) ] varTerm (x,k) | k == 0 = [] | k == 1 = [ (Pos, Var x) ] | k > 0 = [ (Pos, K (Nat k) :* Var x) ] | k == (-1) = [ (Neg, Var x) ] | otherwise = [ (Neg, K (Nat (negate k)) :* Var x) ] add (s1,t1) (s2,t2) = case (s1,s2) of (Pos,Pos) -> (Pos, t1 :+ t2) (Pos,Neg) -> (Pos, t1 :- t2) (Neg,Pos) -> (Pos, t2 :- t1) (Neg,Neg) -> (Neg, t1 :+ t2) posTerm (Pos,x) = x posTerm (Neg,x) = K (Nat 0) :- x crySimpExprStep :: Expr -> Maybe Expr crySimpExprStep e = case crySimpExprStep1 e of Just e1 -> Just e1 Nothing -> do let e1 = normSum e guard (e /= e1) return e1 -- | Make a simplification step, assuming the expression is well-formed. crySimpExprStep1 :: Expr -> Maybe Expr crySimpExprStep1 expr = case expr of K _ -> Nothing Var _ -> Nothing _ :+ _ -> Nothing _ :- _ -> Nothing x :* y -> case (x,y) of (K (Nat 0), _) -> Just zero (K (Nat 1), _) -> Just y (K a, K b) -> Just (K (IN.nMul a b)) (_, K _) -> Just (y :* x) (K a, K b :* z) -> Just (K (IN.nMul a b) :* z) -- Normalize, somewhat (a :* b, _) -> Just (a :* (b :* y)) (Var a, Var b) | b > a -> Just (y :* x) _ -> Nothing Div x y -> case (x,y) of (K (Nat 0), _) -> Just zero (_, K (Nat 1)) -> Just x (_, K Inf) -> Just zero (K a, K b) -> K `fmap` IN.nDiv a b _ | x == y -> Just one _ -> Nothing Mod x y -> case (x,y) of (K (Nat 0), _) -> Just zero (_, K Inf) -> Just x (_, K (Nat 1)) -> Just zero (K a, K b) -> K `fmap` IN.nMod a b _ -> Nothing x :^^ y -> case (x,y) of (_, K (Nat 0)) -> Just one (_, K (Nat 1)) -> Just x (K (Nat 1), _) -> Just one (K a, K b) -> Just (K (IN.nExp a b)) _ -> Nothing Min x y -> case (x,y) of (K (Nat 0), _) -> Just zero (K Inf, _) -> Just y (_, K (Nat 0)) -> Just zero (_, K Inf) -> Just x (K a, K b) -> Just (K (IN.nMin a b)) _ | x == y -> Just x _ -> Nothing Max x y -> case (x,y) of (K (Nat 0), _) -> Just y (K Inf, _) -> Just inf (_, K (Nat 0)) -> Just x (_, K Inf) -> Just inf (K a, K b) -> Just (K (IN.nMax a b)) _ | x == y -> Just x _ -> Nothing Width x -> case x of K a -> Just (K (IN.nWidth a)) K (Nat 2) :^^ e -> Just (one :+ e) K (Nat 2) :^^ e :- K (Nat 1) -> Just e _ -> Nothing LenFromThen x y w -> case (x,y,w) of (K a, K b, K c) -> K `fmap` IN.nLenFromThen a b c _ -> Nothing LenFromThenTo x y z -> case (x,y,z) of (K a, K b, K c) -> K `fmap` IN.nLenFromThenTo a b c _ -> Nothing