packages feed

satplus-0.1.0.0: SAT/Order.hs

{-|
Module      : SAT.Order
Description : Comparison functions on things that live in the SAT-solver

This module provides a type class with functions for asserting the ordering
of two objects, as well as functions that compute whether or
not an object compares to another object.
-}
module SAT.Order(
  -- * Functions
    isGreaterThan
  , isLessThan
  , isGreaterThanEqual
  , isLessThanEqual

  -- * Constraints
  , greaterThan
  , lessThan
  , greaterThanEqual
  , lessThanEqual

  , greaterThanOr
  , lessThanOr
  , greaterThanEqualOr
  , lessThanEqualOr

  -- * Type class
  , Order(..)
  )
 where

import SAT
import SAT.Equal
import SAT.Util

import Prelude
import Control.Monad ( when )

------------------------------------------------------------------------------

-- | Type class for things that can be compared.
--
-- New instances only need to define the 'lessTupleOr' function. However, if
-- there is no natural way to implement lexicographic ordering with the
-- instance type, it is possible to only define 'lessOr', in which case
-- the default definition of 'lessTupleOr' is less efficient.
--
-- For types where it is easy to see statically if the answer is going to
-- be True or False, a special definition of 'newLessLit' can be made. For
-- most types, the default definition should be enough.
class Order a where
  -- | Add constraints to the Solver that state that the first argument is
  -- less than the second, under the presence of a /disjunctive prefix/.
  -- The extra argument specifies if the comparison should be strict (False)
  -- or inclusive (True).
  -- (See 'SAT.Util.unconditionally' for what /prefix/ means.)
  lessOr :: Solver -> [Lit] -> Bool -> a -> a -> IO ()
  lessOr s pre incl x y = lessTupleOr s pre incl (x,()) (y,())

  -- | Create a literal that implies the specified relationship between
  -- the arguments.
  newLessLit :: Solver -> Bool -> a -> a -> IO Lit
  newLessLit s incl x y =
    do q <- newLit s
       lessOr s [neg q] incl x y
       return q

  -- | Add constraints to the Solver that state that the first argument is
  -- less than the second, under the presence of a /disjunctive prefix/.
  -- The extra argument specifies if the comparison should be strict (False)
  -- or inclusive (True).
  -- (See 'SAT.Util.unconditionally' for what /prefix/ means.) This function
  -- is typically not going to be used directly by a user of this library;
  -- use 'compareOr' instead.
  lessTupleOr :: Order b => Solver -> [Lit] -> Bool -> (a,b) -> (a,b) -> IO ()
  lessTupleOr s pre incl (x,p) (y,q) =
    do w <- newLessLit s incl p q
       if w == false || w == true then
         do lessOr s pre (w == true) x y
        else
         do lessOr s pre     True  x y  -- x <= y
            lessOr s (w:pre) False x y  -- x < y | p <~ q

------------------------------------------------------------------------------

-- | Add constraints to the Solver that state that the arguments have the
-- specified relationship.
greaterThan, greaterThanEqual, lessThan, lessThanEqual ::
  Order a => Solver -> a -> a -> IO ()
greaterThan      = unconditionally greaterThanOr
greaterThanEqual = unconditionally greaterThanEqualOr
lessThan         = unconditionally lessThanOr
lessThanEqual    = unconditionally lessThanEqualOr

-- | Add constraints to the Solver that state that the arguments have the
-- specified relationship, under the presence of a /disjunctive prefix/.
-- (See 'SAT.Util.unconditionally' for what /prefix/ means.)
greaterThanOr, greaterThanEqualOr, lessThanOr, lessThanEqualOr ::
  Order a => Solver -> [Lit] -> a -> a -> IO ()
greaterThanOr      s pre x y = lessThanOr      s pre y x
greaterThanEqualOr s pre x y = lessThanEqualOr s pre y x
lessThanOr         s pre x y = lessOr s pre False x y
lessThanEqualOr    s pre x y = lessOr s pre True  x y

-- | Return a literal that indicates whether or not the arguments have
-- the specified relationship.
isGreaterThan, isGreaterThanEqual, isLessThan, isLessThanEqual ::
  Order a => Solver -> a -> a -> IO Lit
isGreaterThan      s x y = isLessThan      s y x
isGreaterThanEqual s x y = isLessThanEqual s y x
isLessThan         s x y = neg `fmap` isGreaterThanEqual s x y
isLessThanEqual    s x y =
  do q <- newLessLit s True x y
     when (q /= false && q /= true) $
       greaterThanOr s [q] x y
     return q

------------------------------------------------------------------------------

instance Order () where
  lessOr s pre True  _ _ = return ()
  lessOr s pre False _ _ = addClause s pre

  newLessLit s True  _ _ = return true
  newLessLit s False _ _ = return false

  lessTupleOr s pre incl (_,p) (_,q) =
    lessOr s pre incl p q

instance Order Bool where
  lessTupleOr s pre incl (x,p) (y,q) =
    case x `compare` y of
      LT -> return ()
      EQ -> lessOr s pre incl p q
      GT -> addClause s pre

  newLessLit s incl x y =
    case x `compare` y of
      LT -> return true
      EQ -> return (bool incl)
      GT -> return false

instance Order Lit where
  lessTupleOr s pre incl (x,p) (y,q)
    | x == y    = lessOr s pre incl p q
    | otherwise =
      do w <- newLessLit s incl p q
         addClause s ([y, w] ++ pre)
         addClause s ([neg x, w] ++ pre)
         addClause s ([neg x, y] ++ pre)

  newLessLit s incl x y
    | x == y     = return (bool incl)
    | x == neg y = return y
    | x == false = return (if incl then true  else y)
    | x == true  = return (if incl then y     else false)
    | y == false = return (if incl then neg x else false)
    | y == true  = return (if incl then true  else neg x)
    | otherwise  = do q <- newLit s
                      lessOr s [neg q] incl x y
                      return q

instance (Order a, Order b) => Order (a,b) where
  lessOr s pre incl t1 t2 =
    lessTupleOr s pre incl t1 t2

  lessTupleOr s pre incl t1 t2 =
    lessTupleOr s pre incl (encTuple t1) (encTuple t2)

encTuple ((x,y),r) = (x,(y,r))

instance (Order a, Order b) => Order (Either a b) where
  lessTupleOr s pre incl (Left x1,z1) (Left x2,z2) =
    lessTupleOr s pre incl (x1,z1) (x2,z2)

  lessTupleOr s pre incl (Right y1,z1) (Right y2,z2) =
    lessTupleOr s pre incl (y1,z1) (y2,z2)

  lessTupleOr s pre incl (Left _,z1) (Right _,z2) =
    return ()

  lessTupleOr s pre incl (Right _,z1) (Left _,z2) =
    addClause s pre

------------------------------------------------------------------------------

instance (Order a, Order b, Order c) => Order (a,b,c) where
  lessTupleOr s pre incl t1 t2 =
    lessTupleOr s pre incl (encTriple t1) (encTriple t2)

encTriple ((x,y,z),r) = (x,(y,(z,r)))

instance Order a => Order (Maybe a) where
  lessTupleOr s pre incl m1 m2 =
    lessTupleOr s pre incl (encMaybe m1) (encMaybe m2)

encMaybe (Nothing, r) = (Left (), r)
encMaybe (Just x,  r) = (Right x, r)

instance Order a => Order [a] where
  lessTupleOr s pre incl l1 l2 =
    lessTupleOr s pre incl (encList l1) (encList l2)

encList ([],     r) = (Left (),      r)
encList ((x:xs), r) = (Right (x,xs), r)