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

  FileName    [exorList.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Exclusive sum-of-product minimization.]

  Synopsis    [Cube lists.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

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

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

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

////////////////////////////////////////////////////////////////////////
///                                                                  ///
///                  Implementation of EXORCISM - 4                  ///
///              An Exclusive Sum-of-Product Minimizer               ///
///                                                                  ///
///               Alan Mishchenko  <alanmi@ee.pdx.edu>               ///
///                                                                  ///
////////////////////////////////////////////////////////////////////////
///                                                                  ///
///                 Iterative Cube Set Minimization                  ///
///                  Iterative ExorLink Procedure                    ///
///                  Support of Cube Pair Queques                    ///
///                                                                  ///
///  Ver. 1.0. Started - July 18, 2000. Last update - July 20, 2000  ///
///  Ver. 1.1. Started - July 24, 2000. Last update - July 29, 2000  ///
///  Ver. 1.2. Started - July 30, 2000. Last update - July 31, 2000  ///
///  Ver. 1.4. Started -  Aug 10, 2000. Last update -  Aug 26, 2000  ///
///  Ver. 1.5. Started -  Aug 30, 2000. Last update -  Aug 30, 2000  ///
///  Ver. 1.6. Started -  Sep 11, 2000. Last update -  Sep 15, 2000  ///
///  Ver. 1.7. Started -  Sep 20, 2000. Last update -  Sep 23, 2000  ///
///                                                                  ///
////////////////////////////////////////////////////////////////////////
///   This software was tested with the BDD package "CUDD", v.2.3.0  ///
///                          by Fabio Somenzi                        ///
///                  http://vlsi.colorado.edu/~fabio/                ///
////////////////////////////////////////////////////////////////////////

#include "exor.h"

ABC_NAMESPACE_IMPL_START

////////////////////////////////////////////////////////////////////////
///                       EXTERNAL VARIABLES                         ///
////////////////////////////////////////////////////////////////////////

// information about options and the cover
extern cinfo g_CoverInfo;

// the look-up table for the number of 1's in unsigned short
extern unsigned char BitCount[];

////////////////////////////////////////////////////////////////////////
///                       EXTERNAL FUNCTIONS                         ///
////////////////////////////////////////////////////////////////////////

extern int GetDistance( Cube* pC1, Cube* pC2 );
// distance computation for two cubes
extern int GetDistancePlus( Cube* pC1, Cube* pC2 );

extern void ExorVar( Cube* pC, int Var, varvalue Val );

extern void AddToFreeCubes( Cube* pC );
// returns a simplified cube back into the free list

//extern void PrintCube( ostream& DebugStream, Cube* pC );
// debug output for cubes

extern Cube* GetFreeCube();

////////////////////////////////////////////////////////////////////////
/// ExorLink Functions
extern int ExorLinkCubeIteratorStart( Cube** pGroup, Cube* pC1, Cube* pC2, cubedist Dist );
// this function starts the Exor-Link IteratorCubePair, which iterates
// through the cube groups starting from the group with min literals
// returns 1 on success, returns 0 if the cubes have wrong distance

extern int ExorLinkCubeIteratorNext( Cube** pGroup );
// give the next group in the decreasing order of sum of literals
// returns 1 on success, returns 0 if there are no more groups

extern int ExorLinkCubeIteratorPick( Cube** pGroup, int g );
// gives the group #g in the order in which the groups were given
// during iteration
// returns 1 on success, returns 0 if something g is too large

extern void ExorLinkCubeIteratorCleanUp( int fTakeLastGroup );
// removes the cubes from the store back into the list of free cubes
// if fTakeLastGroup is 0, removes all cubes
// if fTakeLastGroup is 1, does not store the last group

////////////////////////////////////////////////////////////////////////
///                    FUNCTIONS OF THIS MODULE                      ///
////////////////////////////////////////////////////////////////////////

// iterative ExorLink
int IterativelyApplyExorLink2( char fDistEnable );   
int IterativelyApplyExorLink3( char fDistEnable );   
int IterativelyApplyExorLink4( char fDistEnable );   

// function which performs distance computation and simplifes on the fly
// it is also called from the Pseudo-Kronecker module when cubes are added
int CheckForCloseCubes( Cube* p, int fAddCube );
int CheckAndInsert( Cube* p );

// this function changes the cube back after it was DIST-1 transformed
void UndoRecentChanges();

////////////////////////////////////////////////////////////////////////
// iterating through adjucency pair queques (with authomatic garbage collection)

// start an iterator through cubes of dist CubeDist,
// the resulting pointers are written into ppC1 and ppC2
int IteratorCubePairStart( cubedist Dist, Cube** ppC1, Cube** ppC2 );
// gives the next VALID cube pair (the previous one is automatically dequequed)
int IteratorCubePairNext();

////////////////////////////////////////////////////////////////////////
// the cube storage

// cube storage allocation/delocation
int AllocateCubeSets( int nVarsIn, int nVarsOut );
void DelocateCubeSets();

// insert/extract a cube into/from the storage
void CubeInsert( Cube* p );
Cube* CubeExtract( Cube* p );

////////////////////////////////////////////////////////////////////////
// Cube Set Iterator
Cube* IterCubeSetStart();
// starts an iterator that traverses all the cubes in the ring
Cube* IterCubeSetNext();
// returns the next cube in the ring
// to use it again after it has returned NULL, call IterCubeSetStart() first

////////////////////////////////////////////////////////////////////////
// cube adjacency queques

// adjacency queque allocation/delocation procedures
int AllocateQueques( int nPlaces );
void DelocateQueques();

// conditional adding cube pairs to queques
// reset temporarily stored new range of cube pairs
static void NewRangeReset();
// add temporarily stored new range of cube pairs to the queque
static void NewRangeAdd();
// insert one cube pair into the new range
static void NewRangeInsertCubePair( cubedist Dist, Cube* p1, Cube* p2 );

static void MarkSet();
static void MarkRewind();

void PrintQuequeStats();
int GetQuequeStats( cubedist Dist );

// iterating through the queque (with authomatic garbage collection)
// start an iterator through cubes of dist CubeDist,
// the resulting pointers are written into ppC1 and ppC2
int IteratorCubePairStart( cubedist Dist, Cube** ppC1, Cube** ppC2 );
// gives the next VALID cube pair (the previous one is automatically dequequed)
int IteratorCubePairNext();

////////////////////////////////////////////////////////////////////////
///                      EXPORTED VARIABLES                          ///
////////////////////////////////////////////////////////////////////////`

// the number of allocated places
int s_nPosAlloc;
// the maximum number of occupied places
int s_nPosMax[3];

////////////////////////////////////////////////////////////////////////
///                      Minimization Strategy                       ///
////////////////////////////////////////////////////////////////////////

// 1) check that ExorLink for this cube pair can be performed
//    (it may happen that the group is outdated due to recent reshaping)
// 2) find out what is the improvement achieved by each cube group
// 3) depending on the distance, do the following:
//    a) if ( Dist == 2 ) 
//           try both cube groups, 
//           if one of them leads to improvement, take the cube group right away
//           if none of them leads to improment
//              - take the last one (because it reshapes)
//              - take the last one only in case it does not increase literals
//    b) if ( Dist == 3 )
//           try groups one by one
//           if one of them leads to improvement, take the group right away
//           if none of them leads to improvement
//              - take the group which reshapes
//              - take the reshaping group only in case it does not increase literals
//           if none of them leads to reshaping, do not take any of them
//    c) if ( Dist == 4 )
//           try groups one by one
//           if one of the leads to reshaping, take it right away
//           if none of them leads to reshaping, do not take any of them

////////////////////////////////////////////////////////////////////////
///                       STATIC VARIABLES                           ///
////////////////////////////////////////////////////////////////////////

// Cube set is a list of cubes
static Cube* s_List;

///////////////////////////////////////////////////////////////////////////
// undo information
///////////////////////////////////////////////////////////////////////////
static struct
{
    int fInput;   // 1 if the input was changed
    Cube* p;      // the pointer to the modified cube
    int PrevQa;
    int PrevPa;
    int PrevPz;
    int Var;      // the number of variable that was changed
    int Value;    // the value what was there
    int PrevID;   // the previous ID of the removed cube
} s_ChangeStore;
///////////////////////////////////////////////////////////////////////////

// enable pair accumulation
// from the begginning (while the starting cover is generated)
// only the distance 2 accumulation is enabled
static int s_fDistEnable2 = 1;
static int s_fDistEnable3;
static int s_fDistEnable4;

// temporary storage for cubes generated by the ExorLink iterator
static Cube* s_CubeGroup[5];
// the marks telling whether the given cube is inserted
static int s_fInserted[5];

// enable selection only those Dist2 and Dist3 that do not increase literals
int s_fDecreaseLiterals = 0;

// the counters for display
static int s_cEnquequed;
static int s_cAttempts;
static int s_cReshapes;

// the number of cubes before ExorLink starts
static int s_nCubesBefore;
// the distance code specific for each ExorLink
static cubedist s_Dist;

// other variables
static int s_Gain;
static int s_GainTotal;
static int s_GroupCounter;
static int s_GroupBest;
static Cube *s_pC1, *s_pC2;

////////////////////////////////////////////////////////////////////////
///                  Iterative ExorLink Operation                    ///
////////////////////////////////////////////////////////////////////////

int CheckAndInsert( Cube* p )
{
//  return CheckForCloseCubes( p, 1 );
    CubeInsert( p );
    return 0;
}

int IterativelyApplyExorLink2( char fDistEnable )   
// MEMO: instead of inserting the cubes that have already been checked
// by running CheckForCloseCubes again, try inserting them without checking
// and observe the difference (it will save 50% of checking time)
{
    int z;

    // this var is specific to ExorLink-2
    s_Dist = (cubedist)0;

    // enable pair accumulation
    s_fDistEnable2 = fDistEnable & 1;
    s_fDistEnable3 = fDistEnable & 2;
    s_fDistEnable4 = fDistEnable & 4;

    // initialize counters
    s_cEnquequed = GetQuequeStats( s_Dist );
    s_cAttempts  = 0;
    s_cReshapes  = 0;

    // remember the number of cubes before minimization
    s_nCubesBefore = g_CoverInfo.nCubesInUse;

    for ( z = IteratorCubePairStart( s_Dist, &s_pC1, &s_pC2 ); z; z = IteratorCubePairNext() )
    {
        s_cAttempts++;
        // start ExorLink of the given Distance
        if ( ExorLinkCubeIteratorStart( s_CubeGroup, s_pC1, s_pC2, s_Dist ) )
        {
            // extract old cubes from storage (to prevent EXORing with their derivitives)
            CubeExtract( s_pC1 );
            CubeExtract( s_pC2 );

            // mark the current position in the cube pair queques
            MarkSet();

            // check the first group (generated by ExorLinkCubeIteratorStart())
            if ( CheckForCloseCubes( s_CubeGroup[0], 0 ) ) 
            {  // the first cube leads to improvement - it is already inserted
                CheckForCloseCubes( s_CubeGroup[1], 1 ); // insert the second cube
                goto SUCCESS;
            }
            if ( CheckForCloseCubes( s_CubeGroup[1], 0 ) ) 
            {  // the second cube leads to improvement - it is already inserted
                CheckForCloseCubes( s_CubeGroup[0], 1 ); // insert the first cube
//              CheckAndInsert( s_CubeGroup[0] );
                goto SUCCESS;
            }
            // the first group does not lead to improvement

            // rewind to the previously marked position in the cube pair queques
            MarkRewind();

            // generate the second group
            ExorLinkCubeIteratorNext( s_CubeGroup );

            // check the second group
            if ( CheckForCloseCubes( s_CubeGroup[0], 0 ) ) 
            { // the first cube leads to improvement - it is already inserted
                CheckForCloseCubes( s_CubeGroup[1], 1 ); // insert the second cube
                goto SUCCESS;
            }
            if ( CheckForCloseCubes( s_CubeGroup[1], 0 ) ) 
            { // the second cube leads to improvement - it is already inserted
                CheckForCloseCubes( s_CubeGroup[0], 1 ); // insert the first cube
//              CheckAndInsert( s_CubeGroup[0] );
                goto SUCCESS;
            }
            // the second group does not lead to improvement

            // decide whether to accept the second group, depending on literals
            if ( s_fDecreaseLiterals )
            {
                if ( s_CubeGroup[0]->a + s_CubeGroup[1]->a >= s_pC1->a + s_pC2->a ) 
                // the group increases literals
                { // do not take the last group
                            
                    // rewind to the previously marked position in the cube pair queques
                    MarkRewind();

                    // return the old cubes back to storage
                    CubeInsert( s_pC1 );
                    CubeInsert( s_pC2 );
                    // clean the results of generating ExorLinked cubes
                    ExorLinkCubeIteratorCleanUp( 0 );
                    continue;
                }
            }

            // take the last group
            // there is no need to test these cubes again,
            // because they have been tested and did not yield any improvement
            CubeInsert( s_CubeGroup[0] );
            CubeInsert( s_CubeGroup[1] );
//          CheckForCloseCubes( s_CubeGroup[0], 1 ); 
//          CheckForCloseCubes( s_CubeGroup[1], 1 ); 

SUCCESS:
            // clean the results of generating ExorLinked cubes
            ExorLinkCubeIteratorCleanUp( 1 ); // take the last group
            // free old cubes
            AddToFreeCubes( s_pC1 );
            AddToFreeCubes( s_pC2 );
            // increate the counter
            s_cReshapes++;
        }
    }
    // print the report
    if ( g_CoverInfo.Verbosity == 2 )
    {
    printf( "ExLink-%d", 2 );
    printf( ": Que= %5d", s_cEnquequed );
    printf( "  Att= %4d", s_cAttempts );
    printf( "  Resh= %4d", s_cReshapes );
    printf( "  NoResh= %4d", s_cAttempts - s_cReshapes );
    printf( "  Cubes= %3d", g_CoverInfo.nCubesInUse );
    printf( "  (%d)", s_nCubesBefore - g_CoverInfo.nCubesInUse );
    printf( "\n" );
    }

    // return the number of cubes gained in the process
    return s_nCubesBefore - g_CoverInfo.nCubesInUse;
}

int IterativelyApplyExorLink3( char fDistEnable )   
{
    int z, c, d;
    // this var is specific to ExorLink-3
    s_Dist = (cubedist)1;

    // enable pair accumulation
    s_fDistEnable2 = fDistEnable & 1;
    s_fDistEnable3 = fDistEnable & 2;
    s_fDistEnable4 = fDistEnable & 4;

    // initialize counters
    s_cEnquequed = GetQuequeStats( s_Dist );
    s_cAttempts  = 0;
    s_cReshapes  = 0;

    // remember the number of cubes before minimization
    s_nCubesBefore = g_CoverInfo.nCubesInUse;

    for ( z = IteratorCubePairStart( s_Dist, &s_pC1, &s_pC2 ); z; z = IteratorCubePairNext() )
    {
        s_cAttempts++;
        // start ExorLink of the given Distance
        if ( ExorLinkCubeIteratorStart( s_CubeGroup, s_pC1, s_pC2, s_Dist ) )
        {
            // extract old cubes from storage (to prevent EXORing with their derivitives)
            CubeExtract( s_pC1 );
            CubeExtract( s_pC2 );

            // mark the current position in the cube pair queques
            MarkSet();

            // check cube groups one by one
            s_GroupCounter = 0;
            do  
            {   // check the cubes of this group one by one
                for ( c = 0; c < 3; c++ )
                if ( !s_CubeGroup[c]->fMark ) // this cube has not yet been checked
                {
                    s_Gain = CheckForCloseCubes( s_CubeGroup[c], 0 ); // do not insert the cube, by default
                    if ( s_Gain ) 
                    { // this cube leads to improvement or reshaping - it is already inserted

                        // decide whether to accept this group based on literal count
                        if ( s_fDecreaseLiterals && s_Gain == 1 )
                        if ( s_CubeGroup[0]->a + s_CubeGroup[1]->a + s_CubeGroup[2]->a >
                             s_pC1->a + s_pC2->a + s_ChangeStore.PrevQa ) // the group increases literals
                        { // do not take this group
                            // remember the group
                            s_GroupBest = s_GroupCounter;
                            // undo changes to be able to continue checking other groups
                            UndoRecentChanges();
                            break;
                        }

                        // take this group
                        for ( d = 0; d < 3; d++ ) // insert other cubes
                            if ( d != c )
                            {
                                CheckForCloseCubes( s_CubeGroup[d], 1 ); 
//                              if ( s_CubeGroup[d]->fMark )
//                                  CheckAndInsert( s_CubeGroup[d] );
//                                  CheckOnlyOneCube( s_CubeGroup[d] );
//                                  CheckForCloseCubes( s_CubeGroup[d], 1 ); 
//                              else
//                                  CheckForCloseCubes( s_CubeGroup[d], 1 ); 
                            }

                        // clean the results of generating ExorLinked cubes
                        ExorLinkCubeIteratorCleanUp( 1 ); // take the last group
                        // free old cubes
                        AddToFreeCubes( s_pC1 );
                        AddToFreeCubes( s_pC2 );
                        // update the counter
                        s_cReshapes++;
                        goto END_OF_LOOP;
                    }
                    else // mark the cube as checked
                        s_CubeGroup[c]->fMark = 1;
                }
                // the group is not taken - find the new group
                s_GroupCounter++;
                                        
                // rewind to the previously marked position in the cube pair queques
                MarkRewind();
            } 
            while ( ExorLinkCubeIteratorNext( s_CubeGroup ) );
            // none of the groups leads to improvement

            // return the old cubes back to storage
            CubeInsert( s_pC1 );
            CubeInsert( s_pC2 );
            // clean the results of generating ExorLinked cubes
            ExorLinkCubeIteratorCleanUp( 0 );
        }
END_OF_LOOP: {}
    }

    // print the report
    if ( g_CoverInfo.Verbosity == 2 )
    {
    printf( "ExLink-%d", 3 );
    printf( ": Que= %5d", s_cEnquequed );
    printf( "  Att= %4d", s_cAttempts );
    printf( "  Resh= %4d", s_cReshapes );
    printf( "  NoResh= %4d", s_cAttempts - s_cReshapes );
    printf( "  Cubes= %3d", g_CoverInfo.nCubesInUse );
    printf( "  (%d)", s_nCubesBefore - g_CoverInfo.nCubesInUse );
    printf( "\n" );
    }

    // return the number of cubes gained in the process
    return s_nCubesBefore - g_CoverInfo.nCubesInUse;
}

int IterativelyApplyExorLink4( char fDistEnable )   
{
    int z, c;
    // this var is specific to ExorLink-4
    s_Dist = (cubedist)2;

    // enable pair accumulation
    s_fDistEnable2 = fDistEnable & 1;
    s_fDistEnable3 = fDistEnable & 2;
    s_fDistEnable4 = fDistEnable & 4;

    // initialize counters
    s_cEnquequed = GetQuequeStats( s_Dist );
    s_cAttempts  = 0;
    s_cReshapes  = 0;

    // remember the number of cubes before minimization
    s_nCubesBefore = g_CoverInfo.nCubesInUse;

    for ( z = IteratorCubePairStart( s_Dist, &s_pC1, &s_pC2 ); z; z = IteratorCubePairNext() )
    {
        s_cAttempts++;
        // start ExorLink of the given Distance
        if ( ExorLinkCubeIteratorStart( s_CubeGroup, s_pC1, s_pC2, s_Dist ) )
        {
            // extract old cubes from storage (to prevent EXORing with their derivitives)
            CubeExtract( s_pC1 );
            CubeExtract( s_pC2 );

            // mark the current position in the cube pair queques
            MarkSet();

            // check cube groups one by one
            do  
            {   // check the cubes of this group one by one
                s_GainTotal = 0;
                for ( c = 0; c < 4; c++ )
                if ( !s_CubeGroup[c]->fMark ) // this cube has not yet been checked
                {
                    s_Gain = CheckForCloseCubes( s_CubeGroup[c], 0 ); // do not insert the cube, by default
                    // if the cube leads to gain, it is already inserted
                    s_fInserted[c] = (int)(s_Gain>0);
                    // increment the total gain
                    s_GainTotal += s_Gain;
                }
                else
                    s_fInserted[c] = 0; // the cube has already been checked - it is not inserted

                if ( s_GainTotal == 0 ) // the group does not lead to any gain
                { // mark the cubes
                    for ( c = 0; c < 4; c++ )
                        s_CubeGroup[c]->fMark = 1;
                }
                else if ( s_GainTotal == 1 ) // the group does not lead to substantial gain, too
                { 
                    // undo changes to be able to continue checking groups
                    UndoRecentChanges();
                    // mark those cubes that were not inserted
                    for ( c = 0; c < 4; c++ )
                        s_CubeGroup[c]->fMark = !s_fInserted[c];
                }
                else // if ( s_GainTotal > 1 ) // the group reshapes or improves
                { // accept the group
                    for ( c = 0; c < 4; c++ ) // insert other cubes
                        if ( !s_fInserted[c] )
                            CheckForCloseCubes( s_CubeGroup[c], 1 ); 
//                          CheckAndInsert( s_CubeGroup[c] );
                    // clean the results of generating ExorLinked cubes
                    ExorLinkCubeIteratorCleanUp( 1 ); // take the last group
                    // free old cubes
                    AddToFreeCubes( s_pC1 );
                    AddToFreeCubes( s_pC2 );
                    // update the counter
                    s_cReshapes++;
                    goto END_OF_LOOP;
                }
                                        
                // rewind to the previously marked position in the cube pair queques
                MarkRewind();
            } 
            while ( ExorLinkCubeIteratorNext( s_CubeGroup ) );
            // none of the groups leads to improvement

            // return the old cubes back to storage
            CubeInsert( s_pC1 );
            CubeInsert( s_pC2 );
            // clean the results of generating ExorLinked cubes
            ExorLinkCubeIteratorCleanUp( 0 );
        }
END_OF_LOOP: {}
    }

    // print the report
    if ( g_CoverInfo.Verbosity == 2 )
    {
    printf( "ExLink-%d", 4 );
    printf( ": Que= %5d", s_cEnquequed );
    printf( "  Att= %4d", s_cAttempts );
    printf( "  Resh= %4d", s_cReshapes );
    printf( "  NoResh= %4d", s_cAttempts - s_cReshapes );
    printf( "  Cubes= %3d", g_CoverInfo.nCubesInUse );
    printf( "  (%d)", s_nCubesBefore - g_CoverInfo.nCubesInUse );
    printf( "\n" );
    }

    // return the number of cubes gained in the process
    return s_nCubesBefore - g_CoverInfo.nCubesInUse;
}

// local static variables
Cube* s_q;
int s_Distance;
int s_DiffVarNum;
int s_DiffVarValueP_old;
int s_DiffVarValueP_new;
int s_DiffVarValueQ;

int CheckForCloseCubes( Cube* p, int fAddCube )
// checks the cube storage for a cube that is dist-0 and dist-1 removed 
// from the given one (p) if such a cube is found, extracts it from the data 
// structure, EXORs it with the given cube, adds the resultant cube
// to the data structure and performed the same check for the resultant cube;
// returns the number of cubes gained in the process of reduction;
// if an adjacent cube is not found, inserts the cube only if (fAddCube==1)!!!
{
    // start the new range
    NewRangeReset();

    for ( s_q = s_List; s_q; s_q = s_q->Next )
    {
        s_Distance = GetDistancePlus( p, s_q );
        if ( s_Distance > 4 )
        {
        }
        else if ( s_Distance == 4 )
        {
            if ( s_fDistEnable4 ) 
                NewRangeInsertCubePair( DIST4, p, s_q );
        }
        else if ( s_Distance == 3 )
        {
            if ( s_fDistEnable3 ) 
                NewRangeInsertCubePair( DIST3, p, s_q );
        }
        else if ( s_Distance == 2 )
        { 
            if ( s_fDistEnable2 ) 
                NewRangeInsertCubePair( DIST2, p, s_q );
        }
        else if ( s_Distance == 1 )
        {   // extract the cube from the data structure

            //////////////////////////////////////////////////////////
            // store the changes
            s_ChangeStore.fInput = (s_DiffVarNum != -1);
            s_ChangeStore.p      = p;
            s_ChangeStore.PrevQa = s_q->a;
            s_ChangeStore.PrevPa = p->a;
            s_ChangeStore.PrevPz = p->z;
            s_ChangeStore.Var    = s_DiffVarNum;
            s_ChangeStore.Value  = s_DiffVarValueQ;
            s_ChangeStore.PrevID = s_q->ID;
            //////////////////////////////////////////////////////////

            CubeExtract( s_q );
            // perform the EXOR of the two cubes and write the result into p

            // it is important that the resultant cube is written into p!!!

            if ( s_DiffVarNum == -1 )
            {
                int i;
                // exor the output part
                p->z = 0;
                for ( i = 0; i < g_CoverInfo.nWordsOut; i++ )
                {
                    p->pCubeDataOut[i] ^= s_q->pCubeDataOut[i];
                    p->z += BIT_COUNT(p->pCubeDataOut[i]);
                }
            }
            else
            {
                // the cube has already been updated by GetDistancePlus()

                // modify the parameters of the number of literals in the new cube
//              p->a += s_UpdateLiterals[ s_DiffVarValueP ][ s_DiffVarValueQ ];
                if ( s_DiffVarValueP_old == VAR_NEG || s_DiffVarValueP_old == VAR_POS )
                    p->a--;
                if ( s_DiffVarValueP_new == VAR_NEG || s_DiffVarValueP_new == VAR_POS )
                    p->a++;
            }

            // move q to the free cube list
            AddToFreeCubes( s_q );

            // make sure that nobody with use the pairs created so far
//          NewRangeReset();
            // call the function again for the new cube
            return 1 + CheckForCloseCubes( p, 1 );
        }
        else // if ( Distance == 0 )
        {   // extract the second cube from the data structure and add them both to the free list
            AddToFreeCubes( p );
            AddToFreeCubes( CubeExtract( s_q ) );

            // make sure that nobody with use the pairs created so far
            NewRangeReset();
            return 2;
        }
    }
    
    // add the cube to the data structure if needed
    if ( fAddCube )
        CubeInsert( p );

    // add temporarily stored new range of cube pairs to the queque
    NewRangeAdd();

    return 0;
}

void UndoRecentChanges()
{
    Cube * p, * q;
    // get back cube q that was deleted
    q = GetFreeCube();
    // restore the ID
    q->ID = s_ChangeStore.PrevID;
    // insert the cube into storage again
    CubeInsert( q );

    // extract cube p
    p = CubeExtract( s_ChangeStore.p );

    // modify it back
    if ( s_ChangeStore.fInput ) // the input has changed
    {
        ExorVar( p, s_ChangeStore.Var, (varvalue)s_ChangeStore.Value );
        p->a = s_ChangeStore.PrevPa;
        // p->z did not change
    }
    else // if ( s_ChangeStore.fInput ) // the output has changed
    {
        int i;
        for ( i = 0; i < g_CoverInfo.nWordsOut; i++ )
            p->pCubeDataOut[i] ^= q->pCubeDataOut[i];
        p->z = s_ChangeStore.PrevPz;
        // p->a did not change
    }
}

///////////////////////////////////////////////////////////////////
///               CUBE SET MANIPULATION PROCEDURES              ///
///////////////////////////////////////////////////////////////////

// Cube set is a list of cubes
//static Cube* s_List;

///////////////////////////////////////////////////////////////////
///                 Memory Allocation/Delocation                ///
///////////////////////////////////////////////////////////////////

int AllocateCubeSets( int nVarsIn, int nVarsOut )
{
    s_List = NULL;

    // clean other data
    s_fDistEnable2 = 1;
    s_fDistEnable3 = 0;
    s_fDistEnable4 = 0;
    memset( s_CubeGroup, 0, sizeof(void *) * 5 );
    memset( s_fInserted, 0, sizeof(int) * 5 );
    s_fDecreaseLiterals = 0;
    s_cEnquequed = 0;
    s_cAttempts = 0;
    s_cReshapes = 0;
    s_nCubesBefore = 0;
    s_Gain = 0;
    s_GainTotal = 0;
    s_GroupCounter = 0;
    s_GroupBest = 0;
    s_pC1 = s_pC2 = NULL;

    return 4;
}

void DelocateCubeSets()
{
}

///////////////////////////////////////////////////////////////////
///                     Insertion Operators                     ///
///////////////////////////////////////////////////////////////////

void CubeInsert( Cube* p )
// inserts the cube into storage (puts it at the beginning of the list)
{
    assert( p->Prev == NULL && p->Next == NULL );
    assert( p->ID );

    if ( s_List == NULL )
        s_List = p;
    else
    {
        p->Next = s_List;

        s_List->Prev = p;
        s_List = p;
    }

    g_CoverInfo.nCubesInUse++;
}

Cube* CubeExtract( Cube* p )
// extracts the cube from storage
{
//  assert( p->Prev && p->Next ); // can be done only with rings
    assert( p->ID );

//  if ( s_List == p )
//      s_List = p->Next;
//  if ( p->Prev )
//      p->Prev->Next = p->Next;

    if ( s_List == p )
        s_List = p->Next;
    else
        p->Prev->Next = p->Next;

    if ( p->Next )
        p->Next->Prev = p->Prev;

    p->Prev = NULL;
    p->Next = NULL;

    g_CoverInfo.nCubesInUse--;
    return p;
}

///////////////////////////////////////////////////////////////////
///                       CUBE ITERATOR                         ///
///////////////////////////////////////////////////////////////////

// the iterator starts from the Head and stops when it sees NULL
Cube* s_pCubeLast;

///////////////////////////////////////////////////////////////////
///                     Cube Set Iterator                       ///
///////////////////////////////////////////////////////////////////

Cube* IterCubeSetStart()
// starts an iterator that traverses all the cubes in the ring
{
    assert( s_pCubeLast == NULL );

    // check whether the List has cubes
    if ( s_List == NULL )
        return NULL;

    return ( s_pCubeLast = s_List );
}

Cube* IterCubeSetNext()
// returns the next cube in the cube set
// to use it again after it has returned NULL, first call IterCubeSetStart()
{
    assert( s_pCubeLast );
    return ( s_pCubeLast = s_pCubeLast->Next );
}

///////////////////////////////////////////////////////////////////
////                      ADJACENCY QUEQUES                  //////
///////////////////////////////////////////////////////////////////

typedef struct
{
    Cube** pC1;      // the pointer to the first cube
    Cube** pC2;      // the pointer to the second cube
    byte*  ID1;      // the ID of the first cube
    byte*  ID2;      // the ID of the second cube
    int  PosOut;     // extract position
    int  PosIn;      // insert position
    int  PosCur;     // temporary insert position
    int  PosMark;    // the marked position
    int  fEmpty;     // this flag is 1 if there is nothing in the queque
} que;

static que s_Que[3];  // Dist-2, Dist-3, Dist-4 queques

// the number of allocated places
//int s_nPosAlloc;
// the maximum number of occupied places
//int s_nPosMax[3];

//////////////////////////////////////////////////////////////////////
//            Conditional Adding Cube Pairs To Queques              //
//////////////////////////////////////////////////////////////////////

int GetPosDiff( int PosBeg, int PosEnd )
{
    return (PosEnd - PosBeg + s_nPosAlloc) % s_nPosAlloc;
}

void MarkSet()
// sets marks in the cube pair queques
{
    s_Que[0].PosMark = s_Que[0].PosIn;
    s_Que[1].PosMark = s_Que[1].PosIn;
    s_Que[2].PosMark = s_Que[2].PosIn;
}

void MarkRewind()
// rewinds the queques to the previously set marks
{
    s_Que[0].PosIn = s_Que[0].PosMark;
    s_Que[1].PosIn = s_Que[1].PosMark;
    s_Que[2].PosIn = s_Que[2].PosMark;
}

void NewRangeReset()
// resets temporarily stored new range of cube pairs
{
    s_Que[0].PosCur = s_Que[0].PosIn;
    s_Que[1].PosCur = s_Que[1].PosIn;
    s_Que[2].PosCur = s_Que[2].PosIn;
}

void NewRangeAdd()
// adds temporarily stored new range of cube pairs to the queque
{
    s_Que[0].PosIn = s_Que[0].PosCur;
    s_Que[1].PosIn = s_Que[1].PosCur;
    s_Que[2].PosIn = s_Que[2].PosCur;
}

void NewRangeInsertCubePair( cubedist Dist, Cube* p1, Cube* p2 )
// insert one cube pair into the new range
{
    que* p = &s_Que[Dist];
    int Pos = p->PosCur;

    if ( p->fEmpty || Pos != p->PosOut )
    {
        p->pC1[Pos] = p1;
        p->pC2[Pos] = p2;
        p->ID1[Pos] = p1->ID;
        p->ID2[Pos] = p2->ID;

        p->PosCur = (p->PosCur+1)%s_nPosAlloc;
    }
    else
        assert(0);
//      cout << endl << "DIST-" << (int)(Dist+2) << ": Have run out of queque space!" << endl;
}

void PrintQuequeStats()
{
/*
    cout << endl << "Queque statistics: ";
    cout << " Alloc = " << s_nPosAlloc;
    cout << "   DIST2 = " << GetPosDiff( s_Que[0].PosOut, s_Que[0].PosIn );
    cout << "   DIST3 = " << GetPosDiff( s_Que[1].PosOut, s_Que[1].PosIn );
    cout << "   DIST4 = " << GetPosDiff( s_Que[2].PosOut, s_Que[2].PosIn );
    cout << endl;
    cout << endl;
*/
}

int GetQuequeStats( cubedist Dist )
{
    return GetPosDiff( s_Que[Dist].PosOut, s_Que[Dist].PosIn );
}

//////////////////////////////////////////////////////////////////////
//                       Queque Iterators                           //
//////////////////////////////////////////////////////////////////////

// iterating through the queque (with authomatic garbage collection)
// only one iterator can be active at a time
static struct
{
    int fStarted;    // status of the iterator (1 if working)
    cubedist Dist;   // the currently iterated queque
    Cube** ppC1;     // the position where the first cube pointer goes
    Cube** ppC2;     // the position where the second cube pointer goes
    int PosStop;     // the stop position (to prevent the iterator from
                     // choking when new pairs are added during iteration)
    int CutValue;    // the number of literals below which the cubes are not used
} s_Iter;

static que* pQ;
static Cube *p1, *p2;

int IteratorCubePairStart( cubedist CubeDist, Cube** ppC1, Cube** ppC2 )
// start an iterator through cubes of dist CubeDist,
// the resulting pointers are written into ppC1 and ppC2
// returns 1 if the first cube pair is found
{
    int fEntryFound;

    assert( s_Iter.fStarted == 0 );
    assert( CubeDist >= 0 && CubeDist <= 2 );

    s_Iter.fStarted = 1;
    s_Iter.Dist = CubeDist;
    s_Iter.ppC1 = ppC1;
    s_Iter.ppC2 = ppC2;

    s_Iter.PosStop = s_Que[ CubeDist ].PosIn;

    // determine the cut value
//  s_Iter.CutValue = s_nLiteralsInUse/s_nCubesInUse/2;
    s_Iter.CutValue = -1;

    fEntryFound = 0;
    // go through the entries while there is something in the queque
    for ( pQ = &s_Que[ CubeDist ]; pQ->PosOut != s_Iter.PosStop; pQ->PosOut = (pQ->PosOut+1)%s_nPosAlloc )
    {
        p1 = pQ->pC1[ pQ->PosOut ];
        p2 = pQ->pC2[ pQ->PosOut ];

        // check whether the entry is valid
        if ( p1->ID == pQ->ID1[ pQ->PosOut ] && 
             p2->ID == pQ->ID2[ pQ->PosOut ] ) //&&
             //p1->x + p1->y + p2->x + p2->y > s_Iter.CutValue )
        {
             fEntryFound = 1;
             break;
        }
    }

    if ( fEntryFound )
    { // write the result into the pick-up place
        *ppC1 = pQ->pC1[ pQ->PosOut ];
        *ppC2 = pQ->pC2[ pQ->PosOut ];

        pQ->PosOut = (pQ->PosOut+1)%s_nPosAlloc;
    }
    else
        s_Iter.fStarted = 0;
    return fEntryFound;
}

int IteratorCubePairNext()
// gives the next VALID cube pair (the previous one is automatically dequequed)
{
    int fEntryFound = 0;
    assert( s_Iter.fStarted );

    // go through the entries while there is something in the queque
    for ( pQ = &s_Que[ s_Iter.Dist ]; pQ->PosOut != s_Iter.PosStop; pQ->PosOut = (pQ->PosOut+1)%s_nPosAlloc )
    {
        p1 = pQ->pC1[ pQ->PosOut ];
        p2 = pQ->pC2[ pQ->PosOut ];

        // check whether the entry is valid
        if ( p1->ID == pQ->ID1[ pQ->PosOut ] && 
             p2->ID == pQ->ID2[ pQ->PosOut ] ) //&&
             //p1->x + p1->y + p2->x + p2->y > s_Iter.CutValue )
        {
             fEntryFound = 1;
             break;
        }
    }

    if ( fEntryFound )
    { // write the result into the pick-up place
        *(s_Iter.ppC1) = pQ->pC1[ pQ->PosOut ];
        *(s_Iter.ppC2) = pQ->pC2[ pQ->PosOut ];

        pQ->PosOut = (pQ->PosOut+1)%s_nPosAlloc;
    }
    else // iteration has finished
        s_Iter.fStarted = 0;

    return fEntryFound;
}

//////////////////////////////////////////////////////////////////////
//                     Allocation/Delocation                        //
//////////////////////////////////////////////////////////////////////

int AllocateQueques( int nPlaces )
// nPlaces should be approximately nCubes*nCubes/10
// allocates memory for cube pair queques
{
    int i;
    s_nPosAlloc  = nPlaces;

    for ( i = 0; i < 3; i++ )
    {
        // clean data
        memset( &s_Que[i], 0, sizeof(que) );

        s_Que[i].pC1 = (Cube**) ABC_ALLOC( Cube*, nPlaces );
        s_Que[i].pC2 = (Cube**) ABC_ALLOC( Cube*, nPlaces );
        s_Que[i].ID1 = (byte*) ABC_ALLOC( byte, nPlaces );
        s_Que[i].ID2 = (byte*) ABC_ALLOC( byte, nPlaces );

        if ( s_Que[i].pC1==NULL || s_Que[i].pC2==NULL || s_Que[i].ID1==NULL || s_Que[i].ID2==NULL )
            return 0;

        s_nPosMax[i] = 0;
        s_Que[i].fEmpty = 1;
    }

    return nPlaces * (sizeof(Cube*) + sizeof(Cube*) + 2*sizeof(byte) );
}

void DelocateQueques()
{
    int i;
    for ( i = 0; i < 3; i++ )
    {
        ABC_FREE( s_Que[i].pC1 );
        ABC_FREE( s_Que[i].pC2 );
        ABC_FREE( s_Que[i].ID1 );
        ABC_FREE( s_Que[i].ID2 );
    }
}

///////////////////////////////////////////////////////////////////
////////////              End of File             /////////////////
///////////////////////////////////////////////////////////////////


ABC_NAMESPACE_IMPL_END
