You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							925 lines
						
					
					
						
							25 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							925 lines
						
					
					
						
							25 KiB
						
					
					
				| /** | |
|   @file | |
|  | |
|   @ingroup cudd | |
|  | |
|   @brief Procedures for dynamic variable ordering of ZDDs. | |
|  | |
|   @see cuddLinear.c cuddZddReord.c | |
|  | |
|   @author Fabio Somenzi | |
|  | |
|   @copyright@parblock | |
|   Copyright (c) 1995-2015, Regents of the University of Colorado | |
|  | |
|   All rights reserved. | |
|  | |
|   Redistribution and use in source and binary forms, with or without | |
|   modification, are permitted provided that the following conditions | |
|   are met: | |
|  | |
|   Redistributions of source code must retain the above copyright | |
|   notice, this list of conditions and the following disclaimer. | |
|  | |
|   Redistributions in binary form must reproduce the above copyright | |
|   notice, this list of conditions and the following disclaimer in the | |
|   documentation and/or other materials provided with the distribution. | |
|  | |
|   Neither the name of the University of Colorado nor the names of its | |
|   contributors may be used to endorse or promote products derived from | |
|   this software without specific prior written permission. | |
|  | |
|   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
|   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
|   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | |
|   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | |
|   COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | |
|   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | |
|   BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
|   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |
|   CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
|   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN | |
|   ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
|   POSSIBILITY OF SUCH DAMAGE. | |
|   @endparblock | |
|  | |
| */ | |
| 
 | |
| #include "util.h" | |
| #include "cuddInt.h" | |
|  | |
| /*---------------------------------------------------------------------------*/ | |
| /* Constant declarations                                                     */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| #define CUDD_SWAP_MOVE 0 | |
| #define CUDD_LINEAR_TRANSFORM_MOVE 1 | |
| #define CUDD_INVERSE_TRANSFORM_MOVE 2 | |
|  | |
| /*---------------------------------------------------------------------------*/ | |
| /* Stucture declarations                                                     */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Type declarations                                                         */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Variable declarations                                                     */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Macro declarations                                                        */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| /** \cond */ | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Static function prototypes                                                */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| static int cuddZddLinearInPlace (DdManager * table, int x, int y); | |
| static int cuddZddLinearAux (DdManager *table, int x, int xLow, int xHigh); | |
| static Move * cuddZddLinearUp (DdManager *table, int y, int xLow, Move *prevMoves); | |
| static Move * cuddZddLinearDown (DdManager *table, int x, int xHigh, Move *prevMoves); | |
| static int cuddZddLinearBackward (DdManager *table, int size, Move *moves); | |
| static Move* cuddZddUndoMoves (DdManager *table, Move *moves); | |
| 
 | |
| /** \endcond */ | |
| 
 | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Definition of exported functions                                          */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Definition of internal functions                                          */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| /** | |
|   @brief Implementation of the linear sifting algorithm for ZDDs. | |
|  | |
|   @details Assumes that no dead nodes are present. | |
|     <ol> | |
|     <li> Order all the variables according to the number of entries | |
|     in each unique table. | |
|     <li> Sift the variable up and down and applies the XOR transformation, | |
|     remembering each time the total size of the %DD heap. | |
|     <li> Select the best permutation. | |
|     <li> Repeat 3 and 4 for all variables. | |
|     </ol> | |
|  | |
|   @return 1 if successful; 0 otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
| */ | |
| int | |
| cuddZddLinearSifting( | |
|   DdManager * table, | |
|   int  lower, | |
|   int  upper) | |
| { | |
|     int	i; | |
|     IndexKey *var; | |
|     int	size; | |
|     int	x; | |
|     int	result; | |
| #ifdef DD_STATS | |
|     int	previousSize; | |
| #endif | |
|  | |
|     size = table->sizeZ; | |
| 
 | |
|     /* Find order in which to sift variables. */ | |
|     var = ALLOC(IndexKey, size); | |
|     if (var == NULL) { | |
| 	table->errorCode = CUDD_MEMORY_OUT; | |
| 	goto cuddZddSiftingOutOfMem; | |
|     } | |
| 
 | |
|     for (i = 0; i < size; i++) { | |
| 	x = table->permZ[i]; | |
| 	var[i].index = i; | |
| 	var[i].keys = table->subtableZ[x].keys; | |
|     } | |
| 
 | |
|     util_qsort(var, size, sizeof(IndexKey), cuddZddUniqueCompare); | |
| 
 | |
|     /* Now sift. */ | |
|     for (i = 0; i < ddMin(table->siftMaxVar, size); i++) { | |
| 	if (table->zddTotalNumberSwapping >= table->siftMaxSwap) | |
| 	    break; | |
|         if (util_cpu_time() - table->startTime > table->timeLimit) { | |
|             table->autoDynZ = 0; /* prevent further reordering */ | |
|             break; | |
|         } | |
|         if (table->terminationCallback != NULL && | |
|             table->terminationCallback(table->tcbArg)) { | |
|             table->autoDynZ = 0; /* prevent further reordering */ | |
|             break; | |
|         } | |
| 	x = table->permZ[var[i].index]; | |
| 	if (x < lower || x > upper) continue; | |
| #ifdef DD_STATS | |
| 	previousSize = table->keysZ; | |
| #endif | |
| 	result = cuddZddLinearAux(table, x, lower, upper); | |
| 	if (!result) | |
| 	    goto cuddZddSiftingOutOfMem; | |
| #ifdef DD_STATS | |
| 	if (table->keysZ < (unsigned) previousSize) { | |
| 	    (void) fprintf(table->out,"-"); | |
| 	} else if (table->keysZ > (unsigned) previousSize) { | |
| 	    (void) fprintf(table->out,"+");	/* should never happen */ | |
| 	    (void) fprintf(table->out,"\nSize increased from %d to %d while sifting variable %d\n", previousSize, table->keysZ , var[i].index); | |
| 	} else { | |
| 	    (void) fprintf(table->out,"="); | |
| 	} | |
| 	fflush(table->out); | |
| #endif | |
|     } | |
| 
 | |
|     FREE(var); | |
| 
 | |
|     return(1); | |
| 
 | |
| cuddZddSiftingOutOfMem: | |
| 
 | |
|     if (var != NULL) FREE(var); | |
| 
 | |
|     return(0); | |
| 
 | |
| } /* end of cuddZddLinearSifting */ | |
| 
 | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Definition of static functions                                            */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Linearly combines two adjacent variables. | |
|  | |
|   @details It assumes that no dead nodes are present on entry to this | |
|   procedure.  The procedure then guarantees that no dead nodes will be | |
|   present when it terminates.  cuddZddLinearInPlace assumes that x | |
|   < y. | |
|  | |
|   @return the number of keys in the table if successful; 0 otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
|   @see cuddZddSwapInPlace cuddLinearInPlace | |
|  | |
| */ | |
| static int | |
| cuddZddLinearInPlace( | |
|   DdManager * table, | |
|   int  x, | |
|   int  y) | |
| { | |
|     DdNodePtr *xlist, *ylist; | |
|     int		xindex, yindex; | |
|     int		xslots, yslots; | |
|     int		xshift, yshift; | |
|     int         oldxkeys, oldykeys; | |
|     int         newxkeys, newykeys; | |
|     int		i; | |
|     int		posn; | |
|     DdNode	*f, *f1, *f0, *f11, *f10, *f01, *f00; | |
|     DdNode	*newf1, *newf0, *g, *next, *previous; | |
|     DdNode	*special; | |
|     DdNode	*empty = table->zero; | |
| 
 | |
| #ifdef DD_DEBUG | |
|     assert(x < y); | |
|     assert(cuddZddNextHigh(table,x) == y); | |
|     assert(table->subtableZ[x].keys != 0); | |
|     assert(table->subtableZ[y].keys != 0); | |
|     assert(table->subtableZ[x].dead == 0); | |
|     assert(table->subtableZ[y].dead == 0); | |
| #endif | |
|  | |
|     /* Get parameters of x subtable. */ | |
|     xindex   = table->invpermZ[x]; | |
|     xlist    = table->subtableZ[x].nodelist; | |
|     oldxkeys = table->subtableZ[x].keys; | |
|     xslots   = table->subtableZ[x].slots; | |
|     xshift   = table->subtableZ[x].shift; | |
|     newxkeys = 0; | |
| 
 | |
|     /* Get parameters of y subtable. */ | |
|     yindex   = table->invpermZ[y]; | |
|     ylist    = table->subtableZ[y].nodelist; | |
|     oldykeys = table->subtableZ[y].keys; | |
|     yslots   = table->subtableZ[y].slots; | |
|     yshift   = table->subtableZ[y].shift; | |
|     newykeys = oldykeys; | |
| 
 | |
|     /* The nodes in the x layer are put in two chains.  The chain | |
|     ** pointed by g holds the normal nodes. When re-expressed they stay | |
|     ** in the x list. The chain pointed by special holds the elements | |
|     ** that will move to the y list. | |
|     */ | |
|     g = special = NULL; | |
|     for (i = 0; i < xslots; i++) { | |
| 	f = xlist[i]; | |
| 	if (f == NULL) continue; | |
| 	xlist[i] = NULL; | |
| 	while (f != NULL) { | |
| 	    next = f->next; | |
| 	    f1 = cuddT(f); | |
| 	    /* if (f1->index == yindex) */ cuddSatDec(f1->ref); | |
| 	    f0 = cuddE(f); | |
| 	    /* if (f0->index == yindex) */ cuddSatDec(f0->ref); | |
| 	    if ((int) f1->index == yindex && cuddE(f1) == empty && | |
| 		(int) f0->index != yindex) { | |
| 		f->next = special; | |
| 		special = f; | |
| 	    } else { | |
| 		f->next = g; | |
| 		g = f; | |
| 	    } | |
| 	    f = next; | |
| 	} /* while there are elements in the collision chain */ | |
|     } /* for each slot of the x subtable */ | |
| 
 | |
|     /* Mark y nodes with pointers from above x. We mark them by | |
|     **  changing their index to x. | |
|     */ | |
|     for (i = 0; i < yslots; i++) { | |
| 	f = ylist[i]; | |
| 	while (f != NULL) { | |
| 	    if (f->ref != 0) { | |
| 		f->index = xindex; | |
| 	    } | |
| 	    f = f->next; | |
| 	} /* while there are elements in the collision chain */ | |
|     } /* for each slot of the y subtable */ | |
| 
 | |
|     /* Move special nodes to the y list. */ | |
|     f = special; | |
|     while (f != NULL) { | |
| 	next = f->next; | |
| 	f1 = cuddT(f); | |
| 	f11 = cuddT(f1); | |
| 	cuddT(f) = f11; | |
| 	cuddSatInc(f11->ref); | |
| 	f0 = cuddE(f); | |
| 	cuddSatInc(f0->ref); | |
| 	f->index = yindex; | |
| 	/* Insert at the beginning of the list so that it will be | |
| 	** found first if there is a duplicate. The duplicate will | |
| 	** eventually be moved or garbage collected. No node | |
| 	** re-expression will add a pointer to it. | |
| 	*/ | |
| 	posn = ddHash(f11, f0, yshift); | |
| 	f->next = ylist[posn]; | |
| 	ylist[posn] = f; | |
| 	newykeys++; | |
| 	f = next; | |
|     } | |
| 
 | |
|     /* Take care of the remaining x nodes that must be re-expressed. | |
|     ** They form a linked list pointed by g. | |
|     */ | |
|     f = g; | |
|     while (f != NULL) { | |
| #ifdef DD_COUNT | |
| 	table->swapSteps++; | |
| #endif | |
| 	next = f->next; | |
| 	/* Find f1, f0, f11, f10, f01, f00. */ | |
| 	f1 = cuddT(f); | |
| 	if ((int) f1->index == yindex || (int) f1->index == xindex) { | |
| 	    f11 = cuddT(f1); f10 = cuddE(f1); | |
| 	} else { | |
| 	    f11 = empty; f10 = f1; | |
| 	} | |
| 	f0 = cuddE(f); | |
| 	if ((int) f0->index == yindex || (int) f0->index == xindex) { | |
| 	    f01 = cuddT(f0); f00 = cuddE(f0); | |
| 	} else { | |
| 	    f01 = empty; f00 = f0; | |
| 	} | |
| 	/* Create the new T child. */ | |
| 	if (f01 == empty) { | |
| 	    newf1 = f10; | |
| 	    cuddSatInc(newf1->ref); | |
| 	} else { | |
| 	    /* Check ylist for triple (yindex, f01, f10). */ | |
| 	    posn = ddHash(f01, f10, yshift); | |
| 	    /* For each element newf1 in collision list ylist[posn]. */ | |
| 	    newf1 = ylist[posn]; | |
| 	    /* Search the collision chain skipping the marked nodes. */ | |
| 	    while (newf1 != NULL) { | |
| 		if (cuddT(newf1) == f01 && cuddE(newf1) == f10 && | |
| 		    (int) newf1->index == yindex) { | |
| 		    cuddSatInc(newf1->ref); | |
| 		    break; /* match */ | |
| 		} | |
| 		newf1 = newf1->next; | |
| 	    } /* while newf1 */ | |
| 	    if (newf1 == NULL) {	/* no match */ | |
| 		newf1 = cuddDynamicAllocNode(table); | |
| 		if (newf1 == NULL) | |
| 		    goto zddSwapOutOfMem; | |
| 		newf1->index = yindex; newf1->ref = 1; | |
| 		cuddT(newf1) = f01; | |
| 		cuddE(newf1) = f10; | |
| 		/* Insert newf1 in the collision list ylist[pos]; | |
| 		** increase the ref counts of f01 and f10 | |
| 		*/ | |
| 		newykeys++; | |
| 		newf1->next = ylist[posn]; | |
| 		ylist[posn] = newf1; | |
| 		cuddSatInc(f01->ref); | |
| 		cuddSatInc(f10->ref); | |
| 	    } | |
| 	} | |
| 	cuddT(f) = newf1; | |
| 
 | |
| 	/* Do the same for f0. */ | |
| 	/* Create the new E child. */ | |
| 	if (f11 == empty) { | |
| 	    newf0 = f00; | |
| 	    cuddSatInc(newf0->ref); | |
| 	} else { | |
| 	    /* Check ylist for triple (yindex, f11, f00). */ | |
| 	    posn = ddHash(f11, f00, yshift); | |
| 	    /* For each element newf0 in collision list ylist[posn]. */ | |
| 	    newf0 = ylist[posn]; | |
| 	    while (newf0 != NULL) { | |
| 		if (cuddT(newf0) == f11 && cuddE(newf0) == f00 && | |
| 		    (int) newf0->index == yindex) { | |
| 		    cuddSatInc(newf0->ref); | |
| 		    break; /* match */ | |
| 		} | |
| 		newf0 = newf0->next; | |
| 	    } /* while newf0 */ | |
| 	    if (newf0 == NULL) {	/* no match */ | |
| 		newf0 = cuddDynamicAllocNode(table); | |
| 		if (newf0 == NULL) | |
| 		    goto zddSwapOutOfMem; | |
| 		newf0->index = yindex; newf0->ref = 1; | |
| 		cuddT(newf0) = f11; cuddE(newf0) = f00; | |
| 		/* Insert newf0 in the collision list ylist[posn]; | |
| 		** increase the ref counts of f11 and f00. | |
| 		*/ | |
| 		newykeys++; | |
| 		newf0->next = ylist[posn]; | |
| 		ylist[posn] = newf0; | |
| 		cuddSatInc(f11->ref); | |
| 		cuddSatInc(f00->ref); | |
| 	    } | |
| 	} | |
| 	cuddE(f) = newf0; | |
| 
 | |
| 	/* Re-insert the modified f in xlist. | |
| 	** The modified f does not already exists in xlist. | |
| 	** (Because of the uniqueness of the cofactors.) | |
| 	*/ | |
| 	posn = ddHash(newf1, newf0, xshift); | |
| 	newxkeys++; | |
| 	f->next = xlist[posn]; | |
| 	xlist[posn] = f; | |
| 	f = next; | |
|     } /* while f != NULL */ | |
| 
 | |
|     /* GC the y layer and move the marked nodes to the x list. */ | |
| 
 | |
|     /* For each node f in ylist. */ | |
|     for (i = 0; i < yslots; i++) { | |
| 	previous = NULL; | |
| 	f = ylist[i]; | |
| 	while (f != NULL) { | |
| 	    next = f->next; | |
| 	    if (f->ref == 0) { | |
| 		cuddSatDec(cuddT(f)->ref); | |
| 		cuddSatDec(cuddE(f)->ref); | |
| 		cuddDeallocNode(table, f); | |
| 		newykeys--; | |
| 		if (previous == NULL) | |
| 		    ylist[i] = next; | |
| 		else | |
| 		    previous->next = next; | |
| 	    } else if ((int) f->index == xindex) { /* move marked node */ | |
| 		if (previous == NULL) | |
| 		    ylist[i] = next; | |
| 		else | |
| 		    previous->next = next; | |
| 		f1 = cuddT(f); | |
| 		cuddSatDec(f1->ref); | |
| 		/* Check ylist for triple (yindex, f1, empty). */ | |
| 		posn = ddHash(f1, empty, yshift); | |
| 		/* For each element newf1 in collision list ylist[posn]. */ | |
| 		newf1 = ylist[posn]; | |
| 		while (newf1 != NULL) { | |
| 		    if (cuddT(newf1) == f1 && cuddE(newf1) == empty && | |
| 			(int) newf1->index == yindex) { | |
| 			cuddSatInc(newf1->ref); | |
| 			break; /* match */ | |
| 		    } | |
| 		    newf1 = newf1->next; | |
| 		} /* while newf1 */ | |
| 		if (newf1 == NULL) {	/* no match */ | |
| 		    newf1 = cuddDynamicAllocNode(table); | |
| 		    if (newf1 == NULL) | |
| 			goto zddSwapOutOfMem; | |
| 		    newf1->index = yindex; newf1->ref = 1; | |
| 		    cuddT(newf1) = f1; cuddE(newf1) = empty; | |
| 		    /* Insert newf1 in the collision list ylist[posn]; | |
| 		    ** increase the ref counts of f1 and empty. | |
| 		    */ | |
| 		    newykeys++; | |
| 		    newf1->next = ylist[posn]; | |
| 		    ylist[posn] = newf1; | |
| 		    if (posn == i && previous == NULL) | |
| 			previous = newf1; | |
| 		    cuddSatInc(f1->ref); | |
| 		    cuddSatInc(empty->ref); | |
| 		} | |
| 		cuddT(f) = newf1; | |
| 		f0 = cuddE(f); | |
| 		/* Insert f in x list. */ | |
| 		posn = ddHash(newf1, f0, xshift); | |
| 		newxkeys++; | |
| 		newykeys--; | |
| 		f->next = xlist[posn]; | |
| 		xlist[posn] = f; | |
| 	    } else { | |
| 		previous = f; | |
| 	    } | |
| 	    f = next; | |
| 	} /* while f */ | |
|     } /* for i */ | |
| 
 | |
|     /* Set the appropriate fields in table. */ | |
|     table->subtableZ[x].keys     = newxkeys; | |
|     table->subtableZ[y].keys     = newykeys; | |
| 
 | |
|     table->keysZ += newxkeys + newykeys - oldxkeys - oldykeys; | |
| 
 | |
|     /* Update univ section; univ[x] remains the same. */ | |
|     table->univ[y] = cuddT(table->univ[x]); | |
| 
 | |
| #if 0 | |
|     (void) fprintf(table->out,"x = %d  y = %d\n", x, y); | |
|     (void) Cudd_DebugCheck(table); | |
|     (void) Cudd_CheckKeys(table); | |
| #endif | |
|  | |
|     return (table->keysZ); | |
| 
 | |
| zddSwapOutOfMem: | |
|     (void) fprintf(table->err, "Error: cuddZddSwapInPlace out of memory\n"); | |
| 
 | |
|     return (0); | |
| 
 | |
| } /* end of cuddZddLinearInPlace */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Given xLow <= x <= xHigh moves x up and down between the | |
|   boundaries. | |
|  | |
|   @details Finds the best position and does the required changes. | |
|  | |
|   @return 1 if successful; 0 otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
| */ | |
| static int | |
| cuddZddLinearAux( | |
|   DdManager * table, | |
|   int  x, | |
|   int  xLow, | |
|   int  xHigh) | |
| { | |
|     Move	*move; | |
|     Move	*moveUp;	/* list of up move */ | |
|     Move	*moveDown;	/* list of down move */ | |
| 
 | |
|     int		initial_size; | |
|     int		result; | |
| 
 | |
|     initial_size = table->keysZ; | |
| 
 | |
| #ifdef DD_DEBUG | |
|     assert(table->subtableZ[x].keys > 0); | |
| #endif | |
|  | |
|     moveDown = NULL; | |
|     moveUp = NULL; | |
| 
 | |
|     if (x == xLow) { | |
| 	moveDown = cuddZddLinearDown(table, x, xHigh, NULL); | |
| 	/* At this point x --> xHigh. */ | |
| 	if (moveDown == (Move *) CUDD_OUT_OF_MEM) | |
| 	    goto cuddZddLinearAuxOutOfMem; | |
| 	/* Move backward and stop at best position. */ | |
| 	result = cuddZddLinearBackward(table, initial_size, moveDown); | |
| 	if (!result) | |
| 	    goto cuddZddLinearAuxOutOfMem; | |
| 
 | |
|     } else if (x == xHigh) { | |
| 	moveUp = cuddZddLinearUp(table, x, xLow, NULL); | |
| 	/* At this point x --> xLow. */ | |
| 	if (moveUp == (Move *) CUDD_OUT_OF_MEM) | |
| 	    goto cuddZddLinearAuxOutOfMem; | |
| 	/* Move backward and stop at best position. */ | |
| 	result = cuddZddLinearBackward(table, initial_size, moveUp); | |
| 	if (!result) | |
| 	    goto cuddZddLinearAuxOutOfMem; | |
| 
 | |
|     } else if ((x - xLow) > (xHigh - x)) { /* must go down first: shorter */ | |
| 	moveDown = cuddZddLinearDown(table, x, xHigh, NULL); | |
| 	/* At this point x --> xHigh. */ | |
| 	if (moveDown == (Move *) CUDD_OUT_OF_MEM) | |
| 	    goto cuddZddLinearAuxOutOfMem; | |
| 	moveUp = cuddZddUndoMoves(table,moveDown); | |
| #ifdef DD_DEBUG | |
| 	assert(moveUp == NULL || moveUp->x == (DdHalfWord) x); | |
| #endif | |
| 	moveUp = cuddZddLinearUp(table, x, xLow, moveUp); | |
| 	if (moveUp == (Move *) CUDD_OUT_OF_MEM) | |
| 	    goto cuddZddLinearAuxOutOfMem; | |
| 	/* Move backward and stop at best position. */ | |
| 	result = cuddZddLinearBackward(table, initial_size, moveUp); | |
| 	if (!result) | |
| 	    goto cuddZddLinearAuxOutOfMem; | |
| 
 | |
|     } else { | |
| 	moveUp = cuddZddLinearUp(table, x, xLow, NULL); | |
| 	/* At this point x --> xHigh. */ | |
| 	if (moveUp == (Move *) CUDD_OUT_OF_MEM) | |
| 	    goto cuddZddLinearAuxOutOfMem; | |
| 	/* Then move up. */ | |
| 	moveDown = cuddZddUndoMoves(table,moveUp); | |
| #ifdef DD_DEBUG | |
| 	assert(moveDown == NULL || moveDown->y == (DdHalfWord) x); | |
| #endif | |
| 	moveDown = cuddZddLinearDown(table, x, xHigh, moveDown); | |
| 	if (moveDown == (Move *) CUDD_OUT_OF_MEM) | |
| 	    goto cuddZddLinearAuxOutOfMem; | |
| 	/* Move backward and stop at best position. */ | |
| 	result = cuddZddLinearBackward(table, initial_size, moveDown); | |
| 	if (!result) | |
| 	    goto cuddZddLinearAuxOutOfMem; | |
|     } | |
| 
 | |
|     while (moveDown != NULL) { | |
| 	move = moveDown->next; | |
| 	cuddDeallocMove(table, moveDown); | |
| 	moveDown = move; | |
|     } | |
|     while (moveUp != NULL) { | |
| 	move = moveUp->next; | |
| 	cuddDeallocMove(table, moveUp); | |
| 	moveUp = move; | |
|     } | |
| 
 | |
|     return(1); | |
| 
 | |
| cuddZddLinearAuxOutOfMem: | |
|     if (moveDown != (Move *) CUDD_OUT_OF_MEM) { | |
| 	while (moveDown != NULL) { | |
| 	    move = moveDown->next; | |
| 	    cuddDeallocMove(table, moveDown); | |
| 	    moveDown = move; | |
| 	} | |
|     } | |
|     if (moveUp != (Move *) CUDD_OUT_OF_MEM) { | |
| 	while (moveUp != NULL) { | |
| 	    move = moveUp->next; | |
| 	    cuddDeallocMove(table, moveUp); | |
| 	    moveUp = move; | |
| 	} | |
|     } | |
| 
 | |
|     return(0); | |
| 
 | |
| } /* end of cuddZddLinearAux */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Sifts a variable up applying the XOR transformation. | |
|  | |
|   @details Moves y up until either it reaches the bound (xLow) or the | |
|   size of the %ZDD heap increases too much. | |
|  | |
|   @return the set of moves in case of success; NULL if memory is full. | |
|  | |
|   @sideeffect None | |
|  | |
| */ | |
| static Move * | |
| cuddZddLinearUp( | |
|   DdManager * table, | |
|   int  y, | |
|   int  xLow, | |
|   Move * prevMoves) | |
| { | |
|     Move	*moves; | |
|     Move	*move; | |
|     int		x; | |
|     int		size, newsize; | |
|     int		limitSize; | |
| 
 | |
|     moves = prevMoves; | |
|     limitSize = table->keysZ; | |
| 
 | |
|     x = cuddZddNextLow(table, y); | |
|     while (x >= xLow) { | |
| 	size = cuddZddSwapInPlace(table, x, y); | |
| 	if (size == 0) | |
| 	    goto cuddZddLinearUpOutOfMem; | |
| 	newsize = cuddZddLinearInPlace(table, x, y); | |
| 	if (newsize == 0) | |
| 	    goto cuddZddLinearUpOutOfMem; | |
| 	move = (Move *) cuddDynamicAllocNode(table); | |
| 	if (move == NULL) | |
| 	    goto cuddZddLinearUpOutOfMem; | |
| 	move->x = x; | |
| 	move->y = y; | |
| 	move->next = moves; | |
| 	moves = move; | |
| 	move->flags = CUDD_SWAP_MOVE; | |
| 	if (newsize > size) { | |
| 	    /* Undo transformation. The transformation we apply is | |
| 	    ** its own inverse. Hence, we just apply the transformation | |
| 	    ** again. | |
| 	    */ | |
| 	    newsize = cuddZddLinearInPlace(table,x,y); | |
| 	    if (newsize == 0) goto cuddZddLinearUpOutOfMem; | |
| #ifdef DD_DEBUG | |
| 	    if (newsize != size) { | |
| 		(void) fprintf(table->err,"Change in size after identity transformation! From %d to %d\n",size,newsize); | |
| 	    } | |
| #endif | |
| 	} else { | |
| 	    size = newsize; | |
| 	    move->flags = CUDD_LINEAR_TRANSFORM_MOVE; | |
| 	} | |
| 	move->size = size; | |
| 
 | |
| 	if ((double)size > (double)limitSize * table->maxGrowth) | |
| 	    break; | |
|         if (size < limitSize) | |
| 	    limitSize = size; | |
| 
 | |
| 	y = x; | |
| 	x = cuddZddNextLow(table, y); | |
|     } | |
|     return(moves); | |
| 
 | |
| cuddZddLinearUpOutOfMem: | |
|     while (moves != NULL) { | |
| 	move = moves->next; | |
| 	cuddDeallocMove(table, moves); | |
| 	moves = move; | |
|     } | |
|     return((Move *) CUDD_OUT_OF_MEM); | |
| 
 | |
| } /* end of cuddZddLinearUp */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Sifts a variable down and applies the XOR transformation. | |
|  | |
|   @details Sifts a variable down. Moves x down until either it | |
|   reaches the bound (xHigh) or the size of the %ZDD heap increases too | |
|   much. | |
|  | |
|   @return the set of moves in case of success; NULL if memory is full. | |
|  | |
|   @sideeffect None | |
|  | |
| */ | |
| static Move * | |
| cuddZddLinearDown( | |
|   DdManager * table, | |
|   int  x, | |
|   int  xHigh, | |
|   Move * prevMoves) | |
| { | |
|     Move	*moves; | |
|     Move	*move; | |
|     int		y; | |
|     int		size, newsize; | |
|     int		limitSize; | |
| 
 | |
|     moves = prevMoves; | |
|     limitSize = table->keysZ; | |
| 
 | |
|     y = cuddZddNextHigh(table, x); | |
|     while (y <= xHigh) { | |
| 	size = cuddZddSwapInPlace(table, x, y); | |
| 	if (size == 0) | |
| 	    goto cuddZddLinearDownOutOfMem; | |
| 	newsize = cuddZddLinearInPlace(table, x, y); | |
| 	if (newsize == 0) | |
| 	    goto cuddZddLinearDownOutOfMem; | |
| 	move = (Move *) cuddDynamicAllocNode(table); | |
| 	if (move == NULL) | |
| 	    goto cuddZddLinearDownOutOfMem; | |
| 	move->x = x; | |
| 	move->y = y; | |
| 	move->next = moves; | |
| 	moves = move; | |
| 	move->flags = CUDD_SWAP_MOVE; | |
| 	if (newsize > size) { | |
| 	    /* Undo transformation. The transformation we apply is | |
| 	    ** its own inverse. Hence, we just apply the transformation | |
| 	    ** again. | |
| 	    */ | |
| 	    newsize = cuddZddLinearInPlace(table,x,y); | |
| 	    if (newsize == 0) goto cuddZddLinearDownOutOfMem; | |
| 	    if (newsize != size) { | |
| 		(void) fprintf(table->err,"Change in size after identity transformation! From %d to %d\n",size,newsize); | |
| 	    } | |
| 	} else { | |
| 	    size = newsize; | |
| 	    move->flags = CUDD_LINEAR_TRANSFORM_MOVE; | |
| 	} | |
| 	move->size = size; | |
| 
 | |
| 	if ((double)size > (double)limitSize * table->maxGrowth) | |
| 	    break; | |
|         if (size < limitSize) | |
| 	    limitSize = size; | |
| 
 | |
| 	x = y; | |
| 	y = cuddZddNextHigh(table, x); | |
|     } | |
|     return(moves); | |
| 
 | |
| cuddZddLinearDownOutOfMem: | |
|     while (moves != NULL) { | |
| 	move = moves->next; | |
| 	cuddDeallocMove(table, moves); | |
| 	moves = move; | |
|     } | |
|     return((Move *) CUDD_OUT_OF_MEM); | |
| 
 | |
| } /* end of cuddZddLinearDown */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Given a set of moves, returns the %ZDD heap to the position | |
|   giving the minimum size. | |
|  | |
|   @details In case of ties, returns to the closest position giving the | |
|   minimum size. | |
|  | |
|   @return 1 in case of success; 0 otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
| */ | |
| static int | |
| cuddZddLinearBackward( | |
|   DdManager * table, | |
|   int  size, | |
|   Move * moves) | |
| { | |
|     Move	*move; | |
|     int		res; | |
| 
 | |
|     /* Find the minimum size among moves. */ | |
|     for (move = moves; move != NULL; move = move->next) { | |
| 	if (move->size < size) { | |
| 	    size = move->size; | |
| 	} | |
|     } | |
| 
 | |
|     for (move = moves; move != NULL; move = move->next) { | |
| 	if (move->size == size) return(1); | |
| 	if (move->flags == CUDD_LINEAR_TRANSFORM_MOVE) { | |
| 	    res = cuddZddLinearInPlace(table,(int)move->x,(int)move->y); | |
| 	    if (!res) return(0); | |
| 	} | |
| 	res = cuddZddSwapInPlace(table, move->x, move->y); | |
| 	if (!res) | |
| 	    return(0); | |
| 	if (move->flags == CUDD_INVERSE_TRANSFORM_MOVE) { | |
| 	    res = cuddZddLinearInPlace(table,(int)move->x,(int)move->y); | |
| 	    if (!res) return(0); | |
| 	} | |
|     } | |
| 
 | |
|     return(1); | |
| 
 | |
| } /* end of cuddZddLinearBackward */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Given a set of moves, returns the %ZDD heap to the order | |
|   in effect before the moves. | |
|  | |
|   @return 1 in case of success; 0 otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
| */ | |
| static Move* | |
| cuddZddUndoMoves( | |
|   DdManager * table, | |
|   Move * moves) | |
| { | |
|     Move *invmoves = NULL; | |
|     Move *move; | |
|     Move *invmove; | |
|     int	size; | |
| 
 | |
|     for (move = moves; move != NULL; move = move->next) { | |
| 	invmove = (Move *) cuddDynamicAllocNode(table); | |
| 	if (invmove == NULL) goto cuddZddUndoMovesOutOfMem; | |
| 	invmove->x = move->x; | |
| 	invmove->y = move->y; | |
| 	invmove->next = invmoves; | |
| 	invmoves = invmove; | |
| 	if (move->flags == CUDD_SWAP_MOVE) { | |
| 	    invmove->flags = CUDD_SWAP_MOVE; | |
| 	    size = cuddZddSwapInPlace(table,(int)move->x,(int)move->y); | |
| 	    if (!size) goto cuddZddUndoMovesOutOfMem; | |
| 	} else if (move->flags == CUDD_LINEAR_TRANSFORM_MOVE) { | |
| 	    invmove->flags = CUDD_INVERSE_TRANSFORM_MOVE; | |
| 	    size = cuddZddLinearInPlace(table,(int)move->x,(int)move->y); | |
| 	    if (!size) goto cuddZddUndoMovesOutOfMem; | |
| 	    size = cuddZddSwapInPlace(table,(int)move->x,(int)move->y); | |
| 	    if (!size) goto cuddZddUndoMovesOutOfMem; | |
| 	} else { /* must be CUDD_INVERSE_TRANSFORM_MOVE */ | |
| #ifdef DD_DEBUG | |
| 	    (void) fprintf(table->err,"Unforseen event in ddUndoMoves!\n"); | |
| #endif | |
| 	    invmove->flags = CUDD_LINEAR_TRANSFORM_MOVE; | |
| 	    size = cuddZddSwapInPlace(table,(int)move->x,(int)move->y); | |
| 	    if (!size) goto cuddZddUndoMovesOutOfMem; | |
| 	    size = cuddZddLinearInPlace(table,(int)move->x,(int)move->y); | |
| 	    if (!size) goto cuddZddUndoMovesOutOfMem; | |
| 	} | |
| 	invmove->size = size; | |
|     } | |
| 
 | |
|     return(invmoves); | |
| 
 | |
| cuddZddUndoMovesOutOfMem: | |
|     while (invmoves != NULL) { | |
| 	move = invmoves->next; | |
| 	cuddDeallocMove(table, invmoves); | |
| 	invmoves = move; | |
|     } | |
|     return((Move *) CUDD_OUT_OF_MEM); | |
| 
 | |
| } /* end of cuddZddUndoMoves */ | |
| 
 |