/**CFile*********************************************************************** FileName [cuddTable.c] PackageName [cudd] Synopsis [Unique table management functions.] Description [External procedures included in this module: Internal procedures included in this module: Static procedures included in this module: ] SeeAlso [] Author [Fabio Somenzi] Copyright [This file was created at the University of Colorado at Boulder. The University of Colorado at Boulder makes no warranty about the suitability of this software for any purpose. It is presented on an AS IS basis.] ******************************************************************************/ #include "util_hack.h" #include "cuddInt.h" /*---------------------------------------------------------------------------*/ /* Constant declarations */ /*---------------------------------------------------------------------------*/ #ifndef DD_UNSORTED_FREE_LIST /* Constants for red/black trees. */ #define DD_STACK_SIZE 128 #define DD_RED 0 #define DD_BLACK 1 #define DD_PAGE_SIZE 8192 #define DD_PAGE_MASK ~(DD_PAGE_SIZE - 1) #define DD_INSERT_COMPARE(x,y) \ (((ptruint) (x) & DD_PAGE_MASK) - ((ptruint) (y) & DD_PAGE_MASK)) #endif /*---------------------------------------------------------------------------*/ /* Stucture declarations */ /*---------------------------------------------------------------------------*/ /* This is a hack for when CUDD_VALUE_TYPE is double */ typedef union hack { CUDD_VALUE_TYPE value; unsigned int bits[2]; } hack; /*---------------------------------------------------------------------------*/ /* Type declarations */ /*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /* Variable declarations */ /*---------------------------------------------------------------------------*/ #ifndef lint static char rcsid[] DD_UNUSED = "$Id: cuddTable.c,v 1.1.1.1 2003/02/24 22:23:53 wjiang Exp $"; #endif /*---------------------------------------------------------------------------*/ /* Macro declarations */ /*---------------------------------------------------------------------------*/ #ifndef DD_UNSORTED_FREE_LIST /* Macros for red/black trees. */ #define DD_COLOR(p) ((p)->index) #define DD_IS_BLACK(p) ((p)->index == DD_BLACK) #define DD_IS_RED(p) ((p)->index == DD_RED) #define DD_LEFT(p) cuddT(p) #define DD_RIGHT(p) cuddE(p) #define DD_NEXT(p) ((p)->next) #endif /**AutomaticStart*************************************************************/ /*---------------------------------------------------------------------------*/ /* Static function prototypes */ /*---------------------------------------------------------------------------*/ static void ddRehashZdd ARGS((DdManager *unique, int i)); static int ddResizeTable ARGS((DdManager *unique, int index)); static int cuddFindParent ARGS((DdManager *table, DdNode *node)); DD_INLINE static void ddFixLimits ARGS((DdManager *unique)); static void cuddOrderedInsert ARGS((DdNodePtr *root, DdNodePtr node)); static DdNode * cuddOrderedThread ARGS((DdNode *root, DdNode *list)); static void cuddRotateLeft ARGS((DdNodePtr *nodeP)); static void cuddRotateRight ARGS((DdNodePtr *nodeP)); static void cuddDoRebalance ARGS((DdNodePtr **stack, int stackN)); static void ddPatchTree ARGS((DdManager *dd, MtrNode *treenode)); #ifdef DD_DEBUG static int cuddCheckCollisionOrdering ARGS((DdManager *unique, int i, int j)); #endif static void ddReportRefMess ARGS((DdManager *unique, int i, char *caller)); /**AutomaticEnd***************************************************************/ /*---------------------------------------------------------------------------*/ /* Definition of exported functions */ /*---------------------------------------------------------------------------*/ /**Function******************************************************************** Synopsis [Returns the next prime >= p.] Description [] SideEffects [None] ******************************************************************************/ unsigned int Cudd_Prime( unsigned int p) { int i,pn; p--; do { p++; if (p&1) { pn = 1; i = 3; while ((unsigned) (i * i) <= p) { if (p % i == 0) { pn = 0; break; } i += 2; } } else { pn = 0; } } while (!pn); return(p); } /* end of Cudd_Prime */ /*---------------------------------------------------------------------------*/ /* Definition of internal functions */ /*---------------------------------------------------------------------------*/ /**Function******************************************************************** Synopsis [Fast storage allocation for DdNodes in the table.] Description [Fast storage allocation for DdNodes in the table. The first 4 bytes of a chunk contain a pointer to the next block; the rest contains DD_MEM_CHUNK spaces for DdNodes. Returns a pointer to a new node if successful; NULL is memory is full.] SideEffects [None] SeeAlso [cuddDynamicAllocNode] ******************************************************************************/ DdNode * cuddAllocNode( DdManager * unique) { int i; DdNodePtr *mem; DdNode *list, *node; extern void (*MMoutOfMemory)(long); void (*saveHandler)(long); if (unique->nextFree == NULL) { /* free list is empty */ /* Check for exceeded limits. */ if ((unique->keys - unique->dead) + (unique->keysZ - unique->deadZ) > unique->maxLive) { unique->errorCode = CUDD_TOO_MANY_NODES; return(NULL); } if (unique->stash == NULL || unique->memused > unique->maxmemhard) { (void) cuddGarbageCollect(unique,1); mem = NULL; } if (unique->nextFree == NULL) { if (unique->memused > unique->maxmemhard) { unique->errorCode = CUDD_MAX_MEM_EXCEEDED; return(NULL); } /* Try to allocate a new block. */ saveHandler = MMoutOfMemory; MMoutOfMemory = Cudd_OutOfMem; mem = (DdNodePtr *) ALLOC(DdNode,DD_MEM_CHUNK + 1); MMoutOfMemory = saveHandler; if (mem == NULL) { /* No more memory: Try collecting garbage. If this succeeds, ** we end up with mem still NULL, but unique->nextFree != ** NULL. */ if (cuddGarbageCollect(unique,1) == 0) { /* Last resort: Free the memory stashed away, if there ** any. If this succeeeds, mem != NULL and ** unique->nextFree still NULL. */ if (unique->stash != NULL) { FREE(unique->stash); unique->stash = NULL; /* Inhibit resizing of tables. */ cuddSlowTableGrowth(unique); /* Now try again. */ mem = (DdNodePtr *) ALLOC(DdNode,DD_MEM_CHUNK + 1); } if (mem == NULL) { /* Out of luck. Call the default handler to do ** whatever it specifies for a failed malloc. ** If this handler returns, then set error code, ** print warning, and return. */ (*MMoutOfMemory)(sizeof(DdNode)*(DD_MEM_CHUNK + 1)); unique->errorCode = CUDD_MEMORY_OUT; #ifdef DD_VERBOSE (void) fprintf(unique->err, "cuddAllocNode: out of memory"); (void) fprintf(unique->err, "Memory in use = %ld\n", unique->memused); #endif return(NULL); } } } if (mem != NULL) { /* successful allocation; slice memory */ ptruint offset; unique->memused += (DD_MEM_CHUNK + 1) * sizeof(DdNode); mem[0] = (DdNodePtr) unique->memoryList; unique->memoryList = mem; /* Here we rely on the fact that a DdNode is as large ** as 4 pointers. */ offset = (ptruint) mem & (sizeof(DdNode) - 1); mem += (sizeof(DdNode) - offset) / sizeof(DdNodePtr); assert(((ptruint) mem & (sizeof(DdNode) - 1)) == 0); list = (DdNode *) mem; i = 1; do { list[i - 1].next = &list[i]; } while (++i < DD_MEM_CHUNK); list[DD_MEM_CHUNK-1].next = NULL; unique->nextFree = &list[0]; } } } unique->allocated++; node = unique->nextFree; unique->nextFree = node->next; return(node); } /* end of cuddAllocNode */ /**Function******************************************************************** Synopsis [Creates and initializes the unique table.] Description [Creates and initializes the unique table. Returns a pointer to the table if successful; NULL otherwise.] SideEffects [None] SeeAlso [Cudd_Init cuddFreeTable] ******************************************************************************/ DdManager * cuddInitTable( unsigned int numVars /* Initial number of BDD variables (and subtables) */, unsigned int numVarsZ /* Initial number of ZDD variables (and subtables) */, unsigned int numSlots /* Initial size of the BDD subtables */, unsigned int looseUpTo /* Limit for fast table growth */) { DdManager *unique = ALLOC(DdManager,1); int i, j; DdNodePtr *nodelist; DdNode *sentinel; unsigned int slots; int shift; if (unique == NULL) { return(NULL); } sentinel = &(unique->sentinel); sentinel->ref = 0; sentinel->index = 0; cuddT(sentinel) = NULL; cuddE(sentinel) = NULL; sentinel->next = NULL; unique->epsilon = DD_EPSILON; unique->maxGrowth = DD_MAX_REORDER_GROWTH; unique->maxGrowthAlt = 2.0 * DD_MAX_REORDER_GROWTH; unique->reordCycle = 0; /* do not use alternate threshold */ unique->size = numVars; unique->sizeZ = numVarsZ; unique->maxSize = ddMax(DD_DEFAULT_RESIZE, numVars); unique->maxSizeZ = ddMax(DD_DEFAULT_RESIZE, numVarsZ); /* Adjust the requested number of slots to a power of 2. */ slots = 8; while (slots < numSlots) { slots <<= 1; } unique->initSlots = slots; shift = sizeof(int) * 8 - cuddComputeFloorLog2(slots); unique->slots = (numVars + numVarsZ + 1) * slots; unique->keys = 0; unique->maxLive = ~0; /* very large number */ unique->keysZ = 0; unique->dead = 0; unique->deadZ = 0; unique->gcFrac = DD_GC_FRAC_HI; unique->minDead = (unsigned) (DD_GC_FRAC_HI * (double) unique->slots); unique->looseUpTo = looseUpTo; unique->gcEnabled = 1; unique->allocated = 0; unique->reclaimed = 0; unique->subtables = ALLOC(DdSubtable,unique->maxSize); if (unique->subtables == NULL) { unique->errorCode = CUDD_MEMORY_OUT; return(0); } unique->subtableZ = ALLOC(DdSubtable,unique->maxSizeZ); if (unique->subtableZ == NULL) { unique->errorCode = CUDD_MEMORY_OUT; return(0); } unique->perm = ALLOC(int,unique->maxSize); if (unique->perm == NULL) { unique->errorCode = CUDD_MEMORY_OUT; return(0); } unique->invperm = ALLOC(int,unique->maxSize); if (unique->invperm == NULL) { unique->errorCode = CUDD_MEMORY_OUT; return(0); } unique->permZ = ALLOC(int,unique->maxSizeZ); if (unique->permZ == NULL) { unique->errorCode = CUDD_MEMORY_OUT; return(0); } unique->invpermZ = ALLOC(int,unique->maxSizeZ); if (unique->invpermZ == NULL) { unique->errorCode = CUDD_MEMORY_OUT; return(0); } unique->map = NULL; unique->stack = ALLOC(DdNodePtr,ddMax(unique->maxSize,unique->maxSizeZ)+1); if (unique->stack == NULL) { unique->errorCode = CUDD_MEMORY_OUT; return(0); } unique->stack[0] = NULL; /* to suppress harmless UMR */ #ifndef DD_NO_DEATH_ROW unique->deathRowDepth = 1 << cuddComputeFloorLog2(unique->looseUpTo >> 2); unique->deathRow = ALLOC(DdNodePtr,unique->deathRowDepth); if (unique->deathRow == NULL) { unique->errorCode = CUDD_MEMORY_OUT; return(0); } for (i = 0; i < unique->deathRowDepth; i++) { unique->deathRow[i] = NULL; } unique->nextDead = 0; unique->deadMask = unique->deathRowDepth - 1; #endif for (i = 0; (unsigned) i < numVars; i++) { unique->subtables[i].slots = slots; unique->subtables[i].shift = shift; unique->subtables[i].keys = 0; unique->subtables[i].dead = 0; unique->subtables[i].maxKeys = slots * DD_MAX_SUBTABLE_DENSITY; unique->subtables[i].bindVar = 0; unique->subtables[i].varType = CUDD_VAR_PRIMARY_INPUT; unique->subtables[i].pairIndex = 0; unique->subtables[i].varHandled = 0; unique->subtables[i].varToBeGrouped = CUDD_LAZY_NONE; nodelist = unique->subtables[i].nodelist = ALLOC(DdNodePtr,slots); if (nodelist == NULL) { unique->errorCode = CUDD_MEMORY_OUT; return(NULL); } for (j = 0; (unsigned) j < slots; j++) { nodelist[j] = sentinel; } unique->perm[i] = i; unique->invperm[i] = i; } for (i = 0; (unsigned) i < numVarsZ; i++) { unique->subtableZ[i].slots = slots; unique->subtableZ[i].shift = shift; unique->subtableZ[i].keys = 0; unique->subtableZ[i].dead = 0; unique->subtableZ[i].maxKeys = slots * DD_MAX_SUBTABLE_DENSITY; nodelist = unique->subtableZ[i].nodelist = ALLOC(DdNodePtr,slots); if (nodelist == NULL) { unique->errorCode = CUDD_MEMORY_OUT; return(NULL); } for (j = 0; (unsigned) j < slots; j++) { nodelist[j] = NULL; } unique->permZ[i] = i; unique->invpermZ[i] = i; } unique->constants.slots = slots; unique->constants.shift = shift; unique->constants.keys = 0; unique->constants.dead = 0; unique->constants.maxKeys = slots * DD_MAX_SUBTABLE_DENSITY; nodelist = unique->constants.nodelist = ALLOC(DdNodePtr,slots); if (nodelist == NULL) { unique->errorCode = CUDD_MEMORY_OUT; return(NULL); } for (j = 0; (unsigned) j < slots; j++) { nodelist[j] = NULL; } unique->memoryList = NULL; unique->nextFree = NULL; unique->memused = sizeof(DdManager) + (unique->maxSize + unique->maxSizeZ) * (sizeof(DdSubtable) + 2 * sizeof(int)) + (numVars + 1) * slots * sizeof(DdNodePtr) + (ddMax(unique->maxSize,unique->maxSizeZ) + 1) * sizeof(DdNodePtr); #ifndef DD_NO_DEATH_ROW unique->memused += unique->deathRowDepth * sizeof(DdNodePtr); #endif /* Initialize fields concerned with automatic dynamic reordering */ unique->reorderings = 0; unique->autoDyn = 0; /* initially disabled */ unique->autoDynZ = 0; /* initially disabled */ unique->realign = 0; /* initially disabled */ unique->realignZ = 0; /* initially disabled */ unique->reordered = 0; unique->autoMethod = CUDD_REORDER_SIFT; unique->autoMethodZ = CUDD_REORDER_SIFT; unique->nextDyn = DD_FIRST_REORDER; unique->countDead = ~0; unique->siftMaxVar = DD_SIFT_MAX_VAR; unique->siftMaxSwap = DD_SIFT_MAX_SWAPS; unique->tree = NULL; unique->treeZ = NULL; unique->groupcheck = CUDD_GROUP_CHECK7; unique->recomb = DD_DEFAULT_RECOMB; unique->symmviolation = 0; unique->arcviolation = 0; unique->populationSize = 0; unique->numberXovers = 0; unique->linear = NULL; unique->linearSize = 0; /* Initialize ZDD universe. */ unique->univ = (DdNodePtr *)NULL; /* Initialize auxiliary fields. */ unique->localCaches = NULL; unique->preGCHook = NULL; unique->postGCHook = NULL; unique->preReorderingHook = NULL; unique->postReorderingHook = NULL; unique->out = stdout; unique->err = stderr; unique->errorCode = CUDD_NO_ERROR; /* Initialize statistical counters. */ unique->maxmemhard = (long) ((~ (unsigned long) 0) >> 1); unique->garbageCollections = 0; unique->GCTime = 0; unique->reordTime = 0; #ifdef DD_STATS unique->nodesDropped = 0; unique->nodesFreed = 0; #endif unique->peakLiveNodes = 0; #ifdef DD_UNIQUE_PROFILE unique->uniqueLookUps = 0; unique->uniqueLinks = 0; #endif #ifdef DD_COUNT unique->recursiveCalls = 0; unique->swapSteps = 0; #ifdef DD_STATS unique->nextSample = 250000; #endif #endif return(unique); } /* end of cuddInitTable */ /**Function******************************************************************** Synopsis [Frees the resources associated to a unique table.] Description [] SideEffects [None] SeeAlso [cuddInitTable] ******************************************************************************/ void cuddFreeTable( DdManager * unique) { DdNodePtr *next; DdNodePtr *memlist = unique->memoryList; int i; if (unique->univ != NULL) cuddZddFreeUniv(unique); while (memlist != NULL) { next = (DdNodePtr *) memlist[0]; /* link to next block */ FREE(memlist); memlist = next; } unique->nextFree = NULL; unique->memoryList = NULL; for (i = 0; i < unique->size; i++) { FREE(unique->subtables[i].nodelist); } for (i = 0; i < unique->sizeZ; i++) { FREE(unique->subtableZ[i].nodelist); } FREE(unique->constants.nodelist); FREE(unique->subtables); FREE(unique->subtableZ); FREE(unique->acache); FREE(unique->perm); FREE(unique->permZ); FREE(unique->invperm); FREE(unique->invpermZ); FREE(unique->vars); if (unique->map != NULL) FREE(unique->map); FREE(unique->stack); #ifndef DD_NO_DEATH_ROW FREE(unique->deathRow); #endif if (unique->tree != NULL) Mtr_FreeTree(unique->tree); if (unique->treeZ != NULL) Mtr_FreeTree(unique->treeZ); if (unique->linear != NULL) FREE(unique->linear); while (unique->preGCHook != NULL) Cudd_RemoveHook(unique,unique->preGCHook->f,CUDD_PRE_GC_HOOK); while (unique->postGCHook != NULL) Cudd_RemoveHook(unique,unique->postGCHook->f,CUDD_POST_GC_HOOK); while (unique->preReorderingHook != NULL) Cudd_RemoveHook(unique,unique->preReorderingHook->f, CUDD_PRE_REORDERING_HOOK); while (unique->postReorderingHook != NULL) Cudd_RemoveHook(unique,unique->postReorderingHook->f, CUDD_POST_REORDERING_HOOK); FREE(unique); } /* end of cuddFreeTable */ /**Function******************************************************************** Synopsis [Performs garbage collection on a unique table.] Description [Performs garbage collection on a unique table. If clearCache is 0, the cache is not cleared. This should only be specified if the cache has been cleared right before calling cuddGarbageCollect. (As in the case of dynamic reordering.) Returns the total number of deleted nodes.] SideEffects [None] SeeAlso [cuddGarbageCollectZdd] ******************************************************************************/ int cuddGarbageCollect( DdManager * unique, int clearCache) { DdHook *hook; DdCache *cache = unique->cache; DdNode *sentinel = &(unique->sentinel); DdNodePtr *nodelist; int i, j, deleted, totalDeleted; DdCache *c; DdNode *node,*next; DdNodePtr *lastP; int slots; long localTime; #ifndef DD_UNSORTED_FREE_LIST DdNodePtr tree; #endif #ifndef DD_NO_DEATH_ROW cuddClearDeathRow(unique); #endif hook = unique->preGCHook; while (hook != NULL) { int res = (hook->f)(unique,"BDD",NULL); if (res == 0) return(0); hook = hook->next; } if (unique->dead == 0) { hook = unique->postGCHook; while (hook != NULL) { int res = (hook->f)(unique,"BDD",NULL); if (res == 0) return(0); hook = hook->next; } return(0); } /* If many nodes are being reclaimed, we want to resize the tables ** more aggressively, to reduce the frequency of garbage collection. */ if (clearCache && unique->gcFrac == DD_GC_FRAC_LO && unique->slots <= unique->looseUpTo && unique->stash != NULL) { unique->minDead = (unsigned) (DD_GC_FRAC_HI * (double) unique->slots); #ifdef DD_VERBOSE (void) fprintf(unique->err,"GC fraction = %.2f\t", DD_GC_FRAC_HI); (void) fprintf(unique->err,"minDead = %d\n", unique->minDead); #endif unique->gcFrac = DD_GC_FRAC_HI; return(0); } localTime = util_cpu_time(); unique->garbageCollections++; #ifdef DD_VERBOSE (void) fprintf(unique->err, "garbage collecting (%d dead out of %d, min %d)...", unique->dead, unique->keys, unique->minDead); #endif /* Remove references to garbage collected nodes from the cache. */ if (clearCache) { slots = unique->cacheSlots; for (i = 0; i < slots; i++) { c = &cache[i]; if (c->data != NULL) { if (cuddClean(c->f)->ref == 0 || cuddClean(c->g)->ref == 0 || (((ptruint)c->f & 0x2) && Cudd_Regular(c->h)->ref == 0) || (c->data != DD_NON_CONSTANT && Cudd_Regular(c->data)->ref == 0)) { c->data = NULL; unique->cachedeletions++; } } } cuddLocalCacheClearDead(unique); } /* Now return dead nodes to free list. Count them for sanity check. */ totalDeleted = 0; #ifndef DD_UNSORTED_FREE_LIST tree = NULL; #endif for (i = 0; i < unique->size; i++) { if (unique->subtables[i].dead == 0) continue; nodelist = unique->subtables[i].nodelist; deleted = 0; slots = unique->subtables[i].slots; for (j = 0; j < slots; j++) { lastP = &(nodelist[j]); node = *lastP; while (node != sentinel) { next = node->next; if (node->ref == 0) { deleted++; #ifndef DD_UNSORTED_FREE_LIST #ifdef __osf__ #pragma pointer_size save #pragma pointer_size short #endif cuddOrderedInsert(&tree,node); #ifdef __osf__ #pragma pointer_size restore #endif #else cuddDeallocNode(unique,node); #endif } else { *lastP = node; lastP = &(node->next); } node = next; } *lastP = sentinel; } if ((unsigned) deleted != unique->subtables[i].dead) { ddReportRefMess(unique, i, "cuddGarbageCollect"); } totalDeleted += deleted; unique->subtables[i].keys -= deleted; unique->subtables[i].dead = 0; } if (unique->constants.dead != 0) { nodelist = unique->constants.nodelist; deleted = 0; slots = unique->constants.slots; for (j = 0; j < slots; j++) { lastP = &(nodelist[j]); node = *lastP; while (node != NULL) { next = node->next; if (node->ref == 0) { deleted++; #ifndef DD_UNSORTED_FREE_LIST #ifdef __osf__ #pragma pointer_size save #pragma pointer_size short #endif cuddOrderedInsert(&tree,node); #ifdef __osf__ #pragma pointer_size restore #endif #else cuddDeallocNode(unique,node); #endif } else { *lastP = node; lastP = &(node->next); } node = next; } *lastP = NULL; } if ((unsigned) deleted != unique->constants.dead) { ddReportRefMess(unique, CUDD_CONST_INDEX, "cuddGarbageCollect"); } totalDeleted += deleted; unique->constants.keys -= deleted; unique->constants.dead = 0; } if ((unsigned) totalDeleted != unique->dead) { ddReportRefMess(unique, -1, "cuddGarbageCollect"); } unique->keys -= totalDeleted; unique->dead = 0; #ifdef DD_STATS unique->nodesFreed += (double) totalDeleted; #endif #ifndef DD_UNSORTED_FREE_LIST unique->nextFree = cuddOrderedThread(tree,unique->nextFree); #endif unique->GCTime += util_cpu_time() - localTime; hook = unique->postGCHook; while (hook != NULL) { int res = (hook->f)(unique,"BDD",NULL); if (res == 0) return(0); hook = hook->next; } #ifdef DD_VERBOSE (void) fprintf(unique->err," done\n"); #endif return(totalDeleted); } /* end of cuddGarbageCollect */ /**Function******************************************************************** Synopsis [Performs garbage collection on a ZDD unique table.] Description [Performs garbage collection on a ZDD unique table. If clearCache is 0, the cache is not cleared. This should only be specified if the cache has been cleared right before calling cuddGarbageCollectZdd. (As in the case of dynamic reordering.) Returns the total number of deleted nodes.] SideEffects [None] SeeAlso [cuddGarbageCollect] ******************************************************************************/ int cuddGarbageCollectZdd( DdManager * unique, int clearCache) { DdHook *hook; DdCache *cache = unique->cache; DdNodePtr *nodelist; int i, j, deleted, totalDeleted; DdCache *c; DdNode *node,*next; DdNodePtr *lastP; int slots; long localTime; #ifndef DD_UNSORTED_FREE_LIST DdNodePtr tree; #endif hook = unique->preGCHook; while (hook != NULL) { int res = (hook->f)(unique,"ZDD",NULL); if (res == 0) return(0); hook = hook->next; } if (unique->deadZ == 0) { hook = unique->postGCHook; while (hook != NULL) { int res = (hook->f)(unique,"ZDD",NULL); if (res == 0) return(0); hook = hook->next; } return(0); } /* If many nodes are being reclaimed, we want to resize the tables ** more aggressively, to reduce the frequency of garbage collection. */ if (clearCache && unique->slots <= unique->looseUpTo) { unique->minDead = (unsigned) (DD_GC_FRAC_HI * (double) unique->slots); #ifdef DD_VERBOSE if (unique->gcFrac == DD_GC_FRAC_LO) { (void) fprintf(unique->err,"GC fraction = %.2f\t", DD_GC_FRAC_HI); (void) fprintf(unique->err,"minDead = %d\n", unique->minDead); } #endif unique->gcFrac = DD_GC_FRAC_HI; } localTime = util_cpu_time(); unique->garbageCollections++; #ifdef DD_VERBOSE (void) fprintf(unique->err,"garbage collecting (%d dead out of %d)...", unique->deadZ,unique->keysZ); #endif /* Remove references to garbage collected nodes from the cache. */ if (clearCache) { slots = unique->cacheSlots; for (i = 0; i < slots; i++) { c = &cache[i]; if (c->data != NULL) { if (cuddClean(c->f)->ref == 0 || cuddClean(c->g)->ref == 0 || (((ptruint)c->f & 0x2) && Cudd_Regular(c->h)->ref == 0) || (c->data != DD_NON_CONSTANT && Cudd_Regular(c->data)->ref == 0)) { c->data = NULL; unique->cachedeletions++; } } } } /* Now return dead nodes to free list. Count them for sanity check. */ totalDeleted = 0; #ifndef DD_UNSORTED_FREE_LIST tree = NULL; #endif for (i = 0; i < unique->sizeZ; i++) { if (unique->subtableZ[i].dead == 0) continue; nodelist = unique->subtableZ[i].nodelist; deleted = 0; slots = unique->subtableZ[i].slots; for (j = 0; j < slots; j++) { lastP = &(nodelist[j]); node = *lastP; while (node != NULL) { next = node->next; if (node->ref == 0) { deleted++; #ifndef DD_UNSORTED_FREE_LIST #ifdef __osf__ #pragma pointer_size save #pragma pointer_size short #endif cuddOrderedInsert(&tree,node); #ifdef __osf__ #pragma pointer_size restore #endif #else cuddDeallocNode(unique,node); #endif } else { *lastP = node; lastP = &(node->next); } node = next; } *lastP = NULL; } if ((unsigned) deleted != unique->subtableZ[i].dead) { ddReportRefMess(unique, i, "cuddGarbageCollectZdd"); } totalDeleted += deleted; unique->subtableZ[i].keys -= deleted; unique->subtableZ[i].dead = 0; } /* No need to examine the constant table for ZDDs. ** If we did we should be careful not to count whatever dead ** nodes we found there among the dead ZDD nodes. */ if ((unsigned) totalDeleted != unique->deadZ) { ddReportRefMess(unique, -1, "cuddGarbageCollectZdd"); } unique->keysZ -= totalDeleted; unique->deadZ = 0; #ifdef DD_STATS unique->nodesFreed += (double) totalDeleted; #endif #ifndef DD_UNSORTED_FREE_LIST unique->nextFree = cuddOrderedThread(tree,unique->nextFree); #endif unique->GCTime += util_cpu_time() - localTime; hook = unique->postGCHook; while (hook != NULL) { int res = (hook->f)(unique,"ZDD",NULL); if (res == 0) return(0); hook = hook->next; } #ifdef DD_VERBOSE (void) fprintf(unique->err," done\n"); #endif return(totalDeleted); } /* end of cuddGarbageCollectZdd */ /**Function******************************************************************** Synopsis [Wrapper for cuddUniqueInterZdd.] Description [Wrapper for cuddUniqueInterZdd, which applies the ZDD reduction rule. Returns a pointer to the result node under normal conditions; NULL if reordering occurred or memory was exhausted.] SideEffects [None] SeeAlso [cuddUniqueInterZdd] ******************************************************************************/ DdNode * cuddZddGetNode( DdManager * zdd, int id, DdNode * T, DdNode * E) { DdNode *node; if (T == DD_ZERO(zdd)) return(E); node = cuddUniqueInterZdd(zdd, id, T, E); return(node); } /* end of cuddZddGetNode */ /**Function******************************************************************** Synopsis [Wrapper for cuddUniqueInterZdd that is independent of variable ordering.] Description [Wrapper for cuddUniqueInterZdd that is independent of variable ordering (IVO). This function does not require parameter index to precede the indices of the top nodes of g and h in the variable order. Returns a pointer to the result node under normal conditions; NULL if reordering occurred or memory was exhausted.] SideEffects [None] SeeAlso [cuddZddGetNode cuddZddIsop] ******************************************************************************/ DdNode * cuddZddGetNodeIVO( DdManager * dd, int index, DdNode * g, DdNode * h) { DdNode *f, *r, *t; DdNode *zdd_one = DD_ONE(dd); DdNode *zdd_zero = DD_ZERO(dd); f = cuddUniqueInterZdd(dd, index, zdd_one, zdd_zero); if (f == NULL) { return(NULL); } cuddRef(f); t = cuddZddProduct(dd, f, g); if (t == NULL) { Cudd_RecursiveDerefZdd(dd, f); return(NULL); } cuddRef(t); Cudd_RecursiveDerefZdd(dd, f); r = cuddZddUnion(dd, t, h); if (r == NULL) { Cudd_RecursiveDerefZdd(dd, t); return(NULL); } cuddRef(r); Cudd_RecursiveDerefZdd(dd, t); cuddDeref(r); return(r); } /* end of cuddZddGetNodeIVO */ /**Function******************************************************************** Synopsis [Checks the unique table for the existence of an internal node.] Description [Checks the unique table for the existence of an internal node. If it does not exist, it creates a new one. Does not modify the reference count of whatever is returned. A newly created internal node comes back with a reference count 0. For a newly created node, increments the reference counts of what T and E point to. Returns a pointer to the new node if successful; NULL if memory is exhausted or if reordering took place.] SideEffects [None] SeeAlso [cuddUniqueInterZdd] ******************************************************************************/ DdNode * cuddUniqueInter( DdManager * unique, int index, DdNode * T, DdNode * E) { int pos; unsigned int level; int retval; DdNodePtr *nodelist; DdNode *looking; DdNodePtr *previousP; DdSubtable *subtable; int gcNumber; #ifdef DD_UNIQUE_PROFILE unique->uniqueLookUps++; #endif if (index >= unique->size) { if (!ddResizeTable(unique,index)) return(NULL); } level = unique->perm[index]; subtable = &(unique->subtables[level]); #ifdef DD_DEBUG assert(level < (unsigned) cuddI(unique,T->index)); assert(level < (unsigned) cuddI(unique,Cudd_Regular(E)->index)); #endif pos = ddHash(T, E, subtable->shift); nodelist = subtable->nodelist; previousP = &(nodelist[pos]); looking = *previousP; while (T < cuddT(looking)) { previousP = &(looking->next); looking = *previousP; #ifdef DD_UNIQUE_PROFILE unique->uniqueLinks++; #endif } while (T == cuddT(looking) && E < cuddE(looking)) { previousP = &(looking->next); looking = *previousP; #ifdef DD_UNIQUE_PROFILE unique->uniqueLinks++; #endif } if (T == cuddT(looking) && E == cuddE(looking)) { if (looking->ref == 0) { cuddReclaim(unique,looking); } return(looking); } /* countDead is 0 if deads should be counted and ~0 if they should not. */ if (unique->autoDyn && unique->keys - (unique->dead & unique->countDead) >= unique->nextDyn) { #ifdef DD_DEBUG retval = Cudd_DebugCheck(unique); if (retval != 0) return(NULL); retval = Cudd_CheckKeys(unique); if (retval != 0) return(NULL); #endif retval = Cudd_ReduceHeap(unique,unique->autoMethod,10); /* 10 = whatever */ if (retval == 0) unique->reordered = 2; #ifdef DD_DEBUG retval = Cudd_DebugCheck(unique); if (retval != 0) unique->reordered = 2; retval = Cudd_CheckKeys(unique); if (retval != 0) unique->reordered = 2; #endif return(NULL); } if (subtable->keys > subtable->maxKeys) { if (unique->gcEnabled && ((unique->dead > unique->minDead) || ((unique->dead > unique->minDead / 2) && (subtable->dead > subtable->keys * 0.95)))) { /* too many dead */ (void) cuddGarbageCollect(unique,1); } else { cuddRehash(unique,(int)level); } /* Update pointer to insertion point. In the case of rehashing, ** the slot may have changed. In the case of garbage collection, ** the predecessor may have been dead. */ pos = ddHash(T, E, subtable->shift); nodelist = subtable->nodelist; previousP = &(nodelist[pos]); looking = *previousP; while (T < cuddT(looking)) { previousP = &(looking->next); looking = *previousP; #ifdef DD_UNIQUE_PROFILE unique->uniqueLinks++; #endif } while (T == cuddT(looking) && E < cuddE(looking)) { previousP = &(looking->next); looking = *previousP; #ifdef DD_UNIQUE_PROFILE unique->uniqueLinks++; #endif } } gcNumber = unique->garbageCollections; looking = cuddAllocNode(unique); if (looking == NULL) { return(NULL); } unique->keys++; subtable->keys++; if (gcNumber != unique->garbageCollections) { DdNode *looking2; pos = ddHash(T, E, subtable->shift); nodelist = subtable->nodelist; previousP = &(nodelist[pos]); looking2 = *previousP; while (T < cuddT(looking2)) { previousP = &(looking2->next); looking2 = *previousP; #ifdef DD_UNIQUE_PROFILE unique->uniqueLinks++; #endif } while (T == cuddT(looking2) && E < cuddE(looking2)) { previousP = &(looking2->next); looking2 = *previousP; #ifdef DD_UNIQUE_PROFILE unique->uniqueLinks++; #endif } } looking->ref = 0; looking->index = index; cuddT(looking) = T; cuddE(looking) = E; looking->next = *previousP; *previousP = looking; cuddSatInc(T->ref); /* we know T is a regular pointer */ cuddRef(E); #ifdef DD_DEBUG cuddCheckCollisionOrdering(unique,level,pos); #endif return(looking); } /* end of cuddUniqueInter */ /**Function******************************************************************** Synopsis [Wrapper for cuddUniqueInter that is independent of variable ordering.] Description [Wrapper for cuddUniqueInter that is independent of variable ordering (IVO). This function does not require parameter index to precede the indices of the top nodes of T and E in the variable order. Returns a pointer to the result node under normal conditions; NULL if reordering occurred or memory was exhausted.] SideEffects [None] SeeAlso [cuddUniqueInter Cudd_MakeBddFromZddCover] ******************************************************************************/ DdNode * cuddUniqueInterIVO( DdManager * unique, int index, DdNode * T, DdNode * E) { DdNode *result; DdNode *v; v = cuddUniqueInter(unique, index, DD_ONE(unique), Cudd_Not(DD_ONE(unique))); if (v == NULL) return(NULL); cuddRef(v); result = cuddBddIteRecur(unique, v, T, E); Cudd_RecursiveDeref(unique, v); return(result); } /**Function******************************************************************** Synopsis [Checks the unique table for the existence of an internal ZDD node.] Description [Checks the unique table for the existence of an internal ZDD node. If it does not exist, it creates a new one. Does not modify the reference count of whatever is returned. A newly created internal node comes back with a reference count 0. For a newly created node, increments the reference counts of what T and E point to. Returns a pointer to the new node if successful; NULL if memory is exhausted or if reordering took place.] SideEffects [None] SeeAlso [cuddUniqueInter] ******************************************************************************/ DdNode * cuddUniqueInterZdd( DdManager * unique, int index, DdNode * T, DdNode * E) { int pos; unsigned int level; int retval; DdNodePtr *nodelist; DdNode *looking; DdSubtable *subtable; #ifdef DD_UNIQUE_PROFILE unique->uniqueLookUps++; #endif if (index >= unique->sizeZ) { if (!cuddResizeTableZdd(unique,index)) return(NULL); } level = unique->permZ[index]; subtable = &(unique->subtableZ[level]); #ifdef DD_DEBUG assert(level < (unsigned) cuddIZ(unique,T->index)); assert(level < (unsigned) cuddIZ(unique,Cudd_Regular(E)->index)); #endif if (subtable->keys > subtable->maxKeys) { if (unique->gcEnabled && ((unique->deadZ > unique->minDead) || (10 * subtable->dead > 9 * subtable->keys))) { /* too many dead */ (void) cuddGarbageCollectZdd(unique,1); } else { ddRehashZdd(unique,(int)level); } } pos = ddHash(T, E, subtable->shift); nodelist = subtable->nodelist; looking = nodelist[pos]; while (looking != NULL) { if (cuddT(looking) == T && cuddE(looking) == E) { if (looking->ref == 0) { cuddReclaimZdd(unique,looking); } return(looking); } looking = looking->next; #ifdef DD_UNIQUE_PROFILE unique->uniqueLinks++; #endif } /* countDead is 0 if deads should be counted and ~0 if they should not. */ if (unique->autoDynZ && unique->keysZ - (unique->deadZ & unique->countDead) >= unique->nextDyn) { #ifdef DD_DEBUG retval = Cudd_DebugCheck(unique); if (retval != 0) return(NULL); retval = Cudd_CheckKeys(unique); if (retval != 0) return(NULL); #endif retval = Cudd_zddReduceHeap(unique,unique->autoMethodZ,10); /* 10 = whatever */ if (retval == 0) unique->reordered = 2; #ifdef DD_DEBUG retval = Cudd_DebugCheck(unique); if (retval != 0) unique->reordered = 2; retval = Cudd_CheckKeys(unique); if (retval != 0) unique->reordered = 2; #endif return(NULL); } unique->keysZ++; subtable->keys++; looking = cuddAllocNode(unique); if (looking == NULL) return(NULL); looking->ref = 0; looking->index = index; cuddT(looking) = T; cuddE(looking) = E; looking->next = nodelist[pos]; nodelist[pos] = looking; cuddRef(T); cuddRef(E); return(looking); } /* end of cuddUniqueInterZdd */ /**Function******************************************************************** Synopsis [Checks the unique table for the existence of a constant node.] Description [Checks the unique table for the existence of a constant node. If it does not exist, it creates a new one. Does not modify the reference count of whatever is returned. A newly created internal node comes back with a reference count 0. Returns a pointer to the new node.] SideEffects [None] ******************************************************************************/ DdNode * cuddUniqueConst( DdManager * unique, CUDD_VALUE_TYPE value) { int pos; DdNodePtr *nodelist; DdNode *looking; hack split; #ifdef DD_UNIQUE_PROFILE unique->uniqueLookUps++; #endif if (unique->constants.keys > unique->constants.maxKeys) { if (unique->gcEnabled && ((unique->dead > unique->minDead) || (10 * unique->constants.dead > 9 * unique->constants.keys))) { /* too many dead */ (void) cuddGarbageCollect(unique,1); } else { cuddRehash(unique,CUDD_CONST_INDEX); } } cuddAdjust(value); /* for the case of crippled infinities */ if (ddAbs(value) < unique->epsilon) { value = 0.0; } split.value = value; pos = ddHash(split.bits[0], split.bits[1], unique->constants.shift); nodelist = unique->constants.nodelist; looking = nodelist[pos]; /* Here we compare values both for equality and for difference less * than epsilon. The first comparison is required when values are * infinite, since Infinity - Infinity is NaN and NaN < X is 0 for * every X. */ while (looking != NULL) { if (looking->type.value == value || ddEqualVal(looking->type.value,value,unique->epsilon)) { if (looking->ref == 0) { cuddReclaim(unique,looking); } return(looking); } looking = looking->next; #ifdef DD_UNIQUE_PROFILE unique->uniqueLinks++; #endif } unique->keys++; unique->constants.keys++; looking = cuddAllocNode(unique); if (looking == NULL) return(NULL); looking->ref = 0; looking->index = CUDD_CONST_INDEX; looking->type.value = value; looking->next = nodelist[pos]; nodelist[pos] = looking; return(looking); } /* end of cuddUniqueConst */ /**Function******************************************************************** Synopsis [Rehashes a unique subtable.] Description [Doubles the size of a unique subtable and rehashes its contents.] SideEffects [None] SeeAlso [] ******************************************************************************/ void cuddRehash( DdManager * unique, int i) { unsigned int slots, oldslots; int shift, oldshift; int j, pos; DdNodePtr *nodelist, *oldnodelist; DdNode *node, *next; DdNode *sentinel = &(unique->sentinel); hack split; extern void (*MMoutOfMemory)(long); void (*saveHandler)(long); if (unique->gcFrac == DD_GC_FRAC_HI && unique->slots > unique->looseUpTo) { unique->gcFrac = DD_GC_FRAC_LO; unique->minDead = (unsigned) (DD_GC_FRAC_LO * (double) unique->slots); #ifdef DD_VERBOSE (void) fprintf(unique->err,"GC fraction = %.2f\t", DD_GC_FRAC_LO); (void) fprintf(unique->err,"minDead = %d\n", unique->minDead); #endif } if (unique->gcFrac != DD_GC_FRAC_MIN && unique->memused > unique->maxmem) { unique->gcFrac = DD_GC_FRAC_MIN; unique->minDead = (unsigned) (DD_GC_FRAC_MIN * (double) unique->slots); #ifdef DD_VERBOSE (void) fprintf(unique->err,"GC fraction = %.2f\t", DD_GC_FRAC_MIN); (void) fprintf(unique->err,"minDead = %d\n", unique->minDead); #endif cuddShrinkDeathRow(unique); if (cuddGarbageCollect(unique,1) > 0) return; } if (i != CUDD_CONST_INDEX) { oldslots = unique->subtables[i].slots; oldshift = unique->subtables[i].shift; oldnodelist = unique->subtables[i].nodelist; /* Compute the new size of the subtable. */ slots = oldslots << 1; shift = oldshift - 1; saveHandler = MMoutOfMemory; MMoutOfMemory = Cudd_OutOfMem; nodelist = ALLOC(DdNodePtr, slots); MMoutOfMemory = saveHandler; if (nodelist == NULL) { (void) fprintf(unique->err, "Unable to resize subtable %d for lack of memory\n", i); /* Prevent frequent resizing attempts. */ (void) cuddGarbageCollect(unique,1); if (unique->stash != NULL) { FREE(unique->stash); unique->stash = NULL; /* Inhibit resizing of tables. */ cuddSlowTableGrowth(unique); } return; } unique->subtables[i].nodelist = nodelist; unique->subtables[i].slots = slots; unique->subtables[i].shift = shift; unique->subtables[i].maxKeys = slots * DD_MAX_SUBTABLE_DENSITY; /* Move the nodes from the old table to the new table. ** This code depends on the type of hash function. ** It assumes that the effect of doubling the size of the table ** is to retain one more bit of the 32-bit hash value. ** The additional bit is the LSB. */ for (j = 0; (unsigned) j < oldslots; j++) { DdNodePtr *evenP, *oddP; node = oldnodelist[j]; evenP = &(nodelist[j<<1]); oddP = &(nodelist[(j<<1)+1]); while (node != sentinel) { next = node->next; pos = ddHash(cuddT(node), cuddE(node), shift); if (pos & 1) { *oddP = node; oddP = &(node->next); } else { *evenP = node; evenP = &(node->next); } node = next; } *evenP = *oddP = sentinel; } FREE(oldnodelist); #ifdef DD_VERBOSE (void) fprintf(unique->err, "rehashing layer %d: keys %d dead %d new size %d\n", i, unique->subtables[i].keys, unique->subtables[i].dead, slots); #endif } else { oldslots = unique->constants.slots; oldshift = unique->constants.shift; oldnodelist = unique->constants.nodelist; /* The constant subtable is never subjected to reordering. ** Therefore, when it is resized, it is because it has just ** reached the maximum load. We can safely just double the size, ** with no need for the loop we use for the other tables. */ slots = oldslots << 1; shift = oldshift - 1; saveHandler = MMoutOfMemory; MMoutOfMemory = Cudd_OutOfMem; nodelist = ALLOC(DdNodePtr, slots); MMoutOfMemory = saveHandler; if (nodelist == NULL) { int j; (void) fprintf(unique->err, "Unable to resize constant subtable for lack of memory\n"); (void) cuddGarbageCollect(unique,1); for (j = 0; j < unique->size; j++) { unique->subtables[j].maxKeys <<= 1; } unique->constants.maxKeys <<= 1; return; } unique->constants.slots = slots; unique->constants.shift = shift; unique->constants.maxKeys = slots * DD_MAX_SUBTABLE_DENSITY; unique->constants.nodelist = nodelist; for (j = 0; (unsigned) j < slots; j++) { nodelist[j] = NULL; } for (j = 0; (unsigned) j < oldslots; j++) { node = oldnodelist[j]; while (node != NULL) { next = node->next; split.value = cuddV(node); pos = ddHash(split.bits[0], split.bits[1], shift); node->next = nodelist[pos]; nodelist[pos] = node; node = next; } } FREE(oldnodelist); #ifdef DD_VERBOSE (void) fprintf(unique->err, "rehashing constants: keys %d dead %d new size %d\n", unique->constants.keys,unique->constants.dead,slots); #endif } /* Update global data */ unique->memused += (slots - oldslots) * sizeof(DdNodePtr); unique->slots += (slots - oldslots); ddFixLimits(unique); } /* end of cuddRehash */ /**Function******************************************************************** Synopsis [Shrinks a subtable.] Description [Shrinks a subtable.] SideEffects [None] SeeAlso [cuddRehash] ******************************************************************************/ void cuddShrinkSubtable( DdManager *unique, int i) { int j; int shift, posn; DdNodePtr *nodelist, *oldnodelist; DdNode *node, *next; DdNode *sentinel = &(unique->sentinel); unsigned int slots, oldslots; extern void (*MMoutOfMemory)(long); void (*saveHandler)(long); oldnodelist = unique->subtables[i].nodelist; oldslots = unique->subtables[i].slots; slots = oldslots >> 1; saveHandler = MMoutOfMemory; MMoutOfMemory = Cudd_OutOfMem; nodelist = ALLOC(DdNodePtr, slots); MMoutOfMemory = saveHandler; if (nodelist == NULL) { return; } unique->subtables[i].nodelist = nodelist; unique->subtables[i].slots = slots; unique->subtables[i].shift++; unique->subtables[i].maxKeys = slots * DD_MAX_SUBTABLE_DENSITY; #ifdef DD_VERBOSE (void) fprintf(unique->err, "shrunk layer %d (%d keys) from %d to %d slots\n", i, unique->subtables[i].keys, oldslots, slots); #endif for (j = 0; (unsigned) j < slots; j++) { nodelist[j] = sentinel; } shift = unique->subtables[i].shift; for (j = 0; (unsigned) j < oldslots; j++) { node = oldnodelist[j]; while (node != sentinel) { DdNode *looking, *T, *E; DdNodePtr *previousP; next = node->next; posn = ddHash(cuddT(node), cuddE(node), shift); previousP = &(nodelist[posn]); looking = *previousP; T = cuddT(node); E = cuddE(node); while (T < cuddT(looking)) { previousP = &(looking->next); looking = *previousP; #ifdef DD_UNIQUE_PROFILE unique->uniqueLinks++; #endif } while (T == cuddT(looking) && E < cuddE(looking)) { previousP = &(looking->next); looking = *previousP; #ifdef DD_UNIQUE_PROFILE unique->uniqueLinks++; #endif } node->next = *previousP; *previousP = node; node = next; } } FREE(oldnodelist); unique->memused += ((long) slots - (long) oldslots) * sizeof(DdNode *); unique->slots += slots - oldslots; unique->minDead = (unsigned) (unique->gcFrac * (double) unique->slots); unique->cacheSlack = (int) ddMin(unique->maxCacheHard,DD_MAX_CACHE_TO_SLOTS_RATIO * unique->slots) - 2 * (int) unique->cacheSlots; } /* end of cuddShrinkSubtable */ /**Function******************************************************************** Synopsis [Inserts n new subtables in a unique table at level.] Description [Inserts n new subtables in a unique table at level. The number n should be positive, and level should be an existing level. Returns 1 if successful; 0 otherwise.] SideEffects [None] SeeAlso [cuddDestroySubtables] ******************************************************************************/ int cuddInsertSubtables( DdManager * unique, int n, int level) { DdSubtable *newsubtables; DdNodePtr *newnodelist; DdNodePtr *newvars; DdNode *sentinel = &(unique->sentinel); int oldsize,newsize; int i,j,index,reorderSave; unsigned int numSlots = unique->initSlots; int *newperm, *newinvperm, *newmap; DdNode *one, *zero; #ifdef DD_DEBUG assert(n > 0 && level < unique->size); #endif oldsize = unique->size; /* Easy case: there is still room in the current table. */ if (oldsize + n <= unique->maxSize) { /* Shift the tables at and below level. */ for (i = oldsize - 1; i >= level; i--) { unique->subtables[i+n].slots = unique->subtables[i].slots; unique->subtables[i+n].shift = unique->subtables[i].shift; unique->subtables[i+n].keys = unique->subtables[i].keys; unique->subtables[i+n].maxKeys = unique->subtables[i].maxKeys; unique->subtables[i+n].dead = unique->subtables[i].dead; unique->subtables[i+n].nodelist = unique->subtables[i].nodelist; unique->subtables[i+n].bindVar = unique->subtables[i].bindVar; unique->subtables[i+n].varType = unique->subtables[i].varType; unique->subtables[i+n].pairIndex = unique->subtables[i].pairIndex; unique->subtables[i+n].varHandled = unique->subtables[i].varHandled; unique->subtables[i+n].varToBeGrouped = unique->subtables[i].varToBeGrouped; index = unique->invperm[i]; unique->invperm[i+n] = index; unique->perm[index] += n; } /* Create new subtables. */ for (i = 0; i < n; i++) { unique->subtables[level+i].slots = numSlots; unique->subtables[level+i].shift = sizeof(int) * 8 - cuddComputeFloorLog2(numSlots); unique->subtables[level+i].keys = 0; unique->subtables[level+i].maxKeys = numSlots * DD_MAX_SUBTABLE_DENSITY; unique->subtables[level+i].dead = 0; unique->subtables[level+i].bindVar = 0; unique->subtables[level+i].varType = CUDD_VAR_PRIMARY_INPUT; unique->subtables[level+i].pairIndex = 0; unique->subtables[level+i].varHandled = 0; unique->subtables[level+i].varToBeGrouped = CUDD_LAZY_NONE; unique->perm[oldsize+i] = level + i; unique->invperm[level+i] = oldsize + i; newnodelist = unique->subtables[level+i].nodelist = ALLOC(DdNodePtr, numSlots); if (newnodelist == NULL) { unique->errorCode = CUDD_MEMORY_OUT; return(0); } for (j = 0; j < numSlots; j++) { newnodelist[j] = sentinel; } } if (unique->map != NULL) { for (i = 0; i < n; i++) { unique->map[oldsize+i] = oldsize + i; } } } else { /* The current table is too small: we need to allocate a new, ** larger one; move all old subtables, and initialize the new ** subtables. */ newsize = oldsize + n + DD_DEFAULT_RESIZE; #ifdef DD_VERBOSE (void) fprintf(unique->err, "Increasing the table size from %d to %d\n", unique->maxSize, newsize); #endif /* Allocate memory for new arrays (except nodelists). */ newsubtables = ALLOC(DdSubtable,newsize); if (newsubtables == NULL) { unique->errorCode = CUDD_MEMORY_OUT; return(0); } newvars = ALLOC(DdNodePtr,newsize); if (newvars == NULL) { unique->errorCode = CUDD_MEMORY_OUT; FREE(newsubtables); return(0); } newperm = ALLOC(int,newsize); if (newperm == NULL) { unique->errorCode = CUDD_MEMORY_OUT; FREE(newsubtables); FREE(newvars); return(0); } newinvperm = ALLOC(int,newsize); if (newinvperm == NULL) { unique->errorCode = CUDD_MEMORY_OUT; FREE(newsubtables); FREE(newvars); FREE(newperm); return(0); } if (unique->map != NULL) { newmap = ALLOC(int,newsize); if (newmap == NULL) { unique->errorCode = CUDD_MEMORY_OUT; FREE(newsubtables); FREE(newvars); FREE(newperm); FREE(newinvperm); return(0); } unique->memused += (newsize - unique->maxSize) * sizeof(int); } unique->memused += (newsize - unique->maxSize) * ((numSlots+1) * sizeof(DdNode *) + 2 * sizeof(int) + sizeof(DdSubtable)); /* Copy levels before insertion points from old tables. */ for (i = 0; i < level; i++) { newsubtables[i].slots = unique->subtables[i].slots; newsubtables[i].shift = unique->subtables[i].shift; newsubtables[i].keys = unique->subtables[i].keys; newsubtables[i].maxKeys = unique->subtables[i].maxKeys; newsubtables[i].dead = unique->subtables[i].dead; newsubtables[i].nodelist = unique->subtables[i].nodelist; newsubtables[i].bindVar = unique->subtables[i].bindVar; newsubtables[i].varType = unique->subtables[i].varType; newsubtables[i].pairIndex = unique->subtables[i].pairIndex; newsubtables[i].varHandled = unique->subtables[i].varHandled; newsubtables[i].varToBeGrouped = unique->subtables[i].varToBeGrouped; newvars[i] = unique->vars[i]; newperm[i] = unique->perm[i]; newinvperm[i] = unique->invperm[i]; } /* Finish initializing permutation for new table to old one. */ for (i = level; i < oldsize; i++) { newperm[i] = unique->perm[i]; } /* Initialize new levels. */ for (i = level; i < level + n; i++) { newsubtables[i].slots = numSlots; newsubtables[i].shift = sizeof(int) * 8 - cuddComputeFloorLog2(numSlots); newsubtables[i].keys = 0; newsubtables[i].maxKeys = numSlots * DD_MAX_SUBTABLE_DENSITY; newsubtables[i].dead = 0; newsubtables[i].bindVar = 0; newsubtables[i].varType = CUDD_VAR_PRIMARY_INPUT; newsubtables[i].pairIndex = 0; newsubtables[i].varHandled = 0; newsubtables[i].varToBeGrouped = CUDD_LAZY_NONE; newperm[oldsize + i - level] = i; newinvperm[i] = oldsize + i - level; newnodelist = newsubtables[i].nodelist = ALLOC(DdNodePtr, numSlots); if (newnodelist == NULL) { /* We are going to leak some memory. We should clean up. */ unique->errorCode = CUDD_MEMORY_OUT; return(0); } for (j = 0; j < numSlots; j++) { newnodelist[j] = sentinel; } } /* Copy the old tables for levels past the insertion point. */ for (i = level; i < oldsize; i++) { newsubtables[i+n].slots = unique->subtables[i].slots; newsubtables[i+n].shift = unique->subtables[i].shift; newsubtables[i+n].keys = unique->subtables[i].keys; newsubtables[i+n].maxKeys = unique->subtables[i].maxKeys; newsubtables[i+n].dead = unique->subtables[i].dead; newsubtables[i+n].nodelist = unique->subtables[i].nodelist; newsubtables[i+n].bindVar = unique->subtables[i].bindVar; newsubtables[i+n].varType = unique->subtables[i].varType; newsubtables[i+n].pairIndex = unique->subtables[i].pairIndex; newsubtables[i+n].varHandled = unique->subtables[i].varHandled; newsubtables[i+n].varToBeGrouped = unique->subtables[i].varToBeGrouped; newvars[i] = unique->vars[i]; index = unique->invperm[i]; newinvperm[i+n] = index; newperm[index] += n; } /* Update the map. */ if (unique->map != NULL) { for (i = 0; i < oldsize; i++) { newmap[i] = unique->map[i]; } for (i = oldsize; i < oldsize + n; i++) { newmap[i] = i; } FREE(unique->map); unique->map = newmap; } /* Install the new tables and free the old ones. */ FREE(unique->subtables); unique->subtables = newsubtables; unique->maxSize = newsize; FREE(unique->vars); unique->vars = newvars; FREE(unique->perm); unique->perm = newperm; FREE(unique->invperm); unique->invperm = newinvperm; /* Update the stack for iterative procedures. */ if (newsize > unique->maxSizeZ) { FREE(unique->stack); unique->stack = ALLOC(DdNodePtr,newsize + 1); if (unique->stack == NULL) { unique->errorCode = CUDD_MEMORY_OUT; return(0); } unique->stack[0] = NULL; /* to suppress harmless UMR */ unique->memused += (newsize - ddMax(unique->maxSize,unique->maxSizeZ)) * sizeof(DdNode *); } } /* Update manager parameters to account for the new subtables. */ unique->slots += n * numSlots; ddFixLimits(unique); unique->size += n; /* Now that the table is in a coherent state, create the new ** projection functions. We need to temporarily disable reordering, ** because we cannot reorder without projection functions in place. **/ one = unique->one; zero = Cudd_Not(one); reorderSave = unique->autoDyn; unique->autoDyn = 0; for (i = oldsize; i < oldsize + n; i++) { unique->vars[i] = cuddUniqueInter(unique,i,one,zero); if (unique->vars[i] == NULL) { unique->autoDyn = reorderSave; /* Shift everything back so table remains coherent. */ for (j = oldsize; j < i; j++) { Cudd_IterDerefBdd(unique,unique->vars[j]); cuddDeallocNode(unique,unique->vars[j]); unique->vars[j] = NULL; } for (j = level; j < oldsize; j++) { unique->subtables[j].slots = unique->subtables[j+n].slots; unique->subtables[j].slots = unique->subtables[j+n].slots; unique->subtables[j].shift = unique->subtables[j+n].shift; unique->subtables[j].keys = unique->subtables[j+n].keys; unique->subtables[j].maxKeys = unique->subtables[j+n].maxKeys; unique->subtables[j].dead = unique->subtables[j+n].dead; FREE(unique->subtables[j].nodelist); unique->subtables[j].nodelist = unique->subtables[j+n].nodelist; unique->subtables[j+n].nodelist = NULL; unique->subtables[j].bindVar = unique->subtables[j+n].bindVar; unique->subtables[j].varType = unique->subtables[j+n].varType; unique->subtables[j].pairIndex = unique->subtables[j+n].pairIndex; unique->subtables[j].varHandled = unique->subtables[j+n].varHandled; unique->subtables[j].varToBeGrouped = unique->subtables[j+n].varToBeGrouped; index = unique->invperm[j+n]; unique->invperm[j] = index; unique->perm[index] -= n; } unique->size = oldsize; unique->slots -= n * numSlots; ddFixLimits(unique); (void) Cudd_DebugCheck(unique); return(0); } cuddRef(unique->vars[i]); } if (unique->tree != NULL) { unique->tree->size += n; unique->tree->index = unique->invperm[0]; ddPatchTree(unique,unique->tree); } unique->autoDyn = reorderSave; return(1); } /* end of cuddInsertSubtables */ /**Function******************************************************************** Synopsis [Destroys the n most recently created subtables in a unique table.] Description [Destroys the n most recently created subtables in a unique table. n should be positive. The subtables should not contain any live nodes, except the (isolated) projection function. The projection functions are freed. Returns 1 if successful; 0 otherwise.] SideEffects [The variable map used for fast variable substitution is destroyed if it exists. In this case the cache is also cleared.] SeeAlso [cuddInsertSubtables Cudd_SetVarMap] ******************************************************************************/ int cuddDestroySubtables( DdManager * unique, int n) { DdSubtable *subtables; DdNodePtr *nodelist; DdNodePtr *vars; int firstIndex, lastIndex; int index, level, newlevel; int lowestLevel; int shift; int found; /* Sanity check and set up. */ if (n <= 0) return(0); if (n > unique->size) n = unique->size; subtables = unique->subtables; vars = unique->vars; firstIndex = unique->size - n; lastIndex = unique->size; /* Check for nodes labeled by the variables being destroyed ** that may still be in use. It is allowed to destroy a variable ** only if there are no such nodes. Also, find the lowest level ** among the variables being destroyed. This will make further ** processing more efficient. */ lowestLevel = unique->size; for (index = firstIndex; index < lastIndex; index++) { level = unique->perm[index]; if (level < lowestLevel) lowestLevel = level; nodelist = subtables[level].nodelist; if (subtables[level].keys - subtables[level].dead != 1) return(0); /* The projection function should be isolated. If the ref count ** is 1, everything is OK. If the ref count is saturated, then ** we need to make sure that there are no nodes pointing to it. ** As for the external references, we assume the application is ** responsible for them. */ if (vars[index]->ref != 1) { if (vars[index]->ref != DD_MAXREF) return(0); found = cuddFindParent(unique,vars[index]); if (found) { return(0); } else { vars[index]->ref = 1; } } Cudd_RecursiveDeref(unique,vars[index]); } /* Collect garbage, because we cannot afford having dead nodes pointing ** to the dead nodes in the subtables being destroyed. */ (void) cuddGarbageCollect(unique,1); /* Here we know we can destroy our subtables. */ for (index = firstIndex; index < lastIndex; index++) { level = unique->perm[index]; nodelist = subtables[level].nodelist; #ifdef DD_DEBUG assert(subtables[level].keys == 0); #endif FREE(nodelist); unique->memused -= sizeof(DdNodePtr) * subtables[level].slots; unique->slots -= subtables[level].slots; unique->dead -= subtables[level].dead; } /* Here all subtables to be destroyed have their keys field == 0 and ** their hash tables have been freed. ** We now scan the subtables from level lowestLevel + 1 to level size - 1, ** shifting the subtables as required. We keep a running count of ** how many subtables have been moved, so that we know by how many ** positions each subtable should be shifted. */ shift = 1; for (level = lowestLevel + 1; level < unique->size; level++) { if (subtables[level].keys == 0) { shift++; continue; } newlevel = level - shift; subtables[newlevel].slots = subtables[level].slots; subtables[newlevel].shift = subtables[level].shift; subtables[newlevel].keys = subtables[level].keys; subtables[newlevel].maxKeys = subtables[level].maxKeys; subtables[newlevel].dead = subtables[level].dead; subtables[newlevel].nodelist = subtables[level].nodelist; index = unique->invperm[level]; unique->perm[index] = newlevel; unique->invperm[newlevel] = index; subtables[newlevel].bindVar = subtables[level].bindVar; subtables[newlevel].varType = subtables[level].varType; subtables[newlevel].pairIndex = subtables[level].pairIndex; subtables[newlevel].varHandled = subtables[level].varHandled; subtables[newlevel].varToBeGrouped = subtables[level].varToBeGrouped; } /* Destroy the map. If a surviving variable is ** mapped to a dying variable, and the map were used again, ** an out-of-bounds access to unique->vars would result. */ if (unique->map != NULL) { cuddCacheFlush(unique); FREE(unique->map); unique->map = NULL; } unique->minDead = (unsigned) (unique->gcFrac * (double) unique->slots); unique->size -= n; return(1); } /* end of cuddDestroySubtables */ /**Function******************************************************************** Synopsis [Increases the number of ZDD subtables in a unique table so that it meets or exceeds index.] Description [Increases the number of ZDD subtables in a unique table so that it meets or exceeds index. When new ZDD variables are created, it is possible to preserve the functions unchanged, or it is possible to preserve the covers unchanged, but not both. cuddResizeTableZdd preserves the covers. Returns 1 if successful; 0 otherwise.] SideEffects [None] SeeAlso [ddResizeTable] ******************************************************************************/ int cuddResizeTableZdd( DdManager * unique, int index) { DdSubtable *newsubtables; DdNodePtr *newnodelist; int oldsize,newsize; int i,j,reorderSave; unsigned int numSlots = unique->initSlots; int *newperm, *newinvperm; DdNode *one, *zero; oldsize = unique->sizeZ; /* Easy case: there is still room in the current table. */ if (index < unique->maxSizeZ) { for (i = oldsize; i <= index; i++) { unique->subtableZ[i].slots = numSlots; unique->subtableZ[i].shift = sizeof(int) * 8 - cuddComputeFloorLog2(numSlots); unique->subtableZ[i].keys = 0; unique->subtableZ[i].maxKeys = numSlots * DD_MAX_SUBTABLE_DENSITY; unique->subtableZ[i].dead = 0; unique->permZ[i] = i; unique->invpermZ[i] = i; newnodelist = unique->subtableZ[i].nodelist = ALLOC(DdNodePtr, numSlots); if (newnodelist == NULL) { unique->errorCode = CUDD_MEMORY_OUT; return(0); } for (j = 0; j < numSlots; j++) { newnodelist[j] = NULL; } } } else { /* The current table is too small: we need to allocate a new, ** larger one; move all old subtables, and initialize the new ** subtables up to index included. */ newsize = index + DD_DEFAULT_RESIZE; #ifdef DD_VERBOSE (void) fprintf(unique->err, "Increasing the ZDD table size from %d to %d\n", unique->maxSizeZ, newsize); #endif newsubtables = ALLOC(DdSubtable,newsize); if (newsubtables == NULL) { unique->errorCode = CUDD_MEMORY_OUT; return(0); } newperm = ALLOC(int,newsize); if (newperm == NULL) { unique->errorCode = CUDD_MEMORY_OUT; return(0); } newinvperm = ALLOC(int,newsize); if (newinvperm == NULL) { unique->errorCode = CUDD_MEMORY_OUT; return(0); } unique->memused += (newsize - unique->maxSizeZ) * ((numSlots+1) * sizeof(DdNode *) + 2 * sizeof(int) + sizeof(DdSubtable)); if (newsize > unique->maxSize) { FREE(unique->stack); unique->stack = ALLOC(DdNodePtr,newsize + 1); if (unique->stack == NULL) { unique->errorCode = CUDD_MEMORY_OUT; return(0); } unique->stack[0] = NULL; /* to suppress harmless UMR */ unique->memused += (newsize - ddMax(unique->maxSize,unique->maxSizeZ)) * sizeof(DdNode *); } for (i = 0; i < oldsize; i++) { newsubtables[i].slots = unique->subtableZ[i].slots; newsubtables[i].shift = unique->subtableZ[i].shift; newsubtables[i].keys = unique->subtableZ[i].keys; newsubtables[i].maxKeys = unique->subtableZ[i].maxKeys; newsubtables[i].dead = unique->subtableZ[i].dead; newsubtables[i].nodelist = unique->subtableZ[i].nodelist; newperm[i] = unique->permZ[i]; newinvperm[i] = unique->invpermZ[i]; } for (i = oldsize; i <= index; i++) { newsubtables[i].slots = numSlots; newsubtables[i].shift = sizeof(int) * 8 - cuddComputeFloorLog2(numSlots); newsubtables[i].keys = 0; newsubtables[i].maxKeys = numSlots * DD_MAX_SUBTABLE_DENSITY; newsubtables[i].dead = 0; newperm[i] = i; newinvperm[i] = i; newnodelist = newsubtables[i].nodelist = ALLOC(DdNodePtr, numSlots); if (newnodelist == NULL) { unique->errorCode = CUDD_MEMORY_OUT; return(0); } for (j = 0; j < numSlots; j++) { newnodelist[j] = NULL; } } FREE(unique->subtableZ); unique->subtableZ = newsubtables; unique->maxSizeZ = newsize; FREE(unique->permZ); unique->permZ = newperm; FREE(unique->invpermZ); unique->invpermZ = newinvperm; } unique->slots += (index + 1 - unique->sizeZ) * numSlots; ddFixLimits(unique); unique->sizeZ = index + 1; /* Now that the table is in a coherent state, update the ZDD ** universe. We need to temporarily disable reordering, ** because we cannot reorder without universe in place. */ one = unique->one; zero = unique->zero; reorderSave = unique->autoDynZ; unique->autoDynZ = 0; cuddZddFreeUniv(unique); if (!cuddZddInitUniv(unique)) { unique->autoDynZ = reorderSave; return(0); } unique->autoDynZ = reorderSave; return(1); } /* end of cuddResizeTableZdd */ /**Function******************************************************************** Synopsis [Adjusts parameters of a table to slow down its growth.] Description [] SideEffects [None] SeeAlso [] ******************************************************************************/ void cuddSlowTableGrowth( DdManager *unique) { int i; unique->maxCacheHard = unique->cacheSlots - 1; unique->cacheSlack = -(unique->cacheSlots + 1); for (i = 0; i < unique->size; i++) { unique->subtables[i].maxKeys <<= 2; } unique->gcFrac = DD_GC_FRAC_MIN; unique->minDead = (unsigned) (DD_GC_FRAC_MIN * (double) unique->slots); cuddShrinkDeathRow(unique); (void) fprintf(unique->err,"Slowing down table growth: "); (void) fprintf(unique->err,"GC fraction = %.2f\t", unique->gcFrac); (void) fprintf(unique->err,"minDead = %d\n", unique->minDead); } /* end of cuddSlowTableGrowth */ /*---------------------------------------------------------------------------*/ /* Definition of static functions */ /*---------------------------------------------------------------------------*/ /**Function******************************************************************** Synopsis [Rehashes a ZDD unique subtable.] Description [] SideEffects [None] SeeAlso [cuddRehash] ******************************************************************************/ static void ddRehashZdd( DdManager * unique, int i) { unsigned int slots, oldslots; int shift, oldshift; int j, pos; DdNodePtr *nodelist, *oldnodelist; DdNode *node, *next; extern void (*MMoutOfMemory)(long); void (*saveHandler)(long); if (unique->slots > unique->looseUpTo) { unique->minDead = (unsigned) (DD_GC_FRAC_LO * (double) unique->slots); #ifdef DD_VERBOSE if (unique->gcFrac == DD_GC_FRAC_HI) { (void) fprintf(unique->err,"GC fraction = %.2f\t", DD_GC_FRAC_LO); (void) fprintf(unique->err,"minDead = %d\n", unique->minDead); } #endif unique->gcFrac = DD_GC_FRAC_LO; } assert(i != CUDD_MAXINDEX); oldslots = unique->subtableZ[i].slots; oldshift = unique->subtableZ[i].shift; oldnodelist = unique->subtableZ[i].nodelist; /* Compute the new size of the subtable. Normally, we just ** double. However, after reordering, a table may be severely ** overloaded. Therefore, we iterate. */ slots = oldslots; shift = oldshift; do { slots <<= 1; shift--; } while (slots * DD_MAX_SUBTABLE_DENSITY < unique->subtableZ[i].keys); saveHandler = MMoutOfMemory; MMoutOfMemory = Cudd_OutOfMem; nodelist = ALLOC(DdNodePtr, slots); MMoutOfMemory = saveHandler; if (nodelist == NULL) { int j; (void) fprintf(unique->err, "Unable to resize ZDD subtable %d for lack of memory.\n", i); (void) cuddGarbageCollectZdd(unique,1); for (j = 0; j < unique->sizeZ; j++) { unique->subtableZ[j].maxKeys <<= 1; } return; } unique->subtableZ[i].nodelist = nodelist; unique->subtableZ[i].slots = slots; unique->subtableZ[i].shift = shift; unique->subtableZ[i].maxKeys = slots * DD_MAX_SUBTABLE_DENSITY; for (j = 0; (unsigned) j < slots; j++) { nodelist[j] = NULL; } for (j = 0; (unsigned) j < oldslots; j++) { node = oldnodelist[j]; while (node != NULL) { next = node->next; pos = ddHash(cuddT(node), cuddE(node), shift); node->next = nodelist[pos]; nodelist[pos] = node; node = next; } } FREE(oldnodelist); #ifdef DD_VERBOSE (void) fprintf(unique->err, "rehashing layer %d: keys %d dead %d new size %d\n", i, unique->subtableZ[i].keys, unique->subtableZ[i].dead, slots); #endif /* Update global data. */ unique->memused += (slots - oldslots) * sizeof(DdNode *); unique->slots += (slots - oldslots); ddFixLimits(unique); } /* end of ddRehashZdd */ /**Function******************************************************************** Synopsis [Increases the number of subtables in a unique table so that it meets or exceeds index.] Description [Increases the number of subtables in a unique table so that it meets or exceeds index. Returns 1 if successful; 0 otherwise.] SideEffects [None] SeeAlso [cuddResizeTableZdd] ******************************************************************************/ static int ddResizeTable( DdManager * unique, int index) { DdSubtable *newsubtables; DdNodePtr *newnodelist; DdNodePtr *newvars; DdNode *sentinel = &(unique->sentinel); int oldsize,newsize; int i,j,reorderSave; int numSlots = unique->initSlots; int *newperm, *newinvperm, *newmap; DdNode *one, *zero; oldsize = unique->size; /* Easy case: there is still room in the current table. */ if (index < unique->maxSize) { for (i = oldsize; i <= index; i++) { unique->subtables[i].slots = numSlots; unique->subtables[i].shift = sizeof(int) * 8 - cuddComputeFloorLog2(numSlots); unique->subtables[i].keys = 0; unique->subtables[i].maxKeys = numSlots * DD_MAX_SUBTABLE_DENSITY; unique->subtables[i].dead = 0; unique->subtables[i].bindVar = 0; unique->subtables[i].varType = CUDD_VAR_PRIMARY_INPUT; unique->subtables[i].pairIndex = 0; unique->subtables[i].varHandled = 0; unique->subtables[i].varToBeGrouped = CUDD_LAZY_NONE; unique->perm[i] = i; unique->invperm[i] = i; newnodelist = unique->subtables[i].nodelist = ALLOC(DdNodePtr, numSlots); if (newnodelist == NULL) { for (j = oldsize; j < i; j++) { FREE(unique->subtables[j].nodelist); } unique->errorCode = CUDD_MEMORY_OUT; return(0); } for (j = 0; j < numSlots; j++) { newnodelist[j] = sentinel; } } if (unique->map != NULL) { for (i = oldsize; i <= index; i++) { unique->map[i] = i; } } } else { /* The current table is too small: we need to allocate a new, ** larger one; move all old subtables, and initialize the new ** subtables up to index included. */ newsize = index + DD_DEFAULT_RESIZE; #ifdef DD_VERBOSE (void) fprintf(unique->err, "Increasing the table size from %d to %d\n", unique->maxSize, newsize); #endif newsubtables = ALLOC(DdSubtable,newsize); if (newsubtables == NULL) { unique->errorCode = CUDD_MEMORY_OUT; return(0); } newvars = ALLOC(DdNodePtr,newsize); if (newvars == NULL) { FREE(newsubtables); unique->errorCode = CUDD_MEMORY_OUT; return(0); } newperm = ALLOC(int,newsize); if (newperm == NULL) { FREE(newsubtables); FREE(newvars); unique->errorCode = CUDD_MEMORY_OUT; return(0); } newinvperm = ALLOC(int,newsize); if (newinvperm == NULL) { FREE(newsubtables); FREE(newvars); FREE(newperm); unique->errorCode = CUDD_MEMORY_OUT; return(0); } if (unique->map != NULL) { newmap = ALLOC(int,newsize); if (newmap == NULL) { FREE(newsubtables); FREE(newvars); FREE(newperm); FREE(newinvperm); unique->errorCode = CUDD_MEMORY_OUT; return(0); } unique->memused += (newsize - unique->maxSize) * sizeof(int); } unique->memused += (newsize - unique->maxSize) * ((numSlots+1) * sizeof(DdNode *) + 2 * sizeof(int) + sizeof(DdSubtable)); if (newsize > unique->maxSizeZ) { FREE(unique->stack); unique->stack = ALLOC(DdNodePtr,newsize + 1); if (unique->stack == NULL) { FREE(newsubtables); FREE(newvars); FREE(newperm); FREE(newinvperm); if (unique->map != NULL) { FREE(newmap); } unique->errorCode = CUDD_MEMORY_OUT; return(0); } unique->stack[0] = NULL; /* to suppress harmless UMR */ unique->memused += (newsize - ddMax(unique->maxSize,unique->maxSizeZ)) * sizeof(DdNode *); } for (i = 0; i < oldsize; i++) { newsubtables[i].slots = unique->subtables[i].slots; newsubtables[i].shift = unique->subtables[i].shift; newsubtables[i].keys = unique->subtables[i].keys; newsubtables[i].maxKeys = unique->subtables[i].maxKeys; newsubtables[i].dead = unique->subtables[i].dead; newsubtables[i].nodelist = unique->subtables[i].nodelist; newsubtables[i].bindVar = unique->subtables[i].bindVar; newsubtables[i].varType = unique->subtables[i].varType; newsubtables[i].pairIndex = unique->subtables[i].pairIndex; newsubtables[i].varHandled = unique->subtables[i].varHandled; newsubtables[i].varToBeGrouped = unique->subtables[i].varToBeGrouped; newvars[i] = unique->vars[i]; newperm[i] = unique->perm[i]; newinvperm[i] = unique->invperm[i]; } for (i = oldsize; i <= index; i++) { newsubtables[i].slots = numSlots; newsubtables[i].shift = sizeof(int) * 8 - cuddComputeFloorLog2(numSlots); newsubtables[i].keys = 0; newsubtables[i].maxKeys = numSlots * DD_MAX_SUBTABLE_DENSITY; newsubtables[i].dead = 0; newsubtables[i].bindVar = 0; newsubtables[i].varType = CUDD_VAR_PRIMARY_INPUT; newsubtables[i].pairIndex = 0; newsubtables[i].varHandled = 0; newsubtables[i].varToBeGrouped = CUDD_LAZY_NONE; newperm[i] = i; newinvperm[i] = i; newnodelist = newsubtables[i].nodelist = ALLOC(DdNodePtr, numSlots); if (newnodelist == NULL) { unique->errorCode = CUDD_MEMORY_OUT; return(0); } for (j = 0; j < numSlots; j++) { newnodelist[j] = sentinel; } } if (unique->map != NULL) { for (i = 0; i < oldsize; i++) { newmap[i] = unique->map[i]; } for (i = oldsize; i <= index; i++) { newmap[i] = i; } FREE(unique->map); unique->map = newmap; } FREE(unique->subtables); unique->subtables = newsubtables; unique->maxSize = newsize; FREE(unique->vars); unique->vars = newvars; FREE(unique->perm); unique->perm = newperm; FREE(unique->invperm); unique->invperm = newinvperm; } /* Now that the table is in a coherent state, create the new ** projection functions. We need to temporarily disable reordering, ** because we cannot reorder without projection functions in place. **/ one = unique->one; zero = Cudd_Not(one); unique->size = index + 1; unique->slots += (index + 1 - oldsize) * numSlots; ddFixLimits(unique); reorderSave = unique->autoDyn; unique->autoDyn = 0; for (i = oldsize; i <= index; i++) { unique->vars[i] = cuddUniqueInter(unique,i,one,zero); if (unique->vars[i] == NULL) { unique->autoDyn = reorderSave; for (j = oldsize; j < i; j++) { Cudd_IterDerefBdd(unique,unique->vars[j]); cuddDeallocNode(unique,unique->vars[j]); unique->vars[j] = NULL; } for (j = oldsize; j <= index; j++) { FREE(unique->subtables[j].nodelist); unique->subtables[j].nodelist = NULL; } unique->size = oldsize; unique->slots -= (index + 1 - oldsize) * numSlots; ddFixLimits(unique); return(0); } cuddRef(unique->vars[i]); } unique->autoDyn = reorderSave; return(1); } /* end of ddResizeTable */ /**Function******************************************************************** Synopsis [Searches the subtables above node for a parent.] Description [Searches the subtables above node for a parent. Returns 1 as soon as one parent is found. Returns 0 is the search is fruitless.] SideEffects [None] SeeAlso [] ******************************************************************************/ static int cuddFindParent( DdManager * table, DdNode * node) { int i,j; int slots; DdNodePtr *nodelist; DdNode *f; for (i = cuddI(table,node->index) - 1; i >= 0; i--) { nodelist = table->subtables[i].nodelist; slots = table->subtables[i].slots; for (j = 0; j < slots; j++) { f = nodelist[j]; while (cuddT(f) > node) { f = f->next; } while (cuddT(f) == node && Cudd_Regular(cuddE(f)) > node) { f = f->next; } if (cuddT(f) == node && Cudd_Regular(cuddE(f)) == node) { return(1); } } } return(0); } /* end of cuddFindParent */ /**Function******************************************************************** Synopsis [Adjusts the values of table limits.] Description [Adjusts the values of table fields controlling the. sizes of subtables and computed table. If the computed table is too small according to the new values, it is resized.] SideEffects [Modifies manager fields. May resize computed table.] SeeAlso [] ******************************************************************************/ DD_INLINE static void ddFixLimits( DdManager *unique) { unique->minDead = (unsigned) (unique->gcFrac * (double) unique->slots); unique->cacheSlack = (int) ddMin(unique->maxCacheHard, DD_MAX_CACHE_TO_SLOTS_RATIO * unique->slots) - 2 * (int) unique->cacheSlots; if (unique->cacheSlots < unique->slots/2 && unique->cacheSlack >= 0) cuddCacheResize(unique); return; } /* end of ddFixLimits */ #ifndef DD_UNSORTED_FREE_LIST /**Function******************************************************************** Synopsis [Inserts a DdNode in a red/black search tree.] Description [Inserts a DdNode in a red/black search tree. Nodes from the same "page" (defined by DD_PAGE_MASK) are linked in a LIFO list.] SideEffects [None] SeeAlso [cuddOrderedThread] ******************************************************************************/ static void cuddOrderedInsert( DdNodePtr * root, DdNodePtr node) { DdNode *scan; DdNodePtr *scanP; DdNodePtr *stack[DD_STACK_SIZE]; int stackN = 0; scanP = root; while ((scan = *scanP) != NULL) { stack[stackN++] = scanP; if (DD_INSERT_COMPARE(node, scan) == 0) { /* add to page list */ DD_NEXT(node) = DD_NEXT(scan); DD_NEXT(scan) = node; return; } scanP = (node < scan) ? &DD_LEFT(scan) : &DD_RIGHT(scan); } DD_RIGHT(node) = DD_LEFT(node) = DD_NEXT(node) = NULL; DD_COLOR(node) = DD_RED; *scanP = node; stack[stackN] = &node; cuddDoRebalance(stack,stackN); } /* end of cuddOrderedInsert */ /**Function******************************************************************** Synopsis [Threads all the nodes of a search tree into a linear list.] Description [Threads all the nodes of a search tree into a linear list. For each node of the search tree, the "left" child, if non-null, has a lower address than its parent, and the "right" child, if non-null, has a higher address than its parent. The list is sorted in order of increasing addresses. The search tree is destroyed as a result of this operation. The last element of the linear list is made to point to the address passed in list. Each node if the search tree is a linearly-linked list of nodes from the same memory page (as defined in DD_PAGE_MASK). When a node is added to the linear list, all the elements of the linked list are added.] SideEffects [The search tree is destroyed as a result of this operation.] SeeAlso [cuddOrderedInsert] ******************************************************************************/ static DdNode * cuddOrderedThread( DdNode * root, DdNode * list) { DdNode *current, *next, *prev, *end; current = root; /* The first word in the node is used to implement a stack that holds ** the nodes from the root of the tree to the current node. Here we ** put the root of the tree at the bottom of the stack. */ *((DdNodePtr *) current) = NULL; while (current != NULL) { if (DD_RIGHT(current) != NULL) { /* If possible, we follow the "right" link. Eventually we'll ** find the node with the largest address in the current tree. ** In this phase we use the first word of a node to implemen ** a stack of the nodes on the path from the root to "current". ** Also, we disconnect the "right" pointers to indicate that ** we have already followed them. */ next = DD_RIGHT(current); DD_RIGHT(current) = NULL; *((DdNodePtr *)next) = current; current = next; } else { /* We can't proceed along the "right" links any further. ** Hence "current" is the largest element in the current tree. ** We make this node the new head of "list". (Repeating this ** operation until the tree is empty yields the desired linear ** threading of all nodes.) */ prev = *((DdNodePtr *) current); /* save prev node on stack in prev */ /* Traverse the linked list of current until the end. */ for (end = current; DD_NEXT(end) != NULL; end = DD_NEXT(end)); DD_NEXT(end) = list; /* attach "list" at end and make */ list = current; /* "current" the new head of "list" */ /* Now, if current has a "left" child, we push it on the stack. ** Otherwise, we just continue with the parent of "current". */ if (DD_LEFT(current) != NULL) { next = DD_LEFT(current); *((DdNodePtr *) next) = prev; current = next; } else { current = prev; } } } return(list); } /* end of cuddOrderedThread */ /**Function******************************************************************** Synopsis [Performs the left rotation for red/black trees.] Description [] SideEffects [None] SeeAlso [cuddRotateRight] ******************************************************************************/ DD_INLINE static void cuddRotateLeft( DdNodePtr * nodeP) { DdNode *newRoot; DdNode *oldRoot = *nodeP; *nodeP = newRoot = DD_RIGHT(oldRoot); DD_RIGHT(oldRoot) = DD_LEFT(newRoot); DD_LEFT(newRoot) = oldRoot; } /* end of cuddRotateLeft */ /**Function******************************************************************** Synopsis [Performs the right rotation for red/black trees.] Description [] SideEffects [None] SeeAlso [cuddRotateLeft] ******************************************************************************/ DD_INLINE static void cuddRotateRight( DdNodePtr * nodeP) { DdNode *newRoot; DdNode *oldRoot = *nodeP; *nodeP = newRoot = DD_LEFT(oldRoot); DD_LEFT(oldRoot) = DD_RIGHT(newRoot); DD_RIGHT(newRoot) = oldRoot; } /* end of cuddRotateRight */ /**Function******************************************************************** Synopsis [Rebalances a red/black tree.] Description [] SideEffects [None] SeeAlso [] ******************************************************************************/ static void cuddDoRebalance( DdNodePtr ** stack, int stackN) { DdNodePtr *xP, *parentP, *grandpaP; DdNode *x, *y, *parent, *grandpa; xP = stack[stackN]; x = *xP; /* Work our way back up, re-balancing the tree. */ while (--stackN >= 0) { parentP = stack[stackN]; parent = *parentP; if (DD_IS_BLACK(parent)) break; /* Since the root is black, here a non-null grandparent exists. */ grandpaP = stack[stackN-1]; grandpa = *grandpaP; if (parent == DD_LEFT(grandpa)) { y = DD_RIGHT(grandpa); if (y != NULL && DD_IS_RED(y)) { DD_COLOR(parent) = DD_BLACK; DD_COLOR(y) = DD_BLACK; DD_COLOR(grandpa) = DD_RED; x = grandpa; stackN--; } else { if (x == DD_RIGHT(parent)) { cuddRotateLeft(parentP); DD_COLOR(x) = DD_BLACK; } else { DD_COLOR(parent) = DD_BLACK; } DD_COLOR(grandpa) = DD_RED; cuddRotateRight(grandpaP); break; } } else { y = DD_LEFT(grandpa); if (y != NULL && DD_IS_RED(y)) { DD_COLOR(parent) = DD_BLACK; DD_COLOR(y) = DD_BLACK; DD_COLOR(grandpa) = DD_RED; x = grandpa; stackN--; } else { if (x == DD_LEFT(parent)) { cuddRotateRight(parentP); DD_COLOR(x) = DD_BLACK; } else { DD_COLOR(parent) = DD_BLACK; } DD_COLOR(grandpa) = DD_RED; cuddRotateLeft(grandpaP); } } } DD_COLOR(*(stack[0])) = DD_BLACK; } /* end of cuddDoRebalance */ #endif /**Function******************************************************************** Synopsis [Fixes a variable tree after the insertion of new subtables.] Description [Fixes a variable tree after the insertion of new subtables. After such an insertion, the low fields of the tree below the insertion point are inconsistent.] SideEffects [None] SeeAlso [] ******************************************************************************/ static void ddPatchTree( DdManager *dd, MtrNode *treenode) { MtrNode *auxnode = treenode; while (auxnode != NULL) { auxnode->low = dd->perm[auxnode->index]; if (auxnode->child != NULL) { ddPatchTree(dd, auxnode->child); } auxnode = auxnode->younger; } return; } /* end of ddPatchTree */ #ifdef DD_DEBUG /**Function******************************************************************** Synopsis [Checks whether a collision list is ordered.] Description [] SideEffects [None] SeeAlso [] ******************************************************************************/ static int cuddCheckCollisionOrdering( DdManager *unique, int i, int j) { int slots; DdNode *node, *next; DdNodePtr *nodelist; DdNode *sentinel = &(unique->sentinel); nodelist = unique->subtables[i].nodelist; slots = unique->subtables[i].slots; node = nodelist[j]; if (node == sentinel) return(1); next = node->next; while (next != sentinel) { if (cuddT(node) < cuddT(next) || (cuddT(node) == cuddT(next) && cuddE(node) < cuddE(next))) { (void) fprintf(unique->err, "Unordered list: index %u, position %d\n", i, j); return(0); } node = next; next = node->next; } return(1); } /* end of cuddCheckCollisionOrdering */ #endif /**Function******************************************************************** Synopsis [Reports problem in garbage collection.] Description [] SideEffects [None] SeeAlso [cuddGarbageCollect cuddGarbageCollectZdd] ******************************************************************************/ static void ddReportRefMess( DdManager *unique /* manager */, int i /* table in which the problem occurred */, char *caller /* procedure that detected the problem */) { if (i == CUDD_CONST_INDEX) { (void) fprintf(unique->err, "%s: problem in constants\n", caller); } else if (i != -1) { (void) fprintf(unique->err, "%s: problem in table %d\n", caller, i); } (void) fprintf(unique->err, " dead count != deleted\n"); (void) fprintf(unique->err, " This problem is often due to a missing \ call to Cudd_Ref\n or to an extra call to Cudd_RecursiveDeref.\n \ See the CUDD Programmer's Guide for additional details."); abort(); } /* end of ddReportRefMess */