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

  FileName    [aigSplit.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [AIG package.]

  Synopsis    [Splits the property output cone into a set of cofactor properties.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

  Date        [Ver. 1.0. Started - April 28, 2007.]

  Revision    [$Id: aigSplit.c,v 1.00 2007/04/28 00:00:00 alanmi Exp $]

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

#include "aig.h"
#include "aig/saig/saig.h"
#include "misc/extra/extraBdd.h"

ABC_NAMESPACE_IMPL_START


////////////////////////////////////////////////////////////////////////
///                        DECLARATIONS                              ///
////////////////////////////////////////////////////////////////////////

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

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

  Synopsis    [Converts the node to MUXes.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Aig_Obj_t * Aig_NodeBddToMuxes_rec( DdManager * dd, DdNode * bFunc, Aig_Man_t * pNew, st__table * tBdd2Node )
{
    Aig_Obj_t * pNode, * pNode0, * pNode1, * pNodeC;
    assert( !Cudd_IsComplement(bFunc) );
    if ( st__lookup( tBdd2Node, (char *)bFunc, (char **)&pNode ) )
        return pNode;
    // solve for the children nodes
    pNode0 = Aig_NodeBddToMuxes_rec( dd, Cudd_Regular(cuddE(bFunc)), pNew, tBdd2Node );
    pNode0 = Aig_NotCond( pNode0, Cudd_IsComplement(cuddE(bFunc)) );
    pNode1 = Aig_NodeBddToMuxes_rec( dd, cuddT(bFunc), pNew, tBdd2Node );
    if ( ! st__lookup( tBdd2Node, (char *)Cudd_bddIthVar(dd, bFunc->index), (char **)&pNodeC ) )
        assert( 0 );
    // create the MUX node
    pNode = Aig_Mux( pNew, pNodeC, pNode1, pNode0 );
    st__insert( tBdd2Node, (char *)bFunc, (char *)pNode );
    return pNode;
}

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

  Synopsis    [Derives AIG for the BDDs of the cofactors.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Aig_Man_t * Aig_ManConvertBddsToAigs( Aig_Man_t * p, DdManager * dd, Vec_Ptr_t * vCofs )
{
    DdNode * bFunc;
    st__table * tBdd2Node;
    Aig_Man_t * pNew;
    Aig_Obj_t * pObj;
    int i;
    Aig_ManCleanData( p );
    // generate AIG for BDD
    pNew = Aig_ManStart( Aig_ManObjNum(p) );
    pNew->pName = Abc_UtilStrsav( p->pName );
    Aig_ManConst1(p)->pData = Aig_ManConst1(pNew);
    Aig_ManForEachCi( p, pObj, i )
        pObj->pData = Aig_ObjCreateCi( pNew );
    // create the table mapping BDD nodes into the ABC nodes
    tBdd2Node = st__init_table( st__ptrcmp, st__ptrhash );
    // add the constant and the elementary vars
    st__insert( tBdd2Node, (char *)Cudd_ReadOne(dd), (char *)Aig_ManConst1(pNew) );
    Aig_ManForEachCi( p, pObj, i )
        st__insert( tBdd2Node, (char *)Cudd_bddIthVar(dd, i), (char *)pObj->pData );
    // build primary outputs for the cofactors
    Vec_PtrForEachEntry( DdNode *, vCofs, bFunc, i )
    {
        if ( bFunc == Cudd_ReadLogicZero(dd) )
            continue;
        pObj = Aig_NodeBddToMuxes_rec( dd, Cudd_Regular(bFunc), pNew, tBdd2Node );
        pObj = Aig_NotCond( pObj, Cudd_IsComplement(bFunc) );
        Aig_ObjCreateCo( pNew, pObj );
    }
    st__free_table( tBdd2Node );

    // duplicate the rest of the AIG
    // add the POs
    Aig_ManForEachCo( p, pObj, i )
    {
        if ( i == 0 )
            continue;
        Aig_ManDupSimpleDfs_rec( pNew, p, Aig_ObjFanin0(pObj) );
        pObj->pData = Aig_ObjCreateCo( pNew, Aig_ObjChild0Copy(pObj) );
    }
    Aig_ManCleanup( pNew );
    Aig_ManSetRegNum( pNew, Aig_ManRegNum(p) );
    // check the resulting network
    if ( !Aig_ManCheck(pNew) )
        printf( "Aig_ManConvertBddsToAigs(): The check has failed.\n" );
    return pNew;
}

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

  Synopsis    [Returns the array of constraint candidates.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
DdNode * Aig_ManBuildPoBdd_rec( Aig_Man_t * p, Aig_Obj_t * pObj, DdManager * dd )
{
    DdNode * bBdd0, * bBdd1;
    if ( pObj->pData != NULL )
        return (DdNode *)pObj->pData;
    assert( Aig_ObjIsNode(pObj) );
    bBdd0 = Aig_ManBuildPoBdd_rec( p, Aig_ObjFanin0(pObj), dd ); 
    bBdd1 = Aig_ManBuildPoBdd_rec( p, Aig_ObjFanin1(pObj), dd ); 
    bBdd0 = Cudd_NotCond( bBdd0, Aig_ObjFaninC0(pObj) );
    bBdd1 = Cudd_NotCond( bBdd1, Aig_ObjFaninC1(pObj) );
    pObj->pData = Cudd_bddAnd( dd, bBdd0, bBdd1 );  Cudd_Ref( (DdNode *)pObj->pData );
    return (DdNode *)pObj->pData;
}

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

  Synopsis    [Derive BDDs for the cofactors.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Vec_Ptr_t * Aig_ManCofactorBdds( Aig_Man_t * p, Vec_Ptr_t * vSubset, DdManager * dd, DdNode * bFunc )
{
    Vec_Ptr_t * vCofs;
    DdNode * bCube, * bTemp, * bCof, ** pbVars;
    int i;
    vCofs = Vec_PtrAlloc( 100 );
    pbVars = (DdNode **)Vec_PtrArray(vSubset);
    for ( i = 0; i < (1 << Vec_PtrSize(vSubset)); i++ )
    {
        bCube = Extra_bddBitsToCube( dd, i, Vec_PtrSize(vSubset), pbVars, 1 );  Cudd_Ref( bCube );
        bCof  = Cudd_Cofactor( dd, bFunc, bCube );        Cudd_Ref( bCof );
        bCof  = Cudd_bddAnd( dd, bTemp = bCof, bCube );   Cudd_Ref( bCof );
        Cudd_RecursiveDeref( dd, bTemp );
        Cudd_RecursiveDeref( dd, bCube );
        Vec_PtrPush( vCofs, bCof );
    }
    return vCofs;
}

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

  Synopsis    [Construct BDDs for the primary output.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
DdManager * Aig_ManBuildPoBdd( Aig_Man_t * p, DdNode ** pbFunc )
{
    DdManager * dd;
    Aig_Obj_t * pObj;
    int i;
    assert( Saig_ManPoNum(p) == 1 );
    Aig_ManCleanData( p );
    dd = Cudd_Init( Aig_ManCiNum(p), 0, CUDD_UNIQUE_SLOTS, CUDD_CACHE_SLOTS, 0 );
    Cudd_AutodynEnable( dd,  CUDD_REORDER_SYMM_SIFT );
    pObj = Aig_ManConst1(p);
    pObj->pData = Cudd_ReadOne(dd);  Cudd_Ref( (DdNode *)pObj->pData );
    Aig_ManForEachCi( p, pObj, i )
    {
        pObj->pData = Cudd_bddIthVar(dd, i);  Cudd_Ref( (DdNode *)pObj->pData );
    }
    pObj = Aig_ManCo( p, 0 );
    *pbFunc = Aig_ManBuildPoBdd_rec( p, Aig_ObjFanin0(pObj), dd );  Cudd_Ref( *pbFunc );
    *pbFunc = Cudd_NotCond( *pbFunc, Aig_ObjFaninC0(pObj) );
    Aig_ManForEachObj( p, pObj, i )
    {
        if ( pObj->pData )
            Cudd_RecursiveDeref( dd, (DdNode *)pObj->pData );
    }
    Cudd_ReduceHeap( dd,  CUDD_REORDER_SYMM_SIFT, 1 );
    return dd;
}

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

  Synopsis    [Randomly selects a random subset of inputs.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Vec_Ptr_t * Aig_ManVecRandSubset( Vec_Ptr_t * vVec, int nVars )
{
    Vec_Ptr_t * vRes;
    void * pEntry;
    unsigned Rand; 
    vRes = Vec_PtrDup(vVec);
    while ( Vec_PtrSize(vRes) > nVars )
    {
        Rand   = Aig_ManRandom( 0 );
        pEntry = Vec_PtrEntry( vRes, Rand % Vec_PtrSize(vRes) );
        Vec_PtrRemove( vRes, pEntry );
    }
    return vRes;
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Aig_Man_t * Aig_ManSplit( Aig_Man_t * p, int nVars, int fVerbose )
{
    Aig_Man_t * pRes;
    Aig_Obj_t * pNode;
    DdNode * bFunc;
    DdManager * dd;
    Vec_Ptr_t * vSupp, * vSubs, * vCofs;
    int i;
    clock_t clk = clock();
    if ( Saig_ManPoNum(p) != 1 )
    {
        printf( "Currently works only for one primary output.\n" );
        return NULL;
    }
    if ( nVars < 1 )
    {
        printf( "The number of cofactoring variables should be a positive number.\n" );
        return NULL;
    }
    if ( nVars > 16 )
    {
        printf( "The number of cofactoring variables should be less than 17.\n" );
        return NULL;
    }
    vSupp = Aig_Support( p, Aig_ObjFanin0(Aig_ManCo(p,0)) );
    if ( Vec_PtrSize(vSupp) == 0 )
    {
        printf( "Property output function is a constant.\n" );
        Vec_PtrFree( vSupp );
        return NULL;
    }
    dd    = Aig_ManBuildPoBdd( p, &bFunc ); // bFunc is referenced
    if ( fVerbose )
        printf( "Support =%5d.  BDD size =%6d.  ", Vec_PtrSize(vSupp), Cudd_DagSize(bFunc) );
    vSubs = Aig_ManVecRandSubset( vSupp, nVars );
    // replace nodes by their BDD variables
    Vec_PtrForEachEntry( Aig_Obj_t *, vSubs, pNode, i )
        Vec_PtrWriteEntry( vSubs, i, pNode->pData );
    // derive cofactors and functions
    vCofs = Aig_ManCofactorBdds( p, vSubs, dd, bFunc );
    pRes  = Aig_ManConvertBddsToAigs( p, dd, vCofs );
    Vec_PtrFree( vSupp );
    Vec_PtrFree( vSubs );
    if ( fVerbose )
        printf( "Created %d cofactors (out of %d).  ", Saig_ManPoNum(pRes), Vec_PtrSize(vCofs) );
    if ( fVerbose )
        Abc_PrintTime( 1, "Time", clock() - clk );
    // dereference
    Cudd_RecursiveDeref( dd, bFunc );
    Vec_PtrForEachEntry( DdNode *, vCofs, bFunc, i )
        Cudd_RecursiveDeref( dd, bFunc );
    Vec_PtrFree( vCofs );
    Extra_StopManager( dd );
    return pRes;
}

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


ABC_NAMESPACE_IMPL_END

