/**CFile****************************************************************

  FileName    [abcAttach.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Network and node package.]

  Synopsis    [Attaches the library gates to the current network.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

  Date        [Ver. 1.0. Started - June 20, 2005.]

  Revision    [$Id: abcAttach.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $]

***********************************************************************/

#include "base/abc/abc.h"
#include "base/main/main.h"
#include "map/mio/mio.h"

ABC_NAMESPACE_IMPL_START


////////////////////////////////////////////////////////////////////////
///                        DECLARATIONS                              ///
////////////////////////////////////////////////////////////////////////
 
#define    ATTACH_FULL             (~((unsigned)0))
#define    ATTACH_MASK(n)         ((~((unsigned)0)) >> (32-(n)))

static void Abc_AttachSetupTruthTables( unsigned uTruths[][2] );
static void Abc_AttachComputeTruth( char * pSop, unsigned uTruthsIn[][2], unsigned * uTruthNode );
static Mio_Gate_t * Abc_AttachFind( Mio_Gate_t ** ppGates, unsigned ** puTruthGates, int nGates, unsigned * uTruthNode, int * Perm );
static int Abc_AttachCompare( unsigned ** puTruthGates, int nGates, unsigned * uTruthNode );
static int Abc_NodeAttach( Abc_Obj_t * pNode, Mio_Gate_t ** ppGates, unsigned ** puTruthGates, int nGates, unsigned uTruths[][2] );
static void Abc_TruthPermute( char * pPerm, int nVars, unsigned * uTruthNode, unsigned * uTruthPerm );

static char ** s_pPerms = NULL;
static int s_nPerms;

////////////////////////////////////////////////////////////////////////
///                     FUNCTION DEFINITIONS                         ///
////////////////////////////////////////////////////////////////////////

/**Function*************************************************************

  Synopsis    [Attaches gates from the current library to the internal nodes.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Abc_NtkAttach( Abc_Ntk_t * pNtk )
{
    Mio_Library_t * pGenlib;
    unsigned ** puTruthGates;
    unsigned uTruths[6][2];
    Abc_Obj_t * pNode;
    Mio_Gate_t ** ppGates;
    int nGates, nFanins, i;

    assert( Abc_NtkIsSopLogic(pNtk) );

    // check that the library is available
    pGenlib = (Mio_Library_t *)Abc_FrameReadLibGen();
    if ( pGenlib == NULL )
    {
        printf( "The current library is not available.\n" );
        return 0;
    }

    // start the truth tables
    Abc_AttachSetupTruthTables( uTruths );
    
    // collect all the gates
    ppGates = Mio_CollectRoots( pGenlib, 6, (float)1.0e+20, 1, &nGates, 0 );

    // derive the gate truth tables
    puTruthGates    = ABC_ALLOC( unsigned *, nGates );
    puTruthGates[0] = ABC_ALLOC( unsigned, 2 * nGates );
    for ( i = 1; i < nGates; i++ )
        puTruthGates[i] = puTruthGates[i-1] + 2;
    for ( i = 0; i < nGates; i++ )
        Mio_DeriveTruthTable( ppGates[i], uTruths, Mio_GateReadPinNum(ppGates[i]), 6, puTruthGates[i] );

    // assign the gates to pNode->pCopy
    Abc_NtkCleanCopy( pNtk );
    Abc_NtkForEachNode( pNtk, pNode, i )
    {
        nFanins = Abc_ObjFaninNum(pNode);
        if ( nFanins == 0 )
        {
            if ( Abc_SopIsConst1((char *)pNode->pData) )
                pNode->pCopy = (Abc_Obj_t *)Mio_LibraryReadConst1(pGenlib);
            else
                pNode->pCopy = (Abc_Obj_t *)Mio_LibraryReadConst0(pGenlib);
        }
        else if ( nFanins == 1 )
        {
            if ( Abc_SopIsBuf((char *)pNode->pData) )
                pNode->pCopy = (Abc_Obj_t *)Mio_LibraryReadBuf(pGenlib);
            else
                pNode->pCopy = (Abc_Obj_t *)Mio_LibraryReadInv(pGenlib);
        }
        else if ( nFanins > 6 )
        {
            printf( "Cannot attach gate with more than 6 inputs to node %s.\n", Abc_ObjName(pNode) );
            ABC_FREE( puTruthGates[0] );
            ABC_FREE( puTruthGates );
            ABC_FREE( ppGates );
            return 0;
        }
        else if ( !Abc_NodeAttach( pNode, ppGates, puTruthGates, nGates, uTruths ) )
        {
            printf( "Could not attach the library gate to node %s.\n", Abc_ObjName(pNode) );
            ABC_FREE( puTruthGates[0] );
            ABC_FREE( puTruthGates );
            ABC_FREE( ppGates );
            return 0;
        }
    }
    ABC_FREE( puTruthGates[0] );
    ABC_FREE( puTruthGates );
    ABC_FREE( ppGates );
    ABC_FREE( s_pPerms );

    // perform the final transformation
    Abc_NtkForEachNode( pNtk, pNode, i )
    {
        if ( pNode->pCopy == NULL )
        {
            printf( "Some elementary gates (constant, buffer, or inverter) are missing in the library.\n" );
            return 0;
        }
    }

    // replace SOP representation by the gate representation
    Abc_NtkForEachNode( pNtk, pNode, i )
        pNode->pData = pNode->pCopy, pNode->pCopy = NULL;
    pNtk->ntkFunc = ABC_FUNC_MAP;
    Extra_MmFlexStop( (Extra_MmFlex_t *)pNtk->pManFunc );
    pNtk->pManFunc = pGenlib;

    printf( "Library gates are successfully attached to the nodes.\n" );

    // make sure that everything is okay
    if ( !Abc_NtkCheck( pNtk ) )
    {
        printf( "Abc_NtkAttach: The network check has failed.\n" );
        return 0;
    }
    return 1;
}

/**Function*************************************************************

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Abc_NodeAttach( Abc_Obj_t * pNode, Mio_Gate_t ** ppGates, unsigned ** puTruthGates, int nGates, unsigned uTruths[][2] )
{
    int Perm[10];
    int pTempInts[10];
    unsigned uTruthNode[2];
    Abc_Obj_t * pFanin;
    Mio_Gate_t * pGate;
    int nFanins, i;

    // compute the node's truth table
    Abc_AttachComputeTruth( (char *)pNode->pData, uTruths, uTruthNode );
    // find the matching gate and permutation
    pGate = Abc_AttachFind( ppGates, puTruthGates, nGates, uTruthNode, Perm );
    if ( pGate == NULL )
        return 0;
    // permute the fanins
    nFanins = Abc_ObjFaninNum(pNode);
    Abc_ObjForEachFanin( pNode, pFanin, i )
        pTempInts[i] = pFanin->Id;
    for ( i = 0; i < nFanins; i++ )
        pNode->vFanins.pArray[Perm[i]] = pTempInts[i];
    // set the gate
    pNode->pCopy = (Abc_Obj_t *)pGate;
    return 1;
}

/**Function*************************************************************

  Synopsis    [Sets up the truth tables.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Abc_AttachSetupTruthTables( unsigned uTruths[][2] )
{
    int m, v;
    for ( v = 0; v < 5; v++ )
        uTruths[v][0] = 0;
    // set up the truth tables
    for ( m = 0; m < 32; m++ )
        for ( v = 0; v < 5; v++ )
            if ( m & (1 << v) )
                uTruths[v][0] |= (1 << m);
    // make adjustments for the case of 6 variables
    for ( v = 0; v < 5; v++ )
        uTruths[v][1] = uTruths[v][0];
    uTruths[5][0] = 0;
    uTruths[5][1] = ATTACH_FULL;
}

/**Function*************************************************************

  Synopsis    [Compute the truth table of the node's cover.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Abc_AttachComputeTruth( char * pSop, unsigned uTruthsIn[][2], unsigned * uTruthRes )
{
//    Mvc_Cube_t * pCube;
    unsigned uSignCube[2];
    int Value;
//    int nInputs = pCover->nBits/2;
    int nInputs = 6;
    int nFanins = Abc_SopGetVarNum(pSop);
    char * pCube;
    int k;

    // make sure that the number of input truth tables in equal to the number of gate inputs
    assert( nInputs < 7 );

    // clean the resulting truth table
    uTruthRes[0] = 0;
    uTruthRes[1] = 0;
    if ( nInputs < 6 )
    {
        // consider the case when only one unsigned can be used
//        Mvc_CoverForEachCube( pCover, pCube )
        Abc_SopForEachCube( pSop, nFanins, pCube )
        {
            uSignCube[0] = ATTACH_FULL;
//            Mvc_CubeForEachVarValue( pCover, pCube, Var, Value )
            Abc_CubeForEachVar( pCube, Value, k )
            {
                if ( Value == '0' )
                    uSignCube[0] &= ~uTruthsIn[k][0];
                else if ( Value == '1' )
                    uSignCube[0] &=  uTruthsIn[k][0];
            }
            uTruthRes[0] |= uSignCube[0];
        }
        if ( Abc_SopGetPhase(pSop) == 0 )
            uTruthRes[0] = ~uTruthRes[0];
        if ( nInputs < 5 )
            uTruthRes[0] &= ATTACH_MASK(1<<nInputs);
    }
    else
    {
        // consider the case when two unsigneds should be used
//        Mvc_CoverForEachCube( pCover, pCube )
        Abc_SopForEachCube( pSop, nFanins, pCube )
        {
            uSignCube[0] = ATTACH_FULL;
            uSignCube[1] = ATTACH_FULL;
//            Mvc_CubeForEachVarValue( pCover, pCube, Var, Value )
            Abc_CubeForEachVar( pCube, Value, k )
            {
                if ( Value == '0' )
                {
                    uSignCube[0] &= ~uTruthsIn[k][0];
                    uSignCube[1] &= ~uTruthsIn[k][1];
                }
                else if ( Value == '1' )
                {
                    uSignCube[0] &=  uTruthsIn[k][0];
                    uSignCube[1] &=  uTruthsIn[k][1];
                }
            }
            uTruthRes[0] |= uSignCube[0];
            uTruthRes[1] |= uSignCube[1];
        }

        // complement if the SOP is complemented
        if ( Abc_SopGetPhase(pSop) == 0 )
        {
            uTruthRes[0] = ~uTruthRes[0];
            uTruthRes[1] = ~uTruthRes[1];
        }
    }
}

/**Function*************************************************************

  Synopsis    [Find the gate by truth table.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Mio_Gate_t * Abc_AttachFind( Mio_Gate_t ** ppGates, unsigned ** puTruthGates, int nGates, unsigned * uTruthNode, int * Perm )
{
    unsigned uTruthPerm[2];
    int i, v, iNum;

    // try the gates without permutation
    if ( (iNum = Abc_AttachCompare( puTruthGates, nGates, uTruthNode )) >= 0 )
    {
        for ( v = 0; v < 6; v++ )
            Perm[v] = v;
        return ppGates[iNum];
    }
    // get permutations
    if ( s_pPerms == NULL )
    {
        s_pPerms = Extra_Permutations( 6 );
        s_nPerms = Extra_Factorial( 6 );
    }
    // try permutations
    for ( i = 0; i < s_nPerms; i++ )
    {
        Abc_TruthPermute( s_pPerms[i], 6, uTruthNode, uTruthPerm );
        if ( (iNum = Abc_AttachCompare( puTruthGates, nGates, uTruthPerm )) >= 0 )
        {
            for ( v = 0; v < 6; v++ )
                Perm[v] = (int)s_pPerms[i][v];
            return ppGates[iNum];
        }
    }
    return NULL;
}

/**Function*************************************************************

  Synopsis    [Find the gate by truth table.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Abc_AttachCompare( unsigned ** puTruthGates, int nGates, unsigned * uTruthNode )
{
    int i;
    for ( i = 0; i < nGates; i++ )
        if ( puTruthGates[i][0] == uTruthNode[0] && puTruthGates[i][1] == uTruthNode[1] )
            return i;
    return -1;
}

/**Function*************************************************************

  Synopsis    [Permutes the 6-input truth table.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Abc_TruthPermute( char * pPerm, int nVars, unsigned * uTruthNode, unsigned * uTruthPerm )
{
    int nMints, iMintPerm, iMint, v;
    uTruthPerm[0] = uTruthPerm[1] = 0;
    nMints = (1 << nVars);
    for ( iMint = 0; iMint < nMints; iMint++ )
    {
        if ( (uTruthNode[iMint>>5] & (1 << (iMint&31))) == 0 )
            continue;
        iMintPerm = 0;
        for ( v = 0; v < nVars; v++ )
            if ( iMint & (1 << v) )
                iMintPerm |= (1 << pPerm[v]);
        uTruthPerm[iMintPerm>>5] |= (1 << (iMintPerm&31));     
    }
}

////////////////////////////////////////////////////////////////////////
///                       END OF FILE                                ///
////////////////////////////////////////////////////////////////////////


ABC_NAMESPACE_IMPL_END