VIS

src/res/resLayer.c

Go to the documentation of this file.
00001 
00019 #include "resInt.h"
00020 
00021 static char rcsid[] UNUSED = "$Id: resLayer.c,v 1.36 2005/04/16 07:31:03 fabio Exp $";
00022 
00023 /*---------------------------------------------------------------------------*/
00024 /* Constant declarations                                                     */
00025 /*---------------------------------------------------------------------------*/
00026 
00027 /*---------------------------------------------------------------------------*/
00028 /* Structure declarations                                                    */
00029 /*---------------------------------------------------------------------------*/
00030 
00031 /*---------------------------------------------------------------------------*/
00032 /* Type declarations                                                         */
00033 /*---------------------------------------------------------------------------*/
00034 
00035 /*---------------------------------------------------------------------------*/
00036 /* Variable declarations                                                     */
00037 /*---------------------------------------------------------------------------*/
00038 
00039 /*---------------------------------------------------------------------------*/
00040 /* Macro declarations                                                        */
00041 /*---------------------------------------------------------------------------*/
00042 
00045 /*---------------------------------------------------------------------------*/
00046 /* Static function prototypes                                                */
00047 /*---------------------------------------------------------------------------*/
00048 
00049 static array_t * ComputeCompositionLayersAsap(Ntk_Network_t *network, array_t *outputArray, array_t *ignoreArray);
00050 static array_t * ComputeCompositionLayersAlap(Ntk_Network_t *network, array_t *outputArray, array_t *ignoreArray);
00051 static int ComputeAlapLabelling(Ntk_Network_t *network, st_table *nodeLabelling);
00052 static void ComputeAlapLabellingRecur(Ntk_Node_t *node, st_table *nodeLabelling);
00053 static st_table * ComputeTransitiveFanin(array_t *outputArray);
00054 static void ComputeTransitiveFaninRecur(Ntk_Node_t *nodePtr, st_table *faninTable);
00055 static void RecursiveDecrementFanoutCount(Ntk_Node_t *nodePtr, st_table *fanoutCountTable, st_table *visitedTable);
00056 
00060 /*---------------------------------------------------------------------------*/
00061 /* Definition of exported functions                                          */
00062 /*---------------------------------------------------------------------------*/
00063 
00064 
00065 /*---------------------------------------------------------------------------*/
00066 /* Definition of internal functions                                          */
00067 /*---------------------------------------------------------------------------*/
00068 
00091 array_t *
00092 ResComputeCompositionLayers(Ntk_Network_t *network, 
00093                             array_t *outputArray,   
00094                             array_t *ignoreArray)   
00095 {
00096   ResLayerScheduleMethod layerMethod; /* read from the set value */
00097   array_t *result;  /* Result obtained from procedure */
00098   char *flagValue;  /* To store the value read from the flag */
00099 
00100   /* Read the value from the flag */
00101   flagValue = Cmd_FlagReadByName("residue_layer_schedule");
00102   if (flagValue == NIL(char)) {
00103     layerMethod = ResDefaultScheduleLayerMethod_c;
00104   } 
00105   else {
00106     if (strcmp(flagValue, "asap") == 0) {
00107       layerMethod = ResLayerAsap_c;
00108     }
00109     else if (strcmp(flagValue, "alap") == 0) {
00110       layerMethod = ResLayerAlap_c;
00111     }
00112     else {
00113       (void) fprintf(vis_stderr, "** res error: Unknown method to compute layers.");
00114       (void) fprintf(vis_stderr, "** res error: Assuming default method.\n");
00115       layerMethod = ResDefaultScheduleLayerMethod_c;
00116     }
00117   }
00118 
00119   /* Call the pertinent procedure */
00120   switch (layerMethod) {
00121   case ResLayerAlap_c: {
00122       result = ComputeCompositionLayersAlap(network, outputArray, ignoreArray);
00123       break;
00124       }
00125   case ResLayerAsap_c: {
00126       result = ComputeCompositionLayersAsap(network, outputArray, ignoreArray);
00127       break;
00128       }
00129   default: {
00130       (void) fprintf(vis_stdout, "** res warning: Layer computation method not implemented.");
00131       (void) fprintf(vis_stdout, "** res warning: Executing default method.\n");
00132       result = ComputeCompositionLayersAlap(network, outputArray, ignoreArray);
00133       break;
00134     }
00135   }
00136 
00137   return result;
00138 } /* End of ResComputeCompositionLayers */
00139 
00140 
00155 void
00156 ResLayerPrintInfo(Ntk_Network_t *network, 
00157                   array_t *layerArray)
00158 {
00159 
00160   Ntk_Node_t   *nodePtr, *faninNodePtr; /* Node being processed */
00161   st_table *faninTable;                 /* Current nodes */
00162   int layerIndex, newVars;              /* index of the layer being processed */
00163   int i,j;                              /* For array traversal */
00164   array_t *currentLayer;                /* layer info */
00165 
00166   /* keep track of all nodes that have appeared in the support */
00167   faninTable = st_init_table(st_ptrcmp, st_ptrhash);
00168   /* Loop over the number of elements in layerArray */
00169   for (layerIndex = 0; layerIndex < array_n(layerArray); layerIndex++) {
00170     /* reset new variables in the support of this array */
00171     newVars = 0;
00172     
00173     (void) fprintf(vis_stdout, "Layer %d: ", layerIndex);
00174 
00175     /* Access the layer info */
00176     currentLayer = ResLayerFetchIthLayer(layerArray, layerIndex);  
00177 
00178     /* Print the nodes in a layer that are not PIs */
00179     LayerForEachNode(currentLayer, i, nodePtr) {
00180       (void) fprintf(vis_stdout, "%s ", Ntk_NodeReadName(nodePtr));
00181 
00182       /* store fanin nodes in the table, keep count */
00183       Ntk_NodeForEachFanin(nodePtr, j, faninNodePtr) {
00184         if (!st_is_member(faninTable, (char *)faninNodePtr)) {
00185           if (!((Ntk_NodeTestIsLatch(nodePtr) &&
00186                     (Ntk_NodeTestIsLatchDataInput(faninNodePtr) ||
00187                      Ntk_NodeTestIsLatchInitialInput(faninNodePtr))) ||
00188                 Ntk_NodeTestIsConstant(faninNodePtr))) {
00189             
00190             st_insert(faninTable, (char *)faninNodePtr, NIL(char));
00191             newVars++;
00192           }
00193         }
00194       }
00195     }
00196 
00197     (void) fprintf(vis_stdout,": SUPPORT = %d", newVars);
00198     (void) fprintf(vis_stdout, "\n");
00199 
00200     
00201   } /* End of for every layer */
00202 
00203   /* Clean up */
00204   st_free_table(faninTable);
00205   return;
00206 } /* End of ResLayerPrintInfo */
00207 
00208 
00221 void
00222 ResLayerArrayFree(array_t *layerArray)
00223 {
00224   int i, end;
00225   array_t *currentLayer;
00226 
00227   i = 0;
00228   end = array_n(layerArray);
00229   for (i =0; i < end ; i++) {
00230     currentLayer = ResLayerFetchIthLayer(layerArray, i);
00231     LayerFree(currentLayer);
00232   }
00233   array_free(layerArray);
00234 } /* End of ResLayerArrayFree */
00235 
00236 /*---------------------------------------------------------------------------*/
00237 /* Definition of static functions                                            */
00238 /*---------------------------------------------------------------------------*/
00239 
00263 static array_t *
00264 ComputeCompositionLayersAsap(Ntk_Network_t *network, 
00265                              array_t *outputArray,   
00266                              array_t *ignoreArray)
00267 {
00268   Ntk_Node_t *nodePtr, *faninNodePtr; /* variables to store node pointers */
00269   int i, j, k;                        /* iterators */
00270   st_generator *stGen;                /* generator to step through st_table */
00271   char *key;                          /* values to read from st_table */
00272   lsGen listGen;                      /* list generator to step through nodes */
00273   array_t *currentLayer, *nextLayer;  /* array of nodes belonging to a layer */
00274   array_t *layerArray;                /* array of layers */
00275   st_table *fanoutCountTable;         /* table to store fanout counts of each
00276                                        * node
00277                                        */
00278   int fanoutCount;                    /* variable to store fanout count of a
00279                                        * node
00280                                        */
00281   int value;                          /* to read value off the st_table */
00282   st_table *visitedTable;             /* table to store visited nodes */
00283   
00284   /* create a fanout count table starting with all nodes except PIs,
00285    * constant node or a shadow node for any node but a latch(counts as
00286    * comb output).
00287    */
00288   fanoutCountTable = st_init_table(st_ptrcmp, st_ptrhash); 
00289   Ntk_NetworkForEachNode(network, listGen, nodePtr) {
00290     /* does not nodes like PIs, shadow nodes, constants, undefined
00291      * nodes to compute the fanouts, since they do not have to be
00292      * composed into the circuit.
00293      */
00294     if ((Ntk_NodeTestIsCombOutput(nodePtr)) ||
00295          (!(Ntk_NodeTestIsCombInput(nodePtr) ||
00296           Ntk_NodeTestIsConstant(nodePtr) ||
00297           Ntk_NodeTestIsUndefined(nodePtr) ||  
00298           Ntk_NodeTestIsShadow(nodePtr)))) {
00299       st_insert(fanoutCountTable, (char *)nodePtr, 
00300                 (char *)(long)Ntk_NodeReadNumFanouts(nodePtr));
00301     }
00302   }
00303   
00304   /* take out outputs in directly verified table, if so desired */
00305   /* Assume ignore table has node pointers , reduce fanout count of
00306    * transitive fanin of these nodes
00307    */
00308   if (ignoreArray != NULL) {
00309     visitedTable = st_init_table(st_ptrcmp, st_ptrhash);
00310     arrayForEachItem(Ntk_Node_t *, ignoreArray, i, nodePtr) {
00311       /* each key is an output node */
00312       RecursiveDecrementFanoutCount(nodePtr, fanoutCountTable, visitedTable);
00313     }
00314     st_free_table(visitedTable);
00315 
00316     /* remove all nodes that are primary inputs and those with fanout
00317      * count 0 providing they are not primary outputs/ or latch inputs
00318      * or latch initial values.
00319      */
00320     st_foreach_item_int(fanoutCountTable, stGen, &key, &value) {
00321       fanoutCount = value;
00322       nodePtr = (Ntk_Node_t *)key;
00323       assert(fanoutCount >= -1);
00324       if ((!Ntk_NodeTestIsCombOutput(nodePtr))
00325           && (fanoutCount <= 0)) {
00326         /* delete the item corresponding to fanoutCount i.e. value */
00327         st_delete_int(fanoutCountTable, (char **)&key, &value);
00328       }
00329     }
00330     /* remove all outputs that belong to ignore outputs */
00331     arrayForEachItem(Ntk_Node_t *, ignoreArray, i, nodePtr) {
00332       st_delete_int(fanoutCountTable, &nodePtr, &value);
00333     }
00334   }  
00335 
00336   /* start preparing the layer array */
00337   layerArray = array_alloc(array_t *, 0); 
00338   currentLayer = LayerCreateEmptyLayer();
00339 
00340   /*  outputs form the first layer */
00341   Ntk_NetworkForEachCombOutput(network, listGen, nodePtr) {
00342     if (st_lookup_int(fanoutCountTable, (char *)nodePtr, &fanoutCount)) {
00343       /* insert all outputs that aren't to be ignored in the first layer of the
00344        * layer array
00345        */
00346       LayerAddNodeAtEnd(currentLayer, nodePtr);
00347     }
00348   }
00349 
00350   /* if current layer is not empty */
00351   while (ResLayerNumNodes(currentLayer) ) {
00352     /* insert current layer into layer array */
00353     array_insert_last(array_t *, layerArray, currentLayer);
00354     nextLayer = LayerCreateEmptyLayer();
00355     LayerForEachNode(currentLayer, i, nodePtr) {
00356       Ntk_NodeForEachFanin(nodePtr, j, faninNodePtr) {
00357         /* do not want to get the fanin for latch outputs */
00358         if(!(Ntk_NodeTestIsConstant(nodePtr) ||
00359              (Ntk_NodeTestIsLatch(nodePtr) &&
00360              (Ntk_NodeTestIsLatchDataInput(faninNodePtr) ||
00361               Ntk_NodeTestIsLatchInitialInput(faninNodePtr))))) {
00362           if (!st_lookup_int(fanoutCountTable, (char *)faninNodePtr,
00363                              &fanoutCount)) {
00364             /* maybe it is a PI or constant node */
00365             if (!(Ntk_NodeTestIsCombInput(faninNodePtr) ||
00366                   Ntk_NodeTestIsConstant(faninNodePtr) ||
00367                   Ntk_NodeTestIsUndefined(faninNodePtr) ||
00368                   Ntk_NodeTestIsShadow(faninNodePtr))) {
00369               error_append("Fanin node %s should have been in table");
00370               error_append(Ntk_NodeReadName(faninNodePtr));
00371               /* Cleanup */
00372               arrayForEachItem(array_t *, layerArray, k, currentLayer) {
00373                 LayerFree(currentLayer);
00374               }
00375               array_free(layerArray);
00376               return NIL(array_t);
00377             } /* node is neither PI nor constant */
00378           } else { /* node is found in table */
00379             fanoutCount--;
00380             /* decrement fanout count, will go into a later layer */
00381             if (fanoutCount == 0) {
00382               /*
00383                * it is ready to be put in the array,
00384                * since its fanout count is 1
00385                */
00386               LayerAddNodeAtEnd(nextLayer, faninNodePtr);
00387             } else if (fanoutCount > 0) {
00388                 /* insert new decremented value in table */
00389               st_insert(fanoutCountTable, (char *)faninNodePtr,
00390                         (char *)(long)fanoutCount);
00391 
00392             } /* end of nodes fanout count larger than 1 */
00393           } /* end of fanin node found in fanout count table */
00394         } /* do not want to get the fanin for latch outputs */
00395       } /* end of iteration through all the fanin nodes of current node */
00396     } /* end of iteration through all the nodes of current layer */
00397     /* the fanout count ensures unique entry of elements ????*/
00398     currentLayer = nextLayer;
00399   } /* end of while some nodes in current layer */  
00400   
00401   /* the loop always exits with an empty array */
00402   LayerFree(currentLayer);
00403   st_free_table(fanoutCountTable);
00404   if (array_n(layerArray) == 0) {
00405     array_free(layerArray);
00406     layerArray = NIL(array_t);
00407   }
00408 
00409   return (layerArray);
00410   
00411 } /* End of ComputeCompositionLayersAsap */
00412 
00444 static array_t *
00445 ComputeCompositionLayersAlap(Ntk_Network_t *network,
00446                              array_t *outputArray,  
00447                              array_t *ignoreArray)
00448 {
00449   Ntk_Node_t *nodePtr;       /* node being processed  */
00450   array_t *layerArray;       /* array of layers */
00451   array_t *currentLayer;     /* layer of nodes */
00452   st_table *nodeLabelling;   /* labeling of a node, its farthest distance
00453                               * from the primary input
00454                               */
00455   int numLayers;             /* Number of Layers */
00456   int layerIndex;            /* index of a layer (from the primary output */
00457   int arrayIndex;            /* iterator */
00458   st_generator *stGen;       /* generator to step through table */
00459   char *key;                 /* values to read from table */
00460   int value;                 /* integer value to read from the table */
00461 
00462   /* Initialize the labeling table */
00463   nodeLabelling = ComputeTransitiveFanin(outputArray);
00464 
00465   /* Compute the labeling of the nodes */
00466   numLayers = ComputeAlapLabelling(network, nodeLabelling);
00467 
00468   /* Create the layerArray structure */
00469   layerArray = array_alloc(array_t *, numLayers);
00470   /* initialize layers */
00471   for (layerIndex = 0; layerIndex < numLayers; layerIndex++) {
00472     currentLayer = LayerCreateEmptyLayer();
00473     array_insert(array_t *, layerArray, layerIndex, currentLayer);
00474   } /* end of for */
00475 
00476   /* insert elements of the st_table in the layers */
00477   st_foreach_item_int(nodeLabelling, stGen, &key, &value) {
00478     layerIndex = value;
00479     nodePtr = (Ntk_Node_t *)key;
00480     /* don't put PIs or comb inputs or constants or undefined
00481      * nodes in layer array
00482      */
00483     if ((Ntk_NodeTestIsCombOutput(nodePtr)) ||
00484         ((layerIndex > 0) &&
00485          !(Ntk_NodeTestIsCombInput(nodePtr) ||
00486            Ntk_NodeTestIsConstant(nodePtr) ||
00487            Ntk_NodeTestIsUndefined(nodePtr) ||
00488            Ntk_NodeTestIsShadow(nodePtr)))) {
00489       /* the layers are arranged in reverse order of their farthest
00490        * distance from the node. Hence each node is inserted into
00491        * the numLayers-1-layerIndex. Nodes with layerIndex should be
00492        * put in the last array since they are probably constants
00493        * at the primary outputs or latch initial value. LayerIndex
00494        * could be 0 for primary outputs if it does not appear in the
00495        * transitive fanin of the comb inputs. 
00496        */
00497       if (layerIndex == 0) {
00498         arrayIndex = 0;
00499         assert(Ntk_NodeTestIsCombOutput(nodePtr) == 1);
00500       } else {
00501         arrayIndex = numLayers - layerIndex;
00502       }
00503       currentLayer = array_fetch(array_t *, layerArray, arrayIndex);
00504       LayerAddNodeAtEnd(currentLayer, nodePtr);
00505     }
00506   }
00507   /* Clean up */
00508   st_free_table(nodeLabelling);
00509 
00510   
00511   return layerArray;
00512 } /* End of ComputeCompositionLayersAlap */
00513 
00514 
00531 static int
00532 ComputeAlapLabelling(Ntk_Network_t *network, 
00533                      st_table *nodeLabelling)
00534 {
00535   Ntk_Node_t *nodePtr; /* Node being processed */
00536   lsGen gen;           /* For list traversal */
00537   int numLayers;       /* To return as result */
00538   int outputDepth;     /* Depth of the output being processed */
00539 
00540   assert(nodeLabelling != NIL(st_table));
00541 
00542   /* For primary inputs in the table (those not in the table
00543    * are in the transitive fanin of the ignored outputs only
00544    */
00545   Ntk_NetworkForEachCombInput(network, gen, nodePtr) {
00546     /* do it for fanouts that are in table only */
00547     if (st_is_member(nodeLabelling, (char *)nodePtr)) {
00548       ComputeAlapLabellingRecur(nodePtr, nodeLabelling);
00549     }
00550   }
00551 
00552   /* Compute the number of layers */
00553   numLayers = 0;
00554   Ntk_NetworkForEachCombOutput(network, gen, nodePtr) {
00555     /* only those outputs not be to be ignored */
00556     if (st_lookup_int(nodeLabelling, (char *)nodePtr, &outputDepth)) {
00557       if (outputDepth > numLayers ) {
00558         numLayers = outputDepth;
00559       } /* End of if */
00560     }
00561   }
00562   
00563   return numLayers;
00564 } /* End of ComputeAlapLabelling */
00565 
00582 static void
00583 ComputeAlapLabellingRecur(Ntk_Node_t *node,
00584                           st_table *nodeLabelling)
00585 {
00586   Ntk_Node_t *fanoutPtr;
00587   int nodeDepth;
00588   int fanoutDepth;
00589   int i;
00590 
00591   /* Trivial case */
00592   if (Ntk_NodeReadNumFanouts(node) == 0) {
00593     return;
00594   } /* End of if */
00595 
00596   /* Look up information for the current node */
00597   st_lookup_int(nodeLabelling, (char *)node, &nodeDepth);
00598   
00599   /* Iterate over its fanouts */
00600   Ntk_NodeForEachFanout(node, i, fanoutPtr) {
00601     /* Do not process as soon as an input is found beyond an output */
00602     if (!((Ntk_NodeTestIsLatchDataInput(node) ||
00603            Ntk_NodeTestIsLatchInitialInput(node)) &&
00604           Ntk_NodeTestIsLatch(fanoutPtr))) {
00605       if (st_is_member(nodeLabelling, (char *)fanoutPtr)) {
00606         /* Look up information for the fanout */
00607         st_lookup_int(nodeLabelling, (char *)fanoutPtr, &fanoutDepth);
00608         /* If the fanout depth needs to be modified, do so, and recur */
00609         if (nodeDepth >= fanoutDepth) {
00610           st_insert(nodeLabelling, (char *)fanoutPtr, 
00611                     (char *)(long)(nodeDepth + 1));
00612           ComputeAlapLabellingRecur(fanoutPtr, nodeLabelling);
00613         } /* End of if */
00614       } /* End of if */
00615     } /* End of if */
00616   } /* End of for each fanout */
00617 
00618   return;
00619 } /* End of ComputeAlapLabellingRecur */
00620 
00621 
00637 static st_table *
00638 ComputeTransitiveFanin(array_t *outputArray)
00639 {
00640   Ntk_Node_t *nodePtr;
00641   st_table * faninTable;
00642   int i;
00643   
00644   faninTable = st_init_table(st_ptrcmp, st_ptrhash);
00645   arrayForEachItem(Ntk_Node_t *, outputArray, i, nodePtr) {
00646     ComputeTransitiveFaninRecur(nodePtr, faninTable);
00647   }
00648   return faninTable;
00649 } /* End of ComputeTransitiveFanin */
00650 
00666 static void
00667 ComputeTransitiveFaninRecur(Ntk_Node_t *nodePtr,  
00668                             st_table *faninTable)
00669 {
00670   Ntk_Node_t *faninNodePtr;
00671   int i;
00672 
00673   if (st_lookup(faninTable, (char *)nodePtr, NIL(char *))) {
00674     return;
00675   }
00676 
00677 
00678   /* recur on fanin nodes */
00679   Ntk_NodeForEachFanin(nodePtr, i, faninNodePtr) {
00680     /* Test this case cos other comb inputs have no fanins */
00681     if (!((Ntk_NodeTestIsLatchDataInput(faninNodePtr) ||
00682            Ntk_NodeTestIsLatchInitialInput(faninNodePtr)) &&
00683           Ntk_NodeTestIsLatch(nodePtr))) {
00684       ComputeTransitiveFaninRecur(faninNodePtr, faninTable);
00685     } /* end of if */
00686   } /* iterate over all fanins */
00687   
00688   st_insert(faninTable, (char *)nodePtr, NIL(char));
00689   return;
00690 
00691 } /* End of ComputeTransitiveFaninRecur */
00692 
00707 static void
00708 RecursiveDecrementFanoutCount(Ntk_Node_t *nodePtr, 
00709                               st_table *fanoutCountTable,
00710                               st_table *visitedTable)
00711 {
00712   Ntk_Node_t *faninNodePtr;
00713   int i, fanoutCount;
00714 
00715   /* the following kinds of nodes will not be in the table. */
00716   if ((!Ntk_NodeTestIsCombOutput(nodePtr)) && 
00717        (Ntk_NodeTestIsCombInput(nodePtr) ||
00718         Ntk_NodeTestIsUndefined(nodePtr)||
00719         Ntk_NodeTestIsConstant(nodePtr) ||
00720         Ntk_NodeTestIsShadow(nodePtr))) {
00721     return;
00722   }
00723   /* all nodes called here should exist in table */
00724   if (!st_lookup_int(fanoutCountTable, (char *)nodePtr, &fanoutCount)){
00725     error_append("Node ");
00726     error_append(Ntk_NodeReadName(nodePtr));
00727     error_append(" should have been in table\n");
00728     return;
00729   }
00730 
00731   fanoutCount--;
00732   /* reduce fanout count of node */
00733   st_insert(fanoutCountTable, (char *)nodePtr, (char *)(long)fanoutCount);
00734   /* if this node is visited, decrement its fanout but do not proceed
00735    * any further (i.e. do not proceed with its fanins
00736    */
00737   if (st_is_member(visitedTable, (char *)nodePtr)) {
00738     return;
00739   } else {
00740     st_insert(visitedTable, (char *)nodePtr, NIL(char));
00741 
00742     /* 
00743      * recur with fanin nodes except nodes with no fanins (that arent in table)
00744      * and latch outputs 
00745      */
00746     Ntk_NodeForEachFanin(nodePtr, i, faninNodePtr) {
00747       /* may be a redundant test, since it will pop out in the first line of
00748        * the procedure anyways. 
00749        */
00750       if (!(Ntk_NodeTestIsConstant(faninNodePtr) ||
00751           ((Ntk_NodeTestIsLatchDataInput(faninNodePtr) ||
00752             Ntk_NodeTestIsLatchInitialInput(faninNodePtr)) &&
00753            Ntk_NodeTestIsLatch(nodePtr)))) {
00754         RecursiveDecrementFanoutCount(faninNodePtr, fanoutCountTable, visitedTable);
00755       } 
00756     } /* end of iterating over fanins */
00757   } /* end of else if not visited */
00758   return;
00759 } /* End of RecursiveDecrementFanoutCount */