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

  FileName    [fraigTrans.c]

  PackageName [MVSIS 1.3: Multi-valued logic synthesis system.]

  Synopsis    [Adds the additive and distributive choices to the AIG.]

  Author      [MVSIS Group]
  
  Affiliation [UC Berkeley]

  Date        [Ver. 1.0. Started - February 1, 2003.]

  Revision    [$Id: fraigTrans.c,v 1.1 2005/02/28 05:34:34 alanmi Exp $]

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

#include "fraigInt.h"

ABC_NAMESPACE_IMPL_START


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

////////////////////////////////////////////////////////////////////////
///                     FUNCTION DEFITIONS                           ///
////////////////////////////////////////////////////////////////////////

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

  Synopsis    [Adds choice nodes based on associativity.]

  Description [Make nLimit big AND gates and add all decompositions
               to the Fraig.]
               
  SideEffects []

  SeeAlso     []

***********************************************************************/
void Fraig_ManAddChoices( Fraig_Man_t * pMan, int fVerbose, int nLimit )
{
//    ProgressBar * pProgress;
    char Buffer[100];
    abctime clkTotal = Abc_Clock();
    int i, nNodesBefore, nNodesAfter, nInputs, nMaxNodes;
    int /*nMaxLevel,*/ nDistributive;
    Fraig_Node_t *pNode, *pRepr;
    Fraig_Node_t *pX, *pA, *pB, *pC, /* *pD,*/ *pN, /* *pQ, *pR,*/ *pT;
    int fShortCut = 0;

    nDistributive = 0;

//    Fraig_ManSetApprox( pMan, 1 );

    // NO functional reduction
    if (fShortCut) Fraig_ManSetFuncRed( pMan, 0 );

    // First we mark critical functions i.e. compute those
    // nodes which lie on the critical path. Note that this
    // doesn't update the required times on any choice nodes
    // which are not the representatives
/*
    nMaxLevel = Fraig_GetMaxLevel( pMan );
    for ( i = 0; i < pMan->nOutputs; i++ )
    {
        Fraig_SetNodeRequired( pMan, pMan->pOutputs[i], nMaxLevel );
    }
*/
    nNodesBefore = Fraig_ManReadNodeNum( pMan );
    nInputs      = Fraig_ManReadInputNum( pMan );
    nMaxNodes    = nInputs + nLimit * ( nNodesBefore - nInputs );

    printf ("Limit = %d, Before = %d\n", nMaxNodes, nNodesBefore );

    if (0) 
    {
        char buffer[128];
        sprintf (buffer, "test" );
//        Fraig_MappingShow( pMan, buffer );
    }

//    pProgress = Extra_ProgressBarStart( stdout, nMaxNodes );
Fraig_ManCheckConsistency( pMan );

    for ( i = nInputs+1; (i < Fraig_ManReadNodeNum( pMan )) 
            && (nMaxNodes > Fraig_ManReadNodeNum( pMan )); ++i )
    {
//        if ( i == nNodesBefore )
//            break;

        pNode = Fraig_ManReadIthNode( pMan, i );
        assert ( pNode );

        pRepr = pNode->pRepr ? pNode->pRepr : pNode;
        //printf ("Slack: %d\n", Fraig_NodeReadSlack( pRepr ));
        
        // All the new associative choices we add will have huge slack
        // since we do not redo timing, and timing doesnt handle choices
        // well anyway. However every newly added node is a choice of an
        // existing critical node, so they are considered critical.
//        if ( (Fraig_NodeReadSlack( pRepr ) > 3) && (i < nNodesBefore)  )     
//            continue;

//        if ( pNode->pRepr )
//            continue;

        // Try ((ab)c), x = ab -> (a(bc)) and (b(ac))
        pX = Fraig_NodeReadOne(pNode);
        pC = Fraig_NodeReadTwo(pNode);
        if (Fraig_NodeIsAnd(pX) && !Fraig_IsComplement(pX))
        {
            pA = Fraig_NodeReadOne(Fraig_Regular(pX));
            pB = Fraig_NodeReadTwo(Fraig_Regular(pX));

//            pA = Fraig_NodeGetRepr( pA );
//            pB = Fraig_NodeGetRepr( pB );
//            pC = Fraig_NodeGetRepr( pC );

            if (fShortCut) 
            {
                pT = Fraig_NodeAnd(pMan, pB, pC);
                if ( !pT->pRepr )
                {
                    pN = Fraig_NodeAnd(pMan, pA, pT);
//                    Fraig_NodeAddChoice( pMan, pNode, pN );
                }
            }
            else
                pN = Fraig_NodeAnd(pMan, pA, Fraig_NodeAnd(pMan, pB, pC));
            // assert ( Fraig_NodesEqual(pN, pNode) );


            if (fShortCut) 
            {
                pT = Fraig_NodeAnd(pMan, pA, pC);
                if ( !pT->pRepr )
                {
                    pN = Fraig_NodeAnd(pMan, pB, pT);
//                    Fraig_NodeAddChoice( pMan, pNode, pN );
                }
            }
            else
                pN = Fraig_NodeAnd(pMan, pB, Fraig_NodeAnd(pMan, pA, pC));
            // assert ( Fraig_NodesEqual(pN, pNode) );
        }


        // Try (a(bc)), x = bc -> ((ab)c) and ((ac)b)
        pA = Fraig_NodeReadOne(pNode);
        pX = Fraig_NodeReadTwo(pNode);
        if (Fraig_NodeIsAnd(pX) && !Fraig_IsComplement(pX))
        {
            pB = Fraig_NodeReadOne(Fraig_Regular(pX));
            pC = Fraig_NodeReadTwo(Fraig_Regular(pX));

//            pA = Fraig_NodeGetRepr( pA );
//            pB = Fraig_NodeGetRepr( pB );
//            pC = Fraig_NodeGetRepr( pC );

            if (fShortCut) 
            {
                pT = Fraig_NodeAnd(pMan, pA, pB);
                if ( !pT->pRepr )
                {
                    pN = Fraig_NodeAnd(pMan, pC, pT);
//                    Fraig_NodeAddChoice( pMan, pNode, pN );
                }
            }
            else
                pN = Fraig_NodeAnd(pMan, Fraig_NodeAnd(pMan, pA, pB), pC);
            // assert ( Fraig_NodesEqual(pN, pNode) );

            if (fShortCut) 
            {
                pT = Fraig_NodeAnd(pMan, pA, pC);
                if ( !pT->pRepr )
                {
                    pN = Fraig_NodeAnd(pMan, pB, pT);
//                    Fraig_NodeAddChoice( pMan, pNode, pN );
                }
            }
            else
                pN = Fraig_NodeAnd(pMan, Fraig_NodeAnd(pMan, pA, pC), pB);
            // assert ( Fraig_NodesEqual(pN, pNode) );
        }


/*
        // Try distributive transform
        pQ = Fraig_NodeReadOne(pNode);
        pR = Fraig_NodeReadTwo(pNode);
        if ( (Fraig_IsComplement(pQ) && Fraig_NodeIsAnd(pQ))
             && (Fraig_IsComplement(pR) && Fraig_NodeIsAnd(pR)) )
        {
            pA = Fraig_NodeReadOne(Fraig_Regular(pQ));
            pB = Fraig_NodeReadTwo(Fraig_Regular(pQ));
            pC = Fraig_NodeReadOne(Fraig_Regular(pR));
            pD = Fraig_NodeReadTwo(Fraig_Regular(pR));

            // Now detect the !(xy + xz) pattern, store 
            // x in pA, y in pB and z in pC and set pD = 0 to indicate
            // pattern was found
            assert (pD != 0);
            if (pA == pC) { pC = pD;                   pD = 0; } 
            if (pA == pD) {                            pD = 0; } 
            if (pB == pC) { pB = pA; pA = pC; pC = pD; pD = 0; }
            if (pB == pD) { pB = pA; pA = pD;          pD = 0; }
            if (pD == 0)
            {
                nDistributive++;
                pN = Fraig_Not(Fraig_NodeAnd(pMan, pA, 
                        Fraig_NodeOr(pMan, pB, pC)));
                if (fShortCut) Fraig_NodeAddChoice( pMan, pNode, pN );
                // assert ( Fraig_NodesEqual(pN, pNode) );
            }
        }
*/            
        if ( i % 1000 == 0 )
        {
            sprintf( Buffer, "Adding choice %6d...", i - nNodesBefore );
//            Extra_ProgressBarUpdate( pProgress, i, Buffer );
        }
    }

//    Extra_ProgressBarStop( pProgress );

Fraig_ManCheckConsistency( pMan );

    nNodesAfter = Fraig_ManReadNodeNum( pMan );
    printf ( "Nodes before = %6d. Nodes with associative choices = %6d. Increase = %4.2f %%.\n", 
            nNodesBefore, nNodesAfter, ((float)(nNodesAfter - nNodesBefore)) * 100.0/(nNodesBefore - nInputs) );
    printf ( "Distributive = %d\n", nDistributive );

}

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


ABC_NAMESPACE_IMPL_END