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

  FileName    [abcSymm.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Network and node package.]

  Synopsis    [Computation of two-variable symmetries.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

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

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

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

#include "base/abc/abc.h"
#include "opt/sim/sim.h"
#include "opt/dau/dau.h"
#include "misc/util/utilTruth.h"

#ifdef ABC_USE_CUDD
#include "bdd/extrab/extraBdd.h"
#endif

ABC_NAMESPACE_IMPL_START


////////////////////////////////////////////////////////////////////////
///                        DECLARATIONS                              ///
////////////////////////////////////////////////////////////////////////
 
#ifdef ABC_USE_CUDD

static void Abc_NtkSymmetriesUsingBdds( Abc_Ntk_t * pNtk, int fNaive, int fReorder, int fVerbose );
static void Abc_NtkSymmetriesUsingSandS( Abc_Ntk_t * pNtk, int fVerbose );
static void Ntk_NetworkSymmsBdd( DdManager * dd, Abc_Ntk_t * pNtk, int fNaive, int fVerbose );
static void Ntk_NetworkSymmsPrint( Abc_Ntk_t * pNtk, Extra_SymmInfo_t * pSymms );

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

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

  Synopsis    [The top level procedure to compute symmetries.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Abc_NtkSymmetries( Abc_Ntk_t * pNtk, int fUseBdds, int fNaive, int fReorder, int fVerbose )
{
    if ( fUseBdds || fNaive )
        Abc_NtkSymmetriesUsingBdds( pNtk, fNaive, fReorder, fVerbose );
    else
        Abc_NtkSymmetriesUsingSandS( pNtk, fVerbose );
}

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

  Synopsis    [Symmetry computation using simulation and SAT.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Abc_NtkSymmetriesUsingSandS( Abc_Ntk_t * pNtk, int fVerbose )
{
//    extern int Sim_ComputeTwoVarSymms( Abc_Ntk_t * pNtk, int fVerbose );
    int nSymms = Sim_ComputeTwoVarSymms( pNtk, fVerbose );
    printf( "The total number of symmetries is %d.\n", nSymms );
}

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

  Synopsis    [Symmetry computation using BDDs (both naive and smart).]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Abc_NtkSymmetriesUsingBdds( Abc_Ntk_t * pNtk, int fNaive, int fReorder, int fVerbose )
{
    DdManager * dd;
    abctime clk, clkBdd, clkSym;
    int fGarbCollect = 1;

    // compute the global functions
clk = Abc_Clock();
    dd = (DdManager *)Abc_NtkBuildGlobalBdds( pNtk, 10000000, 1, fReorder, 0, fVerbose );
    printf( "Shared BDD size = %d nodes.\n", Abc_NtkSizeOfGlobalBdds(pNtk) ); 
    Cudd_AutodynDisable( dd );
    if ( !fGarbCollect )
        Cudd_DisableGarbageCollection( dd );
    Cudd_zddVarsFromBddVars( dd, 2 );
clkBdd = Abc_Clock() - clk;
    // create the collapsed network
clk = Abc_Clock();
    Ntk_NetworkSymmsBdd( dd, pNtk, fNaive, fVerbose );
clkSym = Abc_Clock() - clk;
    // undo the global functions
    Abc_NtkFreeGlobalBdds( pNtk, 1 );
printf( "Statistics of BDD-based symmetry detection:\n" );
printf( "Algorithm = %s. Reordering = %s. Garbage collection = %s.\n", 
       fNaive? "naive" : "fast", fReorder? "yes" : "no", fGarbCollect? "yes" : "no" );
ABC_PRT( "Constructing BDDs", clkBdd );
ABC_PRT( "Computing symms  ", clkSym );
ABC_PRT( "TOTAL            ", clkBdd + clkSym );
}

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

  Synopsis    [Symmetry computation using BDDs (both naive and smart).]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Ntk_NetworkSymmsBdd( DdManager * dd, Abc_Ntk_t * pNtk, int fNaive, int fVerbose )
{
    Extra_SymmInfo_t * pSymms;
    Abc_Obj_t * pNode;
    DdNode * bFunc;
    int nSymms = 0;
    int nSupps = 0;
    int i;

    // compute symmetry info for each PO
    Abc_NtkForEachCo( pNtk, pNode, i )
    {
//      bFunc = pNtk->vFuncsGlob->pArray[i];
        bFunc = (DdNode *)Abc_ObjGlobalBdd( pNode );
        nSupps += Cudd_SupportSize( dd, bFunc );
        if ( Cudd_IsConstant(bFunc) )
            continue;
        if ( fNaive )
            pSymms = Extra_SymmPairsComputeNaive( dd, bFunc );
        else
            pSymms = Extra_SymmPairsCompute( dd, bFunc );
        nSymms += pSymms->nSymms;
        if ( fVerbose )
        {
            printf( "Output %6s (%d): ", Abc_ObjName(pNode), pSymms->nSymms );
            Ntk_NetworkSymmsPrint( pNtk, pSymms );
        }
//Extra_SymmPairsPrint( pSymms );
        Extra_SymmPairsDissolve( pSymms );
    }
    printf( "Total number of vars in functional supports = %8d.\n", nSupps );
    printf( "Total number of two-variable symmetries     = %8d.\n", nSymms );
}

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

  Synopsis    [Printing symmetry groups from the symmetry data structure.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Ntk_NetworkSymmsPrint( Abc_Ntk_t * pNtk, Extra_SymmInfo_t * pSymms )
{
    char ** pInputNames;
    int * pVarTaken;
    int i, k, nVars, nSize, fStart;

    // get variable names
    nVars = Abc_NtkCiNum(pNtk);
    pInputNames = Abc_NtkCollectCioNames( pNtk, 0 );

    // alloc the array of marks
    pVarTaken = ABC_ALLOC( int, nVars );
    memset( pVarTaken, 0, sizeof(int) * nVars );

    // print the groups
    fStart = 1;
    nSize = pSymms->nVars;
    for ( i = 0; i < nSize; i++ )
    {
        // skip the variable already considered
        if ( pVarTaken[i] )
            continue;
        // find all the vars symmetric with this one
        for ( k = 0; k < nSize; k++ )
        {
            if ( k == i )
                continue;
            if ( pSymms->pSymms[i][k] == 0 )
                continue;
            // vars i and k are symmetric
            assert( pVarTaken[k] == 0 );
            // there is a new symmetry pair 
            if ( fStart == 1 )
            {  // start a new symmetry class
                fStart = 0;
                printf( "  { %s", pInputNames[ pSymms->pVars[i] ] );
                // mark the var as taken
                pVarTaken[i] = 1;
            }
            printf( " %s", pInputNames[ pSymms->pVars[k] ] );
            // mark the var as taken
            pVarTaken[k] = 1;
        }
        if ( fStart == 0 )
        {
            printf( " }" );
            fStart = 1; 
        }   
    }   
    printf( "\n" );

    ABC_FREE( pInputNames );
    ABC_FREE( pVarTaken );
}

#else

void Abc_NtkSymmetries( Abc_Ntk_t * pNtk, int fUseBdds, int fNaive, int fReorder, int fVerbose ) {}

#endif

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

  Synopsis    [Try different permutations.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Ntk_SymTryRandomFlips( word * pFun, word * pNpn, int nVars )
{
    int Rand[16] = { 17290, 20203, 19027, 12035, 14687, 10920, 10413, 261, 2072, 16899, 4480, 6192, 3978, 8343, 745, 1370 };
    int i, nWords = Abc_TtWordNum(nVars);
    word * pFunT = ABC_CALLOC( word, nWords );
    Abc_TtCopy( pFunT, pFun, nWords, 0 );
    for ( i = 0; i < 16; i++ )
        Abc_TtFlip( pFunT, nWords, Rand[i] % (nVars-1) );
    assert( Abc_TtCompareRev(pNpn, pFunT, nWords) != 1 );
    ABC_FREE( pFunT );
}

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

  Synopsis    [Find canonical form of symmetric function.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Ntk_SymFunDeriveNpn( word * pFun, int nVars, int * pComp )
{
    int i, nWords = Abc_TtWordNum(nVars);
    word * pFunB = ABC_CALLOC( word, nWords );
    Abc_TtCopy( pFunB, pFun, nWords, 1 );
    if ( Abc_TtCompareRev(pFunB, pFun, nWords) == 1 )
        Abc_TtCopy( pFunB, pFun, nWords, 0 );
    for ( i = 0; i < (1 << nVars); i++ )
    {
        Abc_TtFlip( pFun, nWords, pComp[i] );
        if ( Abc_TtCompareRev(pFunB, pFun, nWords) == 1 )
            Abc_TtCopy( pFunB, pFun, nWords, 0 );
        Abc_TtNot( pFun, nWords );
        if ( Abc_TtCompareRev(pFunB, pFun, nWords) == 1 )
            Abc_TtCopy( pFunB, pFun, nWords, 0 );
    }
    //Ntk_SymTryRandomFlips( pFun, pFunB, nVars );
    Abc_TtCopy( pFun, pFunB, nWords, 0 );
    ABC_FREE( pFunB );
}

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

  Synopsis    [Generating NPN classes of all symmetric function of N variables.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Ntk_SymFunGenerate( int nVars, int fVerbose )
{
    int k, m, Class;
    int * pComp = Extra_GreyCodeSchedule( nVars );
    Vec_Mem_t * vTtMem = Vec_MemAlloc( Abc_Truth6WordNum(nVars), 12 );
    Vec_MemHashAlloc( vTtMem, 10000 );
    assert( !(nVars < 1 || nVars > 16) );
    printf( "Generating truth tables of all symmetric functions of %d variables.\n", nVars );
    for ( m = 0; m < (1 << (nVars+1)); m++ )
    {
        word * pFun;
        char Ones[100] = {0};
        for ( k = 0; k <= nVars; k++ )
            Ones[k] = '0' + ((m >> k) & 1);
        if ( fVerbose )
            printf( "%s : ", Ones );
        pFun = Abc_TtSymFunGenerate( Ones, nVars );
        if ( nVars < 6 )
            pFun[0] = Abc_Tt6Stretch( pFun[0], nVars );
        if ( fVerbose )
            Extra_PrintHex( stdout, (unsigned *)pFun, nVars );
        Ntk_SymFunDeriveNpn( pFun, nVars, pComp );
        Class = Vec_MemHashInsert( vTtMem, pFun );
        if ( fVerbose )
        {
            printf( " : NPN " );
            Extra_PrintHex( stdout, (unsigned *)pFun, nVars );
            printf( "  Class %3d", Class );
            printf( "\n" );
        }
        ABC_FREE( pFun );
    }
    printf( "The number of different NPN classes is %d.\n", Vec_MemEntryNum(vTtMem) );
    Vec_MemHashFree( vTtMem );
    Vec_MemFreeP( &vTtMem );
    ABC_FREE( pComp );
}

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


ABC_NAMESPACE_IMPL_END

