packages feed

sop-satisfier-0.3.4.5: src/SoPSat/SoP.hs

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RecordWildCards #-}

module SoPSat.SoP (
  -- * SoP Types
  Atom,
  Symbol,
  Product,
  SoP,
  SoPE (..),
  ToSoP (..),

  -- * Operators
  (|+|),
  (|-|),
  (|*|),
  (|/|),
  (|^|),

  -- * Relations
  OrdRel (..),

  -- * Related
  constants,
  atoms,
  int,
  cons,
  symbol,
  func,

  -- * Predicates
  isConst,
  isFunction,
)
where

import Data.Set (Set, union)
import qualified Data.Set as S

import SoPSat.Internal.SoP

{- | Convertable to a sum of products
with `f` being type to represent functions
and `c` being type to represent constants
-}
class (Ord f, Ord c) => ToSoP f c a where
  toSoP :: a -> SoP f c

-- | Predicate for constant @Atom@s
isConst :: Atom f c -> Bool
isConst (C _) = True
isConst _ = False

-- | Predicate for function @Atom@s
isFunction :: Atom f c -> Bool
isFunction (F _ _) = True
isFunction _ = False

instance (Ord f, Ord c) => ToSoP f c (Symbol f c) where
  toSoP s = simplifySoP $ S [P [s]]

instance (Ord f, Ord c) => ToSoP f c (Product f c) where
  toSoP p = simplifySoP $ S [p]

instance (Ord f, Ord c) => ToSoP f c (SoP f c) where
  toSoP = simplifySoP

-- | Order relationship
data OrdRel
  = -- | Less than or equal relationship
    LeR
  | -- | Equality relationship
    EqR
  | -- | Greater than or equal relationship
    GeR
  deriving (Eq, Ord)

instance Show OrdRel where
  show LeR = "<="
  show EqR = "="
  show GeR = ">="

-- | Expression
data SoPE f c
  = SoPE
  { lhs :: SoP f c
  -- ^ Left hand side of the expression
  , rhs :: SoP f c
  -- ^ Right hand side of the expression
  , op :: OrdRel
  -- ^ Relationship between sides
  }

instance (Eq f, Eq c) => Eq (SoPE f c) where
  (SoPE l1 r1 op1) == (SoPE l2 r2 op2)
    | op1 == op2
    , op1 == EqR =
        -- a = b is the same as b = a
        (l1 == l2) && (r1 == r2) || (l1 == r2) && (r1 == l2)
    | op1 == op2 =
        -- (a <= b) is itself
        (l1 == l2) && (r1 == r2)
    | EqR `notElem` [op1, op2] =
        -- (a <= b) is the same as (b >= a)
        (l1 == r2) && (r1 == l2)
    | otherwise =
        False

instance (Show f, Show c) => Show (SoPE f c) where
  show SoPE{..} = unwords [show lhs, show op, show rhs]

-- | Creates an integer expression
int :: Integer -> SoP f c
int i = S [P [I i]]

-- | Creates expression from an atom
symbol :: Atom f c -> SoP f c
symbol a = S [P [A a]]

-- | Creates a constant expression
cons :: c -> SoP f c
cons c = S [P [A (C c)]]

-- | Creates a function expression
func :: (Ord f, Ord c) => f -> [SoP f c] -> SoP f c
func f args = S [P [A (F f (map simplifySoP args))]]

infixr 8 |^|

-- | Exponentiation of @SoP@s
(|^|) :: (Ord f, Ord c) => SoP f c -> SoP f c -> SoP f c
-- It's a B2 combinator,
(|^|) = (. simplifySoP) . normaliseExp

infixl 6 |+|

-- | Addition of @SoP@s
(|+|) :: (Ord f, Ord c) => SoP f c -> SoP f c -> SoP f c
(|+|) = mergeSoPAdd

infixl 7 |*|

-- | Multiplication of @SoP@s
(|*|) :: (Ord f, Ord c) => SoP f c -> SoP f c -> SoP f c
(|*|) = mergeSoPMul

infixl 6 |-|

-- | Subtraction of @SoP@s
(|-|) :: (Ord f, Ord c) => SoP f c -> SoP f c -> SoP f c
(|-|) = mergeSoPSub

infixl 7 |/|

{- | Division of @SoP@s

Produces a tuple of a quotient and a remainder
NB. Not implemented
-}
(|/|) :: (Ord f, Ord c) => SoP f c -> SoP f c -> (SoP f c, SoP f c)
(|/|) = mergeSoPDiv

-- | Collects @Atom@s used in a @SoP@
atoms :: (Ord f, Ord c) => SoP f c -> Set (Atom f c)
atoms = S.unions . map atomsProduct . unS

{- | Collects @Atom@s used in a @Product@

Used by @atoms@
-}
atomsProduct :: (Ord f, Ord c) => Product f c -> Set (Atom f c)
atomsProduct = S.unions . map atomsSymbol . unP

{- | Collect @Atom@s used in @Symbol@s

Used by @atomsProduct@
-}
atomsSymbol ::
  (Ord f, Ord c) =>
  Symbol f c ->
  -- | - Empty - if the symbol is an integer
  --   - Singleton - if the symbol is an atom
  --   - Set of symbols - if the symbol is an exponentiation
  Set (Atom f c)
atomsSymbol (I _) = S.empty
atomsSymbol (A a) = S.singleton a
atomsSymbol (E b p) = atoms b `union` atomsProduct p

{- | Collects constants used in @SoP@

Almost equivalent to
@Data.Set.filter isConst . atoms@
, but also collects constants used in functions
-}
constants :: (Ord f, Ord c) => SoP f c -> Set c
constants = S.unions . map constsProduct . unS

{- | Collects constants used in @Product@

Used by @constants@
-}
constsProduct :: (Ord f, Ord c) => Product f c -> Set c
constsProduct = S.unions . map constsSymbol . unP

{- | Collects constants used in @Symbol@

Used by @constsProduct@
-}
constsSymbol :: (Ord f, Ord c) => Symbol f c -> Set c
constsSymbol (I _) = S.empty
constsSymbol (A a) = constsAtom a
constsSymbol (E b p) = constants b `union` constsProduct p

{- | Collects constants used in @Atom@

Used by @constsSymbol@
-}
constsAtom ::
  (Ord f, Ord c) =>
  Atom f c ->
  -- | Singleton - if the atom is a constant
  --   Set of constants - if the atom is a function
  Set c
constsAtom (C c) = S.singleton c
constsAtom (F _ args) = S.unions $ map constants args