{-# LANGUAGE CPP #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE UndecidableInstances #-}
--------------------------------------------------------------------
-- |
-- Copyright :  © Edward Kmett 2010-2014, Johan Kiviniemi 2013
-- License   :  BSD3
-- Maintainer:  Edward Kmett <ekmett@gmail.com>
-- Stability :  experimental
-- Portability: non-portable
--
--------------------------------------------------------------------
module Ersatz.Variable
  ( Variable(..)
#ifndef HLINT
  , forall
#endif
  , exists

  , GVariable(..)
  , genericLiterally
  ) where

import Control.Monad
import Ersatz.Internal.Literal
import Ersatz.Problem
import GHC.Generics

exists :: (Variable a, MonadSAT s m)  => m a
exists :: forall a s (m :: * -> *). (Variable a, MonadSAT s m) => m a
exists = forall t s (m :: * -> *).
(Variable t, MonadSAT s m) =>
m Literal -> m t
literally forall s (m :: * -> *). MonadSAT s m => m Literal
literalExists

#ifndef HLINT
forall :: (Variable a, MonadQSAT s m)  => m a
forall :: forall a s (m :: * -> *). (Variable a, MonadQSAT s m) => m a
forall = forall t s (m :: * -> *).
(Variable t, MonadSAT s m) =>
m Literal -> m t
literally forall s (m :: * -> *). MonadQSAT s m => m Literal
literalForall
#endif

class GVariable f where
  gliterally :: MonadSAT s m => m Literal -> m (f a)

instance GVariable U1 where
  gliterally :: forall s (m :: * -> *) a. MonadSAT s m => m Literal -> m (U1 a)
gliterally m Literal
_ = forall (m :: * -> *) a. Monad m => a -> m a
return forall k (p :: k). U1 p
U1

instance (GVariable f, GVariable g) => GVariable (f :*: g) where
  gliterally :: forall s (m :: * -> *) a.
MonadSAT s m =>
m Literal -> m ((:*:) f g a)
gliterally m Literal
m = forall (m :: * -> *) a1 a2 r.
Monad m =>
(a1 -> a2 -> r) -> m a1 -> m a2 -> m r
liftM2 forall k (f :: k -> *) (g :: k -> *) (p :: k).
f p -> g p -> (:*:) f g p
(:*:) (forall (f :: * -> *) s (m :: * -> *) a.
(GVariable f, MonadSAT s m) =>
m Literal -> m (f a)
gliterally m Literal
m) (forall (f :: * -> *) s (m :: * -> *) a.
(GVariable f, MonadSAT s m) =>
m Literal -> m (f a)
gliterally m Literal
m)

instance Variable a => GVariable (K1 i a) where
  gliterally :: forall s (m :: * -> *) a. MonadSAT s m => m Literal -> m (K1 i a a)
gliterally = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall k i c (p :: k). c -> K1 i c p
K1 forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall t s (m :: * -> *).
(Variable t, MonadSAT s m) =>
m Literal -> m t
literally

instance GVariable f => GVariable (M1 i c f) where
  gliterally :: forall s (m :: * -> *) a.
MonadSAT s m =>
m Literal -> m (M1 i c f a)
gliterally = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall k i (c :: Meta) (f :: k -> *) (p :: k). f p -> M1 i c f p
M1 forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (f :: * -> *) s (m :: * -> *) a.
(GVariable f, MonadSAT s m) =>
m Literal -> m (f a)
gliterally

-- | This class describes all types that can be represented
-- by a collection of literals. The class method 'literally'
-- is usually applied to 'literalExists' or 'literalForall',
-- see implementations of 'exists' and 'forall'.
--
-- Instances for this class for product-like types can be automatically derived
-- for any type that is an instance of 'Generic'.
--
-- === __Example usage__
--
-- @
-- {-# language DeriveGeneric, TypeApplications #-}
-- import GHC.Generics
--
-- data T = C Bit Bit deriving Generic
-- instance Variable T
--
-- constraint = do t <- exists @T ; ...
-- @

class Variable t where
  literally :: MonadSAT s m => m Literal -> m t
  default literally ::
    (MonadSAT s m, Generic t, GVariable (Rep t)) =>
    m Literal -> m t
  literally = forall s (m :: * -> *) t.
(MonadSAT s m, Generic t, GVariable (Rep t)) =>
m Literal -> m t
genericLiterally

genericLiterally ::
  (MonadSAT s m, Generic t, GVariable (Rep t)) =>
  m Literal -> m t
genericLiterally :: forall s (m :: * -> *) t.
(MonadSAT s m, Generic t, GVariable (Rep t)) =>
m Literal -> m t
genericLiterally = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap forall a x. Generic a => Rep a x -> a
to forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (f :: * -> *) s (m :: * -> *) a.
(GVariable f, MonadSAT s m) =>
m Literal -> m (f a)
gliterally

instance Variable Literal where
  literally :: forall s (m :: * -> *). MonadSAT s m => m Literal -> m Literal
literally = forall a. a -> a
id

instance (Variable a, Variable b) => Variable (a,b)
instance (Variable a, Variable b, Variable c) => Variable (a,b,c)
instance (Variable a, Variable b, Variable c, Variable d) => Variable (a,b,c,d)
instance (Variable a, Variable b, Variable c, Variable d, Variable e) => Variable (a,b,c,d,e)
instance (Variable a, Variable b, Variable c, Variable d, Variable e, Variable f) => Variable (a,b,c,d,e,f)
instance (Variable a, Variable b, Variable c, Variable d, Variable e, Variable f, Variable g) => Variable (a,b,c,d,e,f,g)