You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							825 lines
						
					
					
						
							24 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							825 lines
						
					
					
						
							24 KiB
						
					
					
				| /** | |
|   @file | |
|  | |
|   @ingroup cudd | |
|  | |
|   @brief Functions to check consistency of data structures. | |
|  | |
|   @author Fabio Somenzi | |
|  | |
|   @copyright@parblock | |
|   Copyright (c) 1995-2015, Regents of the University of Colorado | |
|  | |
|   All rights reserved. | |
|  | |
|   Redistribution and use in source and binary forms, with or without | |
|   modification, are permitted provided that the following conditions | |
|   are met: | |
|  | |
|   Redistributions of source code must retain the above copyright | |
|   notice, this list of conditions and the following disclaimer. | |
|  | |
|   Redistributions in binary form must reproduce the above copyright | |
|   notice, this list of conditions and the following disclaimer in the | |
|   documentation and/or other materials provided with the distribution. | |
|  | |
|   Neither the name of the University of Colorado nor the names of its | |
|   contributors may be used to endorse or promote products derived from | |
|   this software without specific prior written permission. | |
|  | |
|   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
|   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
|   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | |
|   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | |
|   COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | |
|   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | |
|   BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
|   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |
|   CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
|   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN | |
|   ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
|   POSSIBILITY OF SUCH DAMAGE. | |
|   @endparblock | |
|  | |
| */ | |
| 
 | |
| #include "util.h" | |
| #include "mtrInt.h" | |
| #include "cuddInt.h" | |
|  | |
| /*---------------------------------------------------------------------------*/ | |
| /* Constant declarations                                                     */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Stucture declarations                                                     */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Type declarations                                                         */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Variable declarations                                                     */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Macro declarations                                                        */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| /** \cond */ | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Static function prototypes                                                */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| static void debugFindParent (DdManager *table, DdNode *node); | |
| #if 0 | |
| static void debugCheckParent (DdManager *table, DdNode *node); | |
| #endif | |
|  | |
| /** \endcond */ | |
| 
 | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Definition of exported functions                                          */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Checks for inconsistencies in the %DD heap. | |
|  | |
|   @details The following inconsistencies are checked: | |
|   <ul> | |
|   <li> node has illegal index | |
|   <li> live node has dead children | |
|   <li> node has illegal Then or Else pointers | |
|   <li> %BDD/%ADD node has identical children | |
|   <li> %ZDD node has zero then child | |
|   <li> wrong number of total nodes | |
|   <li> wrong number of dead nodes | |
|   <li> ref count error at node | |
|   </ul> | |
|    | |
|   @return 0 if no inconsistencies are found; DD_OUT_OF_MEM if there is | |
|   not enough memory; 1 otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
|   @see Cudd_CheckKeys | |
|  | |
| */ | |
| int | |
| Cudd_DebugCheck( | |
|   DdManager * table) | |
| { | |
|     unsigned int i; | |
|     int		j,count; | |
|     int		slots; | |
|     DdNodePtr	*nodelist; | |
|     DdNode	*f; | |
|     DdNode	*sentinel = &(table->sentinel); | |
|     st_table	*edgeTable;	/* stores internal ref count for each node */ | |
|     st_generator	*gen; | |
|     int		flag = 0; | |
|     int		totalNode; | |
|     int		deadNode; | |
|     int		index; | |
|     int         shift; | |
| 
 | |
|     edgeTable = st_init_table(st_ptrcmp,st_ptrhash); | |
|     if (edgeTable == NULL) return(CUDD_OUT_OF_MEM); | |
| 
 | |
|     /* Check the BDD/ADD subtables. */ | |
|     for (i = 0; i < (unsigned) table->size; i++) { | |
| 	index = table->invperm[i]; | |
| 	if (i != (unsigned) table->perm[index]) { | |
| 	    (void) fprintf(table->err, | |
| 			   "Permutation corrupted: invperm[%u] = %d\t perm[%d] = %d\n", | |
| 			   i, index, index, table->perm[index]); | |
| 	} | |
| 	nodelist = table->subtables[i].nodelist; | |
| 	slots = table->subtables[i].slots; | |
| 	shift = table->subtables[i].shift; | |
| 
 | |
| 	totalNode = 0; | |
| 	deadNode = 0; | |
| 	for (j = 0; j < slots; j++) {	/* for each subtable slot */ | |
| 	    f = nodelist[j]; | |
| 	    while (f != sentinel) { | |
| 		totalNode++; | |
| 		if (cuddT(f) != NULL && cuddE(f) != NULL && f->ref != 0) { | |
| 		    if ((int) f->index != index) { | |
| 			(void) fprintf(table->err, | |
| 				       "Error: node has illegal index\n"); | |
| 			cuddPrintNode(f,table->err); | |
| 			flag = 1; | |
| 		    } | |
| 		    if ((unsigned) cuddI(table,cuddT(f)->index) <= i || | |
| 			(unsigned) cuddI(table,Cudd_Regular(cuddE(f))->index) | |
| 			<= i) { | |
| 			(void) fprintf(table->err, | |
| 				       "Error: node has illegal children\n"); | |
| 			cuddPrintNode(f,table->err); | |
| 			flag = 1; | |
| 		    } | |
| 		    if (Cudd_Regular(cuddT(f)) != cuddT(f)) { | |
| 			(void) fprintf(table->err, | |
| 				       "Error: node has illegal form\n"); | |
| 			cuddPrintNode(f,table->err); | |
| 			flag = 1; | |
| 		    } | |
| 		    if (cuddT(f) == cuddE(f)) { | |
| 			(void) fprintf(table->err, | |
| 				       "Error: node has identical children\n"); | |
| 			cuddPrintNode(f,table->err); | |
| 			flag = 1; | |
| 		    } | |
| 		    if (cuddT(f)->ref == 0 || Cudd_Regular(cuddE(f))->ref == 0) { | |
| 			(void) fprintf(table->err, | |
| 				       "Error: live node has dead children\n"); | |
| 			cuddPrintNode(f,table->err); | |
| 			flag =1; | |
| 		    } | |
|                     if (ddHash(cuddT(f),cuddE(f),shift) != (unsigned) j) { | |
|                         (void) fprintf(table->err, "Error: misplaced node\n"); | |
| 			cuddPrintNode(f,table->err); | |
| 			flag =1; | |
|                     } | |
| 		    /* Increment the internal reference count for the | |
| 		    ** then child of the current node. | |
| 		    */ | |
| 		    if (st_lookup_int(edgeTable,cuddT(f),&count)) { | |
| 			count++; | |
| 		    } else { | |
| 			count = 1; | |
| 		    } | |
| 		    if (st_insert(edgeTable,cuddT(f), | |
| 		    (void *)(ptruint)count) == ST_OUT_OF_MEM) { | |
| 			st_free_table(edgeTable); | |
| 			return(CUDD_OUT_OF_MEM); | |
| 		    } | |
| 
 | |
| 		    /* Increment the internal reference count for the | |
| 		    ** else child of the current node. | |
| 		    */ | |
| 		    if (st_lookup_int(edgeTable,Cudd_Regular(cuddE(f)), | |
| 				      &count)) { | |
| 			count++; | |
| 		    } else { | |
| 			count = 1; | |
| 		    } | |
| 		    if (st_insert(edgeTable,Cudd_Regular(cuddE(f)), | |
| 		    (void *)(ptruint)count) == ST_OUT_OF_MEM) { | |
| 			st_free_table(edgeTable); | |
| 			return(CUDD_OUT_OF_MEM); | |
| 		    } | |
| 		} else if (cuddT(f) != NULL && cuddE(f) != NULL && f->ref == 0) { | |
| 		    deadNode++; | |
| #if 0 | |
| 		    debugCheckParent(table,f); | |
| #endif | |
| 		} else { | |
| 		    fprintf(table->err, | |
| 			    "Error: node has illegal Then or Else pointers\n"); | |
| 		    cuddPrintNode(f,table->err); | |
| 		    flag = 1; | |
| 		} | |
| 
 | |
| 		f = f->next; | |
| 	    }	/* for each element of the collision list */ | |
| 	}	/* for each subtable slot */ | |
| 
 | |
| 	if ((unsigned) totalNode != table->subtables[i].keys) { | |
| 	    fprintf(table->err,"Error: wrong number of total nodes\n"); | |
| 	    flag = 1; | |
| 	} | |
| 	if ((unsigned) deadNode != table->subtables[i].dead) { | |
| 	    fprintf(table->err,"Error: wrong number of dead nodes\n"); | |
| 	    flag = 1; | |
| 	} | |
|     }	/* for each BDD/ADD subtable */ | |
| 
 | |
|     /* Check the ZDD subtables. */ | |
|     for (i = 0; i < (unsigned) table->sizeZ; i++) { | |
| 	index = table->invpermZ[i]; | |
| 	if (i != (unsigned) table->permZ[index]) { | |
| 	    (void) fprintf(table->err, | |
| 			   "Permutation corrupted: invpermZ[%u] = %d\t permZ[%d] = %d in ZDD\n", | |
| 			   i, index, index, table->permZ[index]); | |
| 	} | |
| 	nodelist = table->subtableZ[i].nodelist; | |
| 	slots = table->subtableZ[i].slots; | |
| 
 | |
| 	totalNode = 0; | |
| 	deadNode = 0; | |
| 	for (j = 0; j < slots; j++) {	/* for each subtable slot */ | |
| 	    f = nodelist[j]; | |
| 	    while (f != NULL) { | |
| 		totalNode++; | |
| 		if (cuddT(f) != NULL && cuddE(f) != NULL && f->ref != 0) { | |
| 		    if ((int) f->index != index) { | |
| 			(void) fprintf(table->err, | |
| 				       "Error: ZDD node has illegal index\n"); | |
| 			cuddPrintNode(f,table->err); | |
| 			flag = 1; | |
| 		    } | |
| 		    if (Cudd_IsComplement(cuddT(f)) || | |
| 			Cudd_IsComplement(cuddE(f))) { | |
| 			(void) fprintf(table->err, | |
| 				       "Error: ZDD node has complemented children\n"); | |
| 			cuddPrintNode(f,table->err); | |
| 			flag = 1; | |
| 		    } | |
| 		    if ((unsigned) cuddIZ(table,cuddT(f)->index) <= i || | |
| 		    (unsigned) cuddIZ(table,cuddE(f)->index) <= i) { | |
| 			(void) fprintf(table->err, | |
| 				       "Error: ZDD node has illegal children\n"); | |
| 			cuddPrintNode(f,table->err); | |
| 			cuddPrintNode(cuddT(f),table->err); | |
| 			cuddPrintNode(cuddE(f),table->err); | |
| 			flag = 1; | |
| 		    } | |
| 		    if (cuddT(f) == DD_ZERO(table)) { | |
| 			(void) fprintf(table->err, | |
| 				       "Error: ZDD node has zero then child\n"); | |
| 			cuddPrintNode(f,table->err); | |
| 			flag = 1; | |
| 		    } | |
| 		    if (cuddT(f)->ref == 0 || cuddE(f)->ref == 0) { | |
| 			(void) fprintf(table->err, | |
| 				       "Error: ZDD live node has dead children\n"); | |
| 			cuddPrintNode(f,table->err); | |
| 			flag =1; | |
| 		    } | |
| 		    /* Increment the internal reference count for the | |
| 		    ** then child of the current node. | |
| 		    */ | |
| 		    if (st_lookup_int(edgeTable,cuddT(f),&count)) { | |
| 			count++; | |
| 		    } else { | |
| 			count = 1; | |
| 		    } | |
| 		    if (st_insert(edgeTable,cuddT(f), | |
| 		    (void *)(ptruint)count) == ST_OUT_OF_MEM) { | |
| 			st_free_table(edgeTable); | |
| 			return(CUDD_OUT_OF_MEM); | |
| 		    } | |
| 
 | |
| 		    /* Increment the internal reference count for the | |
| 		    ** else child of the current node. | |
| 		    */ | |
| 		    if (st_lookup_int(edgeTable,cuddE(f),&count)) { | |
| 			count++; | |
| 		    } else { | |
| 			count = 1; | |
| 		    } | |
| 		    if (st_insert(edgeTable,cuddE(f), | |
| 		    (void *)(ptruint)count) == ST_OUT_OF_MEM) { | |
| 			st_free_table(edgeTable); | |
| 			table->errorCode = CUDD_MEMORY_OUT; | |
| 			return(CUDD_OUT_OF_MEM); | |
| 		    } | |
| 		} else if (cuddT(f) != NULL && cuddE(f) != NULL && f->ref == 0) { | |
| 		    deadNode++; | |
| #if 0 | |
| 		    debugCheckParent(table,f); | |
| #endif | |
| 		} else { | |
| 		    fprintf(table->err, | |
| 			    "Error: ZDD node has illegal Then or Else pointers\n"); | |
| 		    cuddPrintNode(f,table->err); | |
| 		    flag = 1; | |
| 		} | |
| 
 | |
| 		f = f->next; | |
| 	    }	/* for each element of the collision list */ | |
| 	}	/* for each subtable slot */ | |
| 
 | |
| 	if ((unsigned) totalNode != table->subtableZ[i].keys) { | |
| 	    fprintf(table->err, | |
| 		    "Error: wrong number of total nodes in ZDD\n"); | |
| 	    flag = 1; | |
| 	} | |
| 	if ((unsigned) deadNode != table->subtableZ[i].dead) { | |
| 	    fprintf(table->err, | |
| 		    "Error: wrong number of dead nodes in ZDD\n"); | |
| 	    flag = 1; | |
| 	} | |
|     }	/* for each ZDD subtable */ | |
| 
 | |
|     /* Check the constant table. */ | |
|     nodelist = table->constants.nodelist; | |
|     slots = table->constants.slots; | |
| 
 | |
|     totalNode = 0; | |
|     deadNode = 0; | |
|     for (j = 0; j < slots; j++) { | |
| 	f = nodelist[j]; | |
| 	while (f != NULL) { | |
| 	    totalNode++; | |
| 	    if (f->ref != 0) { | |
| 		if (f->index != CUDD_CONST_INDEX) { | |
| 		    fprintf(table->err,"Error: node has illegal index\n"); | |
| 		    fprintf(table->err, | |
| 			    "       node 0x%" PRIxPTR ", id = %u, ref = %u, value = %g\n", | |
| 			    (ptruint)f,f->index,f->ref,cuddV(f)); | |
| 		    flag = 1; | |
| 		} | |
| 	    } else { | |
| 		deadNode++; | |
| 	    } | |
| 	    f = f->next; | |
| 	} | |
|     } | |
|     if ((unsigned) totalNode != table->constants.keys) { | |
| 	(void) fprintf(table->err, | |
| 		       "Error: wrong number of total nodes in constants\n"); | |
| 	flag = 1; | |
|     } | |
|     if ((unsigned) deadNode != table->constants.dead) { | |
| 	(void) fprintf(table->err, | |
| 		       "Error: wrong number of dead nodes in constants\n"); | |
| 	flag = 1; | |
|     } | |
|     gen = st_init_gen(edgeTable); | |
|     while (st_gen_int(gen, (void **) &f, &count)) { | |
| 	if (count > (int)(f->ref) && f->ref != DD_MAXREF) { | |
| 	    fprintf(table->err,"ref count error at node 0x%" PRIxPTR ", count = %d, id = %u, ref = %u, then = 0x%" PRIxPTR ", else = 0x%" PRIxPTR "\n", | |
|                 (ptruint)f,count,f->index,f->ref,(ptruint)cuddT(f),(ptruint)cuddE(f)); | |
| 	    debugFindParent(table,f); | |
| 	    flag = 1; | |
| 	} | |
|     } | |
|     st_free_gen(gen); | |
|     st_free_table(edgeTable); | |
| 
 | |
|     return (flag); | |
| 
 | |
| } /* end of Cudd_DebugCheck */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Checks for several conditions that should not occur. | |
|  | |
|   @details Checks for the following conditions: | |
|   <ul> | |
|   <li>Wrong sizes of subtables. | |
|   <li>Wrong number of keys found in unique subtable. | |
|   <li>Wrong number of dead found in unique subtable. | |
|   <li>Wrong number of keys found in the constant table | |
|   <li>Wrong number of dead found in the constant table | |
|   <li>Wrong number of total slots found | |
|   <li>Wrong number of maximum keys found | |
|   <li>Wrong number of total dead found | |
|   </ul> | |
|   Reports the average length of non-empty lists. | |
|  | |
|   @return the number of subtables for which the number of keys is | |
|   wrong. | |
|  | |
|   @sideeffect None | |
|  | |
|   @see Cudd_DebugCheck | |
|  | |
| */ | |
| int | |
| Cudd_CheckKeys( | |
|   DdManager * table) | |
| { | |
|     int size; | |
|     int i,j; | |
|     DdNodePtr *nodelist; | |
|     DdNode *node; | |
|     DdNode *sentinel = &(table->sentinel); | |
|     DdSubtable *subtable; | |
|     int keys; | |
|     int dead; | |
|     int count = 0; | |
|     int totalKeys = 0; | |
|     int totalSlots = 0; | |
|     int totalDead = 0; | |
|     int nonEmpty = 0; | |
|     unsigned int slots; | |
|     int logSlots; | |
|     int shift; | |
| 
 | |
|     size = table->size; | |
| 
 | |
|     for (i = 0; i < size; i++) { | |
| 	subtable = &(table->subtables[i]); | |
| 	nodelist = subtable->nodelist; | |
| 	keys = subtable->keys; | |
| 	dead = subtable->dead; | |
| 	totalKeys += keys; | |
| 	slots = subtable->slots; | |
| 	shift = subtable->shift; | |
| 	logSlots = sizeof(int) * 8 - shift; | |
| 	if (((slots >> logSlots) << logSlots) != slots) { | |
| 	    (void) fprintf(table->err, | |
| 			   "Unique table %d is not the right power of 2\n", i); | |
| 	    (void) fprintf(table->err, | |
| 			   "    slots = %u shift = %d\n", slots, shift); | |
| 	} | |
| 	totalSlots += slots; | |
| 	totalDead += dead; | |
| 	for (j = 0; (unsigned) j < slots; j++) { | |
| 	    node = nodelist[j]; | |
| 	    if (node != sentinel) { | |
| 		nonEmpty++; | |
| 	    } | |
| 	    while (node != sentinel) { | |
| 		keys--; | |
| 		if (node->ref == 0) { | |
| 		    dead--; | |
| 		} | |
| 		node = node->next; | |
| 	    } | |
| 	} | |
| 	if (keys != 0) { | |
| 	    (void) fprintf(table->err, "Wrong number of keys found \ | |
| in unique table %d (difference=%d)\n", i, keys); | |
| 	    count++; | |
| 	} | |
| 	if (dead != 0) { | |
| 	    (void) fprintf(table->err, "Wrong number of dead found \ | |
| in unique table no. %d (difference=%d)\n", i, dead); | |
| 	} | |
|     }	/* for each BDD/ADD subtable */ | |
| 
 | |
|     /* Check the ZDD subtables. */ | |
|     size = table->sizeZ; | |
| 
 | |
|     for (i = 0; i < size; i++) { | |
| 	subtable = &(table->subtableZ[i]); | |
| 	nodelist = subtable->nodelist; | |
| 	keys = subtable->keys; | |
| 	dead = subtable->dead; | |
| 	totalKeys += keys; | |
| 	totalSlots += subtable->slots; | |
| 	totalDead += dead; | |
| 	for (j = 0; (unsigned) j < subtable->slots; j++) { | |
| 	    node = nodelist[j]; | |
| 	    if (node != NULL) { | |
| 		nonEmpty++; | |
| 	    } | |
| 	    while (node != NULL) { | |
| 		keys--; | |
| 		if (node->ref == 0) { | |
| 		    dead--; | |
| 		} | |
| 		node = node->next; | |
| 	    } | |
| 	} | |
| 	if (keys != 0) { | |
| 	    (void) fprintf(table->err, "Wrong number of keys found \ | |
| in ZDD unique table no. %d (difference=%d)\n", i, keys); | |
| 	    count++; | |
| 	} | |
| 	if (dead != 0) { | |
| 	    (void) fprintf(table->err, "Wrong number of dead found \ | |
| in ZDD unique table no. %d (difference=%d)\n", i, dead); | |
| 	} | |
|     }	/* for each ZDD subtable */ | |
| 
 | |
|     /* Check the constant table. */ | |
|     subtable = &(table->constants); | |
|     nodelist = subtable->nodelist; | |
|     keys = subtable->keys; | |
|     dead = subtable->dead; | |
|     totalKeys += keys; | |
|     totalSlots += subtable->slots; | |
|     totalDead += dead; | |
|     for (j = 0; (unsigned) j < subtable->slots; j++) { | |
| 	node = nodelist[j]; | |
| 	if (node != NULL) { | |
| 	    nonEmpty++; | |
| 	} | |
| 	while (node != NULL) { | |
| 	    keys--; | |
| 	    if (node->ref == 0) { | |
| 		dead--; | |
| 	    } | |
| 	    node = node->next; | |
| 	} | |
|     } | |
|     if (keys != 0) { | |
| 	(void) fprintf(table->err, "Wrong number of keys found \ | |
| in the constant table (difference=%d)\n", keys); | |
| 	count++; | |
|     } | |
|     if (dead != 0) { | |
| 	(void) fprintf(table->err, "Wrong number of dead found \ | |
| in the constant table (difference=%d)\n", dead); | |
|     } | |
|     if ((unsigned) totalKeys != table->keys + table->keysZ) { | |
| 	(void) fprintf(table->err, "Wrong number of total keys found \ | |
| (difference=%d)\n", (int) (totalKeys-table->keys)); | |
|     } | |
|     if ((unsigned) totalSlots != table->slots) { | |
| 	(void) fprintf(table->err, "Wrong number of total slots found \ | |
| (difference=%d)\n", (int) (totalSlots-table->slots)); | |
|     } | |
|     if (table->minDead != (unsigned) (table->gcFrac * table->slots)) { | |
| 	(void) fprintf(table->err, "Wrong number of minimum dead found \ | |
| (%u vs. %u)\n", table->minDead, | |
| 	(unsigned) (table->gcFrac * (double) table->slots)); | |
|     } | |
|     if ((unsigned) totalDead != table->dead + table->deadZ) { | |
| 	(void) fprintf(table->err, "Wrong number of total dead found \ | |
| (difference=%d)\n", (int) (totalDead-table->dead)); | |
|     } | |
|     (void) fprintf(table->out,"Average length of non-empty lists = %g\n", | |
|                    (double) table->keys / (double) nonEmpty); | |
| 
 | |
|     return(count); | |
| 
 | |
| } /* end of Cudd_CheckKeys */ | |
| 
 | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Definition of internal functions                                          */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Prints information about the heap. | |
|  | |
|   @details Prints to the manager's stdout the number of live nodes for each | |
|   level of the %DD heap that contains at least one live node.  It also | |
|   prints a summary containing: | |
|   <ul> | |
|   <li> total number of tables; | |
|   <li> number of tables with live nodes; | |
|   <li> table with the largest number of live nodes; | |
|   <li> number of nodes in that table. | |
|   </ul> | |
|   If more than one table contains the maximum number of live nodes, | |
|   only the one of lowest index is reported. | |
|  | |
|   @return 1 in case of success and 0 otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
| */ | |
| int | |
| cuddHeapProfile( | |
|   DdManager * dd) | |
| { | |
|     int ntables = dd->size; | |
|     DdSubtable *subtables = dd->subtables; | |
|     int i,		/* loop index */ | |
| 	nodes,		/* live nodes in i-th layer */ | |
| 	retval,		/* return value of fprintf */ | |
| 	largest = -1,	/* index of the table with most live nodes */ | |
| 	maxnodes = -1,	/* maximum number of live nodes in a table */ | |
| 	nonempty = 0;	/* number of tables with live nodes */ | |
| 
 | |
|     /* Print header. */ | |
|     retval = fprintf(dd->out,"*** DD heap profile for 0x%" PRIxPTR " ***\n", | |
| 		     (ptruint) dd); | |
|     if (retval == EOF) return 0; | |
| 
 | |
|     /* Print number of live nodes for each nonempty table. */ | |
|     for (i=0; i<ntables; i++) { | |
| 	nodes = subtables[i].keys - subtables[i].dead; | |
| 	if (nodes) { | |
| 	    nonempty++; | |
| 	    retval = fprintf(dd->out,"%5d: %5d nodes\n", i, nodes); | |
| 	    if (retval == EOF) return 0; | |
| 	    if (nodes > maxnodes) { | |
| 		maxnodes = nodes; | |
| 		largest = i; | |
| 	    } | |
| 	} | |
|     } | |
| 
 | |
|     nodes = dd->constants.keys - dd->constants.dead; | |
|     if (nodes) { | |
| 	nonempty++; | |
| 	retval = fprintf(dd->out,"const: %5d nodes\n", nodes); | |
| 	if (retval == EOF) return 0; | |
| 	if (nodes > maxnodes) { | |
| 	    maxnodes = nodes; | |
| 	    largest = CUDD_CONST_INDEX; | |
| 	} | |
|     } | |
| 
 | |
|     /* Print summary. */ | |
|     retval = fprintf(dd->out,"Summary: %d tables, %d non-empty, largest: %d ", | |
| 	  ntables+1, nonempty, largest); | |
|     if (retval == EOF) return 0; | |
|     retval = fprintf(dd->out,"(with %d nodes)\n", maxnodes); | |
|     if (retval == EOF) return 0; | |
| 
 | |
|     return(1); | |
| 
 | |
| } /* end of cuddHeapProfile */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Prints out information on a node. | |
|  | |
|   @sideeffect None | |
|  | |
| */ | |
| void | |
| cuddPrintNode( | |
|   DdNode * f, | |
|   FILE *fp) | |
| { | |
|     f = Cudd_Regular(f); | |
|     (void) fprintf(fp,"       node 0x%" PRIxPTR ", id = %u, ref = %u, then = 0x%" PRIxPTR ", else = 0x%" PRIxPTR "\n", | |
|         (ptruint)f,f->index,f->ref,(ptruint)cuddT(f),(ptruint)cuddE(f)); | |
| 
 | |
| } /* end of cuddPrintNode */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Prints the variable groups as a parenthesized list. | |
|  | |
|   @details   For each group the level range that it represents is printed. | |
|   After each group, the group's flags are printed, preceded by a `|'.  For | |
|   each flag (except MTR_TERMINAL) a character is printed. | |
|   <ul> | |
|   <li>F: MTR_FIXED | |
|   <li>N: MTR_NEWNODE | |
|   <li>S: MTR_SOFT | |
|   </ul> | |
|   The second argument, silent, if different from 0, causes | |
|   Cudd_PrintVarGroups to only check the syntax of the group tree. | |
|  | |
|   @sideeffect None | |
|  | |
| */ | |
| void | |
| cuddPrintVarGroups( | |
|   DdManager * dd /**< manager */, | |
|   MtrNode * root /**< root of the group tree */, | |
|   int zdd /**< 0: %BDD; 1: %ZDD */, | |
|   int silent /**< flag to check tree syntax only */) | |
| { | |
|     MtrNode *node; | |
|     int level; | |
| 
 | |
|     assert(root != NULL); | |
|     assert(root->younger == NULL || root->younger->elder == root); | |
|     assert(root->elder == NULL || root->elder->younger == root); | |
|     if (zdd) { | |
| 	level = dd->permZ[root->index]; | |
|     } else { | |
| 	level = dd->perm[root->index]; | |
|     } | |
|     if (!silent) (void) printf("(%d",level); | |
|     if (MTR_TEST(root,MTR_TERMINAL) || root->child == NULL) { | |
| 	if (!silent) (void) printf(","); | |
|     } else { | |
| 	node = root->child; | |
| 	while (node != NULL) { | |
| 	    assert(node->low >= root->low && (int) (node->low + node->size) <= (int) (root->low + root->size)); | |
| 	    assert(node->parent == root); | |
| 	    cuddPrintVarGroups(dd,node,zdd,silent); | |
| 	    node = node->younger; | |
| 	} | |
|     } | |
|     if (!silent) { | |
| 	(void) printf("%d", (int) (level + root->size - 1)); | |
| 	if (root->flags != MTR_DEFAULT) { | |
| 	    (void) printf("|"); | |
| 	    if (MTR_TEST(root,MTR_FIXED)) (void) printf("F"); | |
| 	    if (MTR_TEST(root,MTR_NEWNODE)) (void) printf("N"); | |
| 	    if (MTR_TEST(root,MTR_SOFT)) (void) printf("S"); | |
| 	} | |
| 	(void) printf(")"); | |
| 	if (root->parent == NULL) (void) printf("\n"); | |
|     } | |
|     assert((root->flags &~(MTR_TERMINAL | MTR_SOFT | MTR_FIXED | MTR_NEWNODE)) == 0); | |
|     return; | |
| 
 | |
| } /* end of cuddPrintVarGroups */ | |
| 
 | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Definition of static functions                                            */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Searches the subtables above node for its parents. | |
|  | |
|   @sideeffect None | |
|  | |
| */ | |
| static void | |
| debugFindParent( | |
|   DdManager * table, | |
|   DdNode * node) | |
| { | |
|     int         i,j; | |
|     int		slots; | |
|     DdNodePtr	*nodelist; | |
|     DdNode	*f; | |
| 
 | |
|     for (i = 0; i < cuddI(table,node->index); i++) { | |
| 	nodelist = table->subtables[i].nodelist; | |
| 	slots = table->subtables[i].slots; | |
| 
 | |
| 	for (j=0;j<slots;j++) { | |
| 	    f = nodelist[j]; | |
| 	    while (f != NULL) { | |
| 		if (cuddT(f) == node || Cudd_Regular(cuddE(f)) == node) { | |
| 		    (void) fprintf(table->out,"parent is at 0x%" PRIxPTR ", id = %u, ref = %u, then = 0x%" PRIxPTR ", else = 0x%" PRIxPTR "\n", | |
| 			(ptruint)f,f->index,f->ref,(ptruint)cuddT(f),(ptruint)cuddE(f)); | |
| 		} | |
| 		f = f->next; | |
| 	    } | |
| 	} | |
|     } | |
| 
 | |
| } /* end of debugFindParent */ | |
| 
 | |
| 
 | |
| #if 0 | |
| /** | |
|   @brief Reports an error if a (dead) node has a non-dead parent. | |
|  | |
|   @details Searches all the subtables above node. Very expensive. | |
|   The same check is now implemented more efficiently in ddDebugCheck. | |
|  | |
|   @sideeffect None | |
|  | |
|   @see debugFindParent | |
|  | |
| */ | |
| static void | |
| debugCheckParent( | |
|   DdManager * table, | |
|   DdNode * node) | |
| { | |
|     int         i,j; | |
|     int		slots; | |
|     DdNode	**nodelist,*f; | |
|  | |
|     for (i = 0; i < cuddI(table,node->index); i++) { | |
| 	nodelist = table->subtables[i].nodelist; | |
| 	slots = table->subtables[i].slots; | |
|  | |
| 	for (j=0;j<slots;j++) { | |
| 	    f = nodelist[j]; | |
| 	    while (f != NULL) { | |
| 		if ((Cudd_Regular(cuddE(f)) == node || cuddT(f) == node) && f->ref != 0) { | |
| 		    (void) fprintf(table->err, | |
| 				   "error with zero ref count\n"); | |
| 		    (void) fprintf(table->err,"parent is 0x%x, id = %u, ref = %u, then = 0x%x, else = 0x%x\n",f,f->index,f->ref,cuddT(f),cuddE(f)); | |
| 		    (void) fprintf(table->err,"child  is 0x%x, id = %u, ref = %u, then = 0x%x, else = 0x%x\n",node,node->index,node->ref,cuddT(node),cuddE(node)); | |
| 		} | |
| 		f = f->next; | |
| 	    } | |
| 	} | |
|     } | |
| } | |
| #endif
 |