| /**CFile**************************************************************** |
| |
| FileName [mfsResub.c] |
| |
| SystemName [ABC: Logic synthesis and verification system.] |
| |
| PackageName [The good old minimization with complete don't-cares.] |
| |
| Synopsis [Procedures to perform resubstitution.] |
| |
| Author [Alan Mishchenko] |
| |
| Affiliation [UC Berkeley] |
| |
| Date [Ver. 1.0. Started - June 20, 2005.] |
| |
| Revision [$Id: mfsResub.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] |
| |
| ***********************************************************************/ |
| |
| #include "mfsInt.h" |
| |
| ABC_NAMESPACE_IMPL_START |
| |
| |
| //////////////////////////////////////////////////////////////////////// |
| /// DECLARATIONS /// |
| //////////////////////////////////////////////////////////////////////// |
| |
| //////////////////////////////////////////////////////////////////////// |
| /// FUNCTION DEFINITIONS /// |
| //////////////////////////////////////////////////////////////////////// |
| |
| /**Function************************************************************* |
| |
| Synopsis [Updates the network after resubstitution.] |
| |
| Description [] |
| |
| SideEffects [] |
| |
| SeeAlso [] |
| |
| ***********************************************************************/ |
| void Abc_NtkMfsUpdateNetwork( Mfs_Man_t * p, Abc_Obj_t * pObj, Vec_Ptr_t * vMfsFanins, Hop_Obj_t * pFunc ) |
| { |
| Abc_Obj_t * pObjNew, * pFanin; |
| int k; |
| // create the new node |
| pObjNew = Abc_NtkCreateNode( pObj->pNtk ); |
| pObjNew->pData = pFunc; |
| Vec_PtrForEachEntry( Abc_Obj_t *, vMfsFanins, pFanin, k ) |
| Abc_ObjAddFanin( pObjNew, pFanin ); |
| // replace the old node by the new node |
| //printf( "Replacing node " ); Abc_ObjPrint( stdout, pObj ); |
| //printf( "Inserting node " ); Abc_ObjPrint( stdout, pObjNew ); |
| // update the level of the node |
| Abc_NtkUpdate( pObj, pObjNew, p->vLevels ); |
| } |
| |
| /**Function************************************************************* |
| |
| Synopsis [Prints resub candidate stats.] |
| |
| Description [] |
| |
| SideEffects [] |
| |
| SeeAlso [] |
| |
| ***********************************************************************/ |
| void Abc_NtkMfsPrintResubStats( Mfs_Man_t * p ) |
| { |
| Abc_Obj_t * pFanin, * pNode; |
| int i, k, nAreaCrits = 0, nAreaExpanse = 0; |
| int nFaninMax = Abc_NtkGetFaninMax(p->pNtk); |
| Abc_NtkForEachNode( p->pNtk, pNode, i ) |
| Abc_ObjForEachFanin( pNode, pFanin, k ) |
| { |
| if ( !Abc_ObjIsCi(pFanin) && Abc_ObjFanoutNum(pFanin) == 1 ) |
| { |
| nAreaCrits++; |
| nAreaExpanse += (int)(Abc_ObjFaninNum(pNode) < nFaninMax); |
| } |
| } |
| // printf( "Total area-critical fanins = %d. Belonging to expandable nodes = %d.\n", |
| // nAreaCrits, nAreaExpanse ); |
| } |
| |
| /**Function************************************************************* |
| |
| Synopsis [Tries resubstitution.] |
| |
| Description [Returns 1 if it is feasible, or 0 if c-ex is found.] |
| |
| SideEffects [] |
| |
| SeeAlso [] |
| |
| ***********************************************************************/ |
| int Abc_NtkMfsTryResubOnce( Mfs_Man_t * p, int * pCands, int nCands ) |
| { |
| int fVeryVerbose = 0; |
| unsigned * pData; |
| int RetValue, RetValue2 = -1, iVar, i;//, clk = Abc_Clock(); |
| /* |
| if ( p->pPars->fGiaSat ) |
| { |
| RetValue2 = Abc_NtkMfsTryResubOnceGia( p, pCands, nCands ); |
| p->timeGia += Abc_Clock() - clk; |
| return RetValue2; |
| } |
| */ |
| p->nSatCalls++; |
| RetValue = sat_solver_solve( p->pSat, pCands, pCands + nCands, (ABC_INT64_T)p->pPars->nBTLimit, (ABC_INT64_T)0, (ABC_INT64_T)0, (ABC_INT64_T)0 ); |
| // assert( RetValue == l_False || RetValue == l_True ); |
| |
| if ( RetValue != l_Undef && RetValue2 != -1 ) |
| { |
| assert( (RetValue == l_False) == (RetValue2 == 1) ); |
| } |
| |
| if ( RetValue == l_False ) |
| { |
| if ( fVeryVerbose ) |
| printf( "U " ); |
| return 1; |
| } |
| if ( RetValue != l_True ) |
| { |
| if ( fVeryVerbose ) |
| printf( "T " ); |
| p->nTimeOuts++; |
| return -1; |
| } |
| if ( fVeryVerbose ) |
| printf( "S " ); |
| p->nSatCexes++; |
| // store the counter-example |
| Vec_IntForEachEntry( p->vProjVarsSat, iVar, i ) |
| { |
| pData = (unsigned *)Vec_PtrEntry( p->vDivCexes, i ); |
| if ( !sat_solver_var_value( p->pSat, iVar ) ) // remove 0s!!! |
| { |
| assert( Abc_InfoHasBit(pData, p->nCexes) ); |
| Abc_InfoXorBit( pData, p->nCexes ); |
| } |
| } |
| p->nCexes++; |
| return 0; |
| |
| } |
| |
| /**Function************************************************************* |
| |
| Synopsis [Performs resubstitution for the node.] |
| |
| Description [] |
| |
| SideEffects [] |
| |
| SeeAlso [] |
| |
| ***********************************************************************/ |
| int Abc_NtkMfsSolveSatResub( Mfs_Man_t * p, Abc_Obj_t * pNode, int iFanin, int fOnlyRemove, int fSkipUpdate ) |
| { |
| int fVeryVerbose = 0;//p->pPars->fVeryVerbose && Vec_PtrSize(p->vDivs) < 200;// || pNode->Id == 556; |
| unsigned * pData; |
| int pCands[MFS_FANIN_MAX]; |
| int RetValue, iVar, i, nCands, nWords, w; |
| abctime clk; |
| Abc_Obj_t * pFanin; |
| Hop_Obj_t * pFunc; |
| assert( iFanin >= 0 ); |
| p->nTryRemoves++; |
| |
| // clean simulation info |
| Vec_PtrFillSimInfo( p->vDivCexes, 0, p->nDivWords ); |
| p->nCexes = 0; |
| if ( p->pPars->fVeryVerbose ) |
| { |
| // printf( "\n" ); |
| printf( "%5d : Lev =%3d. Leaf =%3d. Node =%3d. Divs =%3d. Fanin = %4d (%d/%d), MFFC = %d\n", |
| pNode->Id, pNode->Level, Vec_PtrSize(p->vSupp), Vec_PtrSize(p->vNodes), Vec_PtrSize(p->vDivs)-Abc_ObjFaninNum(pNode), |
| Abc_ObjFaninId(pNode, iFanin), iFanin, Abc_ObjFaninNum(pNode), |
| Abc_ObjFanoutNum(Abc_ObjFanin(pNode, iFanin)) == 1 ? Abc_NodeMffcLabel(Abc_ObjFanin(pNode, iFanin)) : 0 ); |
| } |
| |
| // try fanins without the critical fanin |
| nCands = 0; |
| Vec_PtrClear( p->vMfsFanins ); |
| Abc_ObjForEachFanin( pNode, pFanin, i ) |
| { |
| if ( i == iFanin ) |
| continue; |
| Vec_PtrPush( p->vMfsFanins, pFanin ); |
| iVar = Vec_PtrSize(p->vDivs) - Abc_ObjFaninNum(pNode) + i; |
| pCands[nCands++] = toLitCond( Vec_IntEntry( p->vProjVarsSat, iVar ), 1 ); |
| } |
| RetValue = Abc_NtkMfsTryResubOnce( p, pCands, nCands ); |
| if ( RetValue == -1 ) |
| return 0; |
| if ( RetValue == 1 ) |
| { |
| if ( p->pPars->fVeryVerbose ) |
| printf( "Node %d: Fanin %d can be removed.\n", pNode->Id, iFanin ); |
| p->nNodesResub++; |
| p->nNodesGainedLevel++; |
| if ( fSkipUpdate ) |
| return 1; |
| clk = Abc_Clock(); |
| // derive the function |
| pFunc = Abc_NtkMfsInterplate( p, pCands, nCands ); |
| if ( pFunc == NULL ) |
| return 0; |
| // update the network |
| Abc_NtkMfsUpdateNetwork( p, pNode, p->vMfsFanins, pFunc ); |
| p->timeInt += Abc_Clock() - clk; |
| p->nRemoves++; |
| return 1; |
| } |
| |
| if ( fOnlyRemove || p->pPars->fRrOnly ) |
| return 0; |
| |
| p->nTryResubs++; |
| if ( fVeryVerbose ) |
| { |
| for ( i = 0; i < 9; i++ ) |
| printf( " " ); |
| for ( i = 0; i < Vec_PtrSize(p->vDivs)-Abc_ObjFaninNum(pNode); i++ ) |
| printf( "%d", i % 10 ); |
| for ( i = 0; i < Abc_ObjFaninNum(pNode); i++ ) |
| if ( i == iFanin ) |
| printf( "*" ); |
| else |
| printf( "%c", 'a' + i ); |
| printf( "\n" ); |
| } |
| iVar = -1; |
| while ( 1 ) |
| { |
| if ( fVeryVerbose ) |
| { |
| printf( "%3d: %3d ", p->nCexes, iVar ); |
| for ( i = 0; i < Vec_PtrSize(p->vDivs); i++ ) |
| { |
| pData = (unsigned *)Vec_PtrEntry( p->vDivCexes, i ); |
| printf( "%d", Abc_InfoHasBit(pData, p->nCexes-1) ); |
| } |
| printf( "\n" ); |
| } |
| |
| // find the next divisor to try |
| nWords = Abc_BitWordNum(p->nCexes); |
| assert( nWords <= p->nDivWords ); |
| for ( iVar = 0; iVar < Vec_PtrSize(p->vDivs)-Abc_ObjFaninNum(pNode); iVar++ ) |
| { |
| if ( p->pPars->fPower ) |
| { |
| Abc_Obj_t * pDiv = (Abc_Obj_t *)Vec_PtrEntry(p->vDivs, iVar); |
| // only accept the divisor if it is "cool" |
| if ( Abc_MfsObjProb(p, pDiv) >= 0.15 ) |
| continue; |
| } |
| pData = (unsigned *)Vec_PtrEntry( p->vDivCexes, iVar ); |
| for ( w = 0; w < nWords; w++ ) |
| if ( pData[w] != ~0 ) |
| break; |
| if ( w == nWords ) |
| break; |
| } |
| if ( iVar == Vec_PtrSize(p->vDivs)-Abc_ObjFaninNum(pNode) ) |
| return 0; |
| |
| pCands[nCands] = toLitCond( Vec_IntEntry(p->vProjVarsSat, iVar), 1 ); |
| RetValue = Abc_NtkMfsTryResubOnce( p, pCands, nCands+1 ); |
| if ( RetValue == -1 ) |
| return 0; |
| if ( RetValue == 1 ) |
| { |
| if ( p->pPars->fVeryVerbose ) |
| printf( "Node %d: Fanin %d can be replaced by divisor %d.\n", pNode->Id, iFanin, iVar ); |
| p->nNodesResub++; |
| p->nNodesGainedLevel++; |
| if ( fSkipUpdate ) |
| return 1; |
| clk = Abc_Clock(); |
| // derive the function |
| pFunc = Abc_NtkMfsInterplate( p, pCands, nCands+1 ); |
| if ( pFunc == NULL ) |
| return 0; |
| // update the network |
| Vec_PtrPush( p->vMfsFanins, Vec_PtrEntry(p->vDivs, iVar) ); |
| Abc_NtkMfsUpdateNetwork( p, pNode, p->vMfsFanins, pFunc ); |
| p->timeInt += Abc_Clock() - clk; |
| p->nResubs++; |
| return 1; |
| } |
| if ( p->nCexes >= p->pPars->nWinMax ) |
| break; |
| } |
| if ( p->pPars->fVeryVerbose ) |
| printf( "Node %d: Cannot find replacement for fanin %d.\n", pNode->Id, iFanin ); |
| return 0; |
| } |
| |
| /**Function************************************************************* |
| |
| Synopsis [Performs resubstitution for the node.] |
| |
| Description [] |
| |
| SideEffects [] |
| |
| SeeAlso [] |
| |
| ***********************************************************************/ |
| int Abc_NtkMfsSolveSatResub2( Mfs_Man_t * p, Abc_Obj_t * pNode, int iFanin, int iFanin2 ) |
| { |
| int fVeryVerbose = p->pPars->fVeryVerbose && Vec_PtrSize(p->vDivs) < 80; |
| unsigned * pData, * pData2; |
| int pCands[MFS_FANIN_MAX]; |
| int RetValue, iVar, iVar2, i, w, nCands, nWords, fBreak; |
| abctime clk; |
| Abc_Obj_t * pFanin; |
| Hop_Obj_t * pFunc; |
| assert( iFanin >= 0 ); |
| assert( iFanin2 >= 0 || iFanin2 == -1 ); |
| |
| // clean simulation info |
| Vec_PtrFillSimInfo( p->vDivCexes, 0, p->nDivWords ); |
| p->nCexes = 0; |
| if ( fVeryVerbose ) |
| { |
| printf( "\n" ); |
| printf( "Node %5d : Level = %2d. Divs = %3d. Fanins = %d/%d (out of %d). MFFC = %d\n", |
| pNode->Id, pNode->Level, Vec_PtrSize(p->vDivs)-Abc_ObjFaninNum(pNode), |
| iFanin, iFanin2, Abc_ObjFaninNum(pNode), |
| Abc_ObjFanoutNum(Abc_ObjFanin(pNode, iFanin)) == 1 ? Abc_NodeMffcLabel(Abc_ObjFanin(pNode, iFanin)) : 0 ); |
| } |
| |
| // try fanins without the critical fanin |
| nCands = 0; |
| Vec_PtrClear( p->vMfsFanins ); |
| Abc_ObjForEachFanin( pNode, pFanin, i ) |
| { |
| if ( i == iFanin || i == iFanin2 ) |
| continue; |
| Vec_PtrPush( p->vMfsFanins, pFanin ); |
| iVar = Vec_PtrSize(p->vDivs) - Abc_ObjFaninNum(pNode) + i; |
| pCands[nCands++] = toLitCond( Vec_IntEntry( p->vProjVarsSat, iVar ), 1 ); |
| } |
| RetValue = Abc_NtkMfsTryResubOnce( p, pCands, nCands ); |
| if ( RetValue == -1 ) |
| return 0; |
| if ( RetValue == 1 ) |
| { |
| if ( fVeryVerbose ) |
| printf( "Node %d: Fanins %d/%d can be removed.\n", pNode->Id, iFanin, iFanin2 ); |
| p->nNodesResub++; |
| p->nNodesGainedLevel++; |
| clk = Abc_Clock(); |
| // derive the function |
| pFunc = Abc_NtkMfsInterplate( p, pCands, nCands ); |
| if ( pFunc == NULL ) |
| return 0; |
| // update the network |
| Abc_NtkMfsUpdateNetwork( p, pNode, p->vMfsFanins, pFunc ); |
| p->timeInt += Abc_Clock() - clk; |
| return 1; |
| } |
| |
| if ( fVeryVerbose ) |
| { |
| for ( i = 0; i < 11; i++ ) |
| printf( " " ); |
| for ( i = 0; i < Vec_PtrSize(p->vDivs)-Abc_ObjFaninNum(pNode); i++ ) |
| printf( "%d", i % 10 ); |
| for ( i = 0; i < Abc_ObjFaninNum(pNode); i++ ) |
| if ( i == iFanin || i == iFanin2 ) |
| printf( "*" ); |
| else |
| printf( "%c", 'a' + i ); |
| printf( "\n" ); |
| } |
| iVar = iVar2 = -1; |
| while ( 1 ) |
| { |
| #if 1 // sjang |
| #endif |
| if ( fVeryVerbose ) |
| { |
| printf( "%3d: %2d %2d ", p->nCexes, iVar, iVar2 ); |
| for ( i = 0; i < Vec_PtrSize(p->vDivs); i++ ) |
| { |
| pData = (unsigned *)Vec_PtrEntry( p->vDivCexes, i ); |
| printf( "%d", Abc_InfoHasBit(pData, p->nCexes-1) ); |
| } |
| printf( "\n" ); |
| } |
| |
| // find the next divisor to try |
| nWords = Abc_BitWordNum(p->nCexes); |
| assert( nWords <= p->nDivWords ); |
| fBreak = 0; |
| for ( iVar = 1; iVar < Vec_PtrSize(p->vDivs)-Abc_ObjFaninNum(pNode); iVar++ ) |
| { |
| pData = (unsigned *)Vec_PtrEntry( p->vDivCexes, iVar ); |
| #if 1 // sjang |
| if ( p->pPars->fPower ) |
| { |
| Abc_Obj_t * pDiv = (Abc_Obj_t *)Vec_PtrEntry(p->vDivs, iVar); |
| // only accept the divisor if it is "cool" |
| if ( Abc_MfsObjProb(p, pDiv) >= 0.12 ) |
| continue; |
| } |
| #endif |
| for ( iVar2 = 0; iVar2 < iVar; iVar2++ ) |
| { |
| pData2 = (unsigned *)Vec_PtrEntry( p->vDivCexes, iVar2 ); |
| #if 1 // sjang |
| if ( p->pPars->fPower ) |
| { |
| Abc_Obj_t * pDiv = (Abc_Obj_t *)Vec_PtrEntry(p->vDivs, iVar2); |
| // only accept the divisor if it is "cool" |
| if ( Abc_MfsObjProb(p, pDiv) >= 0.12 ) |
| continue; |
| } |
| #endif |
| for ( w = 0; w < nWords; w++ ) |
| if ( (pData[w] | pData2[w]) != ~0 ) |
| break; |
| if ( w == nWords ) |
| { |
| fBreak = 1; |
| break; |
| } |
| } |
| if ( fBreak ) |
| break; |
| } |
| if ( iVar == Vec_PtrSize(p->vDivs)-Abc_ObjFaninNum(pNode) ) |
| return 0; |
| |
| pCands[nCands] = toLitCond( Vec_IntEntry(p->vProjVarsSat, iVar2), 1 ); |
| pCands[nCands+1] = toLitCond( Vec_IntEntry(p->vProjVarsSat, iVar), 1 ); |
| RetValue = Abc_NtkMfsTryResubOnce( p, pCands, nCands+2 ); |
| if ( RetValue == -1 ) |
| return 0; |
| if ( RetValue == 1 ) |
| { |
| if ( fVeryVerbose ) |
| printf( "Node %d: Fanins %d/%d can be replaced by divisors %d/%d.\n", pNode->Id, iFanin, iFanin2, iVar, iVar2 ); |
| p->nNodesResub++; |
| p->nNodesGainedLevel++; |
| clk = Abc_Clock(); |
| // derive the function |
| pFunc = Abc_NtkMfsInterplate( p, pCands, nCands+2 ); |
| if ( pFunc == NULL ) |
| return 0; |
| // update the network |
| Vec_PtrPush( p->vMfsFanins, Vec_PtrEntry(p->vDivs, iVar2) ); |
| Vec_PtrPush( p->vMfsFanins, Vec_PtrEntry(p->vDivs, iVar) ); |
| assert( Vec_PtrSize(p->vMfsFanins) == nCands + 2 ); |
| Abc_NtkMfsUpdateNetwork( p, pNode, p->vMfsFanins, pFunc ); |
| p->timeInt += Abc_Clock() - clk; |
| return 1; |
| } |
| if ( p->nCexes >= p->pPars->nWinMax ) |
| break; |
| } |
| return 0; |
| } |
| |
| |
| /**Function************************************************************* |
| |
| Synopsis [Evaluates the possibility of replacing given edge by another edge.] |
| |
| Description [] |
| |
| SideEffects [] |
| |
| SeeAlso [] |
| |
| ***********************************************************************/ |
| int Abc_NtkMfsEdgeSwapEval( Mfs_Man_t * p, Abc_Obj_t * pNode ) |
| { |
| Abc_Obj_t * pFanin; |
| int i; |
| Abc_ObjForEachFanin( pNode, pFanin, i ) |
| Abc_NtkMfsSolveSatResub( p, pNode, i, 0, 1 ); |
| return 0; |
| } |
| |
| /**Function************************************************************* |
| |
| Synopsis [Evaluates the possibility of replacing given edge by another edge.] |
| |
| Description [] |
| |
| SideEffects [] |
| |
| SeeAlso [] |
| |
| ***********************************************************************/ |
| int Abc_NtkMfsEdgePower( Mfs_Man_t * p, Abc_Obj_t * pNode ) |
| { |
| Abc_Obj_t * pFanin; |
| int i; |
| // try replacing area critical fanins |
| Abc_ObjForEachFanin( pNode, pFanin, i ) |
| { |
| if ( Abc_MfsObjProb(p, pFanin) >= 0.35 ) |
| { |
| if ( Abc_NtkMfsSolveSatResub( p, pNode, i, 0, 0 ) ) |
| return 1; |
| } else if ( Abc_MfsObjProb(p, pFanin) >= 0.25 ) // sjang |
| { |
| if ( Abc_NtkMfsSolveSatResub( p, pNode, i, 1, 0 ) ) |
| return 1; |
| } |
| } |
| return 0; |
| } |
| |
| /**Function************************************************************* |
| |
| Synopsis [Performs resubstitution for the node.] |
| |
| Description [] |
| |
| SideEffects [] |
| |
| SeeAlso [] |
| |
| ***********************************************************************/ |
| int Abc_NtkMfsResubNode( Mfs_Man_t * p, Abc_Obj_t * pNode ) |
| { |
| Abc_Obj_t * pFanin; |
| int i; |
| // try replacing area critical fanins |
| Abc_ObjForEachFanin( pNode, pFanin, i ) |
| if ( !Abc_ObjIsCi(pFanin) && Abc_ObjFanoutNum(pFanin) == 1 ) |
| { |
| if ( Abc_NtkMfsSolveSatResub( p, pNode, i, 0, 0 ) ) |
| return 1; |
| } |
| // try removing redundant edges |
| if ( !p->pPars->fArea ) |
| { |
| Abc_ObjForEachFanin( pNode, pFanin, i ) |
| if ( Abc_ObjIsCi(pFanin) || Abc_ObjFanoutNum(pFanin) != 1 ) |
| { |
| if ( Abc_NtkMfsSolveSatResub( p, pNode, i, 1, 0 ) ) |
| return 1; |
| } |
| } |
| if ( Abc_ObjFaninNum(pNode) == p->nFaninMax ) |
| return 0; |
| /* |
| // try replacing area critical fanins while adding two new fanins |
| Abc_ObjForEachFanin( pNode, pFanin, i ) |
| if ( !Abc_ObjIsCi(pFanin) && Abc_ObjFanoutNum(pFanin) == 1 ) |
| { |
| if ( Abc_NtkMfsSolveSatResub2( p, pNode, i, -1 ) ) |
| return 1; |
| } |
| */ |
| return 0; |
| } |
| |
| /**Function************************************************************* |
| |
| Synopsis [Performs resubstitution for the node.] |
| |
| Description [] |
| |
| SideEffects [] |
| |
| SeeAlso [] |
| |
| ***********************************************************************/ |
| int Abc_NtkMfsResubNode2( Mfs_Man_t * p, Abc_Obj_t * pNode ) |
| { |
| Abc_Obj_t * pFanin, * pFanin2; |
| int i, k; |
| /* |
| Abc_ObjForEachFanin( pNode, pFanin, i ) |
| if ( !Abc_ObjIsCi(pFanin) && Abc_ObjFanoutNum(pFanin) == 1 ) |
| { |
| if ( Abc_NtkMfsSolveSatResub( p, pNode, i, 0, 0 ) ) |
| return 1; |
| } |
| */ |
| if ( Abc_ObjFaninNum(pNode) < 2 ) |
| return 0; |
| // try replacing one area critical fanin and one other fanin while adding two new fanins |
| Abc_ObjForEachFanin( pNode, pFanin, i ) |
| { |
| if ( !Abc_ObjIsCi(pFanin) && Abc_ObjFanoutNum(pFanin) == 1 ) |
| { |
| // consider second fanin to remove at the same time |
| Abc_ObjForEachFanin( pNode, pFanin2, k ) |
| { |
| if ( i != k && Abc_NtkMfsSolveSatResub2( p, pNode, i, k ) ) |
| return 1; |
| } |
| } |
| } |
| return 0; |
| } |
| |
| |
| //////////////////////////////////////////////////////////////////////// |
| /// END OF FILE /// |
| //////////////////////////////////////////////////////////////////////// |
| |
| |
| ABC_NAMESPACE_IMPL_END |
| |