bv-little-1.3.0: util/Operator/Binary/Logical.hs
{-|
Copyright : © 2020 Alex Washburn
License : BSD-3-Clause
Maintainer : github@recursion.ninja
Stability : Stable
-}
{-# Language DeriveAnyClass #-}
{-# Language DeriveDataTypeable #-}
{-# Language DeriveGeneric #-}
{-# Language DerivingStrategies #-}
{-# Language FlexibleInstances #-}
{-# Language MultiParamTypeClasses #-}
{-# Language Safe #-}
module Operator.Binary.Logical
( BinaryLogicalOperator ()
, fromBinaryLogicalFunction
, getBinaryLogicalOperator
, getBinaryLogicalSymbol
) where
import Control.DeepSeq
import Data.Data
import Data.Monoid ()
import GHC.Generics
import Test.QuickCheck hiding (generate)
import Test.SmallCheck.Series
{-|
Representation of all possible /binary/ operators of type @(Bool -> Bool -> Bool)@.
Useful for both property and enumeration based testing.
-}
data BinaryLogicalOperator
= AlwaysFalse
| LogicalNOR
| ConverseNonImplication
| NotFirstArgument
| NonImplication
| NotSecondArgument
| LogicalXOR
| LogicalNAND
| LogicalAND
| LogicalXNOR
| SecondArgument
| Implication
| FirstArgument
| ConverseImplication
| LogicalOR
| AlwaysTrue
deriving anyclass (NFData)
deriving stock (Data, Eq, Generic, Ord)
instance Arbitrary BinaryLogicalOperator where
arbitrary = arbitraryBoundedEnum
instance Bounded BinaryLogicalOperator where
minBound = AlwaysFalse
maxBound = AlwaysTrue
instance CoArbitrary BinaryLogicalOperator where
coarbitrary = coarbitraryEnum
instance Enum BinaryLogicalOperator where
toEnum n = case n `rem` 16 of
0 -> AlwaysFalse
1 -> LogicalNOR
2 -> ConverseNonImplication
3 -> NotFirstArgument
4 -> NonImplication
5 -> NotSecondArgument
6 -> LogicalXOR
7 -> LogicalNAND
8 -> LogicalAND
9 -> LogicalXNOR
10 -> SecondArgument
11 -> Implication
12 -> FirstArgument
13 -> ConverseImplication
14 -> LogicalOR
_ -> AlwaysTrue
fromEnum x = case x of
AlwaysFalse -> 0
LogicalNOR -> 1
ConverseNonImplication -> 2
NotFirstArgument -> 3
NonImplication -> 4
NotSecondArgument -> 5
LogicalXOR -> 6
LogicalNAND -> 7
LogicalAND -> 8
LogicalXNOR -> 9
SecondArgument -> 10
Implication -> 11
FirstArgument -> 12
ConverseImplication -> 13
LogicalOR -> 14
AlwaysTrue -> 15
succ x = case x of
AlwaysFalse -> LogicalNOR
LogicalNOR -> ConverseNonImplication
ConverseNonImplication -> NotFirstArgument
NotFirstArgument -> NonImplication
NonImplication -> NotSecondArgument
NotSecondArgument -> LogicalXOR
LogicalXOR -> LogicalNAND
LogicalNAND -> LogicalAND
LogicalAND -> LogicalXNOR
LogicalXNOR -> SecondArgument
SecondArgument -> Implication
Implication -> FirstArgument
FirstArgument -> ConverseImplication
ConverseImplication -> LogicalOR
LogicalOR -> AlwaysTrue
AlwaysTrue -> AlwaysFalse
pred x = case x of
AlwaysFalse -> AlwaysTrue
LogicalNOR -> AlwaysFalse
ConverseNonImplication -> LogicalNOR
NotFirstArgument -> ConverseNonImplication
NonImplication -> NotFirstArgument
NotSecondArgument -> NonImplication
LogicalXOR -> NotSecondArgument
LogicalNAND -> LogicalXOR
LogicalAND -> LogicalNAND
LogicalXNOR -> LogicalAND
SecondArgument -> LogicalXNOR
Implication -> SecondArgument
FirstArgument -> Implication
ConverseImplication -> FirstArgument
LogicalOR -> ConverseImplication
AlwaysTrue -> LogicalOR
-- enumFrom x = toEnum <$> [fromEnum x .. 15]
-- enumFromTo x y =
instance Monad m => Serial m BinaryLogicalOperator where
series = generate $ const [minBound .. maxBound]
instance Show BinaryLogicalOperator where
show x = "f p q = " <> s
where
s = case x of
AlwaysFalse -> "False (Contradiction)"
LogicalNOR -> "¬p ∧ ¬q (Logical NOR)"
ConverseNonImplication -> "¬p ∧ q (Converse Non-Implication)"
NotFirstArgument -> "¬p (Not First)"
NonImplication -> "p ∧ ¬q (Non-Implication)"
NotSecondArgument -> "¬q (Not Second)"
LogicalXOR -> "(p ∧ ¬q) ∨ (¬p ∧ q) (Logical XOR)"
LogicalNAND -> "¬p ∨ ¬q (Logical NAND)"
LogicalAND -> "p ∧ q (Logical AND)"
LogicalXNOR -> "(p ∧ q) ∨ (¬p ∧ ¬q) (Logical XNOR)"
SecondArgument -> "q (Second)"
Implication -> "¬p ∨ q (Implication)"
FirstArgument -> "p (First)"
ConverseImplication -> "p ∨ ¬q (Converse Implication)"
LogicalOR -> "p ∧ q (Logical OR)"
AlwaysTrue -> "True (Tautology)"
{-|
Convert from a closed, binary function over 'Bool' to a 'BinaryLogicalOperator'.
-}
fromBinaryLogicalFunction :: (Bool -> Bool -> Bool) -> BinaryLogicalOperator
fromBinaryLogicalFunction f = case (f True True, f True False, f False True, f False False) of
(False, False, False, False) -> AlwaysFalse
(False, False, False, True ) -> LogicalNOR
(False, False, True , False) -> ConverseNonImplication
(False, False, True , True ) -> NotFirstArgument
(False, True , False, False) -> NonImplication
(False, True , False, True ) -> NotSecondArgument
(False, True , True , False) -> LogicalXOR
(False, True , True , True ) -> LogicalNAND
(True , False, False, False) -> LogicalAND
(True , False, False, True ) -> LogicalXNOR
(True , False, True , False) -> SecondArgument
(True , False, True , True ) -> Implication
(True , True , False, False) -> FirstArgument
(True , True , False, True ) -> ConverseImplication
(True , True , True , False) -> LogicalOR
(True , True , True , True ) -> AlwaysTrue
{-|
Convert from a 'BinaryLogicalOperator' to a closed, binary function over 'Bool'.
-}
getBinaryLogicalOperator :: BinaryLogicalOperator -> Bool -> Bool -> Bool
getBinaryLogicalOperator x = case x of
AlwaysFalse -> const (const False)
LogicalNOR -> \p q -> not $ p || q
ConverseNonImplication -> \p q -> not p && q
NotFirstArgument -> \p _ -> not p
NonImplication -> \p q -> p && not q
NotSecondArgument -> \_ q -> not q
LogicalXOR -> (/=)
LogicalNAND -> \p q -> not $ p && q
LogicalAND -> (&&)
LogicalXNOR -> (==)
SecondArgument -> \_ q -> q
Implication -> \p q -> not p || q
FirstArgument -> const
ConverseImplication -> \p q -> p || not q
LogicalOR -> (||)
AlwaysTrue -> const (const True)
{-|
Query the Haskell expression of a 'BinaryLogicalOperator' representation symbolically as a 'String'.
-}
getBinaryLogicalSymbol :: BinaryLogicalOperator -> String
getBinaryLogicalSymbol x = case x of
AlwaysFalse -> "(const False)"
LogicalNOR -> "(not . (||))"
ConverseNonImplication -> "(</=)"
NotFirstArgument -> "(not . fst)"
NonImplication -> "(=/>)"
NotSecondArgument -> "(not . snd)"
LogicalXOR -> "(/=)"
LogicalNAND -> "(not . (&&))"
LogicalAND -> "(&&)"
LogicalXNOR -> "(==)"
SecondArgument -> "(snd)"
Implication -> "(==>)"
FirstArgument -> "(fst)"
ConverseImplication -> "(<==)"
LogicalOR -> "(||)"
AlwaysTrue -> "(const True)"