-- | -- Module : Test.Speculate.Reason.Order -- Copyright : (c) 2016-2019 Rudy Matela -- License : 3-Clause BSD (see the file LICENSE) -- Maintainer : Rudy Matela -- -- This module is part o Speculate. -- -- Orders for term rewriting and completion. module Test.Speculate.Reason.Order ( (|>|) , (>|) , (|>) , kboBy , dwoBy , weight , weightExcept , gtExcept , funny , serious ) where import Test.Speculate.Expr import Test.Speculate.Utils (nubMerge) -- | Greater than or equal number of occurences of each variable (>=\/) :: Expr -> Expr -> Bool e1 >=\/ e2 = all (\e -> countVar e e1 >= countVar e e2) (nubVars e1 `nubMerge` nubVars e2) where countVar e = length . filter (== e) . vars -- | Strict order between expressions as defined in TRAAT p103. -- -- > s > t iff |s| > |t| and , for all x in V, |s|_x > |t|_x -- -- This is perhaps the simplest order that can be used with KBC. (|>|) :: Expr -> Expr -> Bool e1 |>| e2 = size e1 > size e2 && e1 >=\/ e2 infix 4 |>| -- | Strict order between expressions loosely as defined in TRAAT p124 (KBO) -- -- Reversed K @>|@ for Knuth, sorry Bendix. (>|) :: Expr -> Expr -> Bool (>|) = kboBy weight (>) infix 4 >| kboBy :: (Expr -> Int) -> (Expr -> Expr -> Bool) -> Expr -> Expr -> Bool kboBy w (->-) e1 e2 = e1 >=\/ e2 && ( w e1 > w e2 || w e1 == w e2 && ( e1 `fn` e2 -- f (f x) > x || e1 `fg` e2 -- f x > g y if f > g || e1 `ff` e2 -- f x y z > f x w v if y > w ) ) where ef :$ (eg :$ ex) `fn` ey | isVar ey && ef == eg = fn (eg :$ ex) ey ef@(Value _ _) :$ ex `fn` ey | isVar ex && isVar ey && ex == ey = True _ `fn` _ = False e1 `fg` e2 = case (unfoldApp e1, unfoldApp e2) of -- do I really need the _:_ instead of just _? -- do I really need to restrict to functional values? (ef:(_:_),eg:(_:_)) | isConst ef && isConst eg -> ef ->- eg _ -> False e1 `ff` e2 = case (unfoldApp e1, unfoldApp e2) of -- Not restricting to functional values. -- Since we are making an equality comparison, -- this hopefully will be strict enough not bo break KBO. (f:xs,g:ys) -> f == g && length xs == length ys && case dropEq xs ys of (x:_,y:_) -> x >=\/ y _ -> False _ -> False -- | Weight function for kboBy: -- -- * Variables weigh 1 -- * Nullary functions weigh 1 (a.k.a. constants) -- * N-ary functions weigh 0 -- * Unary functions weigh 1 -- -- This is the weight when using '>|'. weight :: Expr -> Int weight = w where w (e1 :$ e2) = weight e1 + weight e2 w e | isVar e = 1 | otherwise = case arity e of 0 -> 1 1 -> 1 _ -> 0 -- | Weight function for kboBy: -- -- * Variables weigh 1 -- * Nullary functions weigh 1 (a.k.a. constants) -- * N-ary functions weigh 0 -- * Unary functions weigh 1 except for the one given as argument weightExcept :: Expr -> Expr -> Int weightExcept f0 = w where w (e1 :$ e2) = w e1 + w e2 w e | isVar e = 1 | otherwise = case arity e of 0 -> 1 1 -> if e == f0 then 0 else 1 _ -> 0 -- | To be used alongside weightExcept gtExcept :: (Expr -> Expr -> Bool) -> Expr -> Expr -> Expr -> Bool gtExcept (>) f0 e1 e2 | e2 == f0 = False -- nothing can be greater than f0 | e1 == f0 = True -- f0 is greater than everything | otherwise = e1 > e2 -- compare normally -- Note this default Dershowitz order can sometimes be weird: -- -- > x - y |> x + negate y -- as (-) > (+) -- > negate x + y |> negate (x + negate y) -- as (+) > negate, as I->I->I > I->I -- -- This is not a simplification order on 'funny' 'Expr's. (|>) :: Expr -> Expr -> Bool (|>) = dwoBy (>) infix 4 |> -- | Dershowitz reduction order as defined in TRAAT -- -- @|>@ a "D" for Dershowitz -- -- This is not a simplification order on 'funny' 'Expr's. dwoBy :: (Expr -> Expr -> Bool) -> Expr -> Expr -> Bool dwoBy (>) = (|>) where e1 |> e2 | isVar e2 && e2 `elem` nubVars e1 && e1 /= e2 = True e1 |> e2 = any (|>= e2) xs || (notVar f && notVar g && f > g && all (e1 |>) ys) || (notVar f && notVar g && f == g && all (e1 |>) ys && case dropEq xs ys of (x:_,y:_) -> x |> y _ -> False) where (f:xs) = unfoldApp e1 (g:ys) = unfoldApp e2 notVar = not . isVar e1 |>= e2 = e1 == e2 || e1 |> e2 -- | Returns 'True' when an 'Expr'ession has either -- a functional type or functional 'var'iables. -- -- > funny xx = False -- > funny one = False -- > funny idE = True -- > funny (ff one) = True funny :: Expr -> Bool funny e = any (isFunTy . typ) $ e:vars e -- | Returns 'True' when an 'Expr'ession has -- no functional return type and no functional 'var'iables. -- -- > serious xx = True -- > serious one = True -- > serious idE = False -- > serious (ff one) = False serious :: Expr -> Bool serious = not . funny --- Misc Utilities --- dropEq :: Eq a => [a] -> [a] -> ([a],[a]) dropEq (x:xs) (y:ys) | x == y = dropEq xs ys dropEq xs ys = (xs,ys)