{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE CPP #-}
module Data.Algebra.Boolean.CNF.Set (
CNF(..),
fromDoubleList,
toDoubleList,
fromNNF,
module Data.Algebra.Boolean.NormalForm
) where
import Prelude hiding ((||),(&&),not,and,or,any,all)
#if !MIN_VERSION_base(4,11,0)
import Data.Monoid
#endif
import Data.Typeable (Typeable)
import Data.Foldable (Foldable)
import Control.DeepSeq (NFData(rnf))
import Data.Set (Set)
import qualified Data.Set as Set
import Data.List (sortBy, foldl')
import Data.Function (on)
import Data.Algebra.Boolean.NormalForm
import Data.Algebra.Boolean.Negable hiding (not)
import qualified Data.Algebra.Boolean.Negable as Negable
import Data.Algebra.Boolean.NNF.Tree
import Data.Algebra.Boolean
newtype CNF a = CNF { unCNF :: Set (Set a) }
deriving (Eq, Ord, Show, Read, Foldable, Typeable)
instance CoBoolean1 CNF where
toBooleanWith f = all (any f) . unCNF
instance CoBoolean a => CoBoolean (CNF a) where
toBoolean = toBooleanWith toBoolean
toDoubleList :: CNF a -> [[a]]
toDoubleList = map Set.toList . Set.toList . unCNF
fromDoubleList :: (Ord a) => [[a]] -> CNF a
fromDoubleList = CNF . Set.fromList . map Set.fromList
cnfNot :: (Ord a, Negable a) => CNF a -> CNF a
cnfNot = any and . map (map $ toNormalForm . Negable.not) . toDoubleList
instance (Ord a, Negable a) => Negable (CNF a) where
not = cnfNot
instance (Ord a, Negable a) => Boolean (CNF a) where
false = CNF $ Set.singleton Set.empty
true = CNF Set.empty
(CNF a) && (CNF b) = CNF (a <> b)
(CNF a) || (CNF b) = CNF $ Set.fromList [a' <> b' | a' <- Set.toList a, b' <- Set.toList b ]
not = cnfNot
fromLeft :: Either a b -> a
fromLeft (Left x) = x
fromLeft _ = error "fromLeft called on Right value"
optimize :: Ord a => Set (Set a) -> Set (Set a)
optimize = Set.fromList . f . sortBy (flip compare `on` Set.size) . Set.toList
where f disj = foldl' g disj disj
g disj item = filter (not . Set.isProperSubsetOf item) disj
fromNNF :: (Ord a, Negable a) => NNF a -> CNF a
fromNNF = toBooleanWith toNormalForm
instance NormalForm CNF where
type NFConstraint CNF a = (Negable a, Ord a)
toNormalForm = CNF . Set.singleton . Set.singleton
simplify f = CNF . optimize . p . g . Set.map h . Set.map (Set.map f') . unCNF
where f' x = case f x of
Just b -> Right b
Nothing -> Left x
h :: Ord a => Set (Either a Bool) -> Either (Set a) Bool
h disj | Right True `Set.member` disj = Right True
| Set.null l = Right False
| otherwise = Left l
where l = Set.mapMonotonic fromLeft $ fst $ Set.split (Right minBound) disj
g :: Ord a => Set (Either a Bool) -> Either (Set a) Bool
g conj | Right False `Set.member` conj = Right False
| Set.null l = Right True
| otherwise = Left l
where l = Set.mapMonotonic fromLeft $ fst $ Set.split (Right minBound) conj
p :: Either (Set (Set a)) Bool -> Set (Set a)
p (Right True) = Set.empty
p (Right False) = Set.singleton Set.empty
p (Left x) = x
fromFreeBoolean = fromNNF . toBooleanWith toNormalForm
instance NFData a => NFData (CNF a) where
rnf (CNF xss) = rnf xss