#include <stdio.h>
#include "base/main/main.h"
#include "aig/aig/aig.h"
#include "aig/saig/saig.h"
#include <string.h>
#include "base/main/mainInt.h"
#include "proof/pdr/pdr.h"
#include <time.h>

ABC_NAMESPACE_IMPL_START

long countCombination(long n, long k)
{
    assert( n >= k );
    if( n == k ) return 1;
    if( k == 1 ) return n;
    return countCombination( n-1, k-1 ) + countCombination( n-1, k );
}    

void listCombination(int n, int t)
{
    Vec_Int_t *vC;
    int i, j, combCounter = 0;

    //Initialization
    vC = Vec_IntAlloc(t+3);
    for(i=-1; i<t; i++)
        Vec_IntPush( vC, i );
    Vec_IntPush( vC, n );
    Vec_IntPush( vC, 0 );

    while(1)
    {
        //visit combination
        printf("Comb-%3d : ", ++combCounter);
        for( i=t; i>0; i--)
            printf("vC[%d] = %d ", i, Vec_IntEntry(vC, i));
        printf("\n");

        j = 1;
        while( Vec_IntEntry( vC, j ) + 1 == Vec_IntEntry( vC, j+1 ) )
        {
            //printf("\nGochon = %d, %d\n", Vec_IntEntry( vC, j ) + 1, Vec_IntEntry( vC, j+1 ));
            Vec_IntWriteEntry( vC, j, j-1 );
            j = j + 1;
        }
        if( j > t ) break;
        Vec_IntWriteEntry( vC, j, Vec_IntEntry( vC, j ) + 1 );
    }
    
    Vec_IntFree(vC);    
}

int generateCombinatorialStabil( Aig_Man_t *pAigNew, Aig_Man_t *pAigOld, 
                Vec_Int_t *vCandidateMonotoneSignals_,
                Vec_Ptr_t *vDisj_nCk_,
                int combN, int combK )
{
    Aig_Obj_t *pObjMonoCand, *pObj;
    int targetPoIndex;

    //Knuth's Data Strcuture
    int totalCombination_KNUTH = 0;
    Vec_Int_t *vC_KNUTH;
    int i_KNUTH, j_KNUTH;

    //Knuth's Data Structure Initialization
    vC_KNUTH = Vec_IntAlloc(combK+3);
    for(i_KNUTH=-1; i_KNUTH<combK; i_KNUTH++)
        Vec_IntPush( vC_KNUTH, i_KNUTH );
    Vec_IntPush( vC_KNUTH, combN );
    Vec_IntPush( vC_KNUTH, 0 );

    while(1)
    {
        totalCombination_KNUTH++;
        pObjMonoCand = Aig_Not(Aig_ManConst1(pAigNew));
        for( i_KNUTH=combK; i_KNUTH>0; i_KNUTH--)
        {
            targetPoIndex = Vec_IntEntry( vCandidateMonotoneSignals_, Vec_IntEntry(vC_KNUTH, i_KNUTH));
            pObj = Aig_ObjChild0Copy(Aig_ManCo( pAigOld, targetPoIndex ));
            pObjMonoCand = Aig_Or( pAigNew, pObj, pObjMonoCand );
        }
        Vec_PtrPush(vDisj_nCk_, pObjMonoCand );

        j_KNUTH = 1;
        while( Vec_IntEntry( vC_KNUTH, j_KNUTH ) + 1 == Vec_IntEntry( vC_KNUTH, j_KNUTH+1 ) )
        {
            Vec_IntWriteEntry( vC_KNUTH, j_KNUTH, j_KNUTH-1 );
            j_KNUTH = j_KNUTH + 1;
        }
        if( j_KNUTH > combK ) break;
        Vec_IntWriteEntry( vC_KNUTH, j_KNUTH, Vec_IntEntry( vC_KNUTH, j_KNUTH ) + 1 );
    }

    Vec_IntFree(vC_KNUTH);

    return totalCombination_KNUTH;
}

int generateCombinatorialStabilExhaust( Aig_Man_t *pAigNew, Aig_Man_t *pAigOld, 
                Vec_Ptr_t *vDisj_nCk_,
                int combN, int combK )
{
    Aig_Obj_t *pObjMonoCand, *pObj;
    int targetPoIndex;

    //Knuth's Data Strcuture
    int totalCombination_KNUTH = 0;
    Vec_Int_t *vC_KNUTH;
    int i_KNUTH, j_KNUTH;

    //Knuth's Data Structure Initialization
    vC_KNUTH = Vec_IntAlloc(combK+3);
    for(i_KNUTH=-1; i_KNUTH<combK; i_KNUTH++)
        Vec_IntPush( vC_KNUTH, i_KNUTH );
    Vec_IntPush( vC_KNUTH, combN );
    Vec_IntPush( vC_KNUTH, 0 );

    while(1)
    {
        totalCombination_KNUTH++;
        pObjMonoCand = Aig_Not(Aig_ManConst1(pAigNew));
        for( i_KNUTH=combK; i_KNUTH>0; i_KNUTH--)
        {
            //targetPoIndex = Vec_IntEntry( vCandidateMonotoneSignals_, Vec_IntEntry(vC_KNUTH, i_KNUTH));
            targetPoIndex = Vec_IntEntry(vC_KNUTH, i_KNUTH);
            pObj = (Aig_Obj_t *)(Aig_ManLo( pAigOld, targetPoIndex )->pData);
            pObjMonoCand = Aig_Or( pAigNew, pObj, pObjMonoCand );
        }
        Vec_PtrPush(vDisj_nCk_, pObjMonoCand );

        j_KNUTH = 1;
        while( Vec_IntEntry( vC_KNUTH, j_KNUTH ) + 1 == Vec_IntEntry( vC_KNUTH, j_KNUTH+1 ) )
        {
            Vec_IntWriteEntry( vC_KNUTH, j_KNUTH, j_KNUTH-1 );
            j_KNUTH = j_KNUTH + 1;
        }
        if( j_KNUTH > combK ) break;
        Vec_IntWriteEntry( vC_KNUTH, j_KNUTH, Vec_IntEntry( vC_KNUTH, j_KNUTH ) + 1 );
    }

    Vec_IntFree(vC_KNUTH);

    return totalCombination_KNUTH;
}

Aig_Man_t *generateDisjunctiveTester( Abc_Ntk_t *pNtk, Aig_Man_t *pAig, int combN, int combK )
{
    //AIG creation related data structure
    Aig_Man_t *pNewAig;
    int piCopied = 0, loCopied = 0, loCreated = 0, liCopied = 0, liCreated = 0, poCopied = 0;
    //int i, iElem, nRegCount, hintSingalBeginningMarker, hintSingalEndMarker;
    int i, nRegCount, hintSingalBeginningMarker, hintSingalEndMarker;
    int combN_internal, combK_internal; //, targetPoIndex;
    long longI, lCombinationCount;
    //Aig_Obj_t *pObj, *pObjMonoCand, *pObjLO_nCk, *pObjDisj_nCk;
    Aig_Obj_t *pObj, *pObjLO_nCk, *pObjDisj_nCk;
    Vec_Ptr_t *vLO_nCk, *vPODriver_nCk, *vDisj_nCk;
    Vec_Int_t *vCandidateMonotoneSignals;

    extern Vec_Int_t *findHintOutputs(Abc_Ntk_t *pNtk);
    
    //Knuth's Data Strcuture
    //Vec_Int_t *vC_KNUTH;
    //int i_KNUTH, j_KNUTH, combCounter_KNUTH = 0;

    //Collecting target HINT signals
    vCandidateMonotoneSignals = findHintOutputs(pNtk);
    if( vCandidateMonotoneSignals == NULL )
    {
        printf("\nTraget Signal Set is Empty: Duplicating given AIG\n");
        combN_internal = 0;
    }
    else
    {
        //Vec_IntForEachEntry( vCandidateMonotoneSignals, iElem, i )
        //    printf("Po[%d] = %s\n", iElem, Abc_ObjName( Abc_NtkPo(pNtk, iElem) ) );
        hintSingalBeginningMarker = Vec_IntEntry( vCandidateMonotoneSignals, 0 );
        hintSingalEndMarker = Vec_IntEntry( vCandidateMonotoneSignals, Vec_IntSize(vCandidateMonotoneSignals) - 1 );
        combN_internal = hintSingalEndMarker - hintSingalBeginningMarker + 1;
    }

    //combK_internal = combK;

    //Knuth's Data Structure Initialization
    //vC_KNUTH = Vec_IntAlloc(combK_internal+3);
    //for(i_KNUTH=-1; i_KNUTH<combK_internal; i_KNUTH++)
    //    Vec_IntPush( vC_KNUTH, i_KNUTH );
    //Vec_IntPush( vC_KNUTH, combN_internal );
    //Vec_IntPush( vC_KNUTH, 0 );

    //Standard AIG duplication begins
    //Standard AIG Manager Creation
    pNewAig = Aig_ManStart( Aig_ManObjNumMax(pAig) );
    pNewAig->pName = (char *)malloc( strlen( pAig->pName ) + strlen("_nCk") + 1 );
    sprintf(pNewAig->pName, "%s_%s", pAig->pName, "nCk");
        pNewAig->pSpec = NULL;

    //Standard Mapping of Constant Nodes
    pObj = Aig_ManConst1( pAig );
        pObj->pData = Aig_ManConst1( pNewAig );

    //Standard AIG PI duplication
    Saig_ManForEachPi( pAig, pObj, i )
    {
        piCopied++;
        pObj->pData = Aig_ObjCreateCi(pNewAig);
    }

    //Standard AIG LO duplication
    Saig_ManForEachLo( pAig, pObj, i )
        {
        loCopied++;
        pObj->pData = Aig_ObjCreateCi(pNewAig);
        }

    //nCk LO creation
    lCombinationCount = 0;
    for(combK_internal=1; combK_internal<=combK; combK_internal++)
        lCombinationCount += countCombination( combN_internal, combK_internal );
    assert( lCombinationCount > 0 );
    vLO_nCk = Vec_PtrAlloc(lCombinationCount);
    for( longI = 0; longI < lCombinationCount; longI++ )
    {
        loCreated++;
        pObj = Aig_ObjCreateCi(pNewAig);
        Vec_PtrPush( vLO_nCk, pObj );
    }

    //Standard Node duplication
    Aig_ManForEachNode( pAig, pObj, i )
    {
        pObj->pData = Aig_And( pNewAig, Aig_ObjChild0Copy(pObj), Aig_ObjChild1Copy(pObj) );
    }
    
    //nCk specific logic creation (i.e. nCk number of OR gates)
    vDisj_nCk = Vec_PtrAlloc(lCombinationCount);

    

    //while(1)
    //{
    //    //visit combination
    //    //printf("Comb-%3d : ", ++combCounter_KNUTH);
    //    pObjMonoCand = Aig_Not(Aig_ManConst1(pNewAig));
    //    for( i_KNUTH=combK_internal; i_KNUTH>0; i_KNUTH--)
    //    {
    //        //printf("vC[%d] = %d ", i_KNUTH, Vec_IntEntry(vC_KNUTH, i_KNUTH));
    //        targetPoIndex = Vec_IntEntry( vCandidateMonotoneSignals, Vec_IntEntry(vC_KNUTH, i_KNUTH));
    //        pObj = Aig_ObjChild0Copy(Aig_ManCo( pAig, targetPoIndex ));
    //        pObjMonoCand = Aig_Or( pNewAig, pObj, pObjMonoCand );
    //    }
    //    Vec_PtrPush(vDisj_nCk, pObjMonoCand );
    //    //printf("\n");

    //    j_KNUTH = 1;
    //    while( Vec_IntEntry( vC_KNUTH, j_KNUTH ) + 1 == Vec_IntEntry( vC_KNUTH, j_KNUTH+1 ) )
    //    {
    //        Vec_IntWriteEntry( vC_KNUTH, j_KNUTH, j_KNUTH-1 );
    //        j_KNUTH = j_KNUTH + 1;
    //    }
    //    if( j_KNUTH > combK_internal ) break;
    //    Vec_IntWriteEntry( vC_KNUTH, j_KNUTH, Vec_IntEntry( vC_KNUTH, j_KNUTH ) + 1 );
    //}
    for( combK_internal=1; combK_internal<=combK; combK_internal++ )
        generateCombinatorialStabil( pNewAig, pAig, vCandidateMonotoneSignals,
                vDisj_nCk, combN_internal, combK_internal );


    //creation of implication logic
    vPODriver_nCk = Vec_PtrAlloc(lCombinationCount);
    for( longI = 0; longI < lCombinationCount; longI++ )
    {
        pObjLO_nCk = (Aig_Obj_t *)(Vec_PtrEntry( vLO_nCk, longI ));
        pObjDisj_nCk = (Aig_Obj_t *)(Vec_PtrEntry( vDisj_nCk, longI ));

        pObj = Aig_Or( pNewAig, Aig_Not(pObjDisj_nCk), pObjLO_nCk);
        Vec_PtrPush(vPODriver_nCk, pObj);
    }
    
    //Standard PO duplication
    Saig_ManForEachPo( pAig, pObj, i )
    {
        poCopied++;
        pObj->pData = Aig_ObjCreateCo( pNewAig, Aig_ObjChild0Copy(pObj) ); 
    }

    //nCk specific PO creation
    for( longI = 0; longI < lCombinationCount; longI++ )
    {
        Aig_ObjCreateCo( pNewAig, (Aig_Obj_t *)(Vec_PtrEntry( vPODriver_nCk, longI )) );
    }

    //Standard LI duplication
    Saig_ManForEachLi( pAig, pObj, i )
    {
        liCopied++;
        Aig_ObjCreateCo( pNewAig, Aig_ObjChild0Copy(pObj) );
    }

    //nCk specific LI creation
    for( longI = 0; longI < lCombinationCount; longI++ )
    {
        liCreated++;
        Aig_ObjCreateCo( pNewAig, (Aig_Obj_t *)(Vec_PtrEntry( vPODriver_nCk, longI )) );
    }

    //clean-up, book-keeping
    assert( liCopied + liCreated == loCopied + loCreated );    
    nRegCount = loCopied + loCreated;

    Aig_ManSetRegNum( pNewAig, nRegCount );
    Aig_ManCleanup( pNewAig );
    assert( Aig_ManCheck( pNewAig ) );
    
    //Vec_IntFree(vC_KNUTH);    
    return pNewAig;
}

Aig_Man_t *generateGeneralDisjunctiveTester( Abc_Ntk_t *pNtk, Aig_Man_t *pAig, int combK )
{
    //AIG creation related data structure
    Aig_Man_t *pNewAig;
    int piCopied = 0, loCopied = 0, loCreated = 0, liCopied = 0, liCreated = 0, poCopied = 0;
    //int i, iElem, nRegCount, hintSingalBeginningMarker, hintSingalEndMarker;
    int i, nRegCount;
    int combN_internal, combK_internal; //, targetPoIndex;
    long longI, lCombinationCount;
    //Aig_Obj_t *pObj, *pObjMonoCand, *pObjLO_nCk, *pObjDisj_nCk;
    Aig_Obj_t *pObj, *pObjLO_nCk, *pObjDisj_nCk;
    Vec_Ptr_t *vLO_nCk, *vPODriver_nCk, *vDisj_nCk;

    extern Vec_Int_t *findHintOutputs(Abc_Ntk_t *pNtk);
    
    //Knuth's Data Strcuture
    //Vec_Int_t *vC_KNUTH;
    //int i_KNUTH, j_KNUTH, combCounter_KNUTH = 0;

    //Collecting target HINT signals
    //vCandidateMonotoneSignals = findHintOutputs(pNtk);
    //if( vCandidateMonotoneSignals == NULL )
    //{
    //    printf("\nTraget Signal Set is Empty: Duplicating given AIG\n");
    //    combN_internal = 0;
    //}
    //else
    //{
        //Vec_IntForEachEntry( vCandidateMonotoneSignals, iElem, i )
        //    printf("Po[%d] = %s\n", iElem, Abc_ObjName( Abc_NtkPo(pNtk, iElem) ) );
    //    hintSingalBeginningMarker = Vec_IntEntry( vCandidateMonotoneSignals, 0 );
    //    hintSingalEndMarker = Vec_IntEntry( vCandidateMonotoneSignals, Vec_IntSize(vCandidateMonotoneSignals) - 1 );
    //    combN_internal = hintSingalEndMarker - hintSingalBeginningMarker + 1;
    //}

    combN_internal = Aig_ManRegNum(pAig);

    pNewAig = Aig_ManStart( Aig_ManObjNumMax(pAig) );
    pNewAig->pName = (char *)malloc( strlen( pAig->pName ) + strlen("_nCk") + 1 );
    sprintf(pNewAig->pName, "%s_%s", pAig->pName, "nCk");
        pNewAig->pSpec = NULL;

    //Standard Mapping of Constant Nodes
    pObj = Aig_ManConst1( pAig );
        pObj->pData = Aig_ManConst1( pNewAig );

    //Standard AIG PI duplication
    Saig_ManForEachPi( pAig, pObj, i )
    {
        piCopied++;
        pObj->pData = Aig_ObjCreateCi(pNewAig);
    }

    //Standard AIG LO duplication
    Saig_ManForEachLo( pAig, pObj, i )
        {
        loCopied++;
        pObj->pData = Aig_ObjCreateCi(pNewAig);
        }

    //nCk LO creation
    lCombinationCount = 0;
    for(combK_internal=1; combK_internal<=combK; combK_internal++)
        lCombinationCount += countCombination( combN_internal, combK_internal );
    assert( lCombinationCount > 0 );
    vLO_nCk = Vec_PtrAlloc(lCombinationCount);
    for( longI = 0; longI < lCombinationCount; longI++ )
    {
        loCreated++;
        pObj = Aig_ObjCreateCi(pNewAig);
        Vec_PtrPush( vLO_nCk, pObj );
    }

    //Standard Node duplication
    Aig_ManForEachNode( pAig, pObj, i )
    {
        pObj->pData = Aig_And( pNewAig, Aig_ObjChild0Copy(pObj), Aig_ObjChild1Copy(pObj) );
    }
    
    //nCk specific logic creation (i.e. nCk number of OR gates)
    vDisj_nCk = Vec_PtrAlloc(lCombinationCount);

    for( combK_internal=1; combK_internal<=combK; combK_internal++ )
    {
        generateCombinatorialStabilExhaust( pNewAig, pAig,
                vDisj_nCk, combN_internal, combK_internal );
    }


    //creation of implication logic
    vPODriver_nCk = Vec_PtrAlloc(lCombinationCount);
    for( longI = 0; longI < lCombinationCount; longI++ )
    {
        pObjLO_nCk = (Aig_Obj_t *)(Vec_PtrEntry( vLO_nCk, longI ));
        pObjDisj_nCk = (Aig_Obj_t *)(Vec_PtrEntry( vDisj_nCk, longI ));

        pObj = Aig_Or( pNewAig, Aig_Not(pObjDisj_nCk), pObjLO_nCk);
        Vec_PtrPush(vPODriver_nCk, pObj);
    }
    
    //Standard PO duplication
    Saig_ManForEachPo( pAig, pObj, i )
    {
        poCopied++;
        pObj->pData = Aig_ObjCreateCo( pNewAig, Aig_ObjChild0Copy(pObj) ); 
    }

    //nCk specific PO creation
    for( longI = 0; longI < lCombinationCount; longI++ )
    {
        Aig_ObjCreateCo( pNewAig, (Aig_Obj_t *)(Vec_PtrEntry( vPODriver_nCk, longI )) );
    }

    //Standard LI duplication
    Saig_ManForEachLi( pAig, pObj, i )
    {
        liCopied++;
        Aig_ObjCreateCo( pNewAig, Aig_ObjChild0Copy(pObj) );
    }

    //nCk specific LI creation
    for( longI = 0; longI < lCombinationCount; longI++ )
    {
        liCreated++;
        Aig_ObjCreateCo( pNewAig, (Aig_Obj_t *)(Vec_PtrEntry( vPODriver_nCk, longI )) );
    }

    //clean-up, book-keeping
    assert( liCopied + liCreated == loCopied + loCreated );    
    nRegCount = loCopied + loCreated;

    Aig_ManSetRegNum( pNewAig, nRegCount );
    Aig_ManCleanup( pNewAig );
    assert( Aig_ManCheck( pNewAig ) );
    
    Vec_PtrFree(vLO_nCk);
    Vec_PtrFree(vPODriver_nCk);
    Vec_PtrFree(vDisj_nCk);
    //Vec_IntFree(vC_KNUTH);    
    return pNewAig;
}

ABC_NAMESPACE_IMPL_END