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

  FileName    [extraBddAuto.c]

  PackageName [extra]

  Synopsis    [Computation of autosymmetries.]

  Author      [Alan Mishchenko]

  Affiliation [UC Berkeley]

  Date        [Ver. 2.0. Started - September 1, 2003.]

  Revision    [$Id: extraBddAuto.c,v 1.0 2003/05/21 18:03:50 alanmi Exp $]

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

#include "extra.h"

ABC_NAMESPACE_IMPL_START


/*---------------------------------------------------------------------------*/
/* Constant declarations                                                     */
/*---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*/
/* Stucture declarations                                                     */
/*---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*/
/* Type declarations                                                         */
/*---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*/
/* Variable declarations                                                     */
/*---------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------*/
/* Macro declarations                                                        */
/*---------------------------------------------------------------------------*/


/**AutomaticStart*************************************************************/

/*---------------------------------------------------------------------------*/
/* Static function prototypes                                                */
/*---------------------------------------------------------------------------*/

/**AutomaticEnd***************************************************************/


/*
    LinearSpace(f) = Space(f,f)

    Space(f,g)
    {
        if ( f = const )
        { 
            if ( f = g )  return 1;
            else          return 0;
        }
        if ( g = const )  return 0;
        return x' * Space(fx',gx') * Space(fx,gx) + x * Space(fx',gx) * Space(fx,gx');
    }

    Equations(s) = Pos(s) + Neg(s);

    Pos(s)
    {
        if ( s  = 0 )   return 1;
        if ( s  = 1 )   return 0;
        if ( sx'= 0 )   return Pos(sx) + x;
        if ( sx = 0 )   return Pos(sx');
        return 1 * [Pos(sx') & Pos(sx)] + x * [Pos(sx') & Neg(sx)];
    }

    Neg(s)
    {
        if ( s  = 0 )   return 1;
        if ( s  = 1 )   return 0;
        if ( sx'= 0 )   return Neg(sx);
        if ( sx = 0 )   return Neg(sx') + x;
        return 1 * [Neg(sx') & Neg(sx)] + x * [Neg(sx') & Pos(sx)];
    }


    SpaceP(A)
    {
        if ( A = 0 )    return 1;
        if ( A = 1 )    return 1;
        return x' * SpaceP(Ax') * SpaceP(Ax) + x * SpaceP(Ax') * SpaceN(Ax);
    }

    SpaceN(A)
    {
        if ( A = 0 )    return 1;
        if ( A = 1 )    return 0;
        return x' * SpaceN(Ax') * SpaceN(Ax) + x * SpaceN(Ax') * SpaceP(Ax);
    }


    LinInd(A)
    {
        if ( A = const )     return 1;
        if ( !LinInd(Ax') )  return 0;
        if ( !LinInd(Ax)  )  return 0;
        if (  LinSumOdd(Ax')  & LinSumEven(Ax) != 0 )  return 0;
        if (  LinSumEven(Ax') & LinSumEven(Ax) != 0 )  return 0;
        return 1;
    }

    LinSumOdd(A)
    {
        if ( A = 0 )    return 0;                 // Odd0  ---e-- Odd1
        if ( A = 1 )    return 1;                 //       \   o
        Odd0  = LinSumOdd(Ax');  // x is absent   //         \ 
        Even0 = LinSumEven(Ax'); // x is absent   //       /   o  
        Odd1  = LinSumOdd(Ax);   // x is present  // Even0 ---e-- Even1 
        Even1 = LinSumEven(Ax);  // x is absent
        return 1 * [Odd0 + ExorP(Odd0, Even1)] + x * [Odd1 + ExorP(Odd1, Even0)];
    }

    LinSumEven(A)
    {
        if ( A = 0 )    return 0;
        if ( A = 1 )    return 0;
        Odd0  = LinSumOdd(Ax');  // x is absent
        Even0 = LinSumEven(Ax'); // x is absent
        Odd1  = LinSumOdd(Ax);   // x is present
        Even1 = LinSumEven(Ax);  // x is absent
        return 1 * [Even0 + Even1 + ExorP(Even0, Even1)] + x * [ExorP(Odd0, Odd1)];
    }
    
*/

/*---------------------------------------------------------------------------*/
/* Definition of exported functions                                          */
/*---------------------------------------------------------------------------*/

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

  Synopsis    []

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
DdNode * Extra_bddSpaceFromFunctionFast( DdManager * dd, DdNode * bFunc )
{
    int * pSupport;
    int * pPermute;
    int * pPermuteBack;
    DdNode ** pCompose;
    DdNode * bCube, * bTemp;
    DdNode * bSpace, * bFunc1, * bFunc2, * bSpaceShift;
    int nSupp, Counter;
    int i, lev;

    // get the support
    pSupport = ABC_ALLOC( int, ddMax(dd->size,dd->sizeZ) );
    Extra_SupportArray( dd, bFunc, pSupport );
    nSupp = 0;
    for ( i = 0; i < dd->size; i++ )
        if ( pSupport[i] )
            nSupp++;

    // make sure the manager has enough variables
    if ( 2*nSupp > dd->size )
    {
        printf( "Cannot derive linear space, because DD manager does not have enough variables.\n" );
        fflush( stdout );
        ABC_FREE( pSupport );
        return NULL;
    }

    // create the permutation arrays
    pPermute     = ABC_ALLOC( int, dd->size );
    pPermuteBack = ABC_ALLOC( int, dd->size );
    pCompose     = ABC_ALLOC( DdNode *, dd->size );
    for ( i = 0; i < dd->size; i++ )
    {
        pPermute[i]     = i;
        pPermuteBack[i] = i;
        pCompose[i]     = dd->vars[i];   Cudd_Ref( pCompose[i] );
    }

    // remap the function in such a way that the variables are interleaved
    Counter = 0;
    bCube = b1;  Cudd_Ref( bCube );
    for ( lev = 0; lev < dd->size; lev++ )
        if ( pSupport[ dd->invperm[lev] ] )
        {   // var "dd->invperm[lev]" on level "lev" should go to level 2*Counter;
            pPermute[ dd->invperm[lev] ] = dd->invperm[2*Counter];
            // var from level 2*Counter+1 should go back to the place of this var
            pPermuteBack[ dd->invperm[2*Counter+1] ] = dd->invperm[lev];
            // the permutation should be defined in such a way that variable
            // on level 2*Counter is replaced by an EXOR of itself and var on the next level
            Cudd_Deref( pCompose[ dd->invperm[2*Counter] ] );
            pCompose[ dd->invperm[2*Counter] ] = 
                Cudd_bddXor( dd, dd->vars[ dd->invperm[2*Counter] ], dd->vars[ dd->invperm[2*Counter+1] ] );  
            Cudd_Ref( pCompose[ dd->invperm[2*Counter] ] );
            // add this variable to the cube
            bCube = Cudd_bddAnd( dd, bTemp = bCube, dd->vars[ dd->invperm[2*Counter] ] );  Cudd_Ref( bCube );
            Cudd_RecursiveDeref( dd, bTemp );
            // increment the counter
            Counter ++;
        }

    // permute the functions
    bFunc1 = Cudd_bddPermute( dd, bFunc, pPermute );         Cudd_Ref( bFunc1 );
    // compose to gate the function depending on both vars
    bFunc2 = Cudd_bddVectorCompose( dd, bFunc1, pCompose );  Cudd_Ref( bFunc2 );
    // gate the vector space
    // L(a) = ForAll x [ F(x) = F(x+a) ] = Not( Exist x [ F(x) (+) F(x+a) ] )
    bSpaceShift = Cudd_bddXorExistAbstract( dd, bFunc1, bFunc2, bCube );  Cudd_Ref( bSpaceShift );
    bSpaceShift = Cudd_Not( bSpaceShift );
    // permute the space back into the original mapping
    bSpace = Cudd_bddPermute( dd, bSpaceShift, pPermuteBack ); Cudd_Ref( bSpace );
    Cudd_RecursiveDeref( dd, bFunc1 );
    Cudd_RecursiveDeref( dd, bFunc2 );
    Cudd_RecursiveDeref( dd, bSpaceShift );
    Cudd_RecursiveDeref( dd, bCube );

    for ( i = 0; i < dd->size; i++ )
        Cudd_RecursiveDeref( dd, pCompose[i] );
    ABC_FREE( pPermute );
    ABC_FREE( pPermuteBack );
    ABC_FREE( pCompose );
    ABC_FREE( pSupport );

    Cudd_Deref( bSpace );
    return bSpace;
}



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

  Synopsis    []

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
DdNode * Extra_bddSpaceFromFunction( DdManager * dd, DdNode * bF, DdNode * bG )
{
    DdNode * bRes;
    do {
        dd->reordered = 0;
        bRes = extraBddSpaceFromFunction( dd, bF, bG );
    } while (dd->reordered == 1);
    return bRes;
}

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

  Synopsis    []

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
DdNode * Extra_bddSpaceFromFunctionPos( DdManager * dd, DdNode * bFunc )
{
    DdNode * bRes;
    do {
        dd->reordered = 0;
        bRes = extraBddSpaceFromFunctionPos( dd, bFunc );
    } while (dd->reordered == 1);
    return bRes;
}

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

  Synopsis    []

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
DdNode * Extra_bddSpaceFromFunctionNeg( DdManager * dd, DdNode * bFunc )
{
    DdNode * bRes;
    do {
        dd->reordered = 0;
        bRes = extraBddSpaceFromFunctionNeg( dd, bFunc );
    } while (dd->reordered == 1);
    return bRes;
}

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

  Synopsis    []

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
DdNode * Extra_bddSpaceCanonVars( DdManager * dd, DdNode * bSpace )
{
    DdNode * bRes;
    do {
        dd->reordered = 0;
        bRes = extraBddSpaceCanonVars( dd, bSpace );
    } while (dd->reordered == 1);
    return bRes;
}

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

  Synopsis    []

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
DdNode * Extra_bddSpaceReduce( DdManager * dd, DdNode * bFunc, DdNode * bCanonVars )
{
    DdNode * bNegCube;
    DdNode * bResult;
    bNegCube = Extra_bddSupportNegativeCube( dd, bCanonVars );  Cudd_Ref( bNegCube );
    bResult  = Cudd_Cofactor( dd, bFunc, bNegCube );            Cudd_Ref( bResult );
    Cudd_RecursiveDeref( dd, bNegCube );
    Cudd_Deref( bResult );
    return bResult;
}



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

  Synopsis    []

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
DdNode * Extra_bddSpaceEquations( DdManager * dd, DdNode * bSpace )
{
    DdNode * zRes;
    DdNode * zEquPos;
    DdNode * zEquNeg;
    zEquPos = Extra_bddSpaceEquationsPos( dd, bSpace );  Cudd_Ref( zEquPos );
    zEquNeg = Extra_bddSpaceEquationsNeg( dd, bSpace );  Cudd_Ref( zEquNeg );
    zRes    = Cudd_zddUnion( dd, zEquPos, zEquNeg );     Cudd_Ref( zRes );
    Cudd_RecursiveDerefZdd( dd, zEquPos );
    Cudd_RecursiveDerefZdd( dd, zEquNeg );
    Cudd_Deref( zRes );
    return zRes;
}


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

  Synopsis    []

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
DdNode * Extra_bddSpaceEquationsPos( DdManager * dd, DdNode * bSpace )
{
    DdNode * zRes;
    do {
        dd->reordered = 0;
        zRes = extraBddSpaceEquationsPos( dd, bSpace );
    } while (dd->reordered == 1);
    return zRes;
}


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

  Synopsis    []

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
DdNode * Extra_bddSpaceEquationsNeg( DdManager * dd, DdNode * bSpace )
{
    DdNode * zRes;
    do {
        dd->reordered = 0;
        zRes = extraBddSpaceEquationsNeg( dd, bSpace );
    } while (dd->reordered == 1);
    return zRes;
}


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

  Synopsis    []

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
DdNode * Extra_bddSpaceFromMatrixPos( DdManager * dd, DdNode * zA )
{
    DdNode * bRes;
    do {
        dd->reordered = 0;
        bRes = extraBddSpaceFromMatrixPos( dd, zA );
    } while (dd->reordered == 1);
    return bRes;
}

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

  Synopsis    []

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
DdNode * Extra_bddSpaceFromMatrixNeg( DdManager * dd, DdNode * zA )
{
    DdNode * bRes;
    do {
        dd->reordered = 0;
        bRes = extraBddSpaceFromMatrixNeg( dd, zA );
    } while (dd->reordered == 1);
    return bRes;
}

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

  Synopsis    [Counts the number of literals in one combination.]

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
int Extra_zddLitCountComb( DdManager * dd, DdNode * zComb )
{
    int Counter;
    if ( zComb == z0 )
        return 0;
    Counter = 0;
    for ( ; zComb != z1; zComb = cuddT(zComb) )
        Counter++;
    return Counter;
}

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

  Synopsis    []

  Description [Returns the array of ZDDs with the number equal to the number of 
  vars in the DD manager. If the given var is non-canonical, this array contains
  the referenced ZDD representing literals in the corresponding EXOR equation.]

  SideEffects []

  SeeAlso     []

***********************************************************************/
DdNode ** Extra_bddSpaceExorGates( DdManager * dd, DdNode * bFuncRed, DdNode * zEquations )
{
    DdNode ** pzRes;
    int * pVarsNonCan;
    DdNode * zEquRem;
    int iVarNonCan;
    DdNode * zExor, * zTemp;

    // get the set of non-canonical variables
    pVarsNonCan = ABC_ALLOC( int, ddMax(dd->size,dd->sizeZ) );
    Extra_SupportArray( dd, bFuncRed, pVarsNonCan );

    // allocate storage for the EXOR sets
    pzRes = ABC_ALLOC( DdNode *, dd->size );
    memset( pzRes, 0, sizeof(DdNode *) * dd->size );

    // go through all the equations
    zEquRem = zEquations;  Cudd_Ref( zEquRem );
    while ( zEquRem != z0 )
    {
        // extract one product
        zExor = Extra_zddSelectOneSubset( dd, zEquRem );   Cudd_Ref( zExor );
        // remove it from the set
        zEquRem = Cudd_zddDiff( dd, zTemp = zEquRem, zExor );  Cudd_Ref( zEquRem );
        Cudd_RecursiveDerefZdd( dd, zTemp );

        // locate the non-canonical variable
        iVarNonCan = -1;
        for ( zTemp = zExor; zTemp != z1; zTemp = cuddT(zTemp) )
        {
            if ( pVarsNonCan[zTemp->index/2] == 1 )
            {
                assert( iVarNonCan == -1 );
                iVarNonCan = zTemp->index/2;
            }
        }
        assert( iVarNonCan != -1 );

        if ( Extra_zddLitCountComb( dd, zExor ) > 1 )
            pzRes[ iVarNonCan ] = zExor; // takes ref
        else
            Cudd_RecursiveDerefZdd( dd, zExor );
    }
    Cudd_RecursiveDerefZdd( dd, zEquRem );

    ABC_FREE( pVarsNonCan );
    return pzRes;
}


/*---------------------------------------------------------------------------*/
/* Definition of internal functions                                          */
/*---------------------------------------------------------------------------*/

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

  Synopsis    [Performs the recursive steps of Extra_bddSpaceFromFunction.]

  Description []

  SideEffects []

  SeeAlso     []

******************************************************************************/
DdNode * extraBddSpaceFromFunction( DdManager * dd, DdNode * bF, DdNode * bG )
{
    DdNode * bRes;
    DdNode * bFR, * bGR;

    bFR = Cudd_Regular( bF ); 
    bGR = Cudd_Regular( bG ); 
    if ( cuddIsConstant(bFR) )
    {
        if ( bF == bG )
            return b1;
        else 
            return b0;
    }
    if ( cuddIsConstant(bGR) )
        return b0;
    // both bFunc and bCore are not constants

    // the operation is commutative - normalize the problem
    if ( (unsigned)(ABC_PTRUINT_T)bF > (unsigned)(ABC_PTRUINT_T)bG )
        return extraBddSpaceFromFunction(dd, bG, bF);


    if ( (bRes = cuddCacheLookup2(dd, extraBddSpaceFromFunction, bF, bG)) )
        return bRes;
    else
    {
        DdNode * bF0, * bF1;
        DdNode * bG0, * bG1;
        DdNode * bTemp1, * bTemp2;
        DdNode * bRes0, * bRes1;
        int LevelF, LevelG;
        int index;

        LevelF = dd->perm[bFR->index];
        LevelG = dd->perm[bGR->index];
        if ( LevelF <= LevelG )
        {
            index = dd->invperm[LevelF];
            if ( bFR != bF )
            {
                bF0 = Cudd_Not( cuddE(bFR) );
                bF1 = Cudd_Not( cuddT(bFR) );
            }
            else
            {
                bF0 = cuddE(bFR);
                bF1 = cuddT(bFR);
            }
        }
        else
        {
            index = dd->invperm[LevelG];
            bF0 = bF1 = bF;
        }

        if ( LevelG <= LevelF )
        {
            if ( bGR != bG )
            {
                bG0 = Cudd_Not( cuddE(bGR) );
                bG1 = Cudd_Not( cuddT(bGR) );
            }
            else
            {
                bG0 = cuddE(bGR);
                bG1 = cuddT(bGR);
            }
        }
        else
            bG0 = bG1 = bG;

        bTemp1 = extraBddSpaceFromFunction( dd, bF0, bG0 );
        if ( bTemp1 == NULL ) 
            return NULL;
        cuddRef( bTemp1 );

        bTemp2 = extraBddSpaceFromFunction( dd, bF1, bG1 );
        if ( bTemp2 == NULL ) 
        {
            Cudd_RecursiveDeref( dd, bTemp1 );
            return NULL;
        }
        cuddRef( bTemp2 );


        bRes0  = cuddBddAndRecur( dd, bTemp1, bTemp2 );
        if ( bRes0 == NULL )
        {
            Cudd_RecursiveDeref( dd, bTemp1 );
            Cudd_RecursiveDeref( dd, bTemp2 );
            return NULL;
        }
        cuddRef( bRes0 );
        Cudd_RecursiveDeref( dd, bTemp1 );
        Cudd_RecursiveDeref( dd, bTemp2 );


        bTemp1  = extraBddSpaceFromFunction( dd, bF0, bG1 );
        if ( bTemp1 == NULL )
        {
            Cudd_RecursiveDeref( dd, bRes0 );
            return NULL;
        }
        cuddRef( bTemp1 );

        bTemp2  = extraBddSpaceFromFunction( dd, bF1, bG0 );
        if ( bTemp2 == NULL )
        {
            Cudd_RecursiveDeref( dd, bRes0 );
            Cudd_RecursiveDeref( dd, bTemp1 );
            return NULL;
        }
        cuddRef( bTemp2 );

        bRes1  = cuddBddAndRecur( dd, bTemp1, bTemp2 );
        if ( bRes1 == NULL )
        {
            Cudd_RecursiveDeref( dd, bRes0 );
            Cudd_RecursiveDeref( dd, bTemp1 );
            Cudd_RecursiveDeref( dd, bTemp2 );
            return NULL;
        }
        cuddRef( bRes1 );
        Cudd_RecursiveDeref( dd, bTemp1 );
        Cudd_RecursiveDeref( dd, bTemp2 );



        // consider the case when Res0 and Res1 are the same node 
        if ( bRes0 == bRes1 )
            bRes = bRes1;
        // consider the case when Res1 is complemented 
        else if ( Cudd_IsComplement(bRes1) ) 
        {
            bRes = cuddUniqueInter(dd, index, Cudd_Not(bRes1), Cudd_Not(bRes0));
            if ( bRes == NULL ) 
            {
                Cudd_RecursiveDeref(dd,bRes0);
                Cudd_RecursiveDeref(dd,bRes1);
                return NULL;
            }
            bRes = Cudd_Not(bRes);
        } 
        else 
        {
            bRes = cuddUniqueInter( dd, index, bRes1, bRes0 );
            if ( bRes == NULL ) 
            {
                Cudd_RecursiveDeref(dd,bRes0);
                Cudd_RecursiveDeref(dd,bRes1);
                return NULL;
            }
        }
        cuddDeref( bRes0 );
        cuddDeref( bRes1 );
            
        // insert the result into cache 
        cuddCacheInsert2(dd, extraBddSpaceFromFunction, bF, bG, bRes);
        return bRes;
    }
}  /* end of extraBddSpaceFromFunction */



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

  Synopsis    [Performs the recursive step of Extra_bddSpaceFromFunctionPos().]

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
DdNode * extraBddSpaceFromFunctionPos( DdManager * dd, DdNode * bF )
{
    DdNode * bRes, * bFR;
    statLine( dd );

    bFR = Cudd_Regular(bF);
    if ( cuddIsConstant(bFR) )
        return b1;

    if ( (bRes = cuddCacheLookup1(dd, extraBddSpaceFromFunctionPos, bF)) )
        return bRes;
    else
    {
        DdNode * bF0,   * bF1;
        DdNode * bPos0, * bPos1;
        DdNode * bNeg0, * bNeg1;
        DdNode * bRes0, * bRes1; 

        if ( bFR != bF ) // bF is complemented 
        {
            bF0 = Cudd_Not( cuddE(bFR) );
            bF1 = Cudd_Not( cuddT(bFR) );
        }
        else
        {
            bF0 = cuddE(bFR);
            bF1 = cuddT(bFR);
        }


        bPos0  = extraBddSpaceFromFunctionPos( dd, bF0 );
        if ( bPos0 == NULL )
            return NULL;
        cuddRef( bPos0 );

        bPos1  = extraBddSpaceFromFunctionPos( dd, bF1 );
        if ( bPos1 == NULL )
        {
            Cudd_RecursiveDeref( dd, bPos0 );
            return NULL;
        }
        cuddRef( bPos1 );

        bRes0  = cuddBddAndRecur( dd, bPos0, bPos1 );
        if ( bRes0 == NULL )
        {
            Cudd_RecursiveDeref( dd, bPos0 );
            Cudd_RecursiveDeref( dd, bPos1 );
            return NULL;
        }
        cuddRef( bRes0 );
        Cudd_RecursiveDeref( dd, bPos0 );
        Cudd_RecursiveDeref( dd, bPos1 );


        bNeg0  = extraBddSpaceFromFunctionNeg( dd, bF0 );
        if ( bNeg0 == NULL )
        {
            Cudd_RecursiveDeref( dd, bRes0 );
            return NULL;
        }
        cuddRef( bNeg0 );

        bNeg1  = extraBddSpaceFromFunctionNeg( dd, bF1 );
        if ( bNeg1 == NULL )
        {
            Cudd_RecursiveDeref( dd, bRes0 );
            Cudd_RecursiveDeref( dd, bNeg0 );
            return NULL;
        }
        cuddRef( bNeg1 );

        bRes1  = cuddBddAndRecur( dd, bNeg0, bNeg1 );
        if ( bRes1 == NULL )
        {
            Cudd_RecursiveDeref( dd, bRes0 );
            Cudd_RecursiveDeref( dd, bNeg0 );
            Cudd_RecursiveDeref( dd, bNeg1 );
            return NULL;
        }
        cuddRef( bRes1 );
        Cudd_RecursiveDeref( dd, bNeg0 );
        Cudd_RecursiveDeref( dd, bNeg1 );


        // consider the case when Res0 and Res1 are the same node 
        if ( bRes0 == bRes1 )
            bRes = bRes1;
        // consider the case when Res1 is complemented 
        else if ( Cudd_IsComplement(bRes1) ) 
        {
            bRes = cuddUniqueInter( dd, bFR->index, Cudd_Not(bRes1), Cudd_Not(bRes0) );
            if ( bRes == NULL ) 
            {
                Cudd_RecursiveDeref(dd,bRes0);
                Cudd_RecursiveDeref(dd,bRes1);
                return NULL;
            }
            bRes = Cudd_Not(bRes);
        } 
        else 
        {
            bRes = cuddUniqueInter( dd, bFR->index, bRes1, bRes0 );
            if ( bRes == NULL ) 
            {
                Cudd_RecursiveDeref(dd,bRes0);
                Cudd_RecursiveDeref(dd,bRes1);
                return NULL;
            }
        }
        cuddDeref( bRes0 );
        cuddDeref( bRes1 );

        cuddCacheInsert1( dd, extraBddSpaceFromFunctionPos, bF, bRes );
        return bRes;
    }
}



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

  Synopsis    [Performs the recursive step of Extra_bddSpaceFromFunctionPos().]

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
DdNode * extraBddSpaceFromFunctionNeg( DdManager * dd, DdNode * bF )
{
    DdNode * bRes, * bFR;
    statLine( dd );

    bFR = Cudd_Regular(bF);
    if ( cuddIsConstant(bFR) )
        return b0;

    if ( (bRes = cuddCacheLookup1(dd, extraBddSpaceFromFunctionNeg, bF)) )
        return bRes;
    else
    {
        DdNode * bF0,   * bF1;
        DdNode * bPos0, * bPos1;
        DdNode * bNeg0, * bNeg1;
        DdNode * bRes0, * bRes1; 

        if ( bFR != bF ) // bF is complemented 
        {
            bF0 = Cudd_Not( cuddE(bFR) );
            bF1 = Cudd_Not( cuddT(bFR) );
        }
        else
        {
            bF0 = cuddE(bFR);
            bF1 = cuddT(bFR);
        }


        bPos0  = extraBddSpaceFromFunctionNeg( dd, bF0 );
        if ( bPos0 == NULL )
            return NULL;
        cuddRef( bPos0 );

        bPos1  = extraBddSpaceFromFunctionNeg( dd, bF1 );
        if ( bPos1 == NULL )
        {
            Cudd_RecursiveDeref( dd, bPos0 );
            return NULL;
        }
        cuddRef( bPos1 );

        bRes0  = cuddBddAndRecur( dd, bPos0, bPos1 );
        if ( bRes0 == NULL )
        {
            Cudd_RecursiveDeref( dd, bPos0 );
            Cudd_RecursiveDeref( dd, bPos1 );
            return NULL;
        }
        cuddRef( bRes0 );
        Cudd_RecursiveDeref( dd, bPos0 );
        Cudd_RecursiveDeref( dd, bPos1 );


        bNeg0  = extraBddSpaceFromFunctionPos( dd, bF0 );
        if ( bNeg0 == NULL )
        {
            Cudd_RecursiveDeref( dd, bRes0 );
            return NULL;
        }
        cuddRef( bNeg0 );

        bNeg1  = extraBddSpaceFromFunctionPos( dd, bF1 );
        if ( bNeg1 == NULL )
        {
            Cudd_RecursiveDeref( dd, bRes0 );
            Cudd_RecursiveDeref( dd, bNeg0 );
            return NULL;
        }
        cuddRef( bNeg1 );

        bRes1  = cuddBddAndRecur( dd, bNeg0, bNeg1 );
        if ( bRes1 == NULL )
        {
            Cudd_RecursiveDeref( dd, bRes0 );
            Cudd_RecursiveDeref( dd, bNeg0 );
            Cudd_RecursiveDeref( dd, bNeg1 );
            return NULL;
        }
        cuddRef( bRes1 );
        Cudd_RecursiveDeref( dd, bNeg0 );
        Cudd_RecursiveDeref( dd, bNeg1 );


        // consider the case when Res0 and Res1 are the same node 
        if ( bRes0 == bRes1 )
            bRes = bRes1;
        // consider the case when Res1 is complemented 
        else if ( Cudd_IsComplement(bRes1) ) 
        {
            bRes = cuddUniqueInter( dd, bFR->index, Cudd_Not(bRes1), Cudd_Not(bRes0) );
            if ( bRes == NULL ) 
            {
                Cudd_RecursiveDeref(dd,bRes0);
                Cudd_RecursiveDeref(dd,bRes1);
                return NULL;
            }
            bRes = Cudd_Not(bRes);
        } 
        else 
        {
            bRes = cuddUniqueInter( dd, bFR->index, bRes1, bRes0 );
            if ( bRes == NULL ) 
            {
                Cudd_RecursiveDeref(dd,bRes0);
                Cudd_RecursiveDeref(dd,bRes1);
                return NULL;
            }
        }
        cuddDeref( bRes0 );
        cuddDeref( bRes1 );

        cuddCacheInsert1( dd, extraBddSpaceFromFunctionNeg, bF, bRes );
        return bRes;
    }
}



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

  Synopsis    [Performs the recursive step of Extra_bddSpaceCanonVars().]

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
DdNode * extraBddSpaceCanonVars( DdManager * dd, DdNode * bF )
{
    DdNode * bRes, * bFR;
    statLine( dd );

    bFR = Cudd_Regular(bF);
    if ( cuddIsConstant(bFR) )
        return bF;

    if ( (bRes = cuddCacheLookup1(dd, extraBddSpaceCanonVars, bF)) )
        return bRes;
    else
    {
        DdNode * bF0,  * bF1;
        DdNode * bRes, * bRes0; 

        if ( bFR != bF ) // bF is complemented 
        {
            bF0 = Cudd_Not( cuddE(bFR) );
            bF1 = Cudd_Not( cuddT(bFR) );
        }
        else
        {
            bF0 = cuddE(bFR);
            bF1 = cuddT(bFR);
        }

        if ( bF0 == b0 )
        {
            bRes = extraBddSpaceCanonVars( dd, bF1 );
            if ( bRes == NULL )
                return NULL;
        }
        else if ( bF1 == b0 )
        {
            bRes = extraBddSpaceCanonVars( dd, bF0 );
            if ( bRes == NULL )
                return NULL;
        }
        else
        {
            bRes0 = extraBddSpaceCanonVars( dd, bF0 );
            if ( bRes0 == NULL )
                return NULL;
            cuddRef( bRes0 );

            bRes = cuddUniqueInter( dd, bFR->index, bRes0, b0 );
            if ( bRes == NULL ) 
            {
                Cudd_RecursiveDeref( dd,bRes0 );
                return NULL;
            }
            cuddDeref( bRes0 );
        }

        cuddCacheInsert1( dd, extraBddSpaceCanonVars, bF, bRes );
        return bRes;
    }
}

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

  Synopsis    [Performs the recursive step of Extra_bddSpaceEquationsPos().]

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
DdNode * extraBddSpaceEquationsPos( DdManager * dd, DdNode * bF )
{
    DdNode * zRes;
    statLine( dd );

    if ( bF == b0 )
        return z1;
    if ( bF == b1 )
        return z0;
    
    if ( (zRes = cuddCacheLookup1Zdd(dd, extraBddSpaceEquationsPos, bF)) )
        return zRes;
    else
    {
        DdNode * bFR, * bF0,  * bF1;
        DdNode * zPos0, * zPos1, * zNeg1; 
        DdNode * zRes, * zRes0, * zRes1;

        bFR = Cudd_Regular(bF);
        if ( bFR != bF ) // bF is complemented 
        {
            bF0 = Cudd_Not( cuddE(bFR) );
            bF1 = Cudd_Not( cuddT(bFR) );
        }
        else
        {
            bF0 = cuddE(bFR);
            bF1 = cuddT(bFR);
        }

        if ( bF0 == b0 )
        {
            zRes1 = extraBddSpaceEquationsPos( dd, bF1 );
            if ( zRes1 == NULL )
                return NULL;
            cuddRef( zRes1 );

            // add the current element to the set
            zRes = cuddZddGetNode( dd, 2*bFR->index, z1, zRes1 );
            if ( zRes == NULL ) 
            {
                Cudd_RecursiveDerefZdd(dd, zRes1);
                return NULL;
            }
            cuddDeref( zRes1 );
        }
        else if ( bF1 == b0 )
        {
            zRes = extraBddSpaceEquationsPos( dd, bF0 );
            if ( zRes == NULL )
                return NULL;
        }
        else
        {
            zPos0 = extraBddSpaceEquationsPos( dd, bF0 );
            if ( zPos0 == NULL )
                return NULL;
            cuddRef( zPos0 );

            zPos1 = extraBddSpaceEquationsPos( dd, bF1 );
            if ( zPos1 == NULL )
            {
                Cudd_RecursiveDerefZdd(dd, zPos0);
                return NULL;
            }
            cuddRef( zPos1 );

            zNeg1 = extraBddSpaceEquationsNeg( dd, bF1 );
            if ( zNeg1 == NULL )
            {
                Cudd_RecursiveDerefZdd(dd, zPos0);
                Cudd_RecursiveDerefZdd(dd, zPos1);
                return NULL;
            }
            cuddRef( zNeg1 );


            zRes0 = cuddZddIntersect( dd, zPos0, zPos1 );
            if ( zRes0 == NULL )
            {
                Cudd_RecursiveDerefZdd(dd, zNeg1);
                Cudd_RecursiveDerefZdd(dd, zPos0);
                Cudd_RecursiveDerefZdd(dd, zPos1);
                return NULL;
            }
            cuddRef( zRes0 );

            zRes1 = cuddZddIntersect( dd, zPos0, zNeg1 );
            if ( zRes1 == NULL )
            {
                Cudd_RecursiveDerefZdd(dd, zRes0);
                Cudd_RecursiveDerefZdd(dd, zNeg1);
                Cudd_RecursiveDerefZdd(dd, zPos0);
                Cudd_RecursiveDerefZdd(dd, zPos1);
                return NULL;
            }
            cuddRef( zRes1 );
            Cudd_RecursiveDerefZdd(dd, zNeg1);
            Cudd_RecursiveDerefZdd(dd, zPos0);
            Cudd_RecursiveDerefZdd(dd, zPos1);
            // only zRes0 and zRes1 are refed at this point

            zRes = cuddZddGetNode( dd, 2*bFR->index, zRes1, zRes0 );
            if ( zRes == NULL ) 
            {
                Cudd_RecursiveDerefZdd(dd, zRes0);
                Cudd_RecursiveDerefZdd(dd, zRes1);
                return NULL;
            }
            cuddDeref( zRes0 );
            cuddDeref( zRes1 );
        }

        cuddCacheInsert1( dd, extraBddSpaceEquationsPos, bF, zRes );
        return zRes;
    }
}


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

  Synopsis    [Performs the recursive step of Extra_bddSpaceEquationsNev().]

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
DdNode * extraBddSpaceEquationsNeg( DdManager * dd, DdNode * bF )
{
    DdNode * zRes;
    statLine( dd );

    if ( bF == b0 )
        return z1;
    if ( bF == b1 )
        return z0;
    
    if ( (zRes = cuddCacheLookup1Zdd(dd, extraBddSpaceEquationsNeg, bF)) )
        return zRes;
    else
    {
        DdNode * bFR, * bF0,  * bF1;
        DdNode * zPos0, * zPos1, * zNeg1; 
        DdNode * zRes, * zRes0, * zRes1;

        bFR = Cudd_Regular(bF);
        if ( bFR != bF ) // bF is complemented 
        {
            bF0 = Cudd_Not( cuddE(bFR) );
            bF1 = Cudd_Not( cuddT(bFR) );
        }
        else
        {
            bF0 = cuddE(bFR);
            bF1 = cuddT(bFR);
        }

        if ( bF0 == b0 )
        {
            zRes = extraBddSpaceEquationsNeg( dd, bF1 );
            if ( zRes == NULL )
                return NULL;
        }
        else if ( bF1 == b0 )
        {
            zRes0 = extraBddSpaceEquationsNeg( dd, bF0 );
            if ( zRes0 == NULL )
                return NULL;
            cuddRef( zRes0 );

            // add the current element to the set
            zRes = cuddZddGetNode( dd, 2*bFR->index, z1, zRes0 );
            if ( zRes == NULL ) 
            {
                Cudd_RecursiveDerefZdd(dd, zRes0);
                return NULL;
            }
            cuddDeref( zRes0 );
        }
        else
        {
            zPos0 = extraBddSpaceEquationsNeg( dd, bF0 );
            if ( zPos0 == NULL )
                return NULL;
            cuddRef( zPos0 );

            zPos1 = extraBddSpaceEquationsNeg( dd, bF1 );
            if ( zPos1 == NULL )
            {
                Cudd_RecursiveDerefZdd(dd, zPos0);
                return NULL;
            }
            cuddRef( zPos1 );

            zNeg1 = extraBddSpaceEquationsPos( dd, bF1 );
            if ( zNeg1 == NULL )
            {
                Cudd_RecursiveDerefZdd(dd, zPos0);
                Cudd_RecursiveDerefZdd(dd, zPos1);
                return NULL;
            }
            cuddRef( zNeg1 );


            zRes0 = cuddZddIntersect( dd, zPos0, zPos1 );
            if ( zRes0 == NULL )
            {
                Cudd_RecursiveDerefZdd(dd, zNeg1);
                Cudd_RecursiveDerefZdd(dd, zPos0);
                Cudd_RecursiveDerefZdd(dd, zPos1);
                return NULL;
            }
            cuddRef( zRes0 );

            zRes1 = cuddZddIntersect( dd, zPos0, zNeg1 );
            if ( zRes1 == NULL )
            {
                Cudd_RecursiveDerefZdd(dd, zRes0);
                Cudd_RecursiveDerefZdd(dd, zNeg1);
                Cudd_RecursiveDerefZdd(dd, zPos0);
                Cudd_RecursiveDerefZdd(dd, zPos1);
                return NULL;
            }
            cuddRef( zRes1 );
            Cudd_RecursiveDerefZdd(dd, zNeg1);
            Cudd_RecursiveDerefZdd(dd, zPos0);
            Cudd_RecursiveDerefZdd(dd, zPos1);
            // only zRes0 and zRes1 are refed at this point

            zRes = cuddZddGetNode( dd, 2*bFR->index, zRes1, zRes0 );
            if ( zRes == NULL ) 
            {
                Cudd_RecursiveDerefZdd(dd, zRes0);
                Cudd_RecursiveDerefZdd(dd, zRes1);
                return NULL;
            }
            cuddDeref( zRes0 );
            cuddDeref( zRes1 );
        }

        cuddCacheInsert1( dd, extraBddSpaceEquationsNeg, bF, zRes );
        return zRes;
    }
}




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

  Synopsis    [Performs the recursive step of Extra_bddSpaceFromFunctionPos().]

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
DdNode * extraBddSpaceFromMatrixPos( DdManager * dd, DdNode * zA )
{
    DdNode * bRes;
    statLine( dd );

    if ( zA == z0 )
        return b1;
    if ( zA == z1 )
        return b1;

    if ( (bRes = cuddCacheLookup1(dd, extraBddSpaceFromMatrixPos, zA)) )
        return bRes;
    else
    {
        DdNode * bP0, * bP1;
        DdNode * bN0, * bN1;
        DdNode * bRes0, * bRes1; 

        bP0  = extraBddSpaceFromMatrixPos( dd, cuddE(zA) );
        if ( bP0 == NULL )
            return NULL;
        cuddRef( bP0 );

        bP1  = extraBddSpaceFromMatrixPos( dd, cuddT(zA) );
        if ( bP1 == NULL )
        {
            Cudd_RecursiveDeref( dd, bP0 );
            return NULL;
        }
        cuddRef( bP1 );

        bRes0  = cuddBddAndRecur( dd, bP0, bP1 );
        if ( bRes0 == NULL )
        {
            Cudd_RecursiveDeref( dd, bP0 );
            Cudd_RecursiveDeref( dd, bP1 );
            return NULL;
        }
        cuddRef( bRes0 );
        Cudd_RecursiveDeref( dd, bP0 );
        Cudd_RecursiveDeref( dd, bP1 );


        bN0  = extraBddSpaceFromMatrixPos( dd, cuddE(zA) );
        if ( bN0 == NULL )
        {
            Cudd_RecursiveDeref( dd, bRes0 );
            return NULL;
        }
        cuddRef( bN0 );

        bN1  = extraBddSpaceFromMatrixNeg( dd, cuddT(zA) );
        if ( bN1 == NULL )
        {
            Cudd_RecursiveDeref( dd, bRes0 );
            Cudd_RecursiveDeref( dd, bN0 );
            return NULL;
        }
        cuddRef( bN1 );

        bRes1  = cuddBddAndRecur( dd, bN0, bN1 );
        if ( bRes1 == NULL )
        {
            Cudd_RecursiveDeref( dd, bRes0 );
            Cudd_RecursiveDeref( dd, bN0 );
            Cudd_RecursiveDeref( dd, bN1 );
            return NULL;
        }
        cuddRef( bRes1 );
        Cudd_RecursiveDeref( dd, bN0 );
        Cudd_RecursiveDeref( dd, bN1 );


        // consider the case when Res0 and Res1 are the same node 
        if ( bRes0 == bRes1 )
            bRes = bRes1;
        // consider the case when Res1 is complemented 
        else if ( Cudd_IsComplement(bRes1) ) 
        {
            bRes = cuddUniqueInter( dd, zA->index/2, Cudd_Not(bRes1), Cudd_Not(bRes0) );
            if ( bRes == NULL ) 
            {
                Cudd_RecursiveDeref(dd,bRes0);
                Cudd_RecursiveDeref(dd,bRes1);
                return NULL;
            }
            bRes = Cudd_Not(bRes);
        } 
        else 
        {
            bRes = cuddUniqueInter( dd, zA->index/2, bRes1, bRes0 );
            if ( bRes == NULL ) 
            {
                Cudd_RecursiveDeref(dd,bRes0);
                Cudd_RecursiveDeref(dd,bRes1);
                return NULL;
            }
        }
        cuddDeref( bRes0 );
        cuddDeref( bRes1 );

        cuddCacheInsert1( dd, extraBddSpaceFromMatrixPos, zA, bRes );
        return bRes;
    }
}


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

  Synopsis    [Performs the recursive step of Extra_bddSpaceFromFunctionPos().]

  Description []

  SideEffects []

  SeeAlso     []

***********************************************************************/
DdNode * extraBddSpaceFromMatrixNeg( DdManager * dd, DdNode * zA )
{
    DdNode * bRes;
    statLine( dd );

    if ( zA == z0 )
        return b1;
    if ( zA == z1 )
        return b0;

    if ( (bRes = cuddCacheLookup1(dd, extraBddSpaceFromMatrixNeg, zA)) )
        return bRes;
    else
    {
        DdNode * bP0, * bP1;
        DdNode * bN0, * bN1;
        DdNode * bRes0, * bRes1; 

        bP0  = extraBddSpaceFromMatrixNeg( dd, cuddE(zA) );
        if ( bP0 == NULL )
            return NULL;
        cuddRef( bP0 );

        bP1  = extraBddSpaceFromMatrixNeg( dd, cuddT(zA) );
        if ( bP1 == NULL )
        {
            Cudd_RecursiveDeref( dd, bP0 );
            return NULL;
        }
        cuddRef( bP1 );

        bRes0  = cuddBddAndRecur( dd, bP0, bP1 );
        if ( bRes0 == NULL )
        {
            Cudd_RecursiveDeref( dd, bP0 );
            Cudd_RecursiveDeref( dd, bP1 );
            return NULL;
        }
        cuddRef( bRes0 );
        Cudd_RecursiveDeref( dd, bP0 );
        Cudd_RecursiveDeref( dd, bP1 );


        bN0  = extraBddSpaceFromMatrixNeg( dd, cuddE(zA) );
        if ( bN0 == NULL )
        {
            Cudd_RecursiveDeref( dd, bRes0 );
            return NULL;
        }
        cuddRef( bN0 );

        bN1  = extraBddSpaceFromMatrixPos( dd, cuddT(zA) );
        if ( bN1 == NULL )
        {
            Cudd_RecursiveDeref( dd, bRes0 );
            Cudd_RecursiveDeref( dd, bN0 );
            return NULL;
        }
        cuddRef( bN1 );

        bRes1  = cuddBddAndRecur( dd, bN0, bN1 );
        if ( bRes1 == NULL )
        {
            Cudd_RecursiveDeref( dd, bRes0 );
            Cudd_RecursiveDeref( dd, bN0 );
            Cudd_RecursiveDeref( dd, bN1 );
            return NULL;
        }
        cuddRef( bRes1 );
        Cudd_RecursiveDeref( dd, bN0 );
        Cudd_RecursiveDeref( dd, bN1 );


        // consider the case when Res0 and Res1 are the same node 
        if ( bRes0 == bRes1 )
            bRes = bRes1;
        // consider the case when Res1 is complemented 
        else if ( Cudd_IsComplement(bRes1) ) 
        {
            bRes = cuddUniqueInter( dd, zA->index/2, Cudd_Not(bRes1), Cudd_Not(bRes0) );
            if ( bRes == NULL ) 
            {
                Cudd_RecursiveDeref(dd,bRes0);
                Cudd_RecursiveDeref(dd,bRes1);
                return NULL;
            }
            bRes = Cudd_Not(bRes);
        } 
        else 
        {
            bRes = cuddUniqueInter( dd, zA->index/2, bRes1, bRes0 );
            if ( bRes == NULL ) 
            {
                Cudd_RecursiveDeref(dd,bRes0);
                Cudd_RecursiveDeref(dd,bRes1);
                return NULL;
            }
        }
        cuddDeref( bRes0 );
        cuddDeref( bRes1 );

        cuddCacheInsert1( dd, extraBddSpaceFromMatrixNeg, zA, bRes );
        return bRes;
    }
}


/*---------------------------------------------------------------------------*/
/* Definition of static functions                                            */
/*---------------------------------------------------------------------------*/

ABC_NAMESPACE_IMPL_END

