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

  FileName    [bm.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Boolean Matching package.]

  Synopsis    [Check P-equivalence and PP-equivalence of two circuits.]

  Author      [Hadi Katebi]
  
  Affiliation [University of Michigan]

  Date        [Ver. 1.0. Started - January, 2009.]

  Revision    [No revisions so far]

  Comments    [This is the cleaned up version of the code I used for DATE 2010 publication.]              
			  [If you have any question or if you find a bug, contact me at hadik@umich.edu.]
			  [I don't guarantee that I can fix all the bugs, but I can definitely point you to 
			   the right direction so you can fix the bugs yourself].

  Debugging   [There are some part of the code that are commented out. Those parts mostly print
               the contents of the data structures to the standard output. Un-comment them if you 
			   find them useful for debugging.]

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

#include "base/abc/abc.h"
#include "opt/sim/sim.h"
#include "sat/bsat/satSolver.h"
#include "misc/extra/extraBdd.h"

ABC_NAMESPACE_IMPL_START


int match1by1(Abc_Ntk_t * pNtk1, Vec_Ptr_t ** nodesInLevel1, Vec_Int_t ** iMatch1, Vec_Int_t ** iDep1, Vec_Int_t * matchedInputs1, int * iGroup1, Vec_Int_t ** oMatch1, int * oGroup1,
			   Abc_Ntk_t * pNtk2, Vec_Ptr_t ** nodesInLevel2, Vec_Int_t ** iMatch2, Vec_Int_t ** iDep2, Vec_Int_t * matchedInputs2, int * iGroup2, Vec_Int_t ** oMatch2, int * oGroup2,
			   Vec_Int_t * matchedOutputs1, Vec_Int_t * matchedOutputs2, Vec_Int_t * oMatchedGroups, Vec_Int_t * iNonSingleton, int ii, int idx);

int Abc_NtkBmSat( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, Vec_Ptr_t * iMatchPairs, Vec_Ptr_t * oMatchPairs, Vec_Int_t * mismatch, int mode);

void getDependencies(Abc_Ntk_t *pNtk, Vec_Int_t** iDep, Vec_Int_t** oDep)
{	
	Vec_Ptr_t * vSuppFun;
	int i, j;	
	
	vSuppFun = Sim_ComputeFunSupp(pNtk, 0);
	for(i = 0; i < Abc_NtkPoNum(pNtk); i++) {
		char * seg = (char *)vSuppFun->pArray[i];
		
		for(j = 0; j < Abc_NtkPiNum(pNtk); j+=8) {
			if(((*seg) & 0x01) == 0x01)
				Vec_IntPushOrder(oDep[i], j);
			if(((*seg) & 0x02) == 0x02)
				Vec_IntPushOrder(oDep[i], j+1);
			if(((*seg) & 0x04) == 0x04)
				Vec_IntPushOrder(oDep[i], j+2);
			if(((*seg) & 0x08) == 0x08)
				Vec_IntPushOrder(oDep[i], j+3);
			if(((*seg) & 0x10) == 0x10)
				Vec_IntPushOrder(oDep[i], j+4);
			if(((*seg) & 0x20) == 0x20)
				Vec_IntPushOrder(oDep[i], j+5);
			if(((*seg) & 0x40) == 0x40)
				Vec_IntPushOrder(oDep[i], j+6);
			if(((*seg) & 0x80) == 0x80)
				Vec_IntPushOrder(oDep[i], j+7);

			seg++;
		}
	}

	for(i = 0; i < Abc_NtkPoNum(pNtk); i++)
		for(j = 0; j < Vec_IntSize(oDep[i]); j++)
			Vec_IntPush(iDep[Vec_IntEntry(oDep[i], j)], i);	
	

	/*for(i = 0; i < Abc_NtkPoNum(pNtk); i++)
	{
		printf("Output %d: ", i);
		for(j = 0; j < Vec_IntSize(oDep[i]); j++)
			printf("%d ", Vec_IntEntry(oDep[i], j));
		printf("\n");
	}

	printf("\n");

	for(i = 0; i < Abc_NtkPiNum(pNtk); i++)
	{
		printf("Input %d: ", i);
		for(j = 0; j < Vec_IntSize(iDep[i]); j++)
			printf("%d ", Vec_IntEntry(iDep[i], j));
		printf("\n");
	}

	printf("\n");	*/	
}

void initMatchList(Abc_Ntk_t *pNtk, Vec_Int_t** iDep, Vec_Int_t** oDep, Vec_Int_t** iMatch, int* iLastItem, Vec_Int_t** oMatch, int* oLastItem, int* iGroup, int* oGroup, int p_equivalence)
{
	int i, j, curr;
	Vec_Int_t** temp;

	if(!p_equivalence) {
		temp = ABC_ALLOC( Vec_Int_t*, Abc_NtkPiNum(pNtk)+1);

		for(i = 0; i < Abc_NtkPiNum(pNtk)+1; i++)	
			temp[i] = Vec_IntAlloc( 0 );	

		for(i = 0; i < Abc_NtkPoNum(pNtk); i++)		
			Vec_IntPush(temp[Vec_IntSize(oDep[i])], i);

		curr = 0;
		for(i = 0; i < Abc_NtkPiNum(pNtk)+1; i++)
		{
			if(Vec_IntSize(temp[i]) == 0)
				Vec_IntFree( temp[i] );

			else
			{
				oMatch[curr] = temp[i];
				
				for(j = 0; j < Vec_IntSize(temp[i]); j++)
					oGroup[Vec_IntEntry(oMatch[curr], j)] = curr;
			
				curr++;
			}
		}

		*oLastItem = curr;

		ABC_FREE( temp );
	}
	else {
		// the else part fixes the outputs for P-equivalence checking
		for(i = 0; i < Abc_NtkPoNum(pNtk); i++)
		{
			Vec_IntPush(oMatch[i], i);
			oGroup[i] = i;
			(*oLastItem) = Abc_NtkPoNum(pNtk);
		}
	}
		
	/*for(j = 0; j < *oLastItem; j++)
	{
		printf("oMatch %d: ", j);
		for(i = 0; i < Vec_IntSize(oMatch[j]); i++)
			printf("%d ", Vec_IntEntry(oMatch[j], i));
		printf("\n");
	}

	for(i = 0; i < Abc_NtkPoNum(pNtk); i++)
		printf("%d: %d ", i, oGroup[i]);*/

	//////////////////////////////////////////////////////////////////////////////

	temp = ABC_ALLOC( Vec_Int_t*, Abc_NtkPoNum(pNtk)+1 );

	for(i = 0; i < Abc_NtkPoNum(pNtk)+1; i++)	
		temp[i] = Vec_IntAlloc( 0 );

	for(i = 0; i < Abc_NtkPiNum(pNtk); i++)		
		Vec_IntPush(temp[Vec_IntSize(iDep[i])], i);

	curr = 0;
	for(i = 0; i < Abc_NtkPoNum(pNtk)+1; i++)
	{
		if(Vec_IntSize(temp[i]) == 0)
			Vec_IntFree( temp[i] );
		else
		{
			iMatch[curr] = temp[i];
			for(j = 0; j < Vec_IntSize(iMatch[curr]); j++)
				iGroup[Vec_IntEntry(iMatch[curr], j)] = curr;
			curr++;
		}		
	}

	*iLastItem = curr;

	ABC_FREE( temp );		

	/*printf("\n");
	for(j = 0; j < *iLastItem; j++)
	{
		printf("iMatch %d: ", j);
		for(i = 0; i < Vec_IntSize(iMatch[j]); i++)
			printf("%d ", Vec_IntEntry(iMatch[j], i));
		printf("\n");
	}

	for(i = 0; i < Abc_NtkPiNum(pNtk); i++)
		printf("%d: %d ", i, iGroup[i]);
	printf("\n");*/
}

void iSortDependencies(Abc_Ntk_t *pNtk, Vec_Int_t** iDep, int* oGroup)
{
	int i, j, k;	
	Vec_Int_t * temp;	
	Vec_Int_t * oGroupList;	

	oGroupList = Vec_IntAlloc( 10 );

	for(i = 0; i < Abc_NtkPiNum(pNtk); i++)
	{
		if(Vec_IntSize(iDep[i]) == 1)
			continue;
		
		temp = Vec_IntAlloc( Vec_IntSize(iDep[i]) );		

		for(j = 0; j < Vec_IntSize(iDep[i]); j++)
			Vec_IntPushUniqueOrder(oGroupList, oGroup[Vec_IntEntry(iDep[i], j)]);

		for(j = 0; j < Vec_IntSize(oGroupList); j++)
		{
			for(k = 0; k < Vec_IntSize(iDep[i]); k++)
				if(oGroup[Vec_IntEntry(iDep[i], k)] == Vec_IntEntry(oGroupList, j))
				{
					Vec_IntPush( temp, Vec_IntEntry(iDep[i], k) );		
					Vec_IntRemove( iDep[i], Vec_IntEntry(iDep[i], k) );
					k--;
				}
		}		

		Vec_IntFree( iDep[i] );		
		iDep[i] = temp;
		Vec_IntClear( oGroupList );

		/*printf("Input %d: ", i);
		for(j = 0; j < Vec_IntSize(iDep[i]); j++)
			printf("%d ", Vec_IntEntry(iDep[i], j));
		printf("\n");*/
	}
	
	Vec_IntFree( oGroupList );
}

void oSortDependencies(Abc_Ntk_t *pNtk, Vec_Int_t** oDep, int* iGroup)
{
	int i, j, k;	
	Vec_Int_t * temp;
	Vec_Int_t * iGroupList;	
	
	iGroupList = Vec_IntAlloc( 10 );

	for(i = 0; i < Abc_NtkPoNum(pNtk); i++)
	{
		if(Vec_IntSize(oDep[i]) == 1)
			continue;	
		
		temp = Vec_IntAlloc( Vec_IntSize(oDep[i]) );

		for(j = 0; j < Vec_IntSize(oDep[i]); j++)
			Vec_IntPushUniqueOrder(iGroupList, iGroup[Vec_IntEntry(oDep[i], j)]);		

		for(j = 0; j < Vec_IntSize(iGroupList); j++)
		{
			for(k = 0; k < Vec_IntSize(oDep[i]); k++)
				if(iGroup[Vec_IntEntry(oDep[i], k)] == Vec_IntEntry(iGroupList, j))
				{
					Vec_IntPush( temp, Vec_IntEntry(oDep[i], k) );
					Vec_IntRemove( oDep[i], Vec_IntEntry(oDep[i], k) );
					k--;
				}
		}

		Vec_IntFree( oDep[i] );
		oDep[i] = temp;
		Vec_IntClear( iGroupList );

		/*printf("Output %d: ", i);
		for(j = 0; j < Vec_IntSize(oDep[i]); j++)
			printf("%d ", Vec_IntEntry(oDep[i], j));
		printf("\n");*/
	}
	
	Vec_IntFree( iGroupList );
}

int oSplitByDep(Abc_Ntk_t *pNtk, Vec_Int_t** oDep, Vec_Int_t** oMatch, int* oGroup, int* oLastItem, int* iGroup)
{
	int i, j, k;
	int numOfItemsAdded;
	Vec_Int_t * array, * sortedArray;	

	numOfItemsAdded = 0;

	for(i = 0; i < *oLastItem; i++)
	{
		if(Vec_IntSize(oMatch[i]) == 1)
			continue;

		array = Vec_IntAlloc( Vec_IntSize(oMatch[i]) );
		sortedArray = Vec_IntAlloc( Vec_IntSize(oMatch[i]) );

		for(j = 0; j < Vec_IntSize(oMatch[i]); j++)
		{
			int factor, encode;

			encode = 0;
			factor = 1;	
			
			for(k = 0; k < Vec_IntSize(oDep[Vec_IntEntry(oMatch[i], j)]); k++)										
				encode += iGroup[Vec_IntEntry(oDep[Vec_IntEntry(oMatch[i], j)], k)] * factor;						
			
			Vec_IntPush(array, encode);
			Vec_IntPushUniqueOrder(sortedArray, encode);

			if( encode < 0)
				printf("WARNING! Integer overflow!");

			//printf("%d ", Vec_IntEntry(array, j));
		}				
		
		while( Vec_IntSize(sortedArray) > 1 )
		{			
			for(k = 0; k < Vec_IntSize(oMatch[i]); k++) 
			{
				if(Vec_IntEntry(array, k) == Vec_IntEntryLast(sortedArray))
				{
					Vec_IntPush(oMatch[*oLastItem+numOfItemsAdded], Vec_IntEntry(oMatch[i], k));
					oGroup[Vec_IntEntry(oMatch[i], k)] = *oLastItem+numOfItemsAdded;
					Vec_IntRemove( oMatch[i], Vec_IntEntry(oMatch[i], k) );		
					Vec_IntRemove( array, Vec_IntEntry(array, k) );			
					k--;
				}	
			}			
			numOfItemsAdded++;
			Vec_IntPop(sortedArray);						
		}

		Vec_IntFree( array );		
		Vec_IntFree( sortedArray );		
		//printf("\n");
	}	

	*oLastItem += numOfItemsAdded;

	/*printf("\n");
	for(j = 0; j < *oLastItem ; j++)
	{
		printf("oMatch %d: ", j);
		for(i = 0; i < Vec_IntSize(oMatch[j]); i++)
			printf("%d ", Vec_IntEntry(oMatch[j], i));
		printf("\n");
	}*/

	return numOfItemsAdded;
}

int iSplitByDep(Abc_Ntk_t *pNtk, Vec_Int_t** iDep, Vec_Int_t** iMatch, int* iGroup, int* iLastItem, int* oGroup)
{
	int i, j, k;	
	int numOfItemsAdded = 0;
	Vec_Int_t * array, * sortedArray;	

	for(i = 0; i < *iLastItem; i++)
	{
		if(Vec_IntSize(iMatch[i]) == 1)
			continue;
		
		array = Vec_IntAlloc( Vec_IntSize(iMatch[i]) );
		sortedArray = Vec_IntAlloc( Vec_IntSize(iMatch[i]) );

		for(j = 0; j < Vec_IntSize(iMatch[i]); j++)
		{		
			int factor, encode;

			encode = 0;
			factor = 1;	
			
			for(k = 0; k < Vec_IntSize(iDep[Vec_IntEntry(iMatch[i], j)]); k++)							
				encode += oGroup[Vec_IntEntry(iDep[Vec_IntEntry(iMatch[i], j)], k)] * factor;						

			Vec_IntPush(array, encode);
			Vec_IntPushUniqueOrder(sortedArray, encode);
			
			//printf("%d ", Vec_IntEntry(array, j));
		}			
				
		while( Vec_IntSize(sortedArray) > 1 )
		{			
			for(k = 0; k < Vec_IntSize(iMatch[i]); k++) 
			{
				if(Vec_IntEntry(array, k) == Vec_IntEntryLast(sortedArray))
				{
					Vec_IntPush(iMatch[*iLastItem+numOfItemsAdded], Vec_IntEntry(iMatch[i], k));
					iGroup[Vec_IntEntry(iMatch[i], k)] = *iLastItem+numOfItemsAdded;
					Vec_IntRemove( iMatch[i], Vec_IntEntry(iMatch[i], k) );							
					Vec_IntRemove( array, Vec_IntEntry(array, k) );	
					k--;
				}
			}
			numOfItemsAdded++;
			Vec_IntPop(sortedArray);	
		}

		Vec_IntFree( array );		
		Vec_IntFree( sortedArray );	
		//printf("\n");
	}	

	*iLastItem += numOfItemsAdded;

	/*printf("\n");
	for(j = 0; j < *iLastItem ; j++)
	{
		printf("iMatch %d: ", j);
		for(i = 0; i < Vec_IntSize(iMatch[j]); i++)
			printf("%d ", Vec_IntEntry(iMatch[j], i));
		printf("\n");
	}*/

	return numOfItemsAdded;	
}

Vec_Ptr_t ** findTopologicalOrder( Abc_Ntk_t * pNtk )
{
    Vec_Ptr_t ** vNodes;
    Abc_Obj_t * pObj, * pFanout;
    int i, k;    
    
	extern void Abc_NtkDfsReverse_rec( Abc_Obj_t * pNode, Vec_Ptr_t * vNodes );
	
	// start the array of nodes
    vNodes = ABC_ALLOC(Vec_Ptr_t *, Abc_NtkPiNum(pNtk));
	for(i = 0; i < Abc_NtkPiNum(pNtk); i++)
		vNodes[i] = Vec_PtrAlloc(50);	
	
    Abc_NtkForEachCi( pNtk, pObj, i )
    {
		// set the traversal ID
		Abc_NtkIncrementTravId( pNtk );
        Abc_NodeSetTravIdCurrent( pObj );
        pObj = Abc_ObjFanout0Ntk(pObj);
        Abc_ObjForEachFanout( pObj, pFanout, k )
            Abc_NtkDfsReverse_rec( pFanout, vNodes[i] );
    }
   
    return vNodes;
}


int * Abc_NtkSimulateOneNode( Abc_Ntk_t * pNtk, int * pModel, int input, Vec_Ptr_t ** topOrder )
{
    Abc_Obj_t * pNode;
	Vec_Ptr_t * vNodes;
    int * pValues, Value0, Value1, i;
   
	vNodes = Vec_PtrAlloc( 50 );
/*
    printf( "Counter example: " );
    Abc_NtkForEachCi( pNtk, pNode, i )
        printf( " %d", pModel[i] );
    printf( "\n" );
*/
    // increment the trav ID
    Abc_NtkIncrementTravId( pNtk );
    // set the CI values     
	Abc_AigConst1(pNtk)->pCopy = (Abc_Obj_t *)1;
	pNode = Abc_NtkCi(pNtk, input);
	pNode->iTemp = pModel[input];
	
    // simulate in the topological order	
    for(i = Vec_PtrSize(topOrder[input])-1; i >= 0; i--)
    {
		pNode = (Abc_Obj_t *)Vec_PtrEntry(topOrder[input], i);		
		
        Value0 = ((int)(ABC_PTRUINT_T)Abc_ObjFanin0(pNode)->pCopy) ^ Abc_ObjFaninC0(pNode);
        Value1 = ((int)(ABC_PTRUINT_T)Abc_ObjFanin1(pNode)->pCopy) ^ Abc_ObjFaninC1(pNode);
        
		if( pNode->iTemp != (Value0 & Value1))
		{
			pNode->iTemp = (Value0 & Value1);
			Vec_PtrPush(vNodes, pNode);
		}
	
    }	
    // fill the output values
    pValues = ABC_ALLOC( int, Abc_NtkCoNum(pNtk) );
    Abc_NtkForEachCo( pNtk, pNode, i )
        pValues[i] = ((int)(ABC_PTRUINT_T)Abc_ObjFanin0(pNode)->pCopy) ^ Abc_ObjFaninC0(pNode);
    
	pNode = Abc_NtkCi(pNtk, input);
	if(pNode->pCopy == (Abc_Obj_t *)1)
		pNode->pCopy = (Abc_Obj_t *)0;
	else
		pNode->pCopy = (Abc_Obj_t *)1;

	for(i = 0; i < Vec_PtrSize(vNodes); i++)
	{
		pNode = (Abc_Obj_t *)Vec_PtrEntry(vNodes, i);

		if(pNode->pCopy == (Abc_Obj_t *)1)
			pNode->pCopy = (Abc_Obj_t *)0;
		else
			pNode->pCopy = (Abc_Obj_t *)1;
	}

	Vec_PtrFree( vNodes );

    return pValues;
}

int refineIOBySimulation(Abc_Ntk_t *pNtk, Vec_Int_t** iMatch, int* iLastItem, int * iGroup, Vec_Int_t** iDep, Vec_Int_t** oMatch, int* oLastItem, int * oGroup, Vec_Int_t** oDep, char * vPiValues, int * observability, Vec_Ptr_t ** topOrder)
{
	Abc_Obj_t * pObj;
	int * pModel;//, ** pModel2;
	int * output, * output2;
	int lastItem;
	int i, j, k;
	Vec_Int_t * iComputedNum, * iComputedNumSorted;	
	Vec_Int_t * oComputedNum;				// encoding the number of flips		
	int factor;	
	int isRefined = FALSE;	

	pModel = ABC_ALLOC( int, Abc_NtkCiNum(pNtk) );	

	Abc_NtkForEachPi( pNtk, pObj, i )
		pModel[i] = vPiValues[i] - '0';
	Abc_NtkForEachLatch( pNtk, pObj, i )
		pModel[Abc_NtkPiNum(pNtk)+i] = pObj->iData - 1;

	output = Abc_NtkVerifySimulatePattern( pNtk, pModel );	

	oComputedNum = Vec_IntAlloc( Abc_NtkPoNum(pNtk) );
	for(i = 0; i < Abc_NtkPoNum(pNtk); i++)
		Vec_IntPush(oComputedNum, 0);
	
	/****************************************************************************************/
	/********** group outputs that produce 1 and outputs that produce 0 together ************/	
	
	lastItem = *oLastItem;
	for(i = 0; i < lastItem && (*oLastItem) != Abc_NtkPoNum(pNtk); i++)
	{
		int flag = FALSE;

		if(Vec_IntSize(oMatch[i]) == 1)
			continue;

		for(j = 1; j < Vec_IntSize(oMatch[i]); j++)
			if(output[Vec_IntEntry(oMatch[i], 0)] != output[Vec_IntEntry(oMatch[i], j)])
			{
				flag = TRUE;
				break;
			}

		if(flag)
		{
			for(j = 0; j < Vec_IntSize(oMatch[i]); j++)
				if(output[Vec_IntEntry(oMatch[i], j)])
				{
					Vec_IntPush(oMatch[*oLastItem], Vec_IntEntry(oMatch[i], j));
					oGroup[Vec_IntEntry(oMatch[i], j)] = *oLastItem;	
					Vec_IntRemove(oMatch[i], Vec_IntEntry(oMatch[i], j));	
					j--;
				}

			(*oLastItem)++;
		}
	}

	if( (*oLastItem) > lastItem )
	{
		isRefined = TRUE;
		iSortDependencies(pNtk, iDep, oGroup);	
	}

	/****************************************************************************************/
	/************* group inputs that make the same number of flips in outpus ****************/		

	lastItem = *iLastItem;
	for(i = 0; i < lastItem && (*iLastItem) != Abc_NtkPiNum(pNtk); i++)
	{
		int num;

		if(Vec_IntSize(iMatch[i]) == 1)
			continue;

		iComputedNum = Vec_IntAlloc( Vec_IntSize(iMatch[i]) );
		iComputedNumSorted = Vec_IntAlloc( Vec_IntSize(iMatch[i]) );	
		
		for(j = 0; j < Vec_IntSize(iMatch[i]); j++)
		{
			if( vPiValues[Vec_IntEntry(iMatch[i], j)] == '0' )				
				pModel[Vec_IntEntry(iMatch[i], j)] = 1;
			else 				
				pModel[Vec_IntEntry(iMatch[i], j)] = 0;		
			
			//output2 = Abc_NtkVerifySimulatePattern( pNtk, pModel );	
			output2 = Abc_NtkSimulateOneNode( pNtk, pModel, Vec_IntEntry(iMatch[i], j), topOrder );
			
			num = 0;
			factor = 1;	
			for(k = 0; k < Vec_IntSize(iDep[Vec_IntEntry(iMatch[i], j)]); k++)
			{
				int outputIndex = Vec_IntEntry(iDep[Vec_IntEntry(iMatch[i], j)], k);		

				if(output2[outputIndex])
					num += (oGroup[outputIndex] + 1) * factor;				
			
				if(output[outputIndex] != output2[outputIndex])
				{
					int temp = Vec_IntEntry(oComputedNum, outputIndex) + i + 1;
					Vec_IntWriteEntry(oComputedNum, outputIndex, temp);	
					observability[Vec_IntEntry(iMatch[i], j)]++;
				}
			}

			Vec_IntPush(iComputedNum, num);
			Vec_IntPushUniqueOrder(iComputedNumSorted, num);			

			pModel[Vec_IntEntry(iMatch[i], j)] = vPiValues[Vec_IntEntry(iMatch[i], j)] - '0';
			ABC_FREE( output2 );
		}	

		while( Vec_IntSize( iComputedNumSorted ) > 1 )
		{
			for(k = 0; k < Vec_IntSize(iMatch[i]); k++)
			{							
				if(Vec_IntEntry(iComputedNum, k) == Vec_IntEntryLast(iComputedNumSorted) )
				{
					Vec_IntPush(iMatch[*iLastItem], Vec_IntEntry(iMatch[i], k));
					iGroup[Vec_IntEntry(iMatch[i], k)] = *iLastItem;
					Vec_IntRemove( iMatch[i], Vec_IntEntry(iMatch[i], k) );
					Vec_IntRemove( iComputedNum, Vec_IntEntry(iComputedNum, k) );		
					k--;
				}				
			}
			(*iLastItem)++;	
			Vec_IntPop( iComputedNumSorted );
		}	

		Vec_IntFree( iComputedNum );
		Vec_IntFree( iComputedNumSorted );
	}

	if( (*iLastItem) > lastItem )
	{
		isRefined = TRUE;
		oSortDependencies(pNtk, oDep, iGroup);
	}

	/****************************************************************************************/
	/********** encode the number of flips in each output by flipping the outputs ***********/
	/********** and group all the outputs that have the same encoding         ***********/	

	lastItem = *oLastItem;
	for(i = 0; i < lastItem && (*oLastItem) != Abc_NtkPoNum(pNtk); i++)
	{
		Vec_Int_t * encode, * sortedEncode;				// encoding the number of flips					

		if(Vec_IntSize(oMatch[i]) == 1)
			continue;
			
		encode = Vec_IntAlloc( Vec_IntSize(oMatch[i]) );
		sortedEncode = Vec_IntAlloc( Vec_IntSize(oMatch[i]) );					
	
		for(j = 0; j < Vec_IntSize(oMatch[i]); j++)
		{
			Vec_IntPush(encode, Vec_IntEntry(oComputedNum, Vec_IntEntry(oMatch[i], j)) );
			Vec_IntPushUniqueOrder( sortedEncode, Vec_IntEntry(encode, j) );
		}

		while( Vec_IntSize(sortedEncode) > 1 )
		{
			for(j = 0; j < Vec_IntSize(oMatch[i]); j++)
				if(Vec_IntEntry(encode, j) == Vec_IntEntryLast(sortedEncode))
				{
					Vec_IntPush(oMatch[*oLastItem], Vec_IntEntry(oMatch[i], j));
					oGroup[Vec_IntEntry(oMatch[i], j)] = *oLastItem;
					Vec_IntRemove( oMatch[i], Vec_IntEntry(oMatch[i], j) );
					Vec_IntRemove( encode, Vec_IntEntry(encode, j) );
					j --;
				}				
			
			(*oLastItem)++;
			Vec_IntPop( sortedEncode );
		}

		Vec_IntFree( encode );
		Vec_IntFree( sortedEncode );
	}

	if( (*oLastItem) > lastItem )
		isRefined = TRUE;	

	ABC_FREE( pModel );
	ABC_FREE( output );
	Vec_IntFree( oComputedNum );

	return isRefined;	
}

Abc_Ntk_t * Abc_NtkMiterBm( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, Vec_Ptr_t * iCurrMatch, Vec_Ptr_t * oCurrMatch )
{
	char Buffer[1000];
    Abc_Ntk_t * pNtkMiter;

	pNtkMiter = Abc_NtkAlloc( ABC_NTK_STRASH, ABC_FUNC_AIG, 1 );
    sprintf( Buffer, "%s_%s_miter", pNtk1->pName, pNtk2->pName );
    pNtkMiter->pName = Extra_UtilStrsav(Buffer);

	//Abc_NtkMiterPrepare( pNtk1, pNtk2, pNtkMiter, fComb, nPartSize );
	{
		Abc_Obj_t * pObj, * pObjNew;
		int i;

		Abc_AigConst1(pNtk1)->pCopy = Abc_AigConst1(pNtkMiter);
		Abc_AigConst1(pNtk2)->pCopy = Abc_AigConst1(pNtkMiter);

		// create new PIs and remember them in the old PIs
		if(iCurrMatch == NULL)
		{
			Abc_NtkForEachCi( pNtk1, pObj, i )
			{
				pObjNew = Abc_NtkCreatePi( pNtkMiter );
				// remember this PI in the old PIs
				pObj->pCopy = pObjNew;
				pObj = Abc_NtkCi(pNtk2, i);  
				pObj->pCopy = pObjNew;
				// add name
				Abc_ObjAssignName( pObjNew, Abc_ObjName(pObj), NULL );
			}
		}
		else
		{
			for(i = 0; i < Vec_PtrSize( iCurrMatch ); i += 2)
			{
				pObjNew = Abc_NtkCreatePi( pNtkMiter );
				pObj = (Abc_Obj_t *)Vec_PtrEntry(iCurrMatch, i);
				pObj->pCopy = pObjNew;
				pObj = (Abc_Obj_t *)Vec_PtrEntry(iCurrMatch, i+1);
				pObj->pCopy = pObjNew;
				// add name
				Abc_ObjAssignName( pObjNew, Abc_ObjName(pObj), NULL );
			}
		}

        // create the only PO
        pObjNew = Abc_NtkCreatePo( pNtkMiter );
        // add the PO name
        Abc_ObjAssignName( pObjNew, "miter", NULL );
	}

	// Abc_NtkMiterAddOne( pNtk1, pNtkMiter );
	{
		Abc_Obj_t * pNode;
		int i;
		assert( Abc_NtkIsDfsOrdered(pNtk1) );
		Abc_AigForEachAnd( pNtk1, pNode, i )
			pNode->pCopy = Abc_AigAnd( (Abc_Aig_t *)pNtkMiter->pManFunc, Abc_ObjChild0Copy(pNode), Abc_ObjChild1Copy(pNode) );
	}

	// Abc_NtkMiterAddOne( pNtk2, pNtkMiter );
	{
		Abc_Obj_t * pNode;
		int i;
		assert( Abc_NtkIsDfsOrdered(pNtk2) );
		Abc_AigForEachAnd( pNtk2, pNode, i )
			pNode->pCopy = Abc_AigAnd( (Abc_Aig_t *)pNtkMiter->pManFunc, Abc_ObjChild0Copy(pNode), Abc_ObjChild1Copy(pNode) );
	}
	
	// Abc_NtkMiterFinalize( pNtk1, pNtk2, pNtkMiter, fComb, nPartSize );	
	{
		Vec_Ptr_t * vPairs;
		Abc_Obj_t * pMiter;
		int i;

		vPairs = Vec_PtrAlloc( 100 );
	    
		// collect the CO nodes for the miter
		if(oCurrMatch != NULL)
		{
			for(i = 0; i < Vec_PtrSize( oCurrMatch ); i += 2)
			{
				Vec_PtrPush( vPairs, Abc_ObjChild0Copy((Abc_Obj_t *)Vec_PtrEntry(oCurrMatch, i)) );
				Vec_PtrPush( vPairs, Abc_ObjChild0Copy((Abc_Obj_t *)Vec_PtrEntry(oCurrMatch, i+1)) );
			}
		}
		else
		{
			Abc_Obj_t * pNode;

			Abc_NtkForEachCo( pNtk1, pNode, i )
			{
				Vec_PtrPush( vPairs, Abc_ObjChild0Copy(pNode) );
				pNode = Abc_NtkCo( pNtk2, i );
				Vec_PtrPush( vPairs, Abc_ObjChild0Copy(pNode) );
			}
		}
		
	 pMiter = Abc_AigMiter( (Abc_Aig_t *)pNtkMiter->pManFunc, vPairs, 0 );
	 Abc_ObjAddFanin( Abc_NtkPo(pNtkMiter,0), pMiter );
	 Vec_PtrFree(vPairs);
	}

	//Abc_AigCleanup(pNtkMiter->pManFunc);
	
	return pNtkMiter;
}

int * pValues1__, * pValues2__;

void Abc_NtkVerifyReportError( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, int * pModel, Vec_Int_t * mismatch )
{
    Vec_Ptr_t * vNodes;
    Abc_Obj_t * pNode;    
    int nErrors, nPrinted, i, iNode = -1;

    assert( Abc_NtkCiNum(pNtk1) == Abc_NtkCiNum(pNtk2) );
    assert( Abc_NtkCoNum(pNtk1) == Abc_NtkCoNum(pNtk2) );
    // get the CO values under this model
    pValues1__ = Abc_NtkVerifySimulatePattern( pNtk1, pModel );
    pValues2__ = Abc_NtkVerifySimulatePattern( pNtk2, pModel );
    // count the mismatches
    nErrors = 0;
    for ( i = 0; i < Abc_NtkCoNum(pNtk1); i++ )
        nErrors += (int)( pValues1__[i] != pValues2__[i] );
    //printf( "Verification failed for at least %d outputs: ", nErrors );
    // print the first 3 outputs
    nPrinted = 0;
    for ( i = 0; i < Abc_NtkCoNum(pNtk1); i++ )
        if ( pValues1__[i] != pValues2__[i] )
        {
            if ( iNode == -1 )
                iNode = i;
            //printf( " %s", Abc_ObjName(Abc_NtkCo(pNtk1,i)) );
            if ( ++nPrinted == 3 )
                break;
        }
    /*if ( nPrinted != nErrors )
        printf( " ..." );
    printf( "\n" );*/
    // report mismatch for the first output
    if ( iNode >= 0 )
    {
        /*printf( "Output %s: Value in Network1 = %d. Value in Network2 = %d.\n", 
            Abc_ObjName(Abc_NtkCo(pNtk1,iNode)), pValues1[iNode], pValues2[iNode] );
        printf( "Input pattern: " );*/
        // collect PIs in the cone
        pNode = Abc_NtkCo(pNtk1,iNode);
        vNodes = Abc_NtkNodeSupport( pNtk1, &pNode, 1 );
        // set the PI numbers
        Abc_NtkForEachCi( pNtk1, pNode, i )
            pNode->iTemp = i;
        // print the model
        pNode = (Abc_Obj_t *)Vec_PtrEntry( vNodes, 0 );
        if ( Abc_ObjIsCi(pNode) )
        {
            Vec_PtrForEachEntry( Abc_Obj_t *, vNodes, pNode, i )
            {
                assert( Abc_ObjIsCi(pNode) );
                //printf( " %s=%d", Abc_ObjName(pNode), pModel[(int)pNode->pCopy] );
				Vec_IntPush(mismatch, Abc_ObjId(pNode)-1);
				Vec_IntPush(mismatch, pModel[(int)(size_t)pNode->pCopy]);
            }
        }
        //printf( "\n" );
        Vec_PtrFree( vNodes );
    }
    free( pValues1__ );
    free( pValues2__ );
}

int Abc_NtkMiterSatBm( Abc_Ntk_t * pNtk, ABC_INT64_T nConfLimit, ABC_INT64_T nInsLimit, int fVerbose, ABC_INT64_T * pNumConfs, ABC_INT64_T * pNumInspects)
{
    static sat_solver * pSat = NULL;
    lbool   status;
    int RetValue;
    abctime clk;	

	extern int Abc_NodeAddClausesTop( sat_solver * pSat, Abc_Obj_t * pNode, Vec_Int_t * vVars );
	extern Vec_Int_t * Abc_NtkGetCiSatVarNums( Abc_Ntk_t * pNtk );	
 
    if ( pNumConfs )
        *pNumConfs = 0;
    if ( pNumInspects )
        *pNumInspects = 0;

    assert( Abc_NtkLatchNum(pNtk) == 0 );

//    if ( Abc_NtkPoNum(pNtk) > 1 )
//        fprintf( stdout, "Warning: The miter has %d outputs. SAT will try to prove all of them.\n", Abc_NtkPoNum(pNtk) );

    // load clauses into the sat_solver
    clk = Abc_Clock();

			
		
	pSat = (sat_solver *)Abc_NtkMiterSatCreate( pNtk, 0 );
			
    if ( pSat == NULL )
        return 1;
//printf( "%d \n", pSat->clauses.size );
//sat_solver_delete( pSat );
//return 1;

//    printf( "Created SAT problem with %d variable and %d clauses. ", sat_solver_nvars(pSat), sat_solver_nclauses(pSat) );
//    PRT( "Time", Abc_Clock() - clk );

    // simplify the problem
    clk = Abc_Clock();
    status = sat_solver_simplify(pSat);
//    printf( "Simplified the problem to %d variables and %d clauses. ", sat_solver_nvars(pSat), sat_solver_nclauses(pSat) );
//    PRT( "Time", Abc_Clock() - clk );
    if ( status == 0 )
    {
        sat_solver_delete( pSat );
//        printf( "The problem is UNSATISFIABLE after simplification.\n" );
        return 1;
    }

    // solve the miter
    clk = Abc_Clock();
    if ( fVerbose )
        pSat->verbosity = 1;
    status = sat_solver_solve( pSat, NULL, NULL, (ABC_INT64_T)nConfLimit, (ABC_INT64_T)nInsLimit, (ABC_INT64_T)0, (ABC_INT64_T)0 );
    if ( status == l_Undef )
    {
//        printf( "The problem timed out.\n" );
        RetValue = -1;
    }
    else if ( status == l_True )
    {
//        printf( "The problem is SATISFIABLE.\n" );
        RetValue = 0;
    }
    else if ( status == l_False )
    {
//        printf( "The problem is UNSATISFIABLE.\n" );
        RetValue = 1;
    }
    else
        assert( 0 );
//    PRT( "SAT sat_solver time", Abc_Clock() - clk );
//    printf( "The number of conflicts = %d.\n", (int)pSat->sat_solver_stats.conflicts );

    // if the problem is SAT, get the counterexample
    if ( status == l_True )
    {
//        Vec_Int_t * vCiIds = Abc_NtkGetCiIds( pNtk );
        Vec_Int_t * vCiIds = Abc_NtkGetCiSatVarNums( pNtk );
        pNtk->pModel = Sat_SolverGetModel( pSat, vCiIds->pArray, vCiIds->nSize );
        Vec_IntFree( vCiIds );
    }
    // free the sat_solver
    if ( fVerbose )
        Sat_SolverPrintStats( stdout, pSat );

    if ( pNumConfs )
        *pNumConfs = (int)pSat->stats.conflicts;
    if ( pNumInspects )
        *pNumInspects = (int)pSat->stats.inspects;

//sat_solver_store_write( pSat, "trace.cnf" );	
	sat_solver_store_free( pSat );
	sat_solver_delete( pSat );
    return RetValue;
}

int Abc_NtkBmSat( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, Vec_Ptr_t * iMatchPairs, Vec_Ptr_t * oMatchPairs, Vec_Int_t * mismatch, int mode)
{    
    extern Abc_Ntk_t * Abc_NtkMulti( Abc_Ntk_t * pNtk, int nThresh, int nFaninMax, int fCnf, int fMulti, int fSimple, int fFactor );	

	Abc_Ntk_t * pMiter = NULL;
    Abc_Ntk_t * pCnf;
    int RetValue;

	// get the miter of the two networks
	if( mode == 0 )
	{
		//Abc_NtkDelete( pMiter );
		pMiter = Abc_NtkMiterBm( pNtk1, pNtk2, iMatchPairs, oMatchPairs );
	}
	else if( mode == 1 )		// add new outputs
	{		
		int i;
		Abc_Obj_t * pObj;
		Vec_Ptr_t * vPairs;
		Abc_Obj_t * pNtkMiter;		

		vPairs = Vec_PtrAlloc( 100 );

		Abc_NtkForEachCo( pMiter, pObj, i )			
			Abc_ObjRemoveFanins( pObj );

		for(i = 0; i < Vec_PtrSize( oMatchPairs ); i += 2)
		{
			Vec_PtrPush( vPairs, Abc_ObjChild0Copy((Abc_Obj_t *)Vec_PtrEntry(oMatchPairs, i)) );
			Vec_PtrPush( vPairs, Abc_ObjChild0Copy((Abc_Obj_t *)Vec_PtrEntry(oMatchPairs, i+1)) );
		}
		pNtkMiter = Abc_AigMiter( (Abc_Aig_t *)pMiter->pManFunc, vPairs, 0 );
		Abc_ObjAddFanin( Abc_NtkPo(pMiter,0), pNtkMiter );	 
		Vec_PtrFree( vPairs);
	}
	else if( mode == 2 )		// add some outputs
	{

	}
	else if( mode == 3)			// remove all outputs
	{
	}

	if ( pMiter == NULL )
	{
		printf("Miter computation has failed.");		
		return -1;
	}
	RetValue = Abc_NtkMiterIsConstant( pMiter );
	if ( RetValue == 0)
	{		
		/*printf("Networks are NOT EQUIVALENT after structural hashing.");	*/
		// report the error    	
		if(mismatch != NULL)
		{
			pMiter->pModel = Abc_NtkVerifyGetCleanModel( pMiter, 1 );
			Abc_NtkVerifyReportError( pNtk1, pNtk2, pMiter->pModel, mismatch );
			ABC_FREE( pMiter->pModel );
		}        
		Abc_NtkDelete( pMiter );
        return RetValue;
	}
	if( RetValue == 1 )
	{		
		/*printf("Networks are equivalent after structural hashing.");	*/
		Abc_NtkDelete( pMiter );
		return RetValue;
	}

	// convert the miter into a CNF
	//if(mode == 0)
	pCnf = Abc_NtkMulti( pMiter, 0, 100, 1, 0, 0, 0 );
    Abc_NtkDelete( pMiter );
    if ( pCnf == NULL )
    {        
		printf("Renoding for CNF has failed.");	
        return -1;
    }

    // solve the CNF using the SAT solver
    RetValue = Abc_NtkMiterSat( pCnf, (ABC_INT64_T)10000, (ABC_INT64_T)0, 0, NULL, NULL);
    /*if ( RetValue == -1 )
		printf("Networks are undecided (SAT solver timed out).");	
    else if ( RetValue == 0 )        
		printf("Networks are NOT EQUIVALENT after SAT.");	
    else
		printf("Networks are equivalent after SAT.");	*/
	if ( mismatch != NULL && pCnf->pModel )
        Abc_NtkVerifyReportError( pNtk1, pNtk2, pCnf->pModel, mismatch );

    ABC_FREE( pCnf->pModel );
    Abc_NtkDelete( pCnf );

	return RetValue;
}

int checkEquivalence( Abc_Ntk_t * pNtk1, Vec_Int_t* matchedInputs1, Vec_Int_t * matchedOutputs1,
					   Abc_Ntk_t * pNtk2, Vec_Int_t* matchedInputs2, Vec_Int_t * matchedOutputs2)
{	
	Vec_Ptr_t * iMatchPairs, * oMatchPairs;
	int i;
	int result;

	iMatchPairs = Vec_PtrAlloc( Abc_NtkPiNum( pNtk1 ) * 2);
	oMatchPairs = Vec_PtrAlloc( Abc_NtkPoNum( pNtk1 ) * 2);

	for(i = 0; i < Abc_NtkPiNum(pNtk1); i++)
	{	
		Vec_PtrPush(iMatchPairs, Abc_NtkPi(pNtk2, Vec_IntEntry(matchedInputs2, i)));
		Vec_PtrPush(iMatchPairs, Abc_NtkPi(pNtk1, Vec_IntEntry(matchedInputs1, i)));
	}
				

	for(i = 0; i < Abc_NtkPoNum(pNtk1); i++)		
	{
		Vec_PtrPush(oMatchPairs, Abc_NtkPo(pNtk2, Vec_IntEntry(matchedOutputs2, i)));
		Vec_PtrPush(oMatchPairs, Abc_NtkPo(pNtk1, Vec_IntEntry(matchedOutputs1, i)));
	}		

	result = Abc_NtkBmSat(pNtk1, pNtk2, iMatchPairs, oMatchPairs, NULL, 0);

	if( result )
		printf("*** Circuits are equivalent ***\n");
	else
		printf("*** Circuits are NOT equivalent ***\n");

	Vec_PtrFree( iMatchPairs );
	Vec_PtrFree( oMatchPairs );	

	return result;
}

Abc_Ntk_t * computeCofactor(Abc_Ntk_t * pNtk, Vec_Ptr_t ** nodesInLevel, int * bitVector, Vec_Int_t * currInputs)
{	
	Abc_Ntk_t * subNtk;	
	Abc_Obj_t * pObj, * pObjNew;
	int i, j, numOfLevels;		

	numOfLevels = Abc_AigLevel( pNtk );		// number of levels excludes PI/POs	

	// start a new network
	subNtk = Abc_NtkAlloc( ABC_NTK_STRASH, ABC_FUNC_AIG, 1 );    
    subNtk->pName = Extra_UtilStrsav("subNtk");

	Abc_AigConst1(pNtk)->pCopy = Abc_AigConst1(subNtk);	

	// clean the node copy fields and mark the nodes that need to be copied to the new network
    Abc_NtkCleanCopy( pNtk );

	if(bitVector != NULL)
	{
		for(i = 0; i < Abc_NtkPiNum(pNtk); i++)
			if(bitVector[i])
			{
				pObj = Abc_NtkPi(pNtk, i);
				pObj->pCopy = (Abc_Obj_t *)(1);
			}
	}

	for(i = 0; i < Vec_IntSize(currInputs); i++)
	{
		pObj = Abc_NtkPi(pNtk, Vec_IntEntry(currInputs, i));
		pObjNew = Abc_NtkDupObj( subNtk, pObj, 1 );
		pObj->pCopy = pObjNew;
	}
		

	// i = 0 are the inputs and the inputs are not added to the 2d array ( nodesInLevel )
	for( i = 0; i <= numOfLevels; i++ )	
		for( j = 0; j < Vec_PtrSize( nodesInLevel[i] ); j++)
		{
			pObj = (Abc_Obj_t *)Vec_PtrEntry( nodesInLevel[i], j );

			if(Abc_ObjChild0Copy(pObj) == NULL && Abc_ObjChild1Copy(pObj) == NULL)
				pObj->pCopy = NULL;
			else if(Abc_ObjChild0Copy(pObj) == NULL && Abc_ObjChild1Copy(pObj) == (void*)(1))
				pObj->pCopy = NULL;
			else if(Abc_ObjChild0Copy(pObj) == NULL && (Abc_ObjChild1Copy(pObj) != (NULL) && Abc_ObjChild1Copy(pObj) != (void*)(1)) )
				pObj->pCopy = NULL;
			else if(Abc_ObjChild0Copy(pObj) == (void*)(1) && Abc_ObjChild1Copy(pObj) == NULL)
				pObj->pCopy = NULL;
			else if(Abc_ObjChild0Copy(pObj) == (void*)(1) && Abc_ObjChild1Copy(pObj) == (void*)(1))
				pObj->pCopy = (Abc_Obj_t *)(1);
			else if(Abc_ObjChild0Copy(pObj) == (void*)(1) && (Abc_ObjChild1Copy(pObj) != (NULL) && Abc_ObjChild1Copy(pObj) != (void*)(1)) )
				pObj->pCopy = Abc_ObjChild1Copy(pObj);
			else if( (Abc_ObjChild0Copy(pObj) != (NULL) && Abc_ObjChild0Copy(pObj) != (void*)(1)) && Abc_ObjChild1Copy(pObj) == NULL )
				pObj->pCopy = NULL;
			else if( (Abc_ObjChild0Copy(pObj) != (NULL) && Abc_ObjChild0Copy(pObj) != (void*)(1)) && Abc_ObjChild1Copy(pObj) == (void*)(1) )
				pObj->pCopy = Abc_ObjChild0Copy(pObj);
			else if( (Abc_ObjChild0Copy(pObj) != (NULL) && Abc_ObjChild0Copy(pObj) != (void*)(1)) &&
					 (Abc_ObjChild1Copy(pObj) != (NULL) && Abc_ObjChild1Copy(pObj) != (void*)(1)) )
				pObj->pCopy = Abc_AigAnd( (Abc_Aig_t *)subNtk->pManFunc, Abc_ObjChild0Copy(pObj), Abc_ObjChild1Copy(pObj) );					
		}	

	for(i = 0; i < Abc_NtkPoNum(pNtk); i++)
	{
		pObj = Abc_NtkPo(pNtk, i);
		pObjNew = Abc_NtkDupObj( subNtk, pObj, 1 );
		
		if( Abc_ObjChild0Copy(pObj) == NULL)
		{
			Abc_ObjAddFanin( pObjNew, Abc_AigConst1(subNtk));
			pObjNew->fCompl0 = 1;
		}
		else if( Abc_ObjChild0Copy(pObj) == (void*)(1) )
		{
			Abc_ObjAddFanin( pObjNew, Abc_AigConst1(subNtk));
			pObjNew->fCompl0 = 0;
		}
		else
			Abc_ObjAddFanin( pObjNew, Abc_ObjChild0Copy(pObj) );
	}

	return subNtk;
}

FILE *matchFile;

int matchNonSingletonOutputs(Abc_Ntk_t * pNtk1, Vec_Ptr_t ** nodesInLevel1, Vec_Int_t ** iMatch1, Vec_Int_t ** iDep1, Vec_Int_t * matchedInputs1, int * iGroup1, Vec_Int_t ** oMatch1, int * oGroup1,
							  Abc_Ntk_t * pNtk2, Vec_Ptr_t ** nodesInLevel2, Vec_Int_t ** iMatch2, Vec_Int_t ** iDep2, Vec_Int_t * matchedInputs2, int * iGroup2, Vec_Int_t ** oMatch2, int * oGroup2,
							  Vec_Int_t * matchedOutputs1, Vec_Int_t * matchedOutputs2, Vec_Int_t * oMatchedGroups, Vec_Int_t * iNonSingleton,						 
							  Abc_Ntk_t * subNtk1, Abc_Ntk_t * subNtk2, Vec_Ptr_t * oMatchPairs,
							  Vec_Int_t * oNonSingleton, int oI, int idx, int ii, int iidx)
{		
	static int MATCH_FOUND;
	int i;
	int j, temp;
	Vec_Int_t * mismatch;		
	int * skipList;	
	static int counter = 0;

	MATCH_FOUND = FALSE;
	
	if( oI == Vec_IntSize( oNonSingleton ) )
	{
		if( iNonSingleton != NULL)			
			if( match1by1(pNtk1, nodesInLevel1, iMatch1, iDep1, matchedInputs1, iGroup1, oMatch1, oGroup1,
				          pNtk2, nodesInLevel2, iMatch2, iDep2, matchedInputs2, iGroup2, oMatch2, oGroup2,
 					      matchedOutputs1, matchedOutputs2, oMatchedGroups, iNonSingleton, ii, iidx) )
				MATCH_FOUND = TRUE;	

		if( iNonSingleton == NULL)
			MATCH_FOUND = TRUE;

		return MATCH_FOUND;
	}

	i = Vec_IntEntry(oNonSingleton, oI);

	mismatch = Vec_IntAlloc(10);
	
	skipList = ABC_ALLOC(int, Vec_IntSize(oMatch1[i]));

	for(j = 0; j < Vec_IntSize(oMatch1[i]); j++)
		skipList[j] = FALSE;

	Vec_PtrPush(oMatchPairs, Abc_NtkPo(subNtk1, Vec_IntEntry(oMatch1[i], idx)) );
	Vec_IntPush(matchedOutputs1, Vec_IntEntry(oMatch1[i], idx));

	for(j = 0; j < Vec_IntSize( oMatch2[i] ) && MATCH_FOUND == FALSE; j++)
	{
		if( Vec_IntEntry(oMatch2[i], j) == -1 || skipList[j] == TRUE)
			continue;

		Vec_PtrPush(oMatchPairs, Abc_NtkPo(subNtk2, Vec_IntEntry(oMatch2[i], j)));
		Vec_IntPush(matchedOutputs2, Vec_IntEntry(oMatch2[i], j));

		counter++;		
		if( Abc_NtkBmSat( subNtk1, subNtk2, NULL, oMatchPairs, mismatch, 0) )
		{
			/*fprintf(matchFile, "%s matched to %s\n", Abc_ObjName(Abc_NtkPo(pNtk1, Vec_IntEntry(oMatch1[i], idx))), 
										 Abc_ObjName(Abc_NtkPo(pNtk2, Vec_IntEntry(oMatch2[i], j))));			*/

			temp =  Vec_IntEntry(oMatch2[i], j);
			Vec_IntWriteEntry(oMatch2[i], j, -1);
			
			if(idx != Vec_IntSize( oMatch1[i] ) - 1)
				// call the same function with idx+1
				matchNonSingletonOutputs(pNtk1, nodesInLevel1, iMatch1, iDep1, matchedInputs1, iGroup1, oMatch1, oGroup1,
										 pNtk2, nodesInLevel2, iMatch2, iDep2, matchedInputs2, iGroup2, oMatch2, oGroup2,
										 matchedOutputs1, matchedOutputs2, oMatchedGroups, iNonSingleton,						 
										 subNtk1, subNtk2, oMatchPairs,
										 oNonSingleton, oI, idx+1, ii, iidx);
			else	
				// call the same function with idx = 0 and oI++
				matchNonSingletonOutputs(pNtk1, nodesInLevel1, iMatch1, iDep1, matchedInputs1, iGroup1, oMatch1, oGroup1,
										 pNtk2, nodesInLevel2, iMatch2, iDep2, matchedInputs2, iGroup2, oMatch2, oGroup2,
										 matchedOutputs1, matchedOutputs2, oMatchedGroups, iNonSingleton,						 
										 subNtk1, subNtk2, oMatchPairs,
										 oNonSingleton, oI+1, 0, ii, iidx);

			Vec_IntWriteEntry(oMatch2[i], j, temp);
		}		
		else
		{
			int * output1, * output2;
			int k;
			Abc_Obj_t * pObj;
			int * pModel;
			char * vPiValues;			
			

			vPiValues = ABC_ALLOC( char,  Abc_NtkPiNum(subNtk1) + 1);
			vPiValues[Abc_NtkPiNum(subNtk1)] = '\0';	
			
			for(k = 0; k < Abc_NtkPiNum(subNtk1); k++)
				vPiValues[k] = '0';

			for(k = 0; k < Vec_IntSize(mismatch); k += 2)			
				vPiValues[Vec_IntEntry(mismatch, k)] = Vec_IntEntry(mismatch, k+1);						

			pModel = ABC_ALLOC( int, Abc_NtkCiNum(subNtk1) );	

			Abc_NtkForEachPi( subNtk1, pObj, k )
				pModel[k] = vPiValues[k] - '0';
			Abc_NtkForEachLatch( subNtk1, pObj, k )
				pModel[Abc_NtkPiNum(subNtk1)+k] = pObj->iData - 1;			
	
			output1 = Abc_NtkVerifySimulatePattern( subNtk1, pModel );

			Abc_NtkForEachLatch( subNtk2, pObj, k )
				pModel[Abc_NtkPiNum(subNtk2)+k] = pObj->iData - 1;

			output2 = Abc_NtkVerifySimulatePattern( subNtk2, pModel );
			

			for(k = 0; k < Vec_IntSize( oMatch1[i] ); k++)
				if(output1[Vec_IntEntry(oMatch1[i], idx)] != output2[Vec_IntEntry(oMatch2[i], k)])
				{
					skipList[k] = TRUE;	
					/*printf("Output is SKIPPED");*/
				}
				
			ABC_FREE( vPiValues );
			ABC_FREE( pModel );
			ABC_FREE( output1 );
			ABC_FREE( output2 );
		}
		
		if(MATCH_FOUND == FALSE )
		{
			Vec_PtrPop(oMatchPairs);
			Vec_IntPop(matchedOutputs2);
		}
	}

	if(MATCH_FOUND == FALSE )
	{
		Vec_PtrPop(oMatchPairs);
		Vec_IntPop(matchedOutputs1);
	}

	if(MATCH_FOUND && counter != 0)
	{		
		/*printf("Number of OUTPUT SAT instances = %d", counter);*/
		counter = 0;
	}

	ABC_FREE( mismatch );
	ABC_FREE( skipList );

	return MATCH_FOUND;
}

int match1by1(Abc_Ntk_t * pNtk1, Vec_Ptr_t ** nodesInLevel1, Vec_Int_t ** iMatch1, Vec_Int_t ** iDep1, Vec_Int_t * matchedInputs1, int * iGroup1, Vec_Int_t ** oMatch1, int * oGroup1,
			   Abc_Ntk_t * pNtk2, Vec_Ptr_t ** nodesInLevel2, Vec_Int_t ** iMatch2, Vec_Int_t ** iDep2, Vec_Int_t * matchedInputs2, int * iGroup2, Vec_Int_t ** oMatch2, int * oGroup2,
			   Vec_Int_t * matchedOutputs1, Vec_Int_t * matchedOutputs2, Vec_Int_t * oMatchedGroups, Vec_Int_t * iNonSingleton, int ii, int idx)
{
	static int MATCH_FOUND = FALSE;
	Abc_Ntk_t * subNtk1, * subNtk2;
	Vec_Int_t * oNonSingleton;	
	Vec_Ptr_t * oMatchPairs;
	int * skipList;
	int j, m;	
	int i;		
	static int counter = 0;

	MATCH_FOUND = FALSE;

	if( ii == Vec_IntSize(iNonSingleton) )
	{
		MATCH_FOUND = TRUE;
		return TRUE;
	}
	
	i = Vec_IntEntry(iNonSingleton, ii);

	if( idx == Vec_IntSize(iMatch1[i]) )
	{		
		// call again with the next element in iNonSingleton
		return match1by1(pNtk1, nodesInLevel1, iMatch1, iDep1, matchedInputs1, iGroup1, oMatch1, oGroup1,
				    	 pNtk2, nodesInLevel2, iMatch2, iDep2, matchedInputs2, iGroup2, oMatch2, oGroup2,
						 matchedOutputs1, matchedOutputs2, oMatchedGroups, iNonSingleton, ii+1, 0);			
		
	}	
	
	oNonSingleton = Vec_IntAlloc(10);
	oMatchPairs = Vec_PtrAlloc(100);	
	skipList = ABC_ALLOC(int, Vec_IntSize(iMatch1[i]));

	for(j = 0; j < Vec_IntSize(iMatch1[i]); j++)
		skipList[j] = FALSE;
	
	Vec_IntPush(matchedInputs1, Vec_IntEntry(iMatch1[i], idx));
	idx++;
	
	if(idx == 1)
	{
		for(j = 0; j < Vec_IntSize(iDep1[Vec_IntEntryLast(iMatch1[i])]); j++)
		{
			if( Vec_IntSize(oMatch1[oGroup1[Vec_IntEntry(iDep1[Vec_IntEntryLast(iMatch1[i])], j)]]) == 1 ) 
				continue;
			if( Vec_IntFind( oMatchedGroups, oGroup1[Vec_IntEntry(iDep1[Vec_IntEntryLast(iMatch1[i])], j)]) != -1)
				continue;
			
			Vec_IntPushUnique(oNonSingleton,  oGroup1[Vec_IntEntry(iDep1[Vec_IntEntryLast(iMatch1[i])], j)]);
			Vec_IntPushUnique(oMatchedGroups, oGroup1[Vec_IntEntry(iDep1[Vec_IntEntryLast(iMatch1[i])], j)]);		
		}
	}

	subNtk1 = computeCofactor(pNtk1, nodesInLevel1, NULL, matchedInputs1);

	for(j = idx-1; j < Vec_IntSize(iMatch2[i]) && MATCH_FOUND == FALSE; j++)
	{
		int tempJ;
		Vec_Int_t * mismatch;

		if( skipList[j] )
			continue;

		mismatch = Vec_IntAlloc(10);

		Vec_IntPush(matchedInputs2, Vec_IntEntry(iMatch2[i], j));		
		
		subNtk2 = computeCofactor(pNtk2, nodesInLevel2, NULL, matchedInputs2);			

		for(m = 0; m < Vec_IntSize(matchedOutputs1); m++)
		{
			Vec_PtrPush(oMatchPairs, Abc_NtkPo(subNtk1, Vec_IntEntry(matchedOutputs1, m)));
			Vec_PtrPush(oMatchPairs, Abc_NtkPo(subNtk2, Vec_IntEntry(matchedOutputs2, m)));
		}

		counter++;

		if( Abc_NtkBmSat( subNtk2, subNtk1, NULL, oMatchPairs, mismatch, 0) )				
		{
			if(idx-1 != j)
			{
				tempJ = Vec_IntEntry(iMatch2[i], idx-1);
				Vec_IntWriteEntry(iMatch2[i], idx-1, Vec_IntEntry(iMatch2[i], j));
				Vec_IntWriteEntry(iMatch2[i], j, tempJ);
			}

			/*fprintf(matchFile, "%s matched to %s\n", Abc_ObjName(Abc_NtkPi(pNtk1, Vec_IntEntry(iMatch1[i], idx-1))), 
										    		  Abc_ObjName(Abc_NtkPi(pNtk2, Vec_IntEntry(iMatch2[i], j))));*/

			// we look for a match for outputs in oNonSingleton								
			matchNonSingletonOutputs(pNtk1, nodesInLevel1, iMatch1, iDep1, matchedInputs1, iGroup1, oMatch1, oGroup1,
									 pNtk2, nodesInLevel2, iMatch2, iDep2, matchedInputs2, iGroup2, oMatch2, oGroup2,
									 matchedOutputs1, matchedOutputs2, oMatchedGroups, iNonSingleton,						 
									 subNtk1, subNtk2, oMatchPairs, oNonSingleton, 0, 0, ii, idx);
			
			
			if(idx-1 != j)
			{
				tempJ = Vec_IntEntry(iMatch2[i], idx-1);
				Vec_IntWriteEntry(iMatch2[i], idx-1, Vec_IntEntry(iMatch2[i], j));
				Vec_IntWriteEntry(iMatch2[i], j, tempJ);
			}
		}
		else
		{
			Abc_Ntk_t * FpNtk1, * FpNtk2;
			int * bitVector1, * bitVector2;
			Vec_Int_t * currInputs1, * currInputs2;			
			Vec_Ptr_t * vSupp;	
			Abc_Obj_t * pObj;
			int suppNum1 = 0;
			int * suppNum2;			
			
			bitVector1 = ABC_ALLOC( int, Abc_NtkPiNum(pNtk1) );
			bitVector2 = ABC_ALLOC( int, Abc_NtkPiNum(pNtk2) );

			currInputs1 = Vec_IntAlloc(10);
			currInputs2 = Vec_IntAlloc(10);		
			
			suppNum2 = ABC_ALLOC(int, Vec_IntSize(iMatch2[i])-idx+1);

			for(m = 0; m < Abc_NtkPiNum(pNtk1); m++)
			{
				bitVector1[m] = 0;
				bitVector2[m] = 0;
			}

			for(m = 0; m < Vec_IntSize(iMatch2[i])-idx+1; m++)						
				suppNum2[m]= 0;			

			// First of all set the value of the inputs that are already matched and are in mismatch
			for(m = 0; m < Vec_IntSize(mismatch); m += 2)
			{
				int n = Vec_IntEntry(mismatch, m);
					
				bitVector1[Vec_IntEntry(matchedInputs1, n)] = Vec_IntEntry(mismatch, m+1);
				bitVector2[Vec_IntEntry(matchedInputs2, n)] = Vec_IntEntry(mismatch, m+1);
				
			}
			
			for(m = idx-1; m < Vec_IntSize(iMatch1[i]); m++)
			{
				Vec_IntPush(currInputs1, Vec_IntEntry(iMatch1[i], m));
				Vec_IntPush(currInputs2, Vec_IntEntry(iMatch2[i], m));
			}

			// Then add all the inputs that are not yet matched to the currInputs
			for(m = 0; m < Abc_NtkPiNum(pNtk1); m++)
			{
				if(Vec_IntFind( matchedInputs1, m ) == -1)
					Vec_IntPushUnique(currInputs1, m);

				if(Vec_IntFind( matchedInputs2, m ) == -1)
					Vec_IntPushUnique(currInputs2, m);
			}

			FpNtk1 = computeCofactor(pNtk1, nodesInLevel1, bitVector1, currInputs1);			
			FpNtk2 = computeCofactor(pNtk2, nodesInLevel2, bitVector2, currInputs2);    

			Abc_NtkForEachPo( FpNtk1, pObj, m )
			{		
				int n;
				vSupp  = Abc_NtkNodeSupport( FpNtk1, &pObj, 1 );			    		
				
				for(n = 0; n < vSupp->nSize; n++)
					if( Abc_ObjId((Abc_Obj_t *)vSupp->pArray[n]) == 1 )
						suppNum1 += Vec_IntFind( matchedOutputs1, m) + 1;				
						
				Vec_PtrFree( vSupp );
			}
			
			Abc_NtkForEachPo( FpNtk2, pObj, m )
			{		
				int n;
				vSupp  = Abc_NtkNodeSupport( FpNtk2, &pObj, 1 );			    		
				
				for(n = 0; n < vSupp->nSize; n++)
					if( (int)Abc_ObjId((Abc_Obj_t *)vSupp->pArray[n])-1 < (Vec_IntSize(iMatch2[i]))-idx+1 &&
						(int)Abc_ObjId((Abc_Obj_t *)vSupp->pArray[n])-1 >= 0)
						suppNum2[Abc_ObjId((Abc_Obj_t *)vSupp->pArray[n])-1] += Vec_IntFind( matchedOutputs2, m) + 1;				
						
				Vec_PtrFree( vSupp );
			}

			/*if(suppNum1 != 0)			
				printf("Ntk1 is trigged");			

			if(suppNum2[0] != 0)
				printf("Ntk2 is trigged");*/

			for(m = 0; m < Vec_IntSize(iMatch2[i])-idx+1; m++)
				if(suppNum2[m] != suppNum1)
				{
					skipList[m+idx-1] = TRUE;					
					/*printf("input is skipped");*/
				}
				
			Abc_NtkDelete( FpNtk1 );
			Abc_NtkDelete( FpNtk2 );
			ABC_FREE( bitVector1 );	
 			ABC_FREE( bitVector2 );	
			Vec_IntFree( currInputs1 );
			Vec_IntFree( currInputs2 );			
			ABC_FREE( suppNum2 );
		}
		
		Vec_PtrClear(oMatchPairs);
		Abc_NtkDelete( subNtk2 );
		Vec_IntFree(mismatch);

		//Vec_IntWriteEntry(iMatch2[i], j, tempJ);		
		
		if( MATCH_FOUND == FALSE )
			Vec_IntPop(matchedInputs2);
	}

	if( MATCH_FOUND == FALSE )
	{
		Vec_IntPop(matchedInputs1);	
		
		if(idx == 1)
		{
			for(m = 0; m < Vec_IntSize(oNonSingleton); m++)
				Vec_IntPop( oMatchedGroups );
		}
	}
	
	Vec_IntFree( oNonSingleton );	
	Vec_PtrFree( oMatchPairs );	
	ABC_FREE( skipList );
	Abc_NtkDelete( subNtk1 );	

	if(MATCH_FOUND && counter != 0)
	{		
		/*printf("Number of INPUT SAT instances = %d\n", counter);*/

		counter = 0;
	}

	return MATCH_FOUND;
}

float refineBySAT(Abc_Ntk_t * pNtk1, Vec_Int_t ** iMatch1, int * iGroup1, Vec_Int_t ** iDep1, int* iLastItem1, Vec_Int_t ** oMatch1, int * oGroup1, Vec_Int_t ** oDep1, int* oLastItem1, int * observability1,
				 Abc_Ntk_t * pNtk2, Vec_Int_t ** iMatch2, int * iGroup2, Vec_Int_t ** iDep2, int* iLastItem2, Vec_Int_t ** oMatch2, int * oGroup2, Vec_Int_t ** oDep2, int* oLastItem2, int * observability2)
{
	int i, j;	
	Abc_Obj_t * pObj;
	Vec_Int_t * iNonSingleton;
	Vec_Int_t * matchedInputs1, * matchedInputs2;
	Vec_Int_t * matchedOutputs1, * matchedOutputs2;
	Vec_Ptr_t ** nodesInLevel1, ** nodesInLevel2;
	Vec_Int_t * oMatchedGroups;
	FILE *result;	
	int matchFound;
	abctime clk = Abc_Clock();
	float satTime = 0.0;

	/*matchFile = fopen("satmatch.txt", "w");*/
	
	iNonSingleton = Vec_IntAlloc(10);	
	
	matchedInputs1 = Vec_IntAlloc( Abc_NtkPiNum(pNtk1) );
	matchedInputs2 = Vec_IntAlloc( Abc_NtkPiNum(pNtk2) );

	matchedOutputs1 = Vec_IntAlloc( Abc_NtkPoNum(pNtk1) );
	matchedOutputs2 = Vec_IntAlloc( Abc_NtkPoNum(pNtk2) );

	nodesInLevel1 = ABC_ALLOC( Vec_Ptr_t *, Abc_AigLevel( pNtk1 ) + 1);	// why numOfLevels+1? because the inputs are in level 0
	for(i = 0; i <= Abc_AigLevel( pNtk1 ); i++)
		nodesInLevel1[i] = Vec_PtrAlloc( 20 );

	// bucket sort the objects based on their levels
	Abc_AigForEachAnd( pNtk1, pObj, i )	
		Vec_PtrPush(nodesInLevel1[Abc_ObjLevel(pObj)], pObj);
	
	nodesInLevel2 = ABC_ALLOC( Vec_Ptr_t *, Abc_AigLevel( pNtk2 ) + 1);	// why numOfLevels+1? because the inputs are in level 0
	for(i = 0; i <= Abc_AigLevel( pNtk2 ); i++)
		nodesInLevel2[i] = Vec_PtrAlloc( 20 );

	// bucket sort the objects based on their levels
	Abc_AigForEachAnd( pNtk2, pObj, i )	
		Vec_PtrPush(nodesInLevel2[Abc_ObjLevel(pObj)], pObj);	

	oMatchedGroups = Vec_IntAlloc( 10 );

	for(i = 0; i < *iLastItem1; i++)
	{
		if(Vec_IntSize(iMatch1[i]) == 1)
		{
			Vec_IntPush(matchedInputs1, Vec_IntEntryLast(iMatch1[i]));
			Vec_IntPush(matchedInputs2, Vec_IntEntryLast(iMatch2[i]));		
		}
		else
			Vec_IntPush(iNonSingleton, i);
	}

	for(i = 0; i < *oLastItem1; i++)
	{
		if(Vec_IntSize(oMatch1[i]) == 1)
		{
			Vec_IntPush(matchedOutputs1, Vec_IntEntryLast(oMatch1[i]));
			Vec_IntPush(matchedOutputs2, Vec_IntEntryLast(oMatch2[i]));
		}
	}
	
	for(i = 0; i < Vec_IntSize(iNonSingleton) - 1; i++)
	{
		for(j = i + 1; j < Vec_IntSize(iNonSingleton); j++)		
			if( observability2[Vec_IntEntry(iMatch2[Vec_IntEntry(iNonSingleton, j)], 0)] > 
				observability2[Vec_IntEntry(iMatch2[Vec_IntEntry(iNonSingleton, i)], 0)] )
			{				
				int temp = Vec_IntEntry(iNonSingleton, i);
				Vec_IntWriteEntry( iNonSingleton, i, Vec_IntEntry(iNonSingleton, j) );
				Vec_IntWriteEntry( iNonSingleton, j, temp );				
			}
			else if( observability2[Vec_IntEntry(iMatch2[Vec_IntEntry(iNonSingleton, j)], 0)] == 
					 observability2[Vec_IntEntry(iMatch2[Vec_IntEntry(iNonSingleton, i)], 0)] )
			{
				if( Vec_IntSize(iMatch2[Vec_IntEntry(iNonSingleton, j)]) < Vec_IntSize(iMatch2[Vec_IntEntry(iNonSingleton, i)]) )
				{
					int temp = Vec_IntEntry(iNonSingleton, i);
					Vec_IntWriteEntry( iNonSingleton, i, Vec_IntEntry(iNonSingleton, j) );
					Vec_IntWriteEntry( iNonSingleton, j, temp );				
				}
			}
	}	

	/*for(i = 0; i < Vec_IntSize(iNonSingleton) - 1; i++)
	{
		for(j = i + 1; j < Vec_IntSize(iNonSingleton); j++)		
			if( Vec_IntSize(oDep2[oGroup2[Vec_IntEntryLast(iMatch2[Vec_IntEntry(iNonSingleton, j)])]]) > 
				Vec_IntSize(oDep2[oGroup2[Vec_IntEntryLast(iMatch2[Vec_IntEntry(iNonSingleton, i)])]]) )
			{				
				int temp = Vec_IntEntry(iNonSingleton, i);
				Vec_IntWriteEntry( iNonSingleton, i, Vec_IntEntry(iNonSingleton, j) );
				Vec_IntWriteEntry( iNonSingleton, j, temp );				
			}
			else if( Vec_IntSize(oDep2[oGroup2[Vec_IntEntryLast(iMatch2[Vec_IntEntry(iNonSingleton, j)])]]) == 
				Vec_IntSize(oDep2[oGroup2[Vec_IntEntryLast(iMatch2[Vec_IntEntry(iNonSingleton, i)])]]) )
			{
				if( observability2[Vec_IntEntry(iMatch2[Vec_IntEntry(iNonSingleton, j)], 0)] > 
						observability2[Vec_IntEntry(iMatch2[Vec_IntEntry(iNonSingleton, i)], 0)] )
				{
					int temp = Vec_IntEntry(iNonSingleton, i);
					Vec_IntWriteEntry( iNonSingleton, i, Vec_IntEntry(iNonSingleton, j) );
					Vec_IntWriteEntry( iNonSingleton, j, temp );				
				}
			}
	}*/

	matchFound = match1by1(pNtk1, nodesInLevel1, iMatch1, iDep1, matchedInputs1, iGroup1, oMatch1, oGroup1,
						   pNtk2, nodesInLevel2, iMatch2, iDep2, matchedInputs2, iGroup2, oMatch2, oGroup2,
						   matchedOutputs1, matchedOutputs2, oMatchedGroups, iNonSingleton, 0, 0);

	if( matchFound && Vec_IntSize(matchedOutputs1) != Abc_NtkPoNum(pNtk1) )
	{
		Vec_Int_t * oNonSingleton;
		Vec_Ptr_t * oMatchPairs;
		Abc_Ntk_t * subNtk1, * subNtk2;

		oNonSingleton = Vec_IntAlloc( 10 );
		
		oMatchPairs = Vec_PtrAlloc(Abc_NtkPoNum(pNtk1) * 2);	

		for(i = 0; i < *oLastItem1; i++)
			if( Vec_IntSize(oMatch1[i]) > 1 && Vec_IntFind( oMatchedGroups, i) == -1 )
				Vec_IntPush(oNonSingleton, i);
			
		subNtk1 = computeCofactor(pNtk1, nodesInLevel1, NULL, matchedInputs1);
		subNtk2 = computeCofactor(pNtk2, nodesInLevel2, NULL, matchedInputs2);
		
		matchFound = matchNonSingletonOutputs(pNtk1, nodesInLevel1, iMatch1, iDep1, matchedInputs1, iGroup1, oMatch1, oGroup1,
								 pNtk2, nodesInLevel2, iMatch2, iDep2, matchedInputs2, iGroup1, oMatch2, oGroup2,
								 matchedOutputs1, matchedOutputs2, oMatchedGroups, NULL,						 
								 subNtk1, subNtk2, oMatchPairs, oNonSingleton, 0, 0, 0, 0);

		Vec_IntFree( oNonSingleton );		
		Vec_PtrFree( oMatchPairs );

		Abc_NtkDelete(subNtk1);
		Abc_NtkDelete(subNtk2);
	}

	satTime = (float)(Abc_Clock() - clk)/(float)(CLOCKS_PER_SEC);	

	if( matchFound )
	{
  		checkEquivalence( pNtk1, matchedInputs1, matchedOutputs1, pNtk2, matchedInputs2, matchedOutputs2);

		result = fopen("IOmatch.txt", "w");

		fprintf(result, "I/O = %d / %d \n\n", Abc_NtkPiNum(pNtk1), Abc_NtkPoNum(pNtk1));
		
		for(i = 0; i < Vec_IntSize(matchedInputs1) ; i++)
  			fprintf(result, "{%s}\t{%s}\n", Abc_ObjName(Abc_NtkPi(pNtk1, Vec_IntEntry(matchedInputs1, i))), Abc_ObjName(Abc_NtkPi(pNtk2, Vec_IntEntry(matchedInputs2, i))) );									

		fprintf(result, "\n-----------------------------------------\n");

		for(i = 0; i < Vec_IntSize(matchedOutputs1) ; i++)
			fprintf(result, "{%s}\t{%s}\n", Abc_ObjName(Abc_NtkPo(pNtk1, Vec_IntEntry(matchedOutputs1, i))), Abc_ObjName(Abc_NtkPo(pNtk2, Vec_IntEntry(matchedOutputs2, i))) );									

		fclose( result );	
	}

	Vec_IntFree( matchedInputs1 );
	Vec_IntFree( matchedInputs2 );
	Vec_IntFree( matchedOutputs1 );
	Vec_IntFree( matchedOutputs2 );
	Vec_IntFree( iNonSingleton );
	Vec_IntFree( oMatchedGroups );
	
	for(i = 0; i <= Abc_AigLevel( pNtk1 ); i++)	
		Vec_PtrFree( nodesInLevel1[i] );
	for(i = 0; i <= Abc_AigLevel( pNtk2 ); i++)	
		Vec_PtrFree( nodesInLevel2[i] );
	

	ABC_FREE( nodesInLevel1 );
	ABC_FREE( nodesInLevel2 );
	/*fclose(matchFile);*/
	
	return satTime;
}

int checkListConsistency(Vec_Int_t ** iMatch1, Vec_Int_t ** oMatch1, Vec_Int_t ** iMatch2, Vec_Int_t ** oMatch2, int iLastItem1, int oLastItem1, int iLastItem2, int oLastItem2)
{
	//int i;

	if(iLastItem1 != iLastItem2 || oLastItem1 != oLastItem2)
		return FALSE;

	/*for(i = 0; i < iLastItem1; i++) {
		if(Vec_IntSize(iMatch1[i]) != Vec_IntSize(iMatch2[i]))
			return FALSE;
	}

	for(i = 0; i < oLastItem1; i++) {
		if(Vec_IntSize(oMatch1[i]) != Vec_IntSize(oMatch2[i]))
			return FALSE;
	}*/
		
	return TRUE;
}


void bmGateWay( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, int p_equivalence )
{	
	Vec_Int_t ** iDep1, ** oDep1;
	Vec_Int_t ** iDep2, ** oDep2;
	Vec_Int_t ** iMatch1, ** oMatch1;
	Vec_Int_t ** iMatch2, ** oMatch2;		
	int * iGroup1, * oGroup1;
	int * iGroup2, * oGroup2;
	int iLastItem1, oLastItem1;
	int iLastItem2, oLastItem2;	
	int i, j;	
	
	char * vPiValues1, * vPiValues2;
	int * observability1, * observability2;
	abctime clk = Abc_Clock();
	float initTime;
	float simulTime;
	float satTime;
	Vec_Ptr_t ** topOrder1 = NULL, ** topOrder2 = NULL;

	extern void getDependencies(Abc_Ntk_t *pNtk, Vec_Int_t** iDep, Vec_Int_t** oDep);
	extern void initMatchList(Abc_Ntk_t *pNtk, Vec_Int_t** iDep, Vec_Int_t** oDep, Vec_Int_t** iMatch, int* iLastItem, Vec_Int_t** oMatch, int* oLastItem, int* iGroup, int* oGroup, int p_equivalence);		
	extern void iSortDependencies(Abc_Ntk_t *pNtk, Vec_Int_t** iDep, int* oGroup);
	extern void oSortDependencies(Abc_Ntk_t *pNtk, Vec_Int_t** oDep, int* iGroup);
	extern int iSplitByDep(Abc_Ntk_t *pNtk, Vec_Int_t** iDep, Vec_Int_t** iMatch, int* iGroup, int* iLastItem, int* oGroup);
	extern int oSplitByDep(Abc_Ntk_t *pNtk, Vec_Int_t** oDep, Vec_Int_t** oMatch, int* oGroup, int* oLastItem, int* iGroup);	
	extern Vec_Ptr_t ** findTopologicalOrder(Abc_Ntk_t * pNtk);
	extern int refineIOBySimulation(Abc_Ntk_t *pNtk, Vec_Int_t** iMatch, int* iLastItem, int * iGroup, Vec_Int_t** iDep, Vec_Int_t** oMatch, int* oLastItem, int * oGroup, Vec_Int_t** oDep, char * vPiValues, int * observability, Vec_Ptr_t ** topOrder);	
	extern float refineBySAT(Abc_Ntk_t * pNtk1, Vec_Int_t ** iMatch1, int * iGroup1, Vec_Int_t ** iDep1, int* iLastItem1, Vec_Int_t ** oMatch1, int * oGroup1, Vec_Int_t ** oDep1, int* oLastItem1, int * observability1,
							Abc_Ntk_t * pNtk2, Vec_Int_t ** iMatch2, int * iGroup2, Vec_Int_t ** iDep2, int* iLastItem2, Vec_Int_t ** oMatch2, int * oGroup2, Vec_Int_t ** oDep2, int* oLastItem2, int * observability2);				
	int checkListConsistency(Vec_Int_t ** iMatch1, Vec_Int_t ** oMatch1, Vec_Int_t ** iMatch2, Vec_Int_t ** oMatch2, int iLastItem1, int oLastItem1, int iLastItem2, int oLastItem2);	

	iDep1 = ABC_ALLOC( Vec_Int_t*,  (unsigned)Abc_NtkPiNum(pNtk1) );
	oDep1 = ABC_ALLOC( Vec_Int_t*,  (unsigned)Abc_NtkPoNum(pNtk1) );

	iDep2 = ABC_ALLOC( Vec_Int_t*,  (unsigned)Abc_NtkPiNum(pNtk2) );
	oDep2 = ABC_ALLOC( Vec_Int_t*,  (unsigned)Abc_NtkPoNum(pNtk2) );

	iMatch1 = ABC_ALLOC( Vec_Int_t*,  (unsigned)Abc_NtkPiNum(pNtk1) );
	oMatch1 = ABC_ALLOC( Vec_Int_t*,  (unsigned)Abc_NtkPoNum(pNtk1) );

	iMatch2 = ABC_ALLOC( Vec_Int_t*,  (unsigned)Abc_NtkPiNum(pNtk2) );
	oMatch2 = ABC_ALLOC( Vec_Int_t*,  (unsigned)Abc_NtkPoNum(pNtk2) );		

	iGroup1 = ABC_ALLOC( int, Abc_NtkPiNum(pNtk1) );
	oGroup1 = ABC_ALLOC( int, Abc_NtkPoNum(pNtk1) );

	iGroup2 = ABC_ALLOC( int, Abc_NtkPiNum(pNtk2) );
	oGroup2 = ABC_ALLOC( int, Abc_NtkPoNum(pNtk2) );

	vPiValues1 = ABC_ALLOC( char,  Abc_NtkPiNum(pNtk1) + 1);
	vPiValues1[Abc_NtkPiNum(pNtk1)] = '\0';	

	vPiValues2 = ABC_ALLOC( char,  Abc_NtkPiNum(pNtk2) + 1);
	vPiValues2[Abc_NtkPiNum(pNtk2)] = '\0';	

	observability1 = ABC_ALLOC(int, (unsigned)Abc_NtkPiNum(pNtk1));
	observability2 = ABC_ALLOC(int, (unsigned)Abc_NtkPiNum(pNtk2));

	for(i = 0; i < Abc_NtkPiNum(pNtk1); i++)
	{		
		iDep1[i] = Vec_IntAlloc( 1 );
		iMatch1[i] = Vec_IntAlloc( 1 );

		iDep2[i] = Vec_IntAlloc( 1 );
		iMatch2[i] = Vec_IntAlloc( 1 );

		vPiValues1[i] = '0';
		vPiValues2[i] = '0';

		observability1[i] = 0;
		observability2[i] = 0;
	}

	for(i = 0; i < Abc_NtkPoNum(pNtk1); i++)
	{
		oDep1[i] = Vec_IntAlloc( 1 );
		oMatch1[i] = Vec_IntAlloc( 1 );

		oDep2[i] = Vec_IntAlloc( 1 );
		oMatch2[i] = Vec_IntAlloc( 1 );
	}	
	
	/************* Strashing ************/	
	pNtk1 = Abc_NtkStrash( pNtk1, 0, 0, 0 );	
	pNtk2 = Abc_NtkStrash( pNtk2, 0, 0, 0 );			
	printf("Network  strashing is done!\n");	
	/************************************/	
	
	/******* Getting Dependencies *******/	
	getDependencies(pNtk1, iDep1, oDep1);
	getDependencies(pNtk2, iDep2, oDep2);			
	printf("Getting dependencies is done!\n");	
	/************************************/

	/***** Intializing match lists ******/	
	initMatchList(pNtk1, iDep1, oDep1, iMatch1, &iLastItem1, oMatch1, &oLastItem1, iGroup1, oGroup1, p_equivalence);			
	initMatchList(pNtk2, iDep2, oDep2, iMatch2, &iLastItem2, oMatch2, &oLastItem2, iGroup2, oGroup2, p_equivalence);	
	printf("Initializing match lists is done!\n");		
	/************************************/

	if( !checkListConsistency(iMatch1, oMatch1, iMatch2, oMatch2, iLastItem1, oLastItem1, iLastItem2, oLastItem2) )
	{
		fprintf( stdout, "I/O dependencies of two circuits are different.\n");
		goto freeAndExit;
	}		

	printf("Refining IOs by dependencies ...");				
	// split match lists further by checking dependencies
	do
	{
		int iNumOfItemsAdded = 1, oNumOfItemsAdded = 1;		

		do
		{	
			if( oNumOfItemsAdded )
			{
				iSortDependencies(pNtk1, iDep1, oGroup1);
				iSortDependencies(pNtk2, iDep2, oGroup2);
			}
			
			if( iNumOfItemsAdded )
			{
				oSortDependencies(pNtk1, oDep1, iGroup1);
				oSortDependencies(pNtk2, oDep2, iGroup2);
			}

			if( iLastItem1 < Abc_NtkPiNum(pNtk1) )
			{				
				iSplitByDep(pNtk1, iDep1, iMatch1, iGroup1, &iLastItem1, oGroup1);
				if( oLastItem1 < Abc_NtkPoNum(pNtk1) )
					oSplitByDep(pNtk1, oDep1, oMatch1, oGroup1, &oLastItem1, iGroup1);
			}				

			if( iLastItem2 < Abc_NtkPiNum(pNtk2) )
			{				
				iNumOfItemsAdded = iSplitByDep(pNtk2, iDep2, iMatch2, iGroup2, &iLastItem2, oGroup2);
				if( oLastItem2 < Abc_NtkPoNum(pNtk2) )		
					oNumOfItemsAdded = oSplitByDep(pNtk2, oDep2, oMatch2, oGroup2, &oLastItem2, iGroup2);
				else
					oNumOfItemsAdded = 0;
			}
			else
				iNumOfItemsAdded = 0;							
			
			if(!checkListConsistency(iMatch1, oMatch1, iMatch2, oMatch2, iLastItem1, oLastItem1, iLastItem2, oLastItem2))
			{
				fprintf( stdout, "I/O dependencies of two circuits are different.\n");
				goto freeAndExit;
			}		
		}while(iNumOfItemsAdded != 0 || oNumOfItemsAdded != 0);

	}while(0);

	printf(" done!\n");

	initTime = ((float)(Abc_Clock() - clk)/(float)(CLOCKS_PER_SEC));	
	clk = Abc_Clock();

	topOrder1 = findTopologicalOrder(pNtk1);
	topOrder2 = findTopologicalOrder(pNtk2);

	printf("Refining IOs by simulation ...");				

	do
	{
		int counter = 0;
		int ioSuccess1, ioSuccess2;	
		
		do
		{
			for(i = 0; i < iLastItem1; i++)
			{
				int temp = (int)(SIM_RANDOM_UNSIGNED % 2);		
				
				if(Vec_IntSize(iMatch1[i]) != Vec_IntSize(iMatch2[i]))
				{
					fprintf( stdout, "Input refinement by simulation finds two circuits different.\n");				
					goto freeAndExit;
				}
				
				for(j = 0; j < Vec_IntSize(iMatch1[i]); j++)
				{
					vPiValues1[Vec_IntEntry(iMatch1[i], j)] = temp + '0';
					vPiValues2[Vec_IntEntry(iMatch2[i], j)] = temp + '0';	
				}			
			}					
			
			ioSuccess1 = refineIOBySimulation(pNtk1, iMatch1, &iLastItem1, iGroup1, iDep1, oMatch1, &oLastItem1, oGroup1, oDep1, vPiValues1, observability1, topOrder1);				
			ioSuccess2 = refineIOBySimulation(pNtk2, iMatch2, &iLastItem2, iGroup2, iDep2, oMatch2, &oLastItem2, oGroup2, oDep2, vPiValues2, observability2, topOrder2);
			
			if(ioSuccess1 && ioSuccess2)
				counter = 0;
			else
				counter++;						
			
			if(ioSuccess1 != ioSuccess2 ||
			   !checkListConsistency(iMatch1, oMatch1, iMatch2, oMatch2, iLastItem1, oLastItem1, iLastItem2, oLastItem2))
			{
				fprintf( stdout, "Input refinement by simulation finds two circuits different.\n");				
				goto freeAndExit;
			}
		}while(counter <= 200);				
		
	}while(0);	

	printf(" done!\n");
	
	simulTime = (float)(Abc_Clock() - clk)/(float)(CLOCKS_PER_SEC);
	printf("SAT-based search started ...\n");

	satTime = refineBySAT(pNtk1, iMatch1, iGroup1, iDep1, &iLastItem1, oMatch1, oGroup1, oDep1, &oLastItem1, observability1,
		   					   pNtk2, iMatch2, iGroup2, iDep2, &iLastItem2, oMatch2, oGroup2, oDep2, &oLastItem2, observability2);

	printf( "Init Time = %4.2f\n", initTime );	
	printf( "Simulation Time = %4.2f\n", simulTime );
	printf( "SAT Time = %4.2f\n", satTime );
	printf( "Overall Time = %4.2f\n", initTime + simulTime + satTime );
	
freeAndExit:

	for(i = 0; i < iLastItem1 ; i++)
	{			
		
		Vec_IntFree( iMatch1[i] );
		Vec_IntFree( iMatch2[i] );
	}
	
	for(i = 0; i < oLastItem1 ; i++)
	{			
		
		Vec_IntFree( oMatch1[i] );
		Vec_IntFree( oMatch2[i] );
	}

	for(i = 0; i < Abc_NtkPiNum(pNtk1); i++)
	{
		Vec_IntFree( iDep1[i] );
		Vec_IntFree( iDep2[i] );
		if(topOrder1 != NULL) {
			Vec_PtrFree( topOrder1[i] );
			Vec_PtrFree( topOrder2[i] );
		}
	}

	for(i = 0; i < Abc_NtkPoNum(pNtk1); i++)
	{
		Vec_IntFree( oDep1[i] );
		Vec_IntFree( oDep2[i] );
	}

	ABC_FREE( iMatch1 );
	ABC_FREE( iMatch2 );
	ABC_FREE( oMatch1 );
	ABC_FREE( oMatch2 );
	ABC_FREE( iDep1 );
	ABC_FREE( iDep2 );
	ABC_FREE( oDep1 );
	ABC_FREE( oDep2 );
	ABC_FREE( iGroup1 );
	ABC_FREE( iGroup2 );
	ABC_FREE( oGroup1 );
	ABC_FREE( oGroup2 );	
	ABC_FREE( vPiValues1 );
	ABC_FREE( vPiValues2 );
	ABC_FREE( observability1 );
	ABC_FREE( observability2 );
	if(topOrder1 != NULL) {
		ABC_FREE( topOrder1 );
		ABC_FREE( topOrder2 );
	}
}ABC_NAMESPACE_IMPL_END

