packages feed

bv-little-1.3.0: util/Operator/Unary/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.Unary.Logical
    ( UnaryLogicalOperator ()
    , fromUnaryLogicalFunction
    , getUnaryLogicalOperator
    , getUnaryLogicalSymbol
    ) 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 /unary/ operators of type @(Bool -> Bool)@.
Useful for both property and enumeration based testing.
-}
data UnaryLogicalOperator
    = AlwaysFalse
    | Identity
    | Negation
    | AlwaysTrue
    deriving anyclass (NFData)
    deriving stock (Data, Eq, Generic, Ord)


instance Arbitrary UnaryLogicalOperator where

    arbitrary = arbitraryBoundedEnum


instance Bounded UnaryLogicalOperator where

    minBound = AlwaysFalse

    maxBound = AlwaysTrue


instance CoArbitrary UnaryLogicalOperator where

    coarbitrary = coarbitraryEnum


instance Enum UnaryLogicalOperator where

    toEnum n = case n `rem` 4 of
        0 -> AlwaysFalse
        1 -> Identity
        2 -> Negation
        _ -> AlwaysTrue

    fromEnum x = case x of
        AlwaysFalse -> 0
        Identity    -> 1
        Negation    -> 2
        AlwaysTrue  -> 3

    succ x = case x of
        AlwaysFalse -> Identity
        Identity    -> Negation
        Negation    -> AlwaysTrue
        AlwaysTrue  -> AlwaysFalse

    pred x = case x of
        AlwaysFalse -> AlwaysTrue
        Identity    -> AlwaysFalse
        Negation    -> Identity
        AlwaysTrue  -> Negation


instance Monad m => Serial m UnaryLogicalOperator where

    series = generate $ const [minBound .. maxBound]


instance Show UnaryLogicalOperator where

    show x = "f p q = " <> s
        where
            s = case x of
                AlwaysFalse -> "False (Contradiction)"
                Identity    -> "id (Identity)"
                Negation    -> "not (Negation)"
                AlwaysTrue  -> "True (Tautology)"


{-|
Convert from a closed, unnary function over 'Bool' to a 'UnaryLogicalOperator'.
-}
fromUnaryLogicalFunction :: (Bool -> Bool) -> UnaryLogicalOperator
fromUnaryLogicalFunction f = case (f False, f True) of
    (False, False) -> AlwaysFalse
    (False, True ) -> Identity
    (True , False) -> Negation
    (True , True ) -> AlwaysTrue


{-|
Convert from a 'UnaryLogicalOperator' to a closed, unary function over 'Bool'.
-}
getUnaryLogicalOperator :: UnaryLogicalOperator -> Bool -> Bool
getUnaryLogicalOperator x = case x of
    AlwaysFalse -> const False
    Identity    -> id
    Negation    -> not
    AlwaysTrue  -> const True


{-|
Query the Haskell expression of a 'UnaryLogicalOperator' representation symbolically as a 'String'.
-}
getUnaryLogicalSymbol :: UnaryLogicalOperator -> String
getUnaryLogicalSymbol x = case x of
    AlwaysFalse -> "(const False)"
    Identity    -> "(id)"
    Negation    -> "(not)"
    AlwaysTrue  -> "(const True)"