abc-master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
ifUtil.c File Reference
#include "if.h"

Go to the source code of this file.

Functions

ABC_NAMESPACE_IMPL_START void If_ManCleanNodeCopy (If_Man_t *p)
 DECLARATIONS ///. More...
 
void If_ManCleanCutData (If_Man_t *p)
 
void If_ManCleanMarkV (If_Man_t *p)
 
void If_ManResetOriginalRefs (If_Man_t *p)
 
int If_ManCrossCut (If_Man_t *p)
 
Vec_Ptr_tIf_ManReverseOrder (If_Man_t *p)
 
float If_ManMarkMapping_rec (If_Man_t *p, If_Obj_t *pObj)
 
void If_ManMarkMapping (If_Man_t *p)
 
Vec_Ptr_tIf_ManCollectMappingDirect (If_Man_t *p)
 
Vec_Int_tIf_ManCollectMappingInt (If_Man_t *p)
 
int If_ManCountSpecialPos (If_Man_t *p)
 
static void If_CutTraverse_rec (If_Obj_t *pNode, Vec_Ptr_t *vNodes)
 
void If_CutTraverse (If_Man_t *p, If_Obj_t *pRoot, If_Cut_t *pCut, Vec_Ptr_t *vNodes)
 
void If_CutTraverseTest (If_Man_t *p, If_Obj_t *pRoot, If_Cut_t *pCut)
 
void If_ObjPrint (If_Obj_t *pObj)
 

Function Documentation

void If_CutTraverse ( If_Man_t p,
If_Obj_t pRoot,
If_Cut_t pCut,
Vec_Ptr_t vNodes 
)

Definition at line 565 of file ifUtil.c.

566 {
567  If_Obj_t * pLeaf;
568  int i;
569  // collect the internal nodes of the cut
570  Vec_PtrClear( vNodes );
571  If_CutForEachLeaf( p, pCut, pLeaf, i )
572  {
573  Vec_PtrPush( vNodes, pLeaf );
574  assert( pLeaf->fMark == 0 );
575  pLeaf->fMark = 1;
576  }
577  // collect other nodes
578  If_CutTraverse_rec( pRoot, vNodes );
579  // clean the mark
580  Vec_PtrForEachEntry( If_Obj_t *, vNodes, pLeaf, i )
581  pLeaf->fMark = 0;
582 }
Definition: if.h:303
#define If_CutForEachLeaf(p, pCut, pLeaf, i)
Definition: if.h:474
static void Vec_PtrPush(Vec_Ptr_t *p, void *Entry)
Definition: vecPtr.h:606
static void If_CutTraverse_rec(If_Obj_t *pNode, Vec_Ptr_t *vNodes)
Definition: ifUtil.c:553
#define assert(ex)
Definition: util_old.h:213
static void Vec_PtrClear(Vec_Ptr_t *p)
Definition: vecPtr.h:545
#define Vec_PtrForEachEntry(Type, vVec, pEntry, i)
MACRO DEFINITIONS ///.
Definition: vecPtr.h:55
unsigned fMark
Definition: if.h:310
static void If_CutTraverse_rec ( If_Obj_t pNode,
Vec_Ptr_t vNodes 
)
static

Function*************************************************************

Synopsis [Traverse the cut and counts its volume.]

Description []

SideEffects []

SeeAlso []

Definition at line 553 of file ifUtil.c.

554 {
555  if ( pNode->fMark )
556  return;
557  pNode->fMark = 1;
558 // assert( !If_ObjIsCi(pNode) ); // does not hold with cut minimization
559  if ( If_ObjIsAnd(pNode) )
560  If_CutTraverse_rec( If_ObjFanin0(pNode), vNodes );
561  if ( If_ObjIsAnd(pNode) )
562  If_CutTraverse_rec( If_ObjFanin1(pNode), vNodes );
563  Vec_PtrPush( vNodes, pNode );
564 }
static int If_ObjIsAnd(If_Obj_t *pObj)
Definition: if.h:377
static If_Obj_t * If_ObjFanin0(If_Obj_t *pObj)
Definition: if.h:380
static void Vec_PtrPush(Vec_Ptr_t *p, void *Entry)
Definition: vecPtr.h:606
static void If_CutTraverse_rec(If_Obj_t *pNode, Vec_Ptr_t *vNodes)
Definition: ifUtil.c:553
static If_Obj_t * If_ObjFanin1(If_Obj_t *pObj)
Definition: if.h:381
unsigned fMark
Definition: if.h:310
void If_CutTraverseTest ( If_Man_t p,
If_Obj_t pRoot,
If_Cut_t pCut 
)

Definition at line 583 of file ifUtil.c.

584 {
585  Vec_Ptr_t * vNodes;
586  vNodes = Vec_PtrAlloc( 1000 );
587  If_CutTraverse( p, pRoot, pCut, vNodes );
588 //if ( Vec_PtrSize(vNodes) > 30 )
589 //printf( "%d ", Vec_PtrSize(vNodes) );
590  Vec_PtrFree( vNodes );
591 }
typedefABC_NAMESPACE_HEADER_START struct Vec_Ptr_t_ Vec_Ptr_t
INCLUDES ///.
Definition: vecPtr.h:42
void If_CutTraverse(If_Man_t *p, If_Obj_t *pRoot, If_Cut_t *pCut, Vec_Ptr_t *vNodes)
Definition: ifUtil.c:565
static Vec_Ptr_t * Vec_PtrAlloc(int nCap)
FUNCTION DEFINITIONS ///.
Definition: vecPtr.h:83
static void Vec_PtrFree(Vec_Ptr_t *p)
Definition: vecPtr.h:223
void If_ManCleanCutData ( If_Man_t p)

Function*************************************************************

Synopsis [Sets all the cut data to NULL.]

Description []

SideEffects []

SeeAlso []

Definition at line 64 of file ifUtil.c.

65 {
66  If_Obj_t * pObj;
67  int i;
68  If_ManForEachObj( p, pObj, i )
69  If_CutSetData( If_ObjCutBest(pObj), NULL );
70 }
Definition: if.h:303
static void If_CutSetData(If_Cut_t *pCut, void *pData)
Definition: if.h:412
static If_Cut_t * If_ObjCutBest(If_Obj_t *pObj)
Definition: if.h:401
#define If_ManForEachObj(p, pObj, i)
Definition: if.h:462
void If_ManCleanMarkV ( If_Man_t p)

Function*************************************************************

Synopsis [Sets all visited marks to 0.]

Description []

SideEffects []

SeeAlso []

Definition at line 83 of file ifUtil.c.

84 {
85  If_Obj_t * pObj;
86  int i;
87  If_ManForEachObj( p, pObj, i )
88  pObj->fVisit = 0;
89 }
Definition: if.h:303
#define If_ManForEachObj(p, pObj, i)
Definition: if.h:462
ABC_NAMESPACE_IMPL_START void If_ManCleanNodeCopy ( If_Man_t p)

DECLARATIONS ///.

CFile****************************************************************

FileName [ifUtil.c]

SystemName [ABC: Logic synthesis and verification system.]

PackageName [FPGA mapping based on priority cuts.]

Synopsis [Various utilities.]

Author [Alan Mishchenko]

Affiliation [UC Berkeley]

Date [Ver. 1.0. Started - November 21, 2006.]

Revision [

Id:
ifUtil.c,v 1.00 2006/11/21 00:00:00 alanmi Exp

]FUNCTION DEFINITIONS /// Function*************************************************************

Synopsis [Sets all the node copy to NULL.]

Description []

SideEffects []

SeeAlso []

Definition at line 45 of file ifUtil.c.

46 {
47  If_Obj_t * pObj;
48  int i;
49  If_ManForEachObj( p, pObj, i )
50  If_ObjSetCopy( pObj, NULL );
51 }
Definition: if.h:303
#define If_ManForEachObj(p, pObj, i)
Definition: if.h:462
static void If_ObjSetCopy(If_Obj_t *pObj, void *pCopy)
Definition: if.h:387
Vec_Ptr_t* If_ManCollectMappingDirect ( If_Man_t p)

Function*************************************************************

Synopsis [Collects nodes used in the mapping in the topological order.]

Description []

SideEffects []

SeeAlso []

Definition at line 462 of file ifUtil.c.

463 {
464  Vec_Ptr_t * vOrder;
465  If_Obj_t * pObj;
466  int i;
467  If_ManMarkMapping( p );
468  vOrder = Vec_PtrAlloc( If_ManObjNum(p) );
469  If_ManForEachObj( p, pObj, i )
470  if ( If_ObjIsAnd(pObj) && pObj->nRefs )
471  Vec_PtrPush( vOrder, pObj );
472  return vOrder;
473 }
typedefABC_NAMESPACE_HEADER_START struct Vec_Ptr_t_ Vec_Ptr_t
INCLUDES ///.
Definition: vecPtr.h:42
Definition: if.h:303
static int If_ObjIsAnd(If_Obj_t *pObj)
Definition: if.h:377
void If_ManMarkMapping(If_Man_t *p)
Definition: ifUtil.c:434
static void Vec_PtrPush(Vec_Ptr_t *p, void *Entry)
Definition: vecPtr.h:606
#define If_ManForEachObj(p, pObj, i)
Definition: if.h:462
if(last==0)
Definition: sparse_int.h:34
static int If_ManObjNum(If_Man_t *p)
Definition: if.h:363
static Vec_Ptr_t * Vec_PtrAlloc(int nCap)
FUNCTION DEFINITIONS ///.
Definition: vecPtr.h:83
Vec_Int_t* If_ManCollectMappingInt ( If_Man_t p)

Function*************************************************************

Synopsis [Collects nodes used in the mapping in the topological order.]

Description [Represents mapping as an array of integers.]

SideEffects []

SeeAlso []

Definition at line 486 of file ifUtil.c.

487 {
488  Vec_Int_t * vOrder;
489  If_Cut_t * pCutBest;
490  If_Obj_t * pObj;
491  int i, k, nLeaves, * ppLeaves;
492  If_ManMarkMapping( p );
493  vOrder = Vec_IntAlloc( If_ManObjNum(p) );
494  If_ManForEachObj( p, pObj, i )
495  if ( If_ObjIsAnd(pObj) && pObj->nRefs )
496  {
497  pCutBest = If_ObjCutBest( pObj );
498  nLeaves = If_CutLeaveNum( pCutBest );
499  ppLeaves = If_CutLeaves( pCutBest );
500  // save the number of leaves, the leaves, and finally, the root
501  Vec_IntPush( vOrder, nLeaves );
502  for ( k = 0; k < nLeaves; k++ )
503  Vec_IntPush( vOrder, ppLeaves[k] );
504  Vec_IntPush( vOrder, pObj->Id );
505  }
506  return vOrder;
507 }
int Id
Definition: if.h:316
typedefABC_NAMESPACE_IMPL_START struct Vec_Int_t_ Vec_Int_t
DECLARATIONS ///.
Definition: bblif.c:37
Definition: if.h:303
static int If_ObjIsAnd(If_Obj_t *pObj)
Definition: if.h:377
Definition: if.h:275
static int If_CutLeaveNum(If_Cut_t *pCut)
Definition: if.h:390
void If_ManMarkMapping(If_Man_t *p)
Definition: ifUtil.c:434
static If_Cut_t * If_ObjCutBest(If_Obj_t *pObj)
Definition: if.h:401
#define If_ManForEachObj(p, pObj, i)
Definition: if.h:462
static Vec_Int_t * Vec_IntAlloc(int nCap)
FUNCTION DEFINITIONS ///.
Definition: bblif.c:149
static int * If_CutLeaves(If_Cut_t *pCut)
Definition: if.h:391
if(last==0)
Definition: sparse_int.h:34
static void Vec_IntPush(Vec_Int_t *p, int Entry)
Definition: bblif.c:468
static int If_ManObjNum(If_Man_t *p)
Definition: if.h:363
int If_ManCountSpecialPos ( If_Man_t p)

Function*************************************************************

Synopsis [Returns the number of POs pointing to the same internal nodes.]

Description []

SideEffects []

SeeAlso []

Definition at line 520 of file ifUtil.c.

521 {
522  If_Obj_t * pObj;
523  int i, Counter = 0;
524  // clean all marks
525  If_ManForEachPo( p, pObj, i )
526  If_ObjFanin0(pObj)->fMark = 0;
527  // label nodes
528  If_ManForEachPo( p, pObj, i )
529  if ( !If_ObjFaninC0(pObj) )
530  If_ObjFanin0(pObj)->fMark = 1;
531  // label nodes
532  If_ManForEachPo( p, pObj, i )
533  if ( If_ObjFaninC0(pObj) )
534  Counter += If_ObjFanin0(pObj)->fMark;
535  // clean all marks
536  If_ManForEachPo( p, pObj, i )
537  If_ObjFanin0(pObj)->fMark = 0;
538  return Counter;
539 }
Definition: if.h:303
static int If_ObjFaninC0(If_Obj_t *pObj)
Definition: if.h:382
static If_Obj_t * If_ObjFanin0(If_Obj_t *pObj)
Definition: if.h:380
#define If_ManForEachPo(p, pObj, i)
Definition: if.h:454
if(last==0)
Definition: sparse_int.h:34
static int Counter
int If_ManCrossCut ( If_Man_t p)

Function*************************************************************

Synopsis [Computes cross-cut of the circuit.]

Description []

SideEffects []

SeeAlso []

Definition at line 316 of file ifUtil.c.

317 {
318  If_Obj_t * pObj, * pFanin;
319  int i, nCutSize = 0, nCutSizeMax = 0;
320  If_ManForEachObj( p, pObj, i )
321  {
322  if ( !If_ObjIsAnd(pObj) )
323  continue;
324  // consider the node
325  if ( nCutSizeMax < ++nCutSize )
326  nCutSizeMax = nCutSize;
327  if ( pObj->nVisits == 0 )
328  nCutSize--;
329  // consider the fanins
330  pFanin = If_ObjFanin0(pObj);
331  if ( !If_ObjIsCi(pFanin) && --pFanin->nVisits == 0 )
332  nCutSize--;
333  pFanin = If_ObjFanin1(pObj);
334  if ( !If_ObjIsCi(pFanin) && --pFanin->nVisits == 0 )
335  nCutSize--;
336  // consider the choice class
337  if ( pObj->fRepr )
338  for ( pFanin = pObj; pFanin; pFanin = pFanin->pEquiv )
339  if ( !If_ObjIsCi(pFanin) && --pFanin->nVisits == 0 )
340  nCutSize--;
341  }
342  If_ManForEachObj( p, pObj, i )
343  {
344  assert( If_ObjIsCi(pObj) || pObj->fVisit == 0 );
345  pObj->nVisits = pObj->nVisitsCopy;
346  }
347  assert( nCutSize == 0 );
348 // Abc_Print( 1, "Max cross cut size = %6d.\n", nCutSizeMax );
349  return nCutSizeMax;
350 }
Definition: if.h:303
static int If_ObjIsAnd(If_Obj_t *pObj)
Definition: if.h:377
unsigned fRepr
Definition: if.h:309
static If_Obj_t * If_ObjFanin0(If_Obj_t *pObj)
Definition: if.h:380
static int If_ObjIsCi(If_Obj_t *pObj)
Definition: if.h:373
#define If_ManForEachObj(p, pObj, i)
Definition: if.h:462
unsigned fVisit
Definition: if.h:311
static If_Obj_t * If_ObjFanin1(If_Obj_t *pObj)
Definition: if.h:381
If_Obj_t * pEquiv
Definition: if.h:323
int nVisitsCopy
Definition: if.h:320
int nVisits
Definition: if.h:319
#define assert(ex)
Definition: util_old.h:213
void If_ManMarkMapping ( If_Man_t p)

Function*************************************************************

Synopsis [Computes area, references, and nodes used in the mapping.]

Description []

SideEffects []

SeeAlso []

Definition at line 434 of file ifUtil.c.

435 {
436  If_Obj_t * pObj;
437  int i;
438  If_ManForEachObj( p, pObj, i )
439  {
440  pObj->Required = IF_FLOAT_LARGE;
441  pObj->nVisits = pObj->nVisitsCopy;
442  pObj->nRefs = 0;
443  }
444  p->nNets = 0;
445  p->dPower = 0.0;
446  p->AreaGlo = 0.0;
447  If_ManForEachCo( p, pObj, i )
448  p->AreaGlo += If_ManMarkMapping_rec( p, If_ObjFanin0(pObj) );
449 }
float dPower
Definition: if.h:200
Definition: if.h:303
float Required
Definition: if.h:325
static If_Obj_t * If_ObjFanin0(If_Obj_t *pObj)
Definition: if.h:380
int nRefs
Definition: if.h:318
#define If_ManForEachObj(p, pObj, i)
Definition: if.h:462
float AreaGlo
Definition: if.h:198
int nVisitsCopy
Definition: if.h:320
#define IF_FLOAT_LARGE
Definition: if.h:440
#define If_ManForEachCo(p, pObj, i)
Definition: if.h:448
float If_ManMarkMapping_rec(If_Man_t *p, If_Obj_t *pObj)
Definition: ifUtil.c:400
int nVisits
Definition: if.h:319
int nNets
Definition: if.h:199
float If_ManMarkMapping_rec ( If_Man_t p,
If_Obj_t pObj 
)

Function*************************************************************

Synopsis [Computes area, references, and nodes used in the mapping.]

Description []

SideEffects []

SeeAlso []

Definition at line 400 of file ifUtil.c.

401 {
402  If_Obj_t * pLeaf;
403  If_Cut_t * pCutBest;
404  float * pSwitching = p->vSwitching? (float*)p->vSwitching->pArray : NULL;
405  float aArea;
406  int i;
407  if ( pObj->nRefs++ || If_ObjIsCi(pObj) || If_ObjIsConst1(pObj) )
408  return 0.0;
409  // store the node in the structure by level
410  assert( If_ObjIsAnd(pObj) );
411  // visit the transitive fanin of the selected cut
412  pCutBest = If_ObjCutBest(pObj);
413  p->nNets += pCutBest->nLeaves;
414  aArea = If_CutLutArea( p, pCutBest );
415  If_CutForEachLeaf( p, pCutBest, pLeaf, i )
416  {
417  p->dPower += pSwitching? pSwitching[pLeaf->Id] : 0.0;
418  aArea += If_ManMarkMapping_rec( p, pLeaf );
419  }
420  return aArea;
421 }
unsigned nLeaves
Definition: if.h:289
int Id
Definition: if.h:316
float dPower
Definition: if.h:200
static float If_CutLutArea(If_Man_t *p, If_Cut_t *pCut)
Definition: if.h:428
Definition: if.h:303
static int If_ObjIsAnd(If_Obj_t *pObj)
Definition: if.h:377
Definition: if.h:275
#define If_CutForEachLeaf(p, pCut, pLeaf, i)
Definition: if.h:474
static int If_ObjIsCi(If_Obj_t *pObj)
Definition: if.h:373
static If_Cut_t * If_ObjCutBest(If_Obj_t *pObj)
Definition: if.h:401
Vec_Int_t * vSwitching
Definition: if.h:208
int nRefs
Definition: if.h:318
if(last==0)
Definition: sparse_int.h:34
static int If_ObjIsConst1(If_Obj_t *pObj)
Definition: if.h:372
float If_ManMarkMapping_rec(If_Man_t *p, If_Obj_t *pObj)
Definition: ifUtil.c:400
#define assert(ex)
Definition: util_old.h:213
int nNets
Definition: if.h:199
void If_ManResetOriginalRefs ( If_Man_t p)

Function*************************************************************

Synopsis [Computes area, references, and nodes used in the mapping.]

Description [Collects the nodes in reverse topological order in array p->vMapping.]

SideEffects []

SeeAlso []

Definition at line 287 of file ifUtil.c.

288 {
289  If_Obj_t * pObj;
290  int i;
291  If_ManForEachObj( p, pObj, i )
292  pObj->nRefs = 0;
293  If_ManForEachObj( p, pObj, i )
294  {
295  if ( If_ObjIsAnd(pObj) )
296  {
297  pObj->pFanin0->nRefs++;
298  pObj->pFanin1->nRefs++;
299  }
300  else if ( If_ObjIsCo(pObj) )
301  pObj->pFanin0->nRefs++;
302  }
303 }
Definition: if.h:303
static int If_ObjIsAnd(If_Obj_t *pObj)
Definition: if.h:377
static int If_ObjIsCo(If_Obj_t *pObj)
Definition: if.h:374
#define If_ManForEachObj(p, pObj, i)
Definition: if.h:462
Vec_Ptr_t* If_ManReverseOrder ( If_Man_t p)

Function*************************************************************

Synopsis [Computes the reverse topological order of nodes.]

Description []

SideEffects []

SeeAlso []

Definition at line 363 of file ifUtil.c.

364 {
365  Vec_Ptr_t * vOrder;
366  If_Obj_t * pObj, ** ppStore;
367  int i;
368  // allocate place to store the nodes
369  ppStore = ABC_ALLOC( If_Obj_t *, p->nLevelMax + 1 );
370  memset( ppStore, 0, sizeof(If_Obj_t *) * (p->nLevelMax + 1) );
371  // add the nodes
372  If_ManForEachObj( p, pObj, i )
373  {
374  assert( pObj->Level >= 0 && pObj->Level <= (unsigned)p->nLevelMax );
375  pObj->pCopy = (char *)ppStore[pObj->Level];
376  ppStore[pObj->Level] = pObj;
377  }
378  vOrder = Vec_PtrAlloc( If_ManObjNum(p) );
379  for ( i = p->nLevelMax; i >= 0; i-- )
380  for ( pObj = ppStore[i]; pObj; pObj = (If_Obj_t *)pObj->pCopy )
381  Vec_PtrPush( vOrder, pObj );
382  ABC_FREE( ppStore );
383  // print the order
384 // Vec_PtrForEachEntry( If_Obj_t *, vOrder, pObj, i )
385 // Abc_Print( 1, "Obj %2d Type %d Level = %d\n", pObj->Id, pObj->Type, pObj->Level );
386  return vOrder;
387 }
char * memset()
typedefABC_NAMESPACE_HEADER_START struct Vec_Ptr_t_ Vec_Ptr_t
INCLUDES ///.
Definition: vecPtr.h:42
Definition: if.h:303
int nLevelMax
Definition: if.h:194
static void Vec_PtrPush(Vec_Ptr_t *p, void *Entry)
Definition: vecPtr.h:606
#define ABC_ALLOC(type, num)
Definition: abc_global.h:229
#define If_ManForEachObj(p, pObj, i)
Definition: if.h:462
void * pCopy
Definition: if.h:328
unsigned Level
Definition: if.h:315
static int If_ManObjNum(If_Man_t *p)
Definition: if.h:363
static Vec_Ptr_t * Vec_PtrAlloc(int nCap)
FUNCTION DEFINITIONS ///.
Definition: vecPtr.h:83
#define ABC_FREE(obj)
Definition: abc_global.h:232
#define assert(ex)
Definition: util_old.h:213
void If_ObjPrint ( If_Obj_t pObj)

Function*************************************************************

Synopsis []

Description []

SideEffects []

SeeAlso []

Definition at line 604 of file ifUtil.c.

605 {
606  if ( pObj == NULL )
607  {
608  printf( "Object is NULL." );
609  return;
610  }
611  printf( "Obj %4d : ", If_ObjId(pObj) );
612  if ( If_ObjIsConst1(pObj) )
613  printf( "constant 1" );
614  else if ( If_ObjIsCi(pObj) )
615  printf( "PI" );
616  else if ( If_ObjIsCo(pObj) )
617  printf( "PO( %4d%s )", If_ObjId(If_ObjFanin0(pObj)), (If_ObjFaninC0(pObj)? "\'" : " ") );
618  else
619  printf( "AND( %4d%s, %4d%s )",
620  If_ObjId(If_ObjFanin0(pObj)), (If_ObjFaninC0(pObj)? "\'" : " "),
621  If_ObjId(If_ObjFanin1(pObj)), (If_ObjFaninC1(pObj)? "\'" : " ") );
622  printf( " (refs = %3d)", pObj->nVisitsCopy );
623  printf( "\n" );
624 }
static int If_ObjId(If_Obj_t *pObj)
Definition: if.h:379
static int If_ObjIsCo(If_Obj_t *pObj)
Definition: if.h:374
static int If_ObjFaninC0(If_Obj_t *pObj)
Definition: if.h:382
static If_Obj_t * If_ObjFanin0(If_Obj_t *pObj)
Definition: if.h:380
static int If_ObjIsCi(If_Obj_t *pObj)
Definition: if.h:373
static int If_ObjFaninC1(If_Obj_t *pObj)
Definition: if.h:383
static If_Obj_t * If_ObjFanin1(If_Obj_t *pObj)
Definition: if.h:381
int nVisitsCopy
Definition: if.h:320
static int If_ObjIsConst1(If_Obj_t *pObj)
Definition: if.h:372