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

  FileName    [llb2Sweep.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [BDD based reachability.]

  Synopsis    [Non-linear quantification scheduling.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

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

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

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

#include "llbInt.h"

ABC_NAMESPACE_IMPL_START
 

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

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

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

  Synopsis    [Find good static variable ordering.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Llb_Nonlin4SweepOrder_rec( Aig_Man_t * pAig, Aig_Obj_t * pObj, Vec_Int_t * vOrder, int * pCounter, int fSaveAll )
{
    Aig_Obj_t * pFanin0, * pFanin1;
    if ( Aig_ObjIsTravIdCurrent(pAig, pObj) )
        return;
    Aig_ObjSetTravIdCurrent( pAig, pObj );
    assert( Llb_ObjBddVar(vOrder, pObj) < 0 );
    if ( Aig_ObjIsCi(pObj) )
    {
        Vec_IntWriteEntry( vOrder, Aig_ObjId(pObj), (*pCounter)++ );
        return;
    }
    // try fanins with higher level first
    pFanin0 = Aig_ObjFanin0(pObj);
    pFanin1 = Aig_ObjFanin1(pObj);
//    if ( pFanin0->Level > pFanin1->Level || (pFanin0->Level == pFanin1->Level && pFanin0->Id < pFanin1->Id) )
    if ( pFanin0->Level > pFanin1->Level )
    {
        Llb_Nonlin4SweepOrder_rec( pAig, pFanin0, vOrder, pCounter, fSaveAll );
        Llb_Nonlin4SweepOrder_rec( pAig, pFanin1, vOrder, pCounter, fSaveAll );
    }
    else
    {
        Llb_Nonlin4SweepOrder_rec( pAig, pFanin1, vOrder, pCounter, fSaveAll );
        Llb_Nonlin4SweepOrder_rec( pAig, pFanin0, vOrder, pCounter, fSaveAll );
    }
    if ( fSaveAll || pObj->fMarkA )
        Vec_IntWriteEntry( vOrder, Aig_ObjId(pObj), (*pCounter)++ );
}

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

  Synopsis    [Find good static variable ordering.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Vec_Int_t * Llb_Nonlin4SweepOrder( Aig_Man_t * pAig, int * pCounter, int fSaveAll )
{
    Vec_Int_t * vOrder;
    Aig_Obj_t * pObj;
    int i, Counter = 0;
    // collect nodes in the order
    vOrder = Vec_IntStartFull( Aig_ManObjNumMax(pAig) );
    Aig_ManIncrementTravId( pAig );
    Aig_ObjSetTravIdCurrent( pAig, Aig_ManConst1(pAig) );
    Aig_ManForEachCo( pAig, pObj, i )
    {
        Vec_IntWriteEntry( vOrder, Aig_ObjId(pObj), Counter++ );
        Llb_Nonlin4SweepOrder_rec( pAig, Aig_ObjFanin0(pObj), vOrder, &Counter, fSaveAll );
    }
    Aig_ManForEachCi( pAig, pObj, i )
        if ( Llb_ObjBddVar(vOrder, pObj) < 0 )
            Vec_IntWriteEntry( vOrder, Aig_ObjId(pObj), Counter++ );
//    assert( Counter == Aig_ManObjNum(pAig) - 1 ); // no dangling nodes
    if ( pCounter )
        *pCounter = Counter - Aig_ManCiNum(pAig) - Aig_ManCoNum(pAig);
    return vOrder;
}


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

  Synopsis    [Performs BDD sweep on the netlist.]

  Description [Returns AIG with internal cut points labeled with fMarkA.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Llb4_Nonlin4SweepCutpoints( Aig_Man_t * pAig, Vec_Int_t * vOrder, int nBddLimit, int fVerbose )
{
    DdManager * dd;
    DdNode * bFunc0, * bFunc1, * bFunc;
    Aig_Obj_t * pObj;
    int i, Counter = 0, Counter1 = 0;
    dd = Cudd_Init( Aig_ManObjNumMax(pAig), 0, CUDD_UNIQUE_SLOTS, CUDD_CACHE_SLOTS, 0 );
    // assign elementary variables
    Aig_ManCleanData( pAig );
    Aig_ManForEachCi( pAig, pObj, i )
        pObj->pData = Cudd_bddIthVar( dd, Llb_ObjBddVar(vOrder, pObj) );
    // sweep internal nodes
    Aig_ManForEachNode( pAig, pObj, i )
    {
/*
        if ( pObj->nRefs >= 4 )
        {
            bFunc = Cudd_bddIthVar( dd, Llb_ObjBddVar(vOrder, pObj) );  Cudd_Ref( bFunc );
            pObj->pData = bFunc;
            Counter1++;
            continue;
        }
*/
        bFunc0 = Cudd_NotCond( (DdNode *)Aig_ObjFanin0(pObj)->pData, Aig_ObjFaninC0(pObj) );
        bFunc1 = Cudd_NotCond( (DdNode *)Aig_ObjFanin1(pObj)->pData, Aig_ObjFaninC1(pObj) );
        bFunc  = Cudd_bddAnd( dd, bFunc0, bFunc1 );  Cudd_Ref( bFunc );
        if ( Cudd_DagSize(bFunc) > nBddLimit )
        {
//            if ( fVerbose )
//                printf( "Node %5d : Beg =%5d. ", i, Cudd_DagSize(bFunc) );

            // add cutpoint at a larger one
            Cudd_RecursiveDeref( dd, bFunc );
            if ( Cudd_DagSize(bFunc0) >= Cudd_DagSize(bFunc1) )
            {
                Cudd_RecursiveDeref( dd, (DdNode *)Aig_ObjFanin0(pObj)->pData );
                bFunc = Cudd_bddIthVar( dd, Llb_ObjBddVar(vOrder, Aig_ObjFanin0(pObj)) );
                Aig_ObjFanin0(pObj)->pData = bFunc;  Cudd_Ref( bFunc );
                Aig_ObjFanin0(pObj)->fMarkA = 1;

//                if ( fVerbose )
//                    printf( "Ref =%3d  ", Aig_ObjFanin0(pObj)->nRefs );
            }
            else
            {
                Cudd_RecursiveDeref( dd, (DdNode *)Aig_ObjFanin1(pObj)->pData );
                bFunc = Cudd_bddIthVar( dd, Llb_ObjBddVar(vOrder, Aig_ObjFanin1(pObj)) );
                Aig_ObjFanin1(pObj)->pData = bFunc;  Cudd_Ref( bFunc );
                Aig_ObjFanin1(pObj)->fMarkA = 1;

//                if ( fVerbose )
//                    printf( "Ref =%3d  ", Aig_ObjFanin1(pObj)->nRefs );
            }
            // perform new operation
            bFunc0 = Cudd_NotCond( (DdNode *)Aig_ObjFanin0(pObj)->pData, Aig_ObjFaninC0(pObj) );
            bFunc1 = Cudd_NotCond( (DdNode *)Aig_ObjFanin1(pObj)->pData, Aig_ObjFaninC1(pObj) );
            bFunc  = Cudd_bddAnd( dd, bFunc0, bFunc1 );  Cudd_Ref( bFunc );
//            assert( Cudd_DagSize(bFunc) <= nBddLimit );

//            if ( fVerbose )
//                printf( "End =%5d.\n", Cudd_DagSize(bFunc) );
            Counter++;
        }
        pObj->pData = bFunc;
//printf( "%d ", Cudd_DagSize(bFunc) );
    }
//printf( "\n" );
    // clean up
    Aig_ManForEachNode( pAig, pObj, i )
        Cudd_RecursiveDeref( dd, (DdNode *)pObj->pData );
    Extra_StopManager( dd );
//    Aig_ManCleanMarkA( pAig );
    if ( fVerbose )
    printf( "Added %d cut points.  Used %d high fanout points.\n", Counter, Counter1 );
    return Counter + Counter1;
}

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

  Synopsis    [Derives BDDs for the partitions.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
DdNode * Llb_Nonlin4SweepPartitions_rec( DdManager * dd, Aig_Obj_t * pObj, Vec_Int_t * vOrder, Vec_Ptr_t * vRoots )
{
    DdNode * bBdd, * bBdd0, * bBdd1, * bPart, * vVar;
    if ( Aig_ObjIsConst1(pObj) )
        return Cudd_ReadOne(dd); 
    if ( Aig_ObjIsCi(pObj) )
        return Cudd_bddIthVar( dd, Llb_ObjBddVar(vOrder, pObj) );
    if ( pObj->pData )
        return (DdNode *)pObj->pData;
    if ( Aig_ObjIsCo(pObj) )
    {
        bBdd0 = Cudd_NotCond( Llb_Nonlin4SweepPartitions_rec(dd, Aig_ObjFanin0(pObj), vOrder, vRoots), Aig_ObjFaninC0(pObj) );
        bPart = Cudd_bddXnor( dd, Cudd_bddIthVar( dd, Llb_ObjBddVar(vOrder, pObj) ), bBdd0 );  Cudd_Ref( bPart );
        Vec_PtrPush( vRoots, bPart );
        return NULL;
    }
    bBdd0 = Cudd_NotCond( Llb_Nonlin4SweepPartitions_rec(dd, Aig_ObjFanin0(pObj), vOrder, vRoots), Aig_ObjFaninC0(pObj) );
    bBdd1 = Cudd_NotCond( Llb_Nonlin4SweepPartitions_rec(dd, Aig_ObjFanin1(pObj), vOrder, vRoots), Aig_ObjFaninC1(pObj) );
    bBdd  = Cudd_bddAnd( dd, bBdd0, bBdd1 );   Cudd_Ref( bBdd );
    if ( Llb_ObjBddVar(vOrder, pObj) >= 0 )
    {
        vVar  = Cudd_bddIthVar( dd, Llb_ObjBddVar(vOrder, pObj) );
        bPart = Cudd_bddXnor( dd, vVar, bBdd );  Cudd_Ref( bPart );
        Vec_PtrPush( vRoots, bPart );
        Cudd_RecursiveDeref( dd, bBdd );
        bBdd = vVar;  Cudd_Ref( vVar );
    }
    pObj->pData = bBdd;
    return bBdd;
}

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

  Synopsis    [Derives BDDs for the partitions.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Vec_Ptr_t * Llb_Nonlin4SweepPartitions( DdManager * dd, Aig_Man_t * pAig, Vec_Int_t * vOrder, int fTransition )
{
    Vec_Ptr_t * vRoots;
    Aig_Obj_t * pObj;
    int i;
    Aig_ManCleanData( pAig );
    vRoots = Vec_PtrAlloc( 100 );
    if ( fTransition )
    {
        Saig_ManForEachLi( pAig, pObj, i )
            Llb_Nonlin4SweepPartitions_rec( dd, pObj, vOrder, vRoots );
    }
    else
    {
        Saig_ManForEachPo( pAig, pObj, i )
            Llb_Nonlin4SweepPartitions_rec( dd, pObj, vOrder, vRoots );
    }
    Aig_ManForEachNode( pAig, pObj, i )
        if ( pObj->pData )
            Cudd_RecursiveDeref( dd, (DdNode *)pObj->pData );
    return vRoots;
}

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

  Synopsis    [Get bad state monitor.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
DdNode * Llb4_Nonlin4SweepBadMonitor( Aig_Man_t * pAig, Vec_Int_t * vOrder, DdManager * dd )
{
    Aig_Obj_t * pObj;
    DdNode * bRes, * bVar, * bTemp;
    int i;
    abctime TimeStop;
    TimeStop = dd->TimeStop;  dd->TimeStop = 0;
    bRes = Cudd_ReadOne( dd );   Cudd_Ref( bRes );
    Saig_ManForEachPo( pAig, pObj, i )
    {
        bVar = Cudd_bddIthVar( dd, Llb_ObjBddVar(vOrder, pObj) );
        bRes = Cudd_bddAnd( dd, bTemp = bRes, Cudd_Not(bVar) );  Cudd_Ref( bRes );
        Cudd_RecursiveDeref( dd, bTemp );
    }
    Cudd_Deref( bRes );
    dd->TimeStop = TimeStop;
    return Cudd_Not(bRes);
}

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

  Synopsis    [Creates quantifiable variables for both types of traversal.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Vec_Int_t * Llb_Nonlin4SweepVars2Q( Aig_Man_t * pAig, Vec_Int_t * vOrder, int fAddLis )
{
    Vec_Int_t * vVars2Q;
    Aig_Obj_t * pObj;
    int i;
    vVars2Q = Vec_IntAlloc( 0 );
    Vec_IntFill( vVars2Q, Aig_ManObjNumMax(pAig), 1 );
    // add flop outputs
    Saig_ManForEachLo( pAig, pObj, i )
        Vec_IntWriteEntry( vVars2Q, Llb_ObjBddVar(vOrder, pObj), 0 );
    // add flop inputs
    if ( fAddLis )
    Saig_ManForEachLi( pAig, pObj, i )
        Vec_IntWriteEntry( vVars2Q, Llb_ObjBddVar(vOrder, pObj), 0 );
    return vVars2Q;
}

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

  Synopsis    [Multiply every partition by the cube.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Llb_Nonlin4SweepDeref( DdManager * dd, Vec_Ptr_t * vParts )
{
    DdNode * bFunc;
    int i;
    Vec_PtrForEachEntry( DdNode *, vParts, bFunc, i )
        Cudd_RecursiveDeref( dd, bFunc );
    Vec_PtrFree( vParts );
}

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

  Synopsis    [Multiply every partition by the cube.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Llb_Nonlin4SweepPrint( Vec_Ptr_t * vFuncs )
{
    DdNode * bFunc;
    int i;
    printf( "(%d) ", Vec_PtrSize(vFuncs) );
    Vec_PtrForEachEntry( DdNode *, vFuncs, bFunc, i )
        printf( "%d ", Cudd_DagSize(bFunc) );
    printf( "\n" );
}

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

  Synopsis    [Computes bad states.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
DdManager * Llb4_Nonlin4SweepBadStates( Aig_Man_t * pAig, Vec_Int_t * vOrder, int nVars )
{
    DdManager * dd;
    Vec_Ptr_t * vParts;
    Vec_Int_t * vVars2Q;
    DdNode * bMonitor, * bImage;
    // get quantifiable variables
    vVars2Q = Llb_Nonlin4SweepVars2Q( pAig, vOrder, 0 );
    // start BDD manager and create partitions 
    dd = Cudd_Init( nVars, 0, CUDD_UNIQUE_SLOTS, CUDD_CACHE_SLOTS, 0 );
    vParts = Llb_Nonlin4SweepPartitions( dd, pAig, vOrder, 0 );
//printf( "Outputs: " );
//Llb_Nonlin4SweepPrint( vParts );
    // compute image of the partitions
    bMonitor = Llb4_Nonlin4SweepBadMonitor( pAig, vOrder, dd );  Cudd_Ref( bMonitor );
    Cudd_AutodynEnable( dd,  CUDD_REORDER_SYMM_SIFT );
    bImage = Llb_Nonlin4Image( dd, vParts, bMonitor, vVars2Q );  Cudd_Ref( bImage );
    Cudd_RecursiveDeref( dd, bMonitor );
    Llb_Nonlin4SweepDeref( dd, vParts );
    Vec_IntFree( vVars2Q );
    // save image and return
    dd->bFunc = bImage;
    return dd;
}

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

  Synopsis    [Computes clusters.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
DdManager * Llb4_Nonlin4SweepGroups( Aig_Man_t * pAig, Vec_Int_t * vOrder, int nVars, Vec_Ptr_t ** pvGroups, int nBddLimitClp, int fVerbose )
{
    DdManager * dd;
    Vec_Ptr_t * vParts;
    Vec_Int_t * vVars2Q;
    // get quantifiable variables
    vVars2Q = Llb_Nonlin4SweepVars2Q( pAig, vOrder, 1 );
    // start BDD manager and create partitions 
    dd = Cudd_Init( nVars, 0, CUDD_UNIQUE_SLOTS, CUDD_CACHE_SLOTS, 0 );
    vParts = Llb_Nonlin4SweepPartitions( dd, pAig, vOrder, 1 );
//printf( "Transitions: " );
//Llb_Nonlin4SweepPrint( vParts );
    // compute image of the partitions

    Cudd_AutodynEnable( dd,  CUDD_REORDER_SYMM_SIFT );
    *pvGroups = Llb_Nonlin4Group( dd, vParts, vVars2Q, nBddLimitClp );
    Llb_Nonlin4SweepDeref( dd, vParts );
//    *pvGroups = vParts;

if ( fVerbose )
{
printf( "Groups: " );
Llb_Nonlin4SweepPrint( *pvGroups );
}

    Vec_IntFree( vVars2Q );
    return dd;
}


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

  Synopsis    [Creates quantifiable variables for both types of traversal.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Llb_Nonlin4SweepPrintSuppProfile( DdManager * dd, Aig_Man_t * pAig, Vec_Int_t * vOrder, Vec_Ptr_t * vGroups, int fVerbose )
{
    Aig_Obj_t * pObj;
    int i, * pSupp;
    int nSuppAll = 0, nSuppPi = 0, nSuppPo = 0, nSuppLi = 0, nSuppLo = 0, nSuppAnd = 0;

    pSupp = ABC_CALLOC( int, Cudd_ReadSize(dd) );
    Extra_VectorSupportArray( dd, (DdNode **)Vec_PtrArray(vGroups), Vec_PtrSize(vGroups), pSupp );

    Aig_ManForEachObj( pAig, pObj, i )
    {
        if ( Llb_ObjBddVar(vOrder, pObj) < 0 )
            continue;
        // remove variables that do not participate
        if ( pSupp[Llb_ObjBddVar(vOrder, pObj)] == 0 )
        {
            if ( Aig_ObjIsNode(pObj) )
                Vec_IntWriteEntry( vOrder, Aig_ObjId(pObj), -1 );
            continue;
        }
        nSuppAll++;
        if ( Saig_ObjIsPi(pAig, pObj) )
            nSuppPi++;
        else if ( Saig_ObjIsLo(pAig, pObj) )
            nSuppLo++;
        else if ( Saig_ObjIsPo(pAig, pObj) )
            nSuppPo++;
        else if ( Saig_ObjIsLi(pAig, pObj) )
            nSuppLi++;
        else
            nSuppAnd++;
    }
    ABC_FREE( pSupp );

    if ( fVerbose )
    {
    printf( "Groups =%3d  ", Vec_PtrSize(vGroups) );
    printf( "Variables: all =%4d ",  nSuppAll );
    printf( "pi =%4d ",  nSuppPi );
    printf( "po =%4d ",  nSuppPo );
    printf( "lo =%4d ",  nSuppLo );
    printf( "li =%4d ",  nSuppLi );
    printf( "and =%4d",  nSuppAnd );
    printf( "\n" );
    }
}

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

  Synopsis    [Performs BDD sweep on the netlist.]

  Description [Returns BDD manager, ordering, clusters, and bad states 
  inside dd->bFunc.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Llb4_Nonlin4Sweep( Aig_Man_t * pAig, int nSweepMax, int nClusterMax, DdManager ** pdd, Vec_Int_t ** pvOrder, Vec_Ptr_t ** pvGroups, int fVerbose )
{
    DdManager * ddBad, * ddWork;
    Vec_Ptr_t * vGroups;
    Vec_Int_t * vOrder;
    int Counter, nCutPoints;

    // get the original ordering
    Aig_ManCleanMarkA( pAig );
    vOrder = Llb_Nonlin4SweepOrder( pAig, &Counter, 1 );
    assert( Counter == Aig_ManNodeNum(pAig) );
    // mark the nodes
    nCutPoints = Llb4_Nonlin4SweepCutpoints( pAig, vOrder, nSweepMax, fVerbose );
    Vec_IntFree( vOrder );
    // get better ordering
    vOrder = Llb_Nonlin4SweepOrder( pAig, &Counter, 0 );
    assert( Counter == nCutPoints );
    Aig_ManCleanMarkA( pAig );
    // compute the BAD states
    ddBad = Llb4_Nonlin4SweepBadStates( pAig, vOrder, nCutPoints + Aig_ManCiNum(pAig) + Aig_ManCoNum(pAig) );
    // compute the clusters
    ddWork = Llb4_Nonlin4SweepGroups( pAig, vOrder, nCutPoints + Aig_ManCiNum(pAig) + Aig_ManCoNum(pAig), &vGroups, nClusterMax, fVerbose );
    // transfer the result from the Bad manager
//printf( "Bad before = %d.\n", Cudd_DagSize(ddBad->bFunc) );
    ddWork->bFunc = Cudd_bddTransfer( ddBad, ddWork, ddBad->bFunc );   Cudd_Ref( ddWork->bFunc );
    Cudd_RecursiveDeref( ddBad, ddBad->bFunc );  ddBad->bFunc = NULL;
    Extra_StopManager( ddBad );
    // update ordering to exclude quantified variables
//printf( "Bad after = %d.\n", Cudd_DagSize(ddWork->bFunc) );

    Llb_Nonlin4SweepPrintSuppProfile( ddWork, pAig, vOrder, vGroups, fVerbose );

    // return the result
    *pdd = ddWork;
    *pvOrder = vOrder;
    *pvGroups = vGroups;
}

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

  Synopsis    [Performs BDD sweep on the netlist.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Llb4_Nonlin4SweepExperiment( Aig_Man_t * pAig )
{
    DdManager * dd;
    Vec_Int_t * vOrder;
    Vec_Ptr_t * vGroups;
    Llb4_Nonlin4Sweep( pAig, 100, 500, &dd, &vOrder, &vGroups, 1 );

    Llb_Nonlin4SweepDeref( dd, vGroups );

    Cudd_RecursiveDeref( dd, dd->bFunc );
    Extra_StopManager( dd );
    Vec_IntFree( vOrder );
}

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


ABC_NAMESPACE_IMPL_END