| /**CFile*********************************************************************** |
| |
| FileName [cuddLevelQ.c] |
| |
| PackageName [cudd] |
| |
| Synopsis [Procedure to manage level queues.] |
| |
| Description [The functions in this file allow an application to |
| easily manipulate a queue where nodes are prioritized by level. The |
| emphasis is on efficiency. Therefore, the queue items can have |
| variable size. If the application does not need to attach |
| information to the nodes, it can declare the queue items to be of |
| type DdQueueItem. Otherwise, it can declare them to be of a |
| structure type such that the first three fields are data |
| pointers. The third pointer points to the node. The first two |
| pointers are used by the level queue functions. The remaining fields |
| are initialized to 0 when a new item is created, and are then left |
| to the exclusive use of the application. On the DEC Alphas the three |
| pointers must be 32-bit pointers when CUDD is compiled with 32-bit |
| pointers. The level queue functions make sure that each node |
| appears at most once in the queue. They do so by keeping a hash |
| table where the node is used as key. Queue items are recycled via a |
| free list for efficiency. |
| |
| Internal procedures provided by this module: |
| <ul> |
| <li> cuddLevelQueueInit() |
| <li> cuddLevelQueueQuit() |
| <li> cuddLevelQueueEnqueue() |
| <li> cuddLevelQueueDequeue() |
| </ul> |
| Static procedures included in this module: |
| <ul> |
| <li> hashLookup() |
| <li> hashInsert() |
| <li> hashDelete() |
| <li> hashResize() |
| </ul> |
| ] |
| |
| SeeAlso [] |
| |
| Author [Fabio Somenzi] |
| |
| Copyright [Copyright (c) 1995-2004, 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.] |
| |
| ******************************************************************************/ |
| |
| #include "misc/util/util_hack.h" |
| #include "cuddInt.h" |
| |
| ABC_NAMESPACE_IMPL_START |
| |
| |
| |
| /*---------------------------------------------------------------------------*/ |
| /* Constant declarations */ |
| /*---------------------------------------------------------------------------*/ |
| |
| |
| /*---------------------------------------------------------------------------*/ |
| /* Stucture declarations */ |
| /*---------------------------------------------------------------------------*/ |
| |
| /*---------------------------------------------------------------------------*/ |
| /* Type declarations */ |
| /*---------------------------------------------------------------------------*/ |
| |
| /*---------------------------------------------------------------------------*/ |
| /* Variable declarations */ |
| /*---------------------------------------------------------------------------*/ |
| |
| #ifndef lint |
| static char rcsid[] DD_UNUSED = "$Id: cuddLevelQ.c,v 1.13 2009/03/08 02:49:02 fabio Exp $"; |
| #endif |
| |
| /*---------------------------------------------------------------------------*/ |
| /* Macro declarations */ |
| /*---------------------------------------------------------------------------*/ |
| |
| |
| /**Macro*********************************************************************** |
| |
| Synopsis [Hash function for the table of a level queue.] |
| |
| Description [Hash function for the table of a level queue.] |
| |
| SideEffects [None] |
| |
| SeeAlso [hashInsert hashLookup hashDelete] |
| |
| ******************************************************************************/ |
| #if SIZEOF_VOID_P == 8 && SIZEOF_INT == 4 |
| #define lqHash(key,shift) \ |
| (((unsigned)(ptruint)(key) * DD_P1) >> (shift)) |
| #else |
| #define lqHash(key,shift) \ |
| (((unsigned)(key) * DD_P1) >> (shift)) |
| #endif |
| |
| |
| /**AutomaticStart*************************************************************/ |
| |
| /*---------------------------------------------------------------------------*/ |
| /* Static function prototypes */ |
| /*---------------------------------------------------------------------------*/ |
| |
| static DdQueueItem * hashLookup (DdLevelQueue *queue, void *key); |
| static int hashInsert (DdLevelQueue *queue, DdQueueItem *item); |
| static void hashDelete (DdLevelQueue *queue, DdQueueItem *item); |
| static int hashResize (DdLevelQueue *queue); |
| |
| /**AutomaticEnd***************************************************************/ |
| |
| |
| /*---------------------------------------------------------------------------*/ |
| /* Definition of internal functions */ |
| /*---------------------------------------------------------------------------*/ |
| |
| |
| /**Function******************************************************************** |
| |
| Synopsis [Initializes a level queue.] |
| |
| Description [Initializes a level queue. A level queue is a queue |
| where inserts are based on the levels of the nodes. Within each |
| level the policy is FIFO. Level queues are useful in traversing a |
| BDD top-down. Queue items are kept in a free list when dequeued for |
| efficiency. Returns a pointer to the new queue if successful; NULL |
| otherwise.] |
| |
| SideEffects [None] |
| |
| SeeAlso [cuddLevelQueueQuit cuddLevelQueueEnqueue cuddLevelQueueDequeue] |
| |
| ******************************************************************************/ |
| DdLevelQueue * |
| cuddLevelQueueInit( |
| int levels /* number of levels */, |
| int itemSize /* size of the item */, |
| int numBuckets /* initial number of hash buckets */) |
| { |
| DdLevelQueue *queue; |
| int logSize; |
| |
| queue = ABC_ALLOC(DdLevelQueue,1); |
| if (queue == NULL) |
| return(NULL); |
| #ifdef __osf__ |
| #pragma pointer_size save |
| #pragma pointer_size short |
| #endif |
| /* Keep pointers to the insertion points for all levels. */ |
| queue->last = ABC_ALLOC(DdQueueItem *, levels); |
| #ifdef __osf__ |
| #pragma pointer_size restore |
| #endif |
| if (queue->last == NULL) { |
| ABC_FREE(queue); |
| return(NULL); |
| } |
| /* Use a hash table to test for uniqueness. */ |
| if (numBuckets < 2) numBuckets = 2; |
| logSize = cuddComputeFloorLog2(numBuckets); |
| queue->numBuckets = 1 << logSize; |
| queue->shift = sizeof(int) * 8 - logSize; |
| #ifdef __osf__ |
| #pragma pointer_size save |
| #pragma pointer_size short |
| #endif |
| queue->buckets = ABC_ALLOC(DdQueueItem *, queue->numBuckets); |
| #ifdef __osf__ |
| #pragma pointer_size restore |
| #endif |
| if (queue->buckets == NULL) { |
| ABC_FREE(queue->last); |
| ABC_FREE(queue); |
| return(NULL); |
| } |
| #ifdef __osf__ |
| #pragma pointer_size save |
| #pragma pointer_size short |
| #endif |
| memset(queue->last, 0, levels * sizeof(DdQueueItem *)); |
| memset(queue->buckets, 0, queue->numBuckets * sizeof(DdQueueItem *)); |
| #ifdef __osf__ |
| #pragma pointer_size restore |
| #endif |
| queue->first = NULL; |
| queue->freelist = NULL; |
| queue->levels = levels; |
| queue->itemsize = itemSize; |
| queue->size = 0; |
| queue->maxsize = queue->numBuckets * DD_MAX_SUBTABLE_DENSITY; |
| return(queue); |
| |
| } /* end of cuddLevelQueueInit */ |
| |
| |
| /**Function******************************************************************** |
| |
| Synopsis [Shuts down a level queue.] |
| |
| Description [Shuts down a level queue and releases all the |
| associated memory.] |
| |
| SideEffects [None] |
| |
| SeeAlso [cuddLevelQueueInit] |
| |
| ******************************************************************************/ |
| void |
| cuddLevelQueueQuit( |
| DdLevelQueue * queue) |
| { |
| DdQueueItem *item; |
| |
| while (queue->freelist != NULL) { |
| item = queue->freelist; |
| queue->freelist = item->next; |
| ABC_FREE(item); |
| } |
| while (queue->first != NULL) { |
| item = (DdQueueItem *) queue->first; |
| queue->first = item->next; |
| ABC_FREE(item); |
| } |
| ABC_FREE(queue->buckets); |
| ABC_FREE(queue->last); |
| ABC_FREE(queue); |
| return; |
| |
| } /* end of cuddLevelQueueQuit */ |
| |
| |
| /**Function******************************************************************** |
| |
| Synopsis [Inserts a new key in a level queue.] |
| |
| Description [Inserts a new key in a level queue. A new entry is |
| created in the queue only if the node is not already |
| enqueued. Returns a pointer to the queue item if successful; NULL |
| otherwise.] |
| |
| SideEffects [None] |
| |
| SeeAlso [cuddLevelQueueInit cuddLevelQueueDequeue] |
| |
| ******************************************************************************/ |
| void * |
| cuddLevelQueueEnqueue( |
| DdLevelQueue * queue /* level queue */, |
| void * key /* key to be enqueued */, |
| int level /* level at which to insert */) |
| { |
| int plevel; |
| DdQueueItem *item; |
| |
| #ifdef DD_DEBUG |
| assert(level < queue->levels); |
| #endif |
| /* Check whether entry for this node exists. */ |
| item = hashLookup(queue,key); |
| if (item != NULL) return(item); |
| |
| /* Get a free item from either the free list or the memory manager. */ |
| if (queue->freelist == NULL) { |
| item = (DdQueueItem *) ABC_ALLOC(char, queue->itemsize); |
| if (item == NULL) |
| return(NULL); |
| } else { |
| item = queue->freelist; |
| queue->freelist = item->next; |
| } |
| /* Initialize. */ |
| memset(item, 0, queue->itemsize); |
| item->key = key; |
| /* Update stats. */ |
| queue->size++; |
| |
| if (queue->last[level]) { |
| /* There are already items for this level in the queue. */ |
| item->next = queue->last[level]->next; |
| queue->last[level]->next = item; |
| } else { |
| /* There are no items at the current level. Look for the first |
| ** non-empty level preceeding this one. */ |
| plevel = level; |
| while (plevel != 0 && queue->last[plevel] == NULL) |
| plevel--; |
| if (queue->last[plevel] == NULL) { |
| /* No element precedes this one in the queue. */ |
| item->next = (DdQueueItem *) queue->first; |
| queue->first = item; |
| } else { |
| item->next = queue->last[plevel]->next; |
| queue->last[plevel]->next = item; |
| } |
| } |
| queue->last[level] = item; |
| |
| /* Insert entry for the key in the hash table. */ |
| if (hashInsert(queue,item) == 0) { |
| return(NULL); |
| } |
| return(item); |
| |
| } /* end of cuddLevelQueueEnqueue */ |
| |
| |
| /**Function******************************************************************** |
| |
| Synopsis [Remove an item from the front of a level queue.] |
| |
| Description [Remove an item from the front of a level queue.] |
| |
| SideEffects [None] |
| |
| SeeAlso [cuddLevelQueueEnqueue] |
| |
| ******************************************************************************/ |
| void |
| cuddLevelQueueDequeue( |
| DdLevelQueue * queue, |
| int level) |
| { |
| DdQueueItem *item = (DdQueueItem *) queue->first; |
| |
| /* Delete from the hash table. */ |
| hashDelete(queue,item); |
| |
| /* Since we delete from the front, if this is the last item for |
| ** its level, there are no other items for the same level. */ |
| if (queue->last[level] == item) |
| queue->last[level] = NULL; |
| |
| queue->first = item->next; |
| /* Put item on the free list. */ |
| item->next = queue->freelist; |
| queue->freelist = item; |
| /* Update stats. */ |
| queue->size--; |
| return; |
| |
| } /* end of cuddLevelQueueDequeue */ |
| |
| |
| /*---------------------------------------------------------------------------*/ |
| /* Definition of static functions */ |
| /*---------------------------------------------------------------------------*/ |
| |
| |
| /**Function******************************************************************** |
| |
| Synopsis [Looks up a key in the hash table of a level queue.] |
| |
| Description [Looks up a key in the hash table of a level queue. Returns |
| a pointer to the item with the given key if the key is found; NULL |
| otherwise.] |
| |
| SideEffects [None] |
| |
| SeeAlso [cuddLevelQueueEnqueue hashInsert] |
| |
| ******************************************************************************/ |
| static DdQueueItem * |
| hashLookup( |
| DdLevelQueue * queue, |
| void * key) |
| { |
| int posn; |
| DdQueueItem *item; |
| |
| posn = lqHash(key,queue->shift); |
| item = queue->buckets[posn]; |
| |
| while (item != NULL) { |
| if (item->key == key) { |
| return(item); |
| } |
| item = item->cnext; |
| } |
| return(NULL); |
| |
| } /* end of hashLookup */ |
| |
| |
| /**Function******************************************************************** |
| |
| Synopsis [Inserts an item in the hash table of a level queue.] |
| |
| Description [Inserts an item in the hash table of a level queue. Returns |
| 1 if successful; 0 otherwise. No check is performed to see if an item with |
| the same key is already in the hash table.] |
| |
| SideEffects [None] |
| |
| SeeAlso [cuddLevelQueueEnqueue] |
| |
| ******************************************************************************/ |
| static int |
| hashInsert( |
| DdLevelQueue * queue, |
| DdQueueItem * item) |
| { |
| int result; |
| int posn; |
| |
| if (queue->size > queue->maxsize) { |
| result = hashResize(queue); |
| if (result == 0) return(0); |
| } |
| |
| posn = lqHash(item->key,queue->shift); |
| item->cnext = queue->buckets[posn]; |
| queue->buckets[posn] = item; |
| |
| return(1); |
| |
| } /* end of hashInsert */ |
| |
| |
| /**Function******************************************************************** |
| |
| Synopsis [Removes an item from the hash table of a level queue.] |
| |
| Description [Removes an item from the hash table of a level queue. |
| Nothing is done if the item is not in the table.] |
| |
| SideEffects [None] |
| |
| SeeAlso [cuddLevelQueueDequeue hashInsert] |
| |
| ******************************************************************************/ |
| static void |
| hashDelete( |
| DdLevelQueue * queue, |
| DdQueueItem * item) |
| { |
| int posn; |
| DdQueueItem *prevItem; |
| |
| posn = lqHash(item->key,queue->shift); |
| prevItem = queue->buckets[posn]; |
| |
| if (prevItem == NULL) return; |
| if (prevItem == item) { |
| queue->buckets[posn] = prevItem->cnext; |
| return; |
| } |
| |
| while (prevItem->cnext != NULL) { |
| if (prevItem->cnext == item) { |
| prevItem->cnext = item->cnext; |
| return; |
| } |
| prevItem = prevItem->cnext; |
| } |
| return; |
| |
| } /* end of hashDelete */ |
| |
| |
| /**Function******************************************************************** |
| |
| Synopsis [Resizes the hash table of a level queue.] |
| |
| Description [Resizes the hash table of a level queue. Returns 1 if |
| successful; 0 otherwise.] |
| |
| SideEffects [None] |
| |
| SeeAlso [hashInsert] |
| |
| ******************************************************************************/ |
| static int |
| hashResize( |
| DdLevelQueue * queue) |
| { |
| int j; |
| int posn; |
| DdQueueItem *item; |
| DdQueueItem *next; |
| int numBuckets; |
| #ifdef __osf__ |
| #pragma pointer_size save |
| #pragma pointer_size short |
| #endif |
| DdQueueItem **buckets; |
| DdQueueItem **oldBuckets = queue->buckets; |
| #ifdef __osf__ |
| #pragma pointer_size restore |
| #endif |
| int shift; |
| int oldNumBuckets = queue->numBuckets; |
| extern DD_OOMFP MMoutOfMemory; |
| DD_OOMFP saveHandler; |
| |
| /* Compute the new size of the subtable. */ |
| numBuckets = oldNumBuckets << 1; |
| saveHandler = MMoutOfMemory; |
| MMoutOfMemory = Cudd_OutOfMem; |
| #ifdef __osf__ |
| #pragma pointer_size save |
| #pragma pointer_size short |
| #endif |
| buckets = queue->buckets = ABC_ALLOC(DdQueueItem *, numBuckets); |
| MMoutOfMemory = saveHandler; |
| if (buckets == NULL) { |
| queue->maxsize <<= 1; |
| return(1); |
| } |
| |
| queue->numBuckets = numBuckets; |
| shift = --(queue->shift); |
| queue->maxsize <<= 1; |
| memset(buckets, 0, numBuckets * sizeof(DdQueueItem *)); |
| #ifdef __osf__ |
| #pragma pointer_size restore |
| #endif |
| for (j = 0; j < oldNumBuckets; j++) { |
| item = oldBuckets[j]; |
| while (item != NULL) { |
| next = item->cnext; |
| posn = lqHash(item->key, shift); |
| item->cnext = buckets[posn]; |
| buckets[posn] = item; |
| item = next; |
| } |
| } |
| ABC_FREE(oldBuckets); |
| return(1); |
| |
| } /* end of hashResize */ |
| |
| |
| ABC_NAMESPACE_IMPL_END |
| |