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

  FileName    [covMinSop.c]

  SystemName  [ABC: Logic synthesis and verification system.]

  PackageName [Mapping into network of SOPs/ESOPs.]

  Synopsis    [SOP manipulation.]

  Author      [Alan Mishchenko]
  
  Affiliation [UC Berkeley]

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

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

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

#include "covInt.h"

ABC_NAMESPACE_IMPL_START


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

static void Min_SopRewrite( Min_Man_t * p );

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

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Min_SopMinimize( Min_Man_t * p )
{
    int nCubesInit, nCubesOld, nIter;
    if ( p->nCubes < 3 )
        return;
    nIter = 0;
    nCubesInit = p->nCubes;
    do {
        nCubesOld = p->nCubes;
        Min_SopRewrite( p );
        nIter++;
//    printf( "%d:%d->%d ", nIter, nCubesInit, p->nCubes );
    }
    while ( 100.0*(nCubesOld - p->nCubes)/nCubesOld > 3.0 );
//    printf( "\n" );

}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Min_SopRewrite( Min_Man_t * p )
{
    Min_Cube_t * pCube, ** ppPrev;
    Min_Cube_t * pThis, ** ppPrevT;
    Min_Cube_t * pTemp;
    int v00, v01, v10, v11, Var0, Var1, Index, fCont0, fCont1, nCubesOld;
    int nPairs = 0;
/*    
    {
        Min_Cube_t * pCover;
        pCover = Min_CoverCollect( p, p->nVars );
printf( "\n\n" );
Min_CoverWrite( stdout, pCover );
        Min_CoverExpand( p, pCover );
    }
*/

    // insert the bubble before the first cube
    p->pBubble->pNext = p->ppStore[0];
    p->ppStore[0] = p->pBubble;
    p->pBubble->nLits = 0;

    // go through the cubes
    while ( 1 )
    {
        // get the index of the bubble
        Index = p->pBubble->nLits;

        // find the bubble
        Min_CoverForEachCubePrev( p->ppStore[Index], pCube, ppPrev )
            if ( pCube == p->pBubble )
                break;
        assert( pCube == p->pBubble );

        // remove the bubble, get the next cube after the bubble
        *ppPrev = p->pBubble->pNext;
        pCube = p->pBubble->pNext;
        if ( pCube == NULL )
            for ( Index++; Index <= p->nVars; Index++ )
                if ( p->ppStore[Index] )
                {
                    ppPrev = &(p->ppStore[Index]);
                    pCube = p->ppStore[Index];
                    break;
                }
        // stop if there is no more cubes
        if ( pCube == NULL )
            break;

        // find the first dist2 cube
        Min_CoverForEachCubePrev( pCube->pNext, pThis, ppPrevT )
            if ( Min_CubesDistTwo( pCube, pThis, &Var0, &Var1 ) )
                break;
        if ( pThis == NULL && Index < p->nVars )
        Min_CoverForEachCubePrev( p->ppStore[Index+1], pThis, ppPrevT )
            if ( Min_CubesDistTwo( pCube, pThis, &Var0, &Var1 ) )
                break;
        // continue if there is no dist2 cube
        if ( pThis == NULL )
        {
            // insert the bubble after the cube
            p->pBubble->pNext = pCube->pNext;
            pCube->pNext = p->pBubble;
            p->pBubble->nLits = pCube->nLits;
            continue;
        }
        nPairs++;
/*
printf( "\n" );
Min_CubeWrite( stdout, pCube );
Min_CubeWrite( stdout, pThis );
*/
        // remove the cubes, insert the bubble instead of pCube
        *ppPrevT = pThis->pNext;
        *ppPrev = p->pBubble;
        p->pBubble->pNext = pCube->pNext;
        p->pBubble->nLits = pCube->nLits;
        p->nCubes -= 2;

        assert( pCube != p->pBubble && pThis != p->pBubble );


        // save the dist2 parameters
        v00 = Min_CubeGetVar( pCube, Var0 );
        v01 = Min_CubeGetVar( pCube, Var1 );
        v10 = Min_CubeGetVar( pThis, Var0 );
        v11 = Min_CubeGetVar( pThis, Var1 );
        assert( v00 != v10 && v01 != v11 );
        assert( v00 != 3   || v01 != 3 );
        assert( v10 != 3   || v11 != 3 );

//printf( "\n" );
//Min_CubeWrite( stdout, pCube );
//Min_CubeWrite( stdout, pThis );

//printf( "\n" );
//Min_CubeWrite( stdout, pCube );
//Min_CubeWrite( stdout, pThis );

        // consider the case when both cubes have non-empty literals
        if ( v00 != 3 && v01 != 3 && v10 != 3 && v11 != 3 )
        {
            assert( v00 == (v10 ^ 3) );
            assert( v01 == (v11 ^ 3) );
            // create the temporary cube equal to the first corner
            Min_CubeXorVar( pCube, Var0, 3 );
            // check if this cube is contained
            fCont0 = Min_CoverContainsCube( p, pCube );
            // create the temporary cube equal to the first corner
            Min_CubeXorVar( pCube, Var0, 3 );
            Min_CubeXorVar( pCube, Var1, 3 );
//printf( "\n" );
//Min_CubeWrite( stdout, pCube );
//Min_CubeWrite( stdout, pThis );
            // check if this cube is contained
            fCont1 = Min_CoverContainsCube( p, pCube );
            // undo the change
            Min_CubeXorVar( pCube, Var1, 3 );

            // check if the cubes can be overwritten
            if ( fCont0 && fCont1 )
            {
                // one of the cubes can be recycled, the other expanded and added
                Min_CubeRecycle( p, pThis );
                // remove the literals
                Min_CubeXorVar( pCube, Var0, v00 ^ 3 );
                Min_CubeXorVar( pCube, Var1, v01 ^ 3 );
                pCube->nLits -= 2;
                Min_SopAddCube( p, pCube );
            }
            else if ( fCont0 )
            {
                // expand both cubes and add them
                Min_CubeXorVar( pCube, Var0, v00 ^ 3 );
                pCube->nLits--;
                Min_SopAddCube( p, pCube );
                Min_CubeXorVar( pThis, Var1, v11 ^ 3 );
                pThis->nLits--;
                Min_SopAddCube( p, pThis );
            }
            else if ( fCont1 )
            {
                // expand both cubes and add them
                Min_CubeXorVar( pCube, Var1, v01 ^ 3 );
                pCube->nLits--;
                Min_SopAddCube( p, pCube );
                Min_CubeXorVar( pThis, Var0, v10 ^ 3 );
                pThis->nLits--;
                Min_SopAddCube( p, pThis );
            }
            else
            {
                Min_SopAddCube( p, pCube );
                Min_SopAddCube( p, pThis );
            }
            // otherwise, no change is possible
            continue;
        }

        // if one of them does not have DC lit, move it
        if ( v00 != 3 && v01 != 3 )
        {
            assert( v10 == 3 || v11 == 3 );
            pTemp = pCube; pCube = pThis; pThis = pTemp;
            Index = v00; v00 = v10; v10 = Index;
            Index = v01; v01 = v11; v11 = Index;
        }

        // make sure the first cube has first var DC
        if ( v00 != 3 )
        {
            assert( v01 == 3 );
            Index = Var0; Var0 = Var1; Var1 = Index;
            Index = v00; v00 = v01; v01 = Index;
            Index = v10; v10 = v11; v11 = Index;
        }

        // consider both cases: both have DC lit
        if ( v00 == 3 && v11 == 3 )
        {
            assert( v01 != 3 && v10 != 3 );
            // try the remaining minterm
            // create the temporary cube equal to the first corner
            Min_CubeXorVar( pCube, Var0, v10 );
            Min_CubeXorVar( pCube, Var1, 3   );
            pCube->nLits++;
            // check if this cube is contained
            fCont0 = Min_CoverContainsCube( p, pCube );
            // undo the cube transformations
            Min_CubeXorVar( pCube, Var0, v10 );
            Min_CubeXorVar( pCube, Var1, 3   );
            pCube->nLits--;
            // check the case when both are covered
            if ( fCont0 )
            {
                // one of the cubes can be recycled, the other expanded and added
                Min_CubeRecycle( p, pThis );
                // remove the literals
                Min_CubeXorVar( pCube, Var1, v01 ^ 3 );
                pCube->nLits--;
                Min_SopAddCube( p, pCube );
            }
            else
            {
                // try two reduced cubes
                Min_CubeXorVar( pCube, Var0, v10 );
                pCube->nLits++;
                // remember the cubes
                nCubesOld = p->nCubes;
                Min_SopAddCube( p, pCube );
                // check if the cube is absorbed
                if ( p->nCubes < nCubesOld + 1 )
                { // absorbed - add the second cube
                    Min_SopAddCube( p, pThis );
                }
                else
                { // remove this cube, and try another one
                    assert( pCube == p->ppStore[pCube->nLits] );
                    p->ppStore[pCube->nLits] = pCube->pNext;
                    p->nCubes--;

                    // return the cube to the previous state
                    Min_CubeXorVar( pCube, Var0, v10 );
                    pCube->nLits--;

                    // generate another reduced cube
                    Min_CubeXorVar( pThis, Var1, v01 );
                    pThis->nLits++;

                    // add both cubes
                    Min_SopAddCube( p, pCube );
                    Min_SopAddCube( p, pThis );
                }
            }
        }
        else // the first cube has DC lit
        {
            assert( v01 != 3 && v10 != 3 && v11 != 3 );
            // try the remaining minterm
            // create the temporary cube equal to the minterm
            Min_CubeXorVar( pThis, Var0, 3 );
            // check if this cube is contained
            fCont0 = Min_CoverContainsCube( p, pThis );
            // undo the cube transformations
            Min_CubeXorVar( pThis, Var0, 3 );
            // check the case when both are covered
            if ( fCont0 )
            {
                // one of the cubes can be recycled, the other expanded and added
                Min_CubeRecycle( p, pThis );
                // remove the literals
                Min_CubeXorVar( pCube, Var1, v01 ^ 3 );
                pCube->nLits--;
                Min_SopAddCube( p, pCube );
            }
            else
            {
                // try reshaping the cubes
                // reduce the first cube
                Min_CubeXorVar( pCube, Var0, v10 );
                pCube->nLits++;
                // expand the second cube
                Min_CubeXorVar( pThis, Var1, v11 ^ 3 );
                pThis->nLits--;
                // add both cubes
                Min_SopAddCube( p, pCube );
                Min_SopAddCube( p, pThis );
            }
        }
    }
//    printf( "Pairs = %d  ", nPairs );
}

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

  Synopsis    [Adds cube to the SOP cover stored in the manager.]

  Description [Returns 0 if the cube is added or removed. Returns 1
  if the cube is glued with some other cube and has to be added again.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Min_SopAddCubeInt( Min_Man_t * p, Min_Cube_t * pCube )
{
    Min_Cube_t * pThis, * pThis2, ** ppPrev;
    int i;
    // try to find the identical cube
    Min_CoverForEachCube( p->ppStore[pCube->nLits], pThis )
    {
        if ( Min_CubesAreEqual( pCube, pThis ) )
        {
            Min_CubeRecycle( p, pCube );
            return 0;
        }
    }
    // try to find a containing cube
    for ( i = 0; i < (int)pCube->nLits; i++ )
    Min_CoverForEachCube( p->ppStore[i], pThis )
    {
        if ( pThis != p->pBubble && Min_CubeIsContained( pThis, pCube ) )
        {
            Min_CubeRecycle( p, pCube );
            return 0;
        }
    }
    // try to find distance one in the same bin
    Min_CoverForEachCubePrev( p->ppStore[pCube->nLits], pThis, ppPrev )
    {
        if ( Min_CubesDistOne( pCube, pThis, NULL ) )
        {
            *ppPrev = pThis->pNext;
            Min_CubesTransformOr( pCube, pThis );
            pCube->nLits--;
            Min_CubeRecycle( p, pThis );
            p->nCubes--;
            return 1;
        }
    }

    // clean the other cubes using this one
    for ( i = pCube->nLits + 1; i <= (int)pCube->nVars; i++ )
    {
        ppPrev = &p->ppStore[i];
        Min_CoverForEachCubeSafe( p->ppStore[i], pThis, pThis2 )
        {
            if ( pThis != p->pBubble && Min_CubeIsContained( pCube, pThis ) )
            {
                *ppPrev = pThis->pNext;
                Min_CubeRecycle( p, pThis );
                p->nCubes--;
            }
            else
                ppPrev = &pThis->pNext;
        }
    }

    // add the cube
    pCube->pNext = p->ppStore[pCube->nLits];
    p->ppStore[pCube->nLits] = pCube;
    p->nCubes++;
    return 0;
}

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

  Synopsis    [Adds the cube to storage.]

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Min_SopAddCube( Min_Man_t * p, Min_Cube_t * pCube )
{
    assert( Min_CubeCheck( pCube ) );
    assert( pCube != p->pBubble );
    assert( (int)pCube->nLits == Min_CubeCountLits(pCube) );
    while ( Min_SopAddCubeInt( p, pCube ) );
}





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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Min_SopContain( Min_Man_t * p )
{
    Min_Cube_t * pCube, * pCube2, ** ppPrev;
    int i, k;
    for ( i = 0; i <= p->nVars; i++ )
    {
        Min_CoverForEachCube( p->ppStore[i], pCube )
        Min_CoverForEachCubePrev( pCube->pNext, pCube2, ppPrev )
        {
            if ( !Min_CubesAreEqual( pCube, pCube2 ) )
                continue;
            *ppPrev = pCube2->pNext;
            Min_CubeRecycle( p, pCube2 );
            p->nCubes--;
        }
        for ( k = i + 1; k <= p->nVars; k++ )
        Min_CoverForEachCubePrev( p->ppStore[k], pCube2, ppPrev )
        {
            if ( !Min_CubeIsContained( pCube, pCube2 ) )
                continue;
            *ppPrev = pCube2->pNext;
            Min_CubeRecycle( p, pCube2 );
            p->nCubes--;
        }
    }
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Min_SopDist1Merge( Min_Man_t * p )
{
    Min_Cube_t * pCube, * pCube2, * pCubeNew;
    int i;
    for ( i = p->nVars; i >= 0; i-- )
    {
        Min_CoverForEachCube( p->ppStore[i], pCube )
        Min_CoverForEachCube( pCube->pNext, pCube2 )
        {
            assert( pCube->nLits == pCube2->nLits );
            if ( !Min_CubesDistOne( pCube, pCube2, NULL ) )
                continue;
            pCubeNew = Min_CubesXor( p, pCube, pCube2 );
            assert( pCubeNew->nLits == pCube->nLits - 1 );
            pCubeNew->pNext = p->ppStore[pCubeNew->nLits];
            p->ppStore[pCubeNew->nLits] = pCubeNew;
            p->nCubes++;
        }
    }
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
Min_Cube_t * Min_SopComplement( Min_Man_t * p, Min_Cube_t * pSharp )
{
     Vec_Int_t * vVars;
     Min_Cube_t * pCover, * pCube, * pNext, * pReady, * pThis, ** ppPrev;
     int Num, Value, i;

     // get the variables
     vVars = Vec_IntAlloc( 100 );
    // create the tautology cube
     pCover = Min_CubeAlloc( p );
     // sharp it with all cubes
     Min_CoverForEachCube( pSharp, pCube )
     Min_CoverForEachCubePrev( pCover, pThis, ppPrev )
     {
        if ( Min_CubesDisjoint( pThis, pCube ) )
            continue;
        // remember the next pointer
        pNext = pThis->pNext;
        // get the variables, in which pThis is '-' while pCube is fixed
        Min_CoverGetDisjVars( pThis, pCube, vVars );
        // generate the disjoint cubes
        pReady = pThis;
        Vec_IntForEachEntryReverse( vVars, Num, i )
        {
            // correct the literal
            Min_CubeXorVar( pReady, vVars->pArray[i], 3 );
            if ( i == 0 )
                break;
            // create the new cube and clean this value
            Value = Min_CubeGetVar( pReady, vVars->pArray[i] );
            pReady = Min_CubeDup( p, pReady );
            Min_CubeXorVar( pReady, vVars->pArray[i], 3 ^ Value );
            // add to the cover
            *ppPrev = pReady;
            ppPrev = &pReady->pNext;
        }
        pThis = pReady;
        pThis->pNext = pNext;
     }
     Vec_IntFree( vVars );

     // perform dist-1 merge and contain
     Min_CoverExpandRemoveEqual( p, pCover );
     Min_SopDist1Merge( p );
     Min_SopContain( p );
     return Min_CoverCollect( p, p->nVars );
}

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

  Synopsis    []

  Description []
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
int Min_SopCheck( Min_Man_t * p )
{
    Min_Cube_t * pCube, * pThis;
    int i;

    pCube = Min_CubeAlloc( p );
    Min_CubeXorBit( pCube, 2*0+1 );
    Min_CubeXorBit( pCube, 2*1+1 );
    Min_CubeXorBit( pCube, 2*2+0 );
    Min_CubeXorBit( pCube, 2*3+0 );
    Min_CubeXorBit( pCube, 2*4+0 );
    Min_CubeXorBit( pCube, 2*5+1 );
    Min_CubeXorBit( pCube, 2*6+1 );
    pCube->nLits = 7;

//    Min_CubeWrite( stdout, pCube );

    // check that the cubes contain it
    for ( i = 0; i <= p->nVars; i++ )
        Min_CoverForEachCube( p->ppStore[i], pThis )
            if ( pThis != p->pBubble && Min_CubeIsContained( pThis, pCube ) )
            {
                Min_CubeRecycle( p, pCube );
                return 1;
            }
    Min_CubeRecycle( p, pCube );
    return 0;
}

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


ABC_NAMESPACE_IMPL_END