packages feed

ideas-0.6: src/Common/Rewriting/AC.hs

-----------------------------------------------------------------------------
-- Copyright 2010, Open Universiteit Nederland. This file is distributed 
-- under the terms of the GNU General Public License. For more information, 
-- see the file "LICENSE.txt", which is included in the distribution.
-----------------------------------------------------------------------------
-- |
-- Maintainer  :  bastiaan.heeren@ou.nl
-- Stability   :  provisional
-- Portability :  portable (depends on ghc)
--
-----------------------------------------------------------------------------
module Common.Rewriting.AC 
   ( Operator, Operators, constructor, destructor
   , newOperator, associativeOperator, commutativeOperator, acOperator
   , makeAssociative, makeCommutative, isAssociative, isCommutative
   , collectWithOperator, buildWithOperator
   , isOperator, findOperator
   , normalizeWith, equalWith
   , pairings, pairingsMatch
   , pairingsA2, onBoth
   ) where

import Common.Uniplate
import Common.Utils
import Data.List
import Data.Maybe

-----------------------------------------------------------
-- AC theories

type Operators a = [Operator a]

data Operator a = O 
   { constructor   :: a -> a -> a
   , destructor    :: a -> Maybe (a, a)
   , isAssociative :: Bool
   , isCommutative :: Bool
   }
   
newOperator :: (a -> a -> a) ->  (a -> Maybe (a, a)) -> Operator a
newOperator f g = O f g False False

associativeOperator, commutativeOperator, acOperator :: (a -> a -> a) ->  (a -> Maybe (a, a)) -> Operator a
associativeOperator f = makeAssociative . newOperator f
commutativeOperator f = makeCommutative . newOperator f
acOperator          f = makeAssociative . commutativeOperator f

makeCommutative, makeAssociative :: Operator a -> Operator a
makeCommutative op = op { isCommutative = True }
makeAssociative op = op { isAssociative = True  }

collectWithOperator :: Operator a -> a -> [a]
collectWithOperator op a
   | isAssociative op = rec a []
   | otherwise        = maybe [a] (\(x, y) -> [x, y]) (destructor op a)
 where
   rec a = case destructor op a of
              Just (x, y) -> rec x . rec y
              Nothing     -> (a:)

buildWithOperator :: Operator a -> [a] -> a
buildWithOperator op xs 
   | null xs = 
        error "Rewriting.buildWithOperator: empty list"
   | not (isAssociative op) && length xs > 2 =
        error "Rewriting.buildWithOperator: non-associative operator"
   | otherwise = 
        foldr1 (constructor op) xs
   
isOperator :: Operator a -> a -> Bool
isOperator op = isJust . destructor op

findOperator :: Operators a -> a -> Maybe (Operator a)
findOperator ops a = safeHead $ filter (`isOperator` a) ops

normalizeWith :: (Uniplate a, Ord a) => Operators a -> a -> a
normalizeWith ops = rec
 where
   rec a = 
      case findOperator ops a of
         Just op -> 
            buildWithOperator op $ (if isCommutative op then sort else id) $ map rec $ collectWithOperator op a
         Nothing -> 
            let (cs, f) = uniplate a
            in f (map rec cs)

equalWith :: (Uniplate a, Ord a) => Operators a -> a -> a -> Bool
equalWith ops x y = normalizeWith ops x == normalizeWith ops y

-----------------------------------------------------------
-- Pairing terms with an AC theory
-- matchMode: the left-hand sides cannot have the operator at top-level 

pairings, pairingsMatch :: Operator a -> a -> a -> [[(a, a)]]
pairings      = pairingsMode False
pairingsMatch = pairingsMode True

pairingsMode :: Bool -> Operator a -> a -> a -> [[(a, a)]]
pairingsMode matchMode op =
   case (isAssociative op, isCommutative op) of
      (True , True ) -> pairingsAC matchMode op
      (True , False) -> pairingsA  matchMode op
      (False, True ) -> pairingsC op
      (False, False) -> pairingsNone op

-- non-associative, non-commutative pairings
pairingsNone :: Operator a -> a -> a -> [[(a, a)]]
pairingsNone op a b =
   case (destructor op a, destructor op b) of
      (Just (a1, a2), Just (b1, b2)) -> [[(a1, b1), (a2, b2)]]
      _ -> []
      
-- commutative pairings
pairingsC :: Operator a -> a -> a -> [[(a, a)]]
pairingsC op a b = 
   case (destructor op a, destructor op b) of
      (Just (a1, a2), Just (b1, b2)) -> [[(a1, b1), (a2, b2)], [(a1, b2), (a2, b1)]]
      _ -> []

-- associative pairings
pairingsA :: Bool -> Operator a -> a -> a -> [[(a, a)]]
pairingsA matchMode op a b = map (map make) result
 where 
   (as, bs) = onBoth (collectWithOperator op) (a, b)
   result   = pairingsA2 matchMode as bs
   make     = onBoth (buildWithOperator op)

pairingsA2 :: Bool -> [a] -> [a] -> [[([a], [a])]]
pairingsA2 matchMode = rec
 where
   rec [] [] = [[]]
   rec as bs = 
      [ (a1, b1):ps
      | i <- if matchMode && not (null as) then [1] else [1 .. length as]
      , j <- [1 .. length bs]
      , i==1 || j==1
      , let (as1, as2) = splitAt i as
      , let (bs1, bs2) = splitAt j bs
      , let a1 = as1
      , let b1 = bs1
      , ps <- rec as2 bs2
      ]

-- associative/commutative pairings
pairingsAC :: Bool -> Operator a -> a -> a -> [[(a, a)]]  
pairingsAC matchMode op a b = rec (collectWithOperator op a) (collectWithOperator op b)
 where
   rec [] [] = [[]]
   rec [] _  = []
   rec (a:as) bs = 
      [ (a1, b1):ps
      | (asr, as2) <- if matchMode then [([], as)] else splits as
      , let as1 = a:asr
      , (bs1, bs2) <- splits bs
      , not (null bs1)
      , length as1==1 || length bs1==1
      , let a1 = buildWithOperator op as1
      , let b1 = buildWithOperator op bs1
      , ps <- rec as2 bs2
      ]

{-
data Tree = Leaf String | Bin Tree Tree deriving (Show, Eq, Ord)

opBin :: Operator Tree
opBin = Operator isBin Bin
 where
   isBin (Bin a b) = Just (a, b)
   isBin _ = Nothing
   
tree1 = Bin (Bin (Leaf "1") (Leaf "2")) (Bin (Leaf "3") (Leaf "4")) -- Bin (Bin (Leaf "a") (Leaf "b")) (Bin (Leaf "c") (Leaf "d"))
tree2 = Bin (Bin (Leaf "a") (Leaf "b")) (Bin (Leaf "c") (Leaf "d")) --Bin (Bin (Leaf "w") (Leaf "x")) (Bin (Leaf "y") (Leaf "z"))

ex1 = pairingsC opBin tree1 tree2
ex2 = pairingsA  False opBin tree1 tree2
ex3 = pairingsA  True  opBin tree1 tree2
ex4 = pairingsAC False opBin tree1 tree2
ex5 = pairingsAC True opBin tree1 tree2 -}

splits :: [a] -> [([a], [a])]
splits = foldr insert [([], [])]
 where
   insert a ps = 
      let toLeft  (xs, ys) = (a:xs,   ys)
          toRight (xs, ys) = (  xs, a:ys)
      in map toLeft ps ++ map toRight ps

onBoth :: (a -> b) -> (a, a) -> (b, b)
onBoth f (x, y) = (f x, f y)

{-
permutations :: [a] -> [[a]]
permutations = foldr (concatMap . insert) [[]]
 where
   insert a []     = [[a]]
   insert a (x:xs) = (a:x:xs) : map (x:) (insert a xs)
-}