{-# LANGUAGE EmptyDataDecls #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# OPTIONS_GHC -fno-warn-unused-matches #-}

{- |
Module      : Data.ABC.Internal.GIA
Copyright   : Galois, Inc. 2010-2014
License     : BSD3
Maintainer  : jhendrix@galois.com
Stability   : experimental
Portability : non-portable (c2hs, language extensions)

Bindings to @aig\/gia\/gia.h@ for manipulating and
running algorithms on scalable and-inverter graphs (GIA), a
representation that is optimized for memory-efficiency.  These
functions power the next-generation of ABC algorithms that have
not been officially released yet, and can be identified by the
prefix of an ampersand, as in @&cec@, in the interactive ABC
interface.

-}

module Data.ABC.Internal.GIA (
      Gia_Man_t
    , Gia_Man_t_
    , giaManNObjs
    , giaManFanData
    , Gia_Obj_t
    , getGiaObjValue
    , setGiaObjValue
    , GiaVar(..)
    , GiaLit(..)
    , giaManConst0Lit
    , giaManConst1Lit
    , giaLitIsCompl
    , giaLitVar
    , giaVarLit
    , giaLitNotCond
    -- * Memory management
    -- * Base
    -- ** Network getters
    , giaManCexComb
    , giaManConst0
    , giaManCis
    , giaManCos
    , giaManCiNum
    , giaManCoNum
    , giaManPiNum
    , giaManPoNum
    , giaManAndNum
    , getGiaManRegNum
    , setGiaManRegNum

    , giaManCiVar
    , giaManCoVar
    , giaManCi
    , giaManCo
    , giaManObj
    , gia_none
    , giaObjIsCo
    , giaObjDiff0
    , giaObjDiff1
    , giaObjFaninC0
    , giaObjFaninC1
    , giaObjMark0
    , giaObjMark1
    , giaObjChild0
    , giaObjChild1
    , giaObjFaninId0
    , giaObjFaninId1
    , giaObjIsTerm
    , giaObjIsAndOrConst0
    , giaObjId
    , giaManObjNum
    -- ** Handling literals
    , giaLitNot
    , giaRegular
    , giaIsComplement
    , giaObjToLit
    , giaObjFromLit
    -- ** Iterators
    , giaManForEachObj1_
    , giaManForEachCo
    -- ** Construction
    , giaManAppendCi
    , giaManAppendCo
    , giaManAppendAnd
    -- * Functions
    -- ** giaAiger.c
    , giaAigerRead
    , giaAigerWrite
    -- ** giaDup.c
    , giaManMiter
    , giaDupLit
    , giaManDupNormalize
    -- ** giaHash.c
    , giaManHashAlloc
    , giaManHashStart
    , giaManHashStop
    , giaManHashAnd
    , giaManHashXor
    , giaManHashMux
     -- ** giaMan.c
    , giaManStart
    , giaManStop
    , p_giaManStop
    , giaManCleanup
    , giaManFillValue

      -- ** misc
    , clearGiaObj
    ) where

import Control.Applicative
import Control.Exception
import Control.Monad
import Foreign hiding (void)
import Foreign.C

{#import Data.ABC.Internal.ABCGlobal #}
{#import Data.ABC.Internal.VecInt #}

import Data.ABC.Internal.Field

#include "abcbridge.h"
#include "gia.h"

-- Iterators

enumRange :: (Eq a, Enum a) => a -> a -> [a]
enumRange i n | i == n = []
              | otherwise = i : enumRange (succ i) n

lazyAnd :: Monad m => m Bool -> m Bool -> m Bool
lazyAnd mx my = do
  x <- mx
  if x then my else return False

asWordPtr :: (WordPtr -> WordPtr) -> Ptr a -> Ptr b
asWordPtr f = wordPtrToPtr . f . ptrToWordPtr

------------------------------------------------------------------------
-- Gia_Obj_t

data Gia_Obj_t_

-- | A pointer to a GIA object.
-- GIA objects are pointers to structs in ABC, and represent literals
-- in the AIG.  The low-order bit of the pointer is set to 1 if the
-- literal has been complemented, and so care must be taken to only
-- dereference positive pointers.  The object is also a bitfield, so
-- care must be taken when accessing fields.
--
-- Pointers to GIA objects may be invalidated when adding a new
-- object.
{#pointer *Gia_Obj_t -> Gia_Obj_t_ #}

sizeOfGiaObj :: Int
sizeOfGiaObj = {#sizeof Gia_Obj_t #}

-- | Remove negation.
giaRegular :: Gia_Obj_t -> Gia_Obj_t
giaRegular = asWordPtr (.&. complement 0x1)

-- | Returns iDiff0 field of object
-- Note: iDiff0 is a bitfield, so this may be more likely to break on
-- unexpected compilers.
giaObjDiff0 :: Gia_Obj_t -> IO CUInt
giaObjDiff0 = {#get Gia_Obj_t->iDiff0 #}

-- | Get the complement attribute of first fanin
giaObjFaninC0 :: Gia_Obj_t -> IO Bool
giaObjFaninC0 o = toBool `fmap` {#get Gia_Obj_t->fCompl0 #} o

-- | Get first user defined mark
giaObjMark0 :: Gia_Obj_t -> IO Bool
giaObjMark0 o = toBool `fmap` {#get Gia_Obj_t->fMark0 #} o

giaObjIsTerm :: Gia_Obj_t -> IO Bool
giaObjIsTerm o = toBool `fmap` {#get Gia_Obj_t->fTerm #} o

-- | Returns iDiff1 field of object
-- Note: iDiff1 is a bitfield, so this may be more likely to break on
-- unexpected compilers.
giaObjDiff1 :: Gia_Obj_t -> IO CUInt
giaObjDiff1 = {#get Gia_Obj_t->iDiff1 #}

giaObjFaninC1 :: Gia_Obj_t -> IO Bool
giaObjFaninC1 o = toBool `fmap` {#get Gia_Obj_t->fCompl1 #} o

-- | Get second user defined mark
giaObjMark1 :: Gia_Obj_t -> IO Bool
giaObjMark1 o = toBool `fmap` {#get Gia_Obj_t->fMark1 #} o

getGiaObjValue :: Gia_Obj_t -> IO CUInt
getGiaObjValue = {#get Gia_Obj_t->Value #}

setGiaObjValue :: Gia_Obj_t -> CUInt -> IO ()
setGiaObjValue = {#set Gia_Obj_t->Value #}

-- GIA_NONE
gia_none :: CUInt
gia_none = 0x1FFFFFFF

giaObjIsAndOrConst0 :: Gia_Obj_t -> IO Bool
giaObjIsAndOrConst0 o = not <$> giaObjIsTerm o

giaObjDiff0Assigned :: Gia_Obj_t -> IO Bool
giaObjDiff0Assigned o = (/= gia_none) <$> giaObjDiff0 o

giaIsComplement :: Gia_Obj_t -> Bool
giaIsComplement o = ptrToWordPtr o `testBit` 0

giaNot :: Gia_Obj_t -> Gia_Obj_t
giaNot = asWordPtr (xor 1)

giaNotCond :: Gia_Obj_t -> Bool -> Gia_Obj_t
giaNotCond o b = if b then giaNot o else o

-- | Returns true if this is a combinational output (latch or primary output).
giaObjIsCo :: Gia_Obj_t -> IO Bool
giaObjIsCo o = lazyAnd (giaObjIsTerm o) (giaObjDiff0Assigned o)

incObjPtr :: Gia_Obj_t -> CInt -> Gia_Obj_t
incObjPtr o i = o `plusPtr` (sizeOfGiaObj * fromIntegral i)

decObjPtr :: Gia_Obj_t -> CUInt -> Gia_Obj_t
decObjPtr o i = incObjPtr o (negate (fromIntegral i))

objDiff :: Gia_Obj_t -> Gia_Obj_t -> Int
objDiff p q = (p `minusPtr` q) `div` sizeOfGiaObj

-- Gia_ObjFanin0
giaObjFanin0 :: Gia_Obj_t -> IO Gia_Obj_t
giaObjFanin0 o = decObjPtr o <$> giaObjDiff0 o

-- Gia_ObjFanin1
giaObjFanin1 :: Gia_Obj_t -> IO Gia_Obj_t
giaObjFanin1 o = decObjPtr o <$> giaObjDiff1 o

-- Gia_ObjChild0
giaObjChild0 :: Gia_Obj_t -> IO Gia_Obj_t
giaObjChild0 o = giaNotCond <$> giaObjFanin0 o <*> giaObjFaninC0 o

-- Gia_ObjChild1
giaObjChild1 :: Gia_Obj_t -> IO Gia_Obj_t
giaObjChild1 o = giaNotCond <$> giaObjFanin1 o <*> giaObjFaninC1 o

-- Gia_ObjFaninId0
giaObjFaninId0 :: Gia_Obj_t -> GiaVar -> IO GiaVar
giaObjFaninId0 o (GiaVar v) = (\d -> GiaVar (v - fromIntegral d)) <$> giaObjDiff0 o

-- Gia_ObjFaninId1
giaObjFaninId1 :: Gia_Obj_t -> GiaVar -> IO GiaVar
giaObjFaninId1 o (GiaVar v) = (\d -> GiaVar (v - fromIntegral d)) <$> giaObjDiff1 o

------------------------------------------------------------------------
-- GiaVar and GiaLit

-- | Also known as the node's id.  No complement info.
newtype GiaVar = GiaVar { unGiaVar :: CInt } deriving (Eq, Ord, Storable)

-- | Literals store complement information.
newtype GiaLit = GiaLit { unGiaLit :: CInt } deriving (Eq, Ord, Storable)

giaManConst0Lit :: GiaLit
giaManConst0Lit = GiaLit 0

giaManConst1Lit :: GiaLit
giaManConst1Lit = GiaLit 1

giaLitIsCompl :: GiaLit -> Bool
giaLitIsCompl l = unGiaLit l `testBit` 0

giaLitVar :: GiaLit -> GiaVar
giaLitVar (GiaLit l) = GiaVar (l `shiftR` 1)

-- | Returns positive literal associated to var.
giaVarLit :: GiaVar -> GiaLit
giaVarLit (GiaVar v) = GiaLit (v `shiftL` 1)

giaLitNot :: GiaLit -> GiaLit
giaLitNot = GiaLit . xor 1 . unGiaLit

giaLitNotCond :: GiaLit -> Bool -> GiaLit
giaLitNotCond (GiaLit l) b = GiaLit (l `xor` (if b then 1 else 0))

------------------------------------------------------------------------
-- Gia_Man_t

data Gia_Man_t_
{#pointer *Gia_Man_t -> Gia_Man_t_ #}

-- member access

-- this structures are memory managed by Gia_Man_.
giaManCexComb :: Gia_Man_t -> IO Abc_Cex_t
giaManCexComb = {#get Gia_Man_t->pCexComb #}

giaManNObjs :: Field Gia_Man_t CInt
giaManNObjs = fieldFromOffset {#offsetof Gia_Man_t->nObjs #}

giaManObjs :: Field Gia_Man_t Gia_Obj_t
giaManObjs = fieldFromOffset {#offsetof Gia_Man_t->pObjs #}

giaManConst0 :: Gia_Man_t -> IO Gia_Obj_t
giaManConst0 = readAt giaManObjs

giaManCis :: Gia_Man_t -> IO Vec_Int_t
giaManCis = {#get Gia_Man_t->vCis #}

giaManCos :: Gia_Man_t -> IO Vec_Int_t
giaManCos = {#get Gia_Man_t->vCos #}

giaManFanData :: Gia_Man_t -> IO (Ptr CInt)
giaManFanData = {#get Gia_Man_t->pFanData#}

giaManObjNum :: Gia_Man_t -> IO CInt
giaManObjNum = readAt giaManNObjs

giaManCiNum :: Gia_Man_t -> IO CInt
giaManCiNum = vecIntSize <=< giaManCis

giaManCoNum :: Gia_Man_t -> IO CInt
giaManCoNum = vecIntSize <=< giaManCos

getGiaManRegNum :: Gia_Man_t -> IO CInt
getGiaManRegNum = {#get Gia_Man_t->nRegs #}

setGiaManRegNum :: Gia_Man_t -> CInt -> IO ()
setGiaManRegNum = {#set Gia_Man_t->nRegs #}

giaManPiNum :: Gia_Man_t -> IO CInt
giaManPiNum m = (-) <$> giaManCiNum m <*> getGiaManRegNum m

giaManPoNum :: Gia_Man_t -> IO CInt
giaManPoNum m = (-) <$> giaManCoNum m <*> getGiaManRegNum m

giaManAndNum :: Gia_Man_t -> IO CInt
giaManAndNum m = fn <$> giaManObjNum m <*> giaManCiNum m <*> giaManCoNum m
  where fn t i o = t - i - o - 1

-- XXX refactor
giaManForEachObj1_ :: Gia_Man_t -> (Gia_Obj_t -> GiaVar -> IO b) -> IO ()
giaManForEachObj1_ fp action = do
  nMax <- giaManObjNum fp
  forM_ (enumRange 1 nMax) $ \i -> do
    let var = GiaVar (fromIntegral i)
    pObj <- giaManObj fp var
    void $ action pObj var

giaManForEachCo :: Gia_Man_t -> (Gia_Obj_t -> Int -> IO b) -> IO [b]
giaManForEachCo fp action = do
  nMax <- giaManCoNum fp
  forM (enumRange 0 nMax) $ \i -> do
    pObj <- giaManCo fp i
    action pObj (fromIntegral i)

foreign import ccall unsafe "AbcBridge_Gia_ManAppendCi"
  giaManAppendCi_ :: Gia_Man_t -> IO CInt

giaManAppendCi :: Gia_Man_t -> IO GiaLit
giaManAppendCi m = GiaLit <$> giaManAppendCi_ m

foreign import ccall unsafe "AbcBridge_Gia_ManAppendAnd"
  giaManAppendAnd_ :: Gia_Man_t -> CInt -> CInt -> IO CInt

-- | This directly appends the literal to the GIA bypassing
-- any hash-consing.
giaManAppendAnd :: Gia_Man_t -> GiaLit -> GiaLit -> IO GiaLit
giaManAppendAnd m (GiaLit x) (GiaLit y) =
  GiaLit <$> giaManAppendAnd_ m x y

foreign import ccall unsafe "AbcBridge_Gia_ManAppendCo"
  giaManAppendCo_ :: Gia_Man_t -> CInt -> IO CInt

giaManAppendCo :: Gia_Man_t -> GiaLit -> IO GiaLit
giaManAppendCo m (GiaLit l) = GiaLit <$> giaManAppendCo_ m l

-- | Return object associated with gia var.
giaManObj :: Gia_Man_t -> GiaVar -> IO Gia_Obj_t
giaManObj m (GiaVar v) = do
  cnt <- giaManObjNum m
  assert (0 <= v && v < cnt) $ do
    (`incObjPtr` v) <$> giaManConst0 m

-- | Get var index of combinational input at given index.
giaManCiVar :: Gia_Man_t -> CInt -> IO GiaVar
giaManCiVar m i = do
  v <- giaManCis m
  GiaVar <$> vecIntEntry v i

-- | Get combinational input at given index.
giaManCi :: Gia_Man_t -> CInt -> IO Gia_Obj_t
giaManCi m i = giaManObj m =<< giaManCiVar m i

-- | Get combinational output at given index.
giaManCoVar :: Gia_Man_t -> CInt -> IO GiaVar
giaManCoVar m i = do
  v <- giaManCos m
  GiaVar <$> vecIntEntry v i

-- | Get combinational output at given index.
giaManCo :: Gia_Man_t -> CInt -> IO Gia_Obj_t
giaManCo m i = giaManObj m =<< giaManCoVar m i

-- | Returns the variable index associated with the object.
giaObjId :: Gia_Man_t -> Gia_Obj_t -> IO GiaVar
giaObjId p pObj = do
  objs <- giaManConst0 p
  nObjs <- readAt giaManNObjs p
  assert (objs <= pObj && pObj < objs `incObjPtr` nObjs) $ do
    return $ GiaVar $ fromIntegral $ pObj `objDiff` objs

{#fun AbcBridge_Gia_ObjToLit as giaObjToLit
    { id `Gia_Man_t'
    , id `Gia_Obj_t'
    } -> `GiaLit' GiaLit #}
{#fun AbcBridge_Gia_ObjFromLit as giaObjFromLit
    { id `Gia_Man_t'
    , unGiaLit `GiaLit'
    } -> `Gia_Obj_t' id #}

-- giaAiger.c
{#fun Gia_AigerRead as ^
    { `String' -- pFileName
    , `Bool' -- fSkipStrash
    , `Bool' -- fCheck (doesn't do anything)
    } -> `Gia_Man_t' id #}



{#fun Gia_AigerWrite as giaAigerWrite
    { id `Gia_Man_t'
    , `String' -- pFileName
    , `Bool' -- fWriteSymbols
    , `Bool' -- fCompact
    } -> `()' #}

{#fun Gia_ManMiter as giaManMiter
    { id `Gia_Man_t' -- pAig0cd
    , id `Gia_Man_t' -- pAig1
    , `Int'  -- nInsDup
    , `Bool' -- fDualOut
    , `Bool' -- fSeq
    , `Bool' -- fImplic
    , `Bool' -- fVerbose
    } -> `Gia_Man_t' id #}

foreign import ccall unsafe "Gia_ManDupNormalize" giaManDupNormalize
  :: Gia_Man_t -> IO Gia_Man_t

-- | @giaManDupDfsLazyLit pNew p l@ copies a lit @l@ in @p@ to @pNew@
-- and returns the lit in @pNew@.
giaDupLit :: Gia_Man_t -> Gia_Man_t -> GiaLit -> IO GiaLit
giaDupLit pNew p (GiaLit l) = GiaLit <$> giaDupLit' pNew p l

foreign import ccall unsafe "AbcBridge_Gia_DupLit" giaDupLit'
  :: Gia_Man_t -> Gia_Man_t -> CInt -> IO CInt

-- giaHash.c
foreign import ccall unsafe "Gia_ManHashAlloc" giaManHashAlloc
  :: Gia_Man_t -> IO ()

foreign import ccall unsafe "Gia_ManHashStart" giaManHashStart
  :: Gia_Man_t -> IO ()

foreign import ccall unsafe "Gia_ManHashStop" giaManHashStop
  :: Gia_Man_t -> IO ()

{#fun Gia_ManHashAnd as ^
    { id `Gia_Man_t' -- p
    , unGiaLit `GiaLit' -- iLit0
    , unGiaLit `GiaLit' -- iLit1
    } -> `GiaLit' GiaLit #}
{#fun Gia_ManHashXor as ^
    { id `Gia_Man_t' -- p
    , unGiaLit `GiaLit' -- iLit0
    , unGiaLit `GiaLit' -- iLit1
    } -> `GiaLit' GiaLit #}
{#fun Gia_ManHashMux as ^
    { id `Gia_Man_t' -- p
    , unGiaLit `GiaLit' -- iCtrl
    , unGiaLit `GiaLit' -- iData1
    , unGiaLit `GiaLit' -- iData0
    } -> `GiaLit' GiaLit #}

-- giaMan.c
foreign import ccall unsafe "Gia_ManStop"
    giaManStop :: Gia_Man_t -> IO ()

foreign import ccall unsafe "gia.h &Gia_ManStop"
    p_giaManStop :: FunPtr (Gia_Man_t -> IO ())

foreign import ccall unsafe "AbcBridge_Gia_ClearGiaObj"
    clearGiaObj :: Gia_Obj_t -> IO ()

{#fun Gia_ManStart as ^ { id `CInt' } -> `Gia_Man_t' id #}

{#fun Gia_ManCleanup as ^
    { id `Gia_Man_t' -- p
    } -> `Gia_Man_t' id #}

{#fun Gia_ManFillValue as ^
    { id `Gia_Man_t' -- p
    } -> `()' #}
