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.
		
		
		
		
		
			
		
			
				
					
					
						
							2326 lines
						
					
					
						
							56 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							2326 lines
						
					
					
						
							56 KiB
						
					
					
				| /** | |
|   @file | |
|  | |
|   @ingroup cudd | |
|  | |
|   @brief Generalized cofactors for BDDs and ADDs. | |
|  | |
|   @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                                                     */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| /* Codes for edge markings in Cudd_bddLICompaction.  The codes are defined | |
| ** so that they can be bitwise ORed to implement the code priority scheme. | |
| */ | |
| #define DD_LIC_DC 0 | |
| #define DD_LIC_1  1 | |
| #define DD_LIC_0  2 | |
| #define DD_LIC_NL 3 | |
|  | |
| /*---------------------------------------------------------------------------*/ | |
| /* Stucture declarations                                                     */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Type declarations                                                         */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| /** Key for the cache used in the edge marking phase. */ | |
| typedef struct MarkCacheKey { | |
|     DdNode *f; | |
|     DdNode *c; | |
| } MarkCacheKey; | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Variable declarations                                                     */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Macro declarations                                                        */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| /** \cond */ | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Static function prototypes                                                */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| static int cuddBddConstrainDecomp (DdManager *dd, DdNode *f, DdNode **decomp); | |
| static DdNode * cuddBddCharToVect (DdManager *dd, DdNode *f, DdNode *x); | |
| static int cuddBddLICMarkEdges (DdManager *dd, DdNode *f, DdNode *c, st_table *table, st_table *cache); | |
| static DdNode * cuddBddLICBuildResult (DdManager *dd, DdNode *f, st_table *cache, st_table *table); | |
| static int MarkCacheHash (void const *ptr, int modulus); | |
| static int MarkCacheCompare (const void *ptr1, const void *ptr2); | |
| static enum st_retval MarkCacheCleanUp (void *key, void *value, void *arg); | |
| static DdNode * cuddBddSqueeze (DdManager *dd, DdNode *l, DdNode *u); | |
| static DdNode * cuddBddInterpolate (DdManager * dd, DdNode * l, DdNode * u); | |
| 
 | |
| /** \endcond */ | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Definition of exported functions                                          */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Computes f constrain c. | |
|  | |
|   @details Computes f constrain c (f @ c). | |
|   Uses a canonical form: (f' @ c) = (f @ c)'.  (Note: this is not true | |
|   for c.)  List of special cases: | |
|     <ul> | |
|     <li> f @ 0 = 0 | |
|     <li> f @ 1 = f | |
|     <li> 0 @ c = 0 | |
|     <li> 1 @ c = 1 | |
|     <li> f @ f = 1 | |
|     <li> f @ f'= 0 | |
|     </ul> | |
|   Note that if F=(f1,...,fn) and reordering takes place while computing F @ c, | |
|   then the image restriction property (Img(F,c) = Img(F @ c)) is lost. | |
|  | |
|   @return a pointer to the result if successful; NULL otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
|   @see Cudd_bddRestrict Cudd_addConstrain | |
|  | |
| */ | |
| DdNode * | |
| Cudd_bddConstrain( | |
|   DdManager * dd, | |
|   DdNode * f, | |
|   DdNode * c) | |
| { | |
|     DdNode *res; | |
| 
 | |
|     do { | |
| 	dd->reordered = 0; | |
| 	res = cuddBddConstrainRecur(dd,f,c); | |
|     } while (dd->reordered == 1); | |
|     if (dd->errorCode == CUDD_TIMEOUT_EXPIRED && dd->timeoutHandler) { | |
|         dd->timeoutHandler(dd, dd->tohArg); | |
|     } | |
|     return(res); | |
| 
 | |
| } /* end of Cudd_bddConstrain */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief %BDD restrict according to Coudert and Madre's algorithm | |
|   (ICCAD90). | |
|  | |
|   @details If application of restrict results in a %BDD larger than the | |
|   input %BDD, the input %BDD is returned. | |
|  | |
|   @return the restricted %BDD if successful; otherwise NULL. | |
|  | |
|   @sideeffect None | |
|  | |
|   @see Cudd_bddConstrain Cudd_addRestrict | |
|  | |
| */ | |
| DdNode * | |
| Cudd_bddRestrict( | |
|   DdManager * dd, | |
|   DdNode * f, | |
|   DdNode * c) | |
| { | |
|     DdNode *suppF, *suppC, *commonSupport; | |
|     DdNode *cplus, *res; | |
|     int retval; | |
|     int sizeF, sizeRes; | |
| 
 | |
|     /* Check terminal cases here to avoid computing supports in trivial cases. | |
|     ** This also allows us notto check later for the case c == 0, in which | |
|     ** there is no common support. */ | |
|     if (c == Cudd_Not(DD_ONE(dd))) return(Cudd_Not(DD_ONE(dd))); | |
|     if (Cudd_IsConstantInt(f)) return(f); | |
|     if (f == c) return(DD_ONE(dd)); | |
|     if (f == Cudd_Not(c)) return(Cudd_Not(DD_ONE(dd))); | |
| 
 | |
|     /* Check if supports intersect. */ | |
|     retval = Cudd_ClassifySupport(dd,f,c,&commonSupport,&suppF,&suppC); | |
|     if (retval == 0) { | |
| 	return(NULL); | |
|     } | |
|     cuddRef(commonSupport); cuddRef(suppF); cuddRef(suppC); | |
|     Cudd_IterDerefBdd(dd,suppF); | |
| 
 | |
|     if (commonSupport == DD_ONE(dd)) { | |
| 	Cudd_IterDerefBdd(dd,commonSupport); | |
| 	Cudd_IterDerefBdd(dd,suppC); | |
| 	return(f); | |
|     } | |
|     Cudd_IterDerefBdd(dd,commonSupport); | |
| 
 | |
|     /* Abstract from c the variables that do not appear in f. */ | |
|     cplus = Cudd_bddExistAbstract(dd, c, suppC); | |
|     if (cplus == NULL) { | |
| 	Cudd_IterDerefBdd(dd,suppC); | |
| 	return(NULL); | |
|     } | |
|     cuddRef(cplus); | |
|     Cudd_IterDerefBdd(dd,suppC); | |
| 
 | |
|     do { | |
| 	dd->reordered = 0; | |
| 	res = cuddBddRestrictRecur(dd, f, cplus); | |
|     } while (dd->reordered == 1); | |
|     if (res == NULL) { | |
| 	Cudd_IterDerefBdd(dd,cplus); | |
|         if (dd->errorCode == CUDD_TIMEOUT_EXPIRED && dd->timeoutHandler) { | |
|           dd->timeoutHandler(dd, dd->tohArg); | |
|         } | |
| 	return(NULL); | |
|     } | |
|     cuddRef(res); | |
|     Cudd_IterDerefBdd(dd,cplus); | |
|     /* Make restric safe by returning the smaller of the input and the | |
|     ** result. */ | |
|     sizeF = Cudd_DagSize(f); | |
|     sizeRes = Cudd_DagSize(res); | |
|     if (sizeF <= sizeRes) { | |
| 	Cudd_IterDerefBdd(dd, res); | |
| 	return(f); | |
|     } else { | |
| 	cuddDeref(res); | |
| 	return(res); | |
|     } | |
| 
 | |
| } /* end of Cudd_bddRestrict */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Computes f non-polluting-and g. | |
|  | |
|   @details The non-polluting AND of f and g is a hybrid of AND and | |
|   Restrict.  From Restrict, this operation takes the idea of | |
|   existentially quantifying the top variable of the second operand if | |
|   it does not appear in the first.  Therefore, the variables that | |
|   appear in the result also appear in f.  For the rest, the function | |
|   behaves like AND.  Since the two operands play different roles, | |
|   non-polluting AND is not commutative. | |
|  | |
|   @return a pointer to the result if successful; NULL otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
|   @see Cudd_bddConstrain Cudd_bddRestrict | |
|  | |
| */ | |
| DdNode * | |
| Cudd_bddNPAnd( | |
|   DdManager * dd, | |
|   DdNode * f, | |
|   DdNode * g) | |
| { | |
|     DdNode *res; | |
| 
 | |
|     do { | |
| 	dd->reordered = 0; | |
| 	res = cuddBddNPAndRecur(dd,f,g); | |
|     } while (dd->reordered == 1); | |
|     if (dd->errorCode == CUDD_TIMEOUT_EXPIRED && dd->timeoutHandler) { | |
|         dd->timeoutHandler(dd, dd->tohArg); | |
|     } | |
|     return(res); | |
| 
 | |
| } /* end of Cudd_bddNPAnd */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Computes f constrain c for ADDs. | |
|  | |
|   @details Computes f constrain c (f @ c), for f an %ADD and c a 0-1 | |
|   %ADD.  List of special cases: | |
|   <ul> | |
|   <li> F @ 0 = 0 | |
|   <li> F @ 1 = F | |
|   <li> 0 @ c = 0 | |
|   <li> 1 @ c = 1 | |
|   <li> F @ F = 1 | |
|   </ul> | |
|  | |
|   @return a pointer to the result if successful; NULL otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
|   @see Cudd_bddConstrain | |
|  | |
| */ | |
| DdNode * | |
| Cudd_addConstrain( | |
|   DdManager * dd, | |
|   DdNode * f, | |
|   DdNode * c) | |
| { | |
|     DdNode *res; | |
| 
 | |
|     do { | |
| 	dd->reordered = 0; | |
| 	res = cuddAddConstrainRecur(dd,f,c); | |
|     } while (dd->reordered == 1); | |
|     if (dd->errorCode == CUDD_TIMEOUT_EXPIRED && dd->timeoutHandler) { | |
|         dd->timeoutHandler(dd, dd->tohArg); | |
|     } | |
|     return(res); | |
| 
 | |
| } /* end of Cudd_addConstrain */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief %BDD conjunctive decomposition as in McMillan's CAV96 paper. | |
|  | |
|   @details The decomposition is canonical only for a given variable | |
|   order. If canonicity is required, variable ordering must be disabled | |
|   after the decomposition has been computed.  The components of the | |
|   solution have their reference counts already incremented (unlike the | |
|   results of most other functions in the package). | |
|  | |
|   @return an array with one entry for each %BDD variable in the manager | |
|   if successful; otherwise NULL. | |
|  | |
|   @sideeffect None | |
|  | |
|   @see Cudd_bddConstrain Cudd_bddExistAbstract | |
|  | |
| */ | |
| DdNode ** | |
| Cudd_bddConstrainDecomp( | |
|   DdManager * dd, | |
|   DdNode * f) | |
| { | |
|     DdNode **decomp; | |
|     int res; | |
|     int i; | |
| 
 | |
|     /* Create an initialize decomposition array. */ | |
|     decomp = ALLOC(DdNode *,dd->size); | |
|     if (decomp == NULL) { | |
| 	dd->errorCode = CUDD_MEMORY_OUT; | |
| 	return(NULL); | |
|     } | |
|     for (i = 0; i < dd->size; i++) { | |
| 	decomp[i] = NULL; | |
|     } | |
|     do { | |
| 	dd->reordered = 0; | |
| 	/* Clean up the decomposition array in case reordering took place. */ | |
| 	for (i = 0; i < dd->size; i++) { | |
| 	    if (decomp[i] != NULL) { | |
| 		Cudd_IterDerefBdd(dd, decomp[i]); | |
| 		decomp[i] = NULL; | |
| 	    } | |
| 	} | |
| 	res = cuddBddConstrainDecomp(dd,f,decomp); | |
|     } while (dd->reordered == 1); | |
|     if (res == 0) { | |
| 	FREE(decomp); | |
|         if (dd->errorCode == CUDD_TIMEOUT_EXPIRED && dd->timeoutHandler) { | |
|           dd->timeoutHandler(dd, dd->tohArg); | |
|         } | |
| 	return(NULL); | |
|     } | |
|     /* Missing components are constant ones. */ | |
|     for (i = 0; i < dd->size; i++) { | |
| 	if (decomp[i] == NULL) { | |
| 	    decomp[i] = DD_ONE(dd); | |
| 	    cuddRef(decomp[i]); | |
| 	} | |
|     } | |
|     return(decomp); | |
| 
 | |
| } /* end of Cudd_bddConstrainDecomp */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief %ADD restrict according to Coudert and Madre's algorithm | |
|   (ICCAD90). | |
|  | |
|   @details If application of restrict results in an %ADD larger than | |
|   the input %ADD, the input %ADD is returned. | |
|  | |
|   @return the restricted %ADD if successful; otherwise NULL. | |
|  | |
|   @sideeffect None | |
|  | |
|   @see Cudd_addConstrain Cudd_bddRestrict | |
|  | |
| */ | |
| DdNode * | |
| Cudd_addRestrict( | |
|   DdManager * dd, | |
|   DdNode * f, | |
|   DdNode * c) | |
| { | |
|     DdNode *supp_f, *supp_c; | |
|     DdNode *res, *commonSupport; | |
|     int intersection; | |
|     int sizeF, sizeRes; | |
| 
 | |
|     /* Check if supports intersect. */ | |
|     supp_f = Cudd_Support(dd, f); | |
|     if (supp_f == NULL) { | |
| 	return(NULL); | |
|     } | |
|     cuddRef(supp_f); | |
|     supp_c = Cudd_Support(dd, c); | |
|     if (supp_c == NULL) { | |
| 	Cudd_RecursiveDeref(dd,supp_f); | |
| 	return(NULL); | |
|     } | |
|     cuddRef(supp_c); | |
|     commonSupport = Cudd_bddLiteralSetIntersection(dd, supp_f, supp_c); | |
|     if (commonSupport == NULL) { | |
| 	Cudd_RecursiveDeref(dd,supp_f); | |
| 	Cudd_RecursiveDeref(dd,supp_c); | |
| 	return(NULL); | |
|     } | |
|     cuddRef(commonSupport); | |
|     Cudd_RecursiveDeref(dd,supp_f); | |
|     Cudd_RecursiveDeref(dd,supp_c); | |
|     intersection = commonSupport != DD_ONE(dd); | |
|     Cudd_RecursiveDeref(dd,commonSupport); | |
| 
 | |
|     if (intersection) { | |
| 	do { | |
| 	    dd->reordered = 0; | |
| 	    res = cuddAddRestrictRecur(dd, f, c); | |
| 	} while (dd->reordered == 1); | |
|         if (res == 0) { | |
|             if (dd->errorCode == CUDD_TIMEOUT_EXPIRED && dd->timeoutHandler) { | |
|                 dd->timeoutHandler(dd, dd->tohArg); | |
|             } | |
|             return(f); | |
|         } | |
| 	sizeF = Cudd_DagSize(f); | |
| 	sizeRes = Cudd_DagSize(res); | |
| 	if (sizeF <= sizeRes) { | |
| 	    cuddRef(res); | |
| 	    Cudd_RecursiveDeref(dd, res); | |
| 	    return(f); | |
| 	} else { | |
| 	    return(res); | |
| 	} | |
|     } else { | |
| 	return(f); | |
|     } | |
| 
 | |
| } /* end of Cudd_addRestrict */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Computes a vector of BDDs whose image equals a non-zero function. | |
|  | |
|   @details  | |
|   The result depends on the variable order. The i-th component of the vector | |
|   depends only on the first i variables in the order.  Each %BDD in the vector | |
|   is not larger than the %BDD of the given characteristic function.  This | |
|   function is based on the description of char-to-vect in "Verification of | |
|   Sequential Machines Using Boolean Functional Vectors" by O. Coudert, C. | |
|   Berthet and J. C. Madre. | |
|  | |
|   @return a pointer to an array containing the result if successful; | |
|   NULL otherwise.  The size of the array equals the number of | |
|   variables in the manager. The components of the solution have their | |
|   reference counts already incremented (unlike the results of most | |
|   other functions in the package). | |
|  | |
|   @sideeffect None | |
|  | |
|   @see Cudd_bddConstrain | |
|  | |
| */ | |
| DdNode ** | |
| Cudd_bddCharToVect( | |
|   DdManager * dd, | |
|   DdNode * f) | |
| { | |
|     int i, j; | |
|     DdNode **vect; | |
|     DdNode *res = NULL; | |
| 
 | |
|     if (f == Cudd_Not(DD_ONE(dd))) return(NULL); | |
| 
 | |
|     vect = ALLOC(DdNode *, dd->size); | |
|     if (vect == NULL) { | |
| 	dd->errorCode = CUDD_MEMORY_OUT; | |
| 	return(NULL); | |
|     } | |
| 
 | |
|     do { | |
| 	dd->reordered = 0; | |
| 	for (i = 0; i < dd->size; i++) { | |
| 	    res = cuddBddCharToVect(dd,f,dd->vars[dd->invperm[i]]); | |
| 	    if (res == NULL) { | |
| 		/* Clean up the vector array in case reordering took place. */ | |
| 		for (j = 0; j < i; j++) { | |
| 		    Cudd_IterDerefBdd(dd, vect[dd->invperm[j]]); | |
| 		} | |
| 		break; | |
| 	    } | |
| 	    cuddRef(res); | |
| 	    vect[dd->invperm[i]] = res; | |
| 	} | |
|     } while (dd->reordered == 1); | |
|     if (res == NULL) { | |
| 	FREE(vect); | |
|         if (dd->errorCode == CUDD_TIMEOUT_EXPIRED && dd->timeoutHandler) { | |
|             dd->timeoutHandler(dd, dd->tohArg); | |
|         } | |
| 	return(NULL); | |
|     } | |
|     return(vect); | |
| 
 | |
| } /* end of Cudd_bddCharToVect */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Performs safe minimization of a %BDD. | |
|  | |
|   @details Given the %BDD `f` of a function to be minimized and a %BDD | |
|   `c` representing the care set, Cudd_bddLICompaction produces the | |
|   %BDD of a function that agrees with `f` wherever `c` is 1.  Safe | |
|   minimization means that the size of the result is guaranteed not to | |
|   exceed the size of `f`. This function is based on the DAC97 paper by | |
|   Hong et al.. | |
|  | |
|   @return a pointer to the result if successful; NULL otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
|   @see Cudd_bddRestrict | |
|  | |
| */ | |
| DdNode * | |
| Cudd_bddLICompaction( | |
|   DdManager * dd /**< manager */, | |
|   DdNode * f /**< function to be minimized */, | |
|   DdNode * c /**< constraint (care set) */) | |
| { | |
|     DdNode *res; | |
| 
 | |
|     do { | |
| 	dd->reordered = 0; | |
| 	res = cuddBddLICompaction(dd,f,c); | |
|     } while (dd->reordered == 1); | |
|     if (dd->errorCode == CUDD_TIMEOUT_EXPIRED && dd->timeoutHandler) { | |
|         dd->timeoutHandler(dd, dd->tohArg); | |
|     } | |
|     return(res); | |
| 
 | |
| } /* end of Cudd_bddLICompaction */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Finds a small %BDD in a function interval. | |
|  | |
|   @details Given BDDs `l` and `u`, representing the lower bound and | |
|   upper bound of a function interval, Cudd_bddSqueeze produces the | |
|   %BDD of a function within the interval with a small %BDD. | |
|  | |
|   @return a pointer to the result if successful; NULL otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
|   @see Cudd_bddRestrict Cudd_bddLICompaction | |
|  | |
| */ | |
| DdNode * | |
| Cudd_bddSqueeze( | |
|   DdManager * dd /**< manager */, | |
|   DdNode * l /**< lower bound */, | |
|   DdNode * u /**< upper bound */) | |
| { | |
|     DdNode *res; | |
|     int sizeRes, sizeL, sizeU; | |
| 
 | |
|     do { | |
| 	dd->reordered = 0; | |
| 	res = cuddBddSqueeze(dd,l,u); | |
|     } while (dd->reordered == 1); | |
|     if (res == NULL) { | |
|         if (dd->errorCode == CUDD_TIMEOUT_EXPIRED && dd->timeoutHandler) { | |
|             dd->timeoutHandler(dd, dd->tohArg); | |
|         } | |
|         return(NULL); | |
|     } | |
|     /* We now compare the result with the bounds and return the smallest. | |
|     ** We first compare to u, so that in case l == 0 and u == 1, we return | |
|     ** 0 as in other minimization algorithms. */ | |
|     sizeRes = Cudd_DagSize(res); | |
|     sizeU = Cudd_DagSize(u); | |
|     if (sizeU <= sizeRes) { | |
| 	cuddRef(res); | |
| 	Cudd_IterDerefBdd(dd,res); | |
| 	res = u; | |
| 	sizeRes = sizeU; | |
|     } | |
|     sizeL = Cudd_DagSize(l); | |
|     if (sizeL <= sizeRes) { | |
| 	cuddRef(res); | |
| 	Cudd_IterDerefBdd(dd,res); | |
| 	res = l; | |
|     } | |
|     return(res); | |
| 
 | |
| } /* end of Cudd_bddSqueeze */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Finds an interpolant of two functions. | |
|  | |
|   @details Given BDDs `l` and `u`, representing the lower bound and | |
|   upper bound of a function interval, Cudd_bddInterpolate produces the | |
|   %BDD of a function within the interval that only depends on the | |
|   variables common to `l` and `u`. | |
|  | |
|   The approach is based on quantification as in Cudd_bddRestrict(). | |
|   The function assumes that `l` implies `u`, but does not check whether | |
|   that's true. | |
|  | |
|   @return a pointer to the result if successful; NULL otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
|   @see Cudd_bddRestrict Cudd_bddSqueeze | |
|  | |
| */ | |
| DdNode * | |
| Cudd_bddInterpolate( | |
|   DdManager * dd /**< manager */, | |
|   DdNode * l /**< lower bound */, | |
|   DdNode * u /**< upper bound */) | |
| { | |
|     DdNode *res; | |
| 
 | |
|     do { | |
| 	dd->reordered = 0; | |
| 	res = cuddBddInterpolate(dd,l,u); | |
|     } while (dd->reordered == 1); | |
|     if (dd->errorCode == CUDD_TIMEOUT_EXPIRED && dd->timeoutHandler) { | |
|         dd->timeoutHandler(dd, dd->tohArg); | |
|     } | |
|     return(res); | |
| 
 | |
| } /* end of Cudd_bddInterpolate */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Finds a small %BDD that agrees with `f` over `c`. | |
|  | |
|   @return a pointer to the result if successful; NULL otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
|   @see Cudd_bddRestrict Cudd_bddLICompaction Cudd_bddSqueeze | |
|  | |
| */ | |
| DdNode * | |
| Cudd_bddMinimize( | |
|   DdManager * dd, | |
|   DdNode * f, | |
|   DdNode * c) | |
| { | |
|     DdNode *cplus, *res; | |
| 
 | |
|     if (c == Cudd_Not(DD_ONE(dd))) return(c); | |
|     if (Cudd_IsConstantInt(f)) return(f); | |
|     if (f == c) return(DD_ONE(dd)); | |
|     if (f == Cudd_Not(c)) return(Cudd_Not(DD_ONE(dd))); | |
| 
 | |
|     cplus = Cudd_RemapOverApprox(dd,c,0,0,1.0); | |
|     if (cplus == NULL) return(NULL); | |
|     cuddRef(cplus); | |
|     res = Cudd_bddLICompaction(dd,f,cplus); | |
|     if (res == NULL) { | |
| 	Cudd_IterDerefBdd(dd,cplus); | |
| 	return(NULL); | |
|     } | |
|     cuddRef(res); | |
|     Cudd_IterDerefBdd(dd,cplus); | |
|     cuddDeref(res); | |
|     return(res); | |
| 
 | |
| } /* end of Cudd_bddMinimize */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Find a dense subset of %BDD `f`. | |
|  | |
|   @details Density is the ratio of number of minterms to number of | |
|   nodes.  Uses several techniques in series. It is more expensive than | |
|   other subsetting procedures, but often produces better results. See | |
|   Cudd_SubsetShortPaths for a description of the threshold and nvars | |
|   parameters. | |
|  | |
|   @return a pointer to the result if successful; NULL otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
|   @see Cudd_RemapUnderApprox Cudd_SubsetShortPaths | |
|   Cudd_SubsetHeavyBranch Cudd_bddSqueeze | |
|  | |
| */ | |
| DdNode * | |
| Cudd_SubsetCompress( | |
|   DdManager * dd /**< manager */, | |
|   DdNode * f /**< %BDD whose subset is sought */, | |
|   int  nvars /**< number of variables in the support of f */, | |
|   int  threshold /**< maximum number of nodes in the subset */) | |
| { | |
|     DdNode *res, *tmp1, *tmp2; | |
| 
 | |
|     tmp1 = Cudd_SubsetShortPaths(dd, f, nvars, threshold, 0); | |
|     if (tmp1 == NULL) return(NULL); | |
|     cuddRef(tmp1); | |
|     tmp2 = Cudd_RemapUnderApprox(dd,tmp1,nvars,0,0.95); | |
|     if (tmp2 == NULL) { | |
| 	Cudd_IterDerefBdd(dd,tmp1); | |
| 	return(NULL); | |
|     } | |
|     cuddRef(tmp2); | |
|     Cudd_IterDerefBdd(dd,tmp1); | |
|     res = Cudd_bddSqueeze(dd,tmp2,f); | |
|     if (res == NULL) { | |
| 	Cudd_IterDerefBdd(dd,tmp2); | |
| 	return(NULL); | |
|     } | |
|     cuddRef(res); | |
|     Cudd_IterDerefBdd(dd,tmp2); | |
|     cuddDeref(res); | |
|     return(res); | |
| 
 | |
| } /* end of Cudd_SubsetCompress */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Find a dense superset of %BDD `f`. | |
|  | |
|   @details Density is the ratio of number of minterms to number of | |
|   nodes.  Uses several techniques in series. It is more expensive than | |
|   other supersetting procedures, but often produces better | |
|   results. See Cudd_SupersetShortPaths for a description of the | |
|   threshold and nvars parameters. | |
|  | |
|   @return a pointer to the result if successful; NULL otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
|   @see Cudd_SubsetCompress Cudd_SupersetRemap Cudd_SupersetShortPaths | |
|   Cudd_SupersetHeavyBranch Cudd_bddSqueeze | |
|  | |
| */ | |
| DdNode * | |
| Cudd_SupersetCompress( | |
|   DdManager * dd /**< manager */, | |
|   DdNode * f /**< %BDD whose superset is sought */, | |
|   int  nvars /**< number of variables in the support of f */, | |
|   int  threshold /**< maximum number of nodes in the superset */) | |
| { | |
|     DdNode *subset; | |
| 
 | |
|     subset = Cudd_SubsetCompress(dd, Cudd_Not(f),nvars,threshold); | |
| 
 | |
|     return(Cudd_NotCond(subset, (subset != NULL))); | |
| 
 | |
| } /* end of Cudd_SupersetCompress */ | |
| 
 | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Definition of internal functions                                          */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Performs the recursive step of Cudd_bddConstrain. | |
|  | |
|   @return a pointer to the result if successful; NULL otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
|   @see Cudd_bddConstrain | |
|  | |
| */ | |
| DdNode * | |
| cuddBddConstrainRecur( | |
|   DdManager * dd, | |
|   DdNode * f, | |
|   DdNode * c) | |
| { | |
|     DdNode       *Fv, *Fnv, *Cv, *Cnv, *t, *e, *r; | |
|     DdNode	 *one, *zero; | |
|     int		 topf, topc; | |
|     unsigned int index; | |
|     int          comple = 0; | |
| 
 | |
|     statLine(dd); | |
|     one = DD_ONE(dd); | |
|     zero = Cudd_Not(one); | |
| 
 | |
|     /* Trivial cases. */ | |
|     if (c == one)		return(f); | |
|     if (c == zero)		return(zero); | |
|     if (Cudd_IsConstantInt(f))	return(f); | |
|     if (f == c)			return(one); | |
|     if (f == Cudd_Not(c))	return(zero); | |
| 
 | |
|     /* Make canonical to increase the utilization of the cache. */ | |
|     if (Cudd_IsComplement(f)) { | |
| 	f = Cudd_Not(f); | |
| 	comple = 1; | |
|     } | |
|     /* Now f is a regular pointer to a non-constant node; c is also | |
|     ** non-constant, but may be complemented. | |
|     */ | |
| 
 | |
|     /* Check the cache. */ | |
|     r = cuddCacheLookup2(dd, Cudd_bddConstrain, f, c); | |
|     if (r != NULL) { | |
| 	return(Cudd_NotCond(r,comple)); | |
|     } | |
| 
 | |
|     checkWhetherToGiveUp(dd); | |
|      | |
|     /* Recursive step. */ | |
|     topf = dd->perm[f->index]; | |
|     topc = dd->perm[Cudd_Regular(c)->index]; | |
|     if (topf <= topc) { | |
| 	index = f->index; | |
| 	Fv = cuddT(f); Fnv = cuddE(f); | |
|     } else { | |
| 	index = Cudd_Regular(c)->index; | |
| 	Fv = Fnv = f; | |
|     } | |
|     if (topc <= topf) { | |
| 	Cv = cuddT(Cudd_Regular(c)); Cnv = cuddE(Cudd_Regular(c)); | |
| 	if (Cudd_IsComplement(c)) { | |
| 	    Cv = Cudd_Not(Cv); | |
| 	    Cnv = Cudd_Not(Cnv); | |
| 	} | |
|     } else { | |
| 	Cv = Cnv = c; | |
|     } | |
| 
 | |
|     if (!Cudd_IsConstantInt(Cv)) { | |
| 	t = cuddBddConstrainRecur(dd, Fv, Cv); | |
| 	if (t == NULL) | |
| 	    return(NULL); | |
|     } else if (Cv == one) { | |
| 	t = Fv; | |
|     } else {		/* Cv == zero: return Fnv @ Cnv */ | |
| 	if (Cnv == one) { | |
| 	    r = Fnv; | |
| 	} else { | |
| 	    r = cuddBddConstrainRecur(dd, Fnv, Cnv); | |
| 	    if (r == NULL) | |
| 		return(NULL); | |
| 	} | |
| 	return(Cudd_NotCond(r,comple)); | |
|     } | |
|     cuddRef(t); | |
| 
 | |
|     if (!Cudd_IsConstantInt(Cnv)) { | |
| 	e = cuddBddConstrainRecur(dd, Fnv, Cnv); | |
| 	if (e == NULL) { | |
| 	    Cudd_IterDerefBdd(dd, t); | |
| 	    return(NULL); | |
| 	} | |
|     } else if (Cnv == one) { | |
| 	e = Fnv; | |
|     } else {		/* Cnv == zero: return Fv @ Cv previously computed */ | |
| 	cuddDeref(t); | |
| 	return(Cudd_NotCond(t,comple)); | |
|     } | |
|     cuddRef(e); | |
| 
 | |
|     if (Cudd_IsComplement(t)) { | |
| 	t = Cudd_Not(t); | |
| 	e = Cudd_Not(e); | |
| 	r = (t == e) ? t : cuddUniqueInter(dd, index, t, e); | |
| 	if (r == NULL) { | |
| 	    Cudd_IterDerefBdd(dd, e); | |
| 	    Cudd_IterDerefBdd(dd, t); | |
| 	    return(NULL); | |
| 	} | |
| 	r = Cudd_Not(r); | |
|     } else { | |
| 	r = (t == e) ? t : cuddUniqueInter(dd, index, t, e); | |
| 	if (r == NULL) { | |
| 	    Cudd_IterDerefBdd(dd, e); | |
| 	    Cudd_IterDerefBdd(dd, t); | |
| 	    return(NULL); | |
| 	} | |
|     } | |
|     cuddDeref(t); | |
|     cuddDeref(e); | |
| 
 | |
|     cuddCacheInsert2(dd, Cudd_bddConstrain, f, c, r); | |
|     return(Cudd_NotCond(r,comple)); | |
| 
 | |
| } /* end of cuddBddConstrainRecur */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Performs the recursive step of Cudd_bddRestrict. | |
|  | |
|   @return the restricted %BDD if successful; otherwise NULL. | |
|  | |
|   @sideeffect None | |
|  | |
|   @see Cudd_bddRestrict | |
|  | |
| */ | |
| DdNode * | |
| cuddBddRestrictRecur( | |
|   DdManager * dd, | |
|   DdNode * f, | |
|   DdNode * c) | |
| { | |
|     DdNode	 *Fv, *Fnv, *Cv, *Cnv, *t, *e, *r, *one, *zero; | |
|     int		 topf, topc; | |
|     unsigned int index; | |
|     int		 comple = 0; | |
| 
 | |
|     statLine(dd); | |
|     one = DD_ONE(dd); | |
|     zero = Cudd_Not(one); | |
| 
 | |
|     /* Trivial cases */ | |
|     if (c == one)		return(f); | |
|     if (c == zero)		return(zero); | |
|     if (Cudd_IsConstantInt(f))	return(f); | |
|     if (f == c)			return(one); | |
|     if (f == Cudd_Not(c))	return(zero); | |
| 
 | |
|     /* Make canonical to increase the utilization of the cache. */ | |
|     if (Cudd_IsComplement(f)) { | |
| 	f = Cudd_Not(f); | |
| 	comple = 1; | |
|     } | |
|     /* Now f is a regular pointer to a non-constant node; c is also | |
|     ** non-constant, but may be complemented. | |
|     */ | |
| 
 | |
|     /* Check the cache. */ | |
|     r = cuddCacheLookup2(dd, Cudd_bddRestrict, f, c); | |
|     if (r != NULL) { | |
| 	return(Cudd_NotCond(r,comple)); | |
|     } | |
| 
 | |
|     checkWhetherToGiveUp(dd); | |
| 
 | |
|     topf = dd->perm[f->index]; | |
|     topc = dd->perm[Cudd_Regular(c)->index]; | |
| 
 | |
|     if (topc < topf) {	/* abstract top variable from c */ | |
| 	DdNode *d, *s1, *s2; | |
| 
 | |
| 	/* Find complements of cofactors of c. */ | |
| 	if (Cudd_IsComplement(c)) { | |
| 	    s1 = cuddT(Cudd_Regular(c)); | |
| 	    s2 = cuddE(Cudd_Regular(c)); | |
| 	} else { | |
| 	    s1 = Cudd_Not(cuddT(c)); | |
| 	    s2 = Cudd_Not(cuddE(c)); | |
| 	} | |
| 	/* Take the OR by applying DeMorgan. */ | |
| 	d = cuddBddAndRecur(dd, s1, s2); | |
| 	if (d == NULL) return(NULL); | |
| 	d = Cudd_Not(d); | |
| 	cuddRef(d); | |
| 	r = cuddBddRestrictRecur(dd, f, d); | |
| 	if (r == NULL) { | |
| 	    Cudd_IterDerefBdd(dd, d); | |
| 	    return(NULL); | |
| 	} | |
| 	cuddRef(r); | |
| 	Cudd_IterDerefBdd(dd, d); | |
| 	cuddCacheInsert2(dd, Cudd_bddRestrict, f, c, r); | |
| 	cuddDeref(r); | |
| 	return(Cudd_NotCond(r,comple)); | |
|     } | |
| 
 | |
|     /* Recursive step. Here topf <= topc. */ | |
|     index = f->index; | |
|     Fv = cuddT(f); Fnv = cuddE(f); | |
|     if (topc == topf) { | |
| 	Cv = cuddT(Cudd_Regular(c)); Cnv = cuddE(Cudd_Regular(c)); | |
| 	if (Cudd_IsComplement(c)) { | |
| 	    Cv = Cudd_Not(Cv); | |
| 	    Cnv = Cudd_Not(Cnv); | |
| 	} | |
|     } else { | |
| 	Cv = Cnv = c; | |
|     } | |
| 
 | |
|     if (!Cudd_IsConstantInt(Cv)) { | |
| 	t = cuddBddRestrictRecur(dd, Fv, Cv); | |
| 	if (t == NULL) return(NULL); | |
|     } else if (Cv == one) { | |
| 	t = Fv; | |
|     } else {		/* Cv == zero: return(Fnv @ Cnv) */ | |
| 	if (Cnv == one) { | |
| 	    r = Fnv; | |
| 	} else { | |
| 	    r = cuddBddRestrictRecur(dd, Fnv, Cnv); | |
| 	    if (r == NULL) return(NULL); | |
| 	} | |
| 	return(Cudd_NotCond(r,comple)); | |
|     } | |
|     cuddRef(t); | |
| 
 | |
|     if (!Cudd_IsConstantInt(Cnv)) { | |
| 	e = cuddBddRestrictRecur(dd, Fnv, Cnv); | |
| 	if (e == NULL) { | |
| 	    Cudd_IterDerefBdd(dd, t); | |
| 	    return(NULL); | |
| 	} | |
|     } else if (Cnv == one) { | |
| 	e = Fnv; | |
|     } else {		/* Cnv == zero: return (Fv @ Cv) previously computed */ | |
| 	cuddDeref(t); | |
| 	return(Cudd_NotCond(t,comple)); | |
|     } | |
|     cuddRef(e); | |
| 
 | |
|     if (Cudd_IsComplement(t)) { | |
| 	t = Cudd_Not(t); | |
| 	e = Cudd_Not(e); | |
| 	r = (t == e) ? t : cuddUniqueInter(dd, index, t, e); | |
| 	if (r == NULL) { | |
| 	    Cudd_IterDerefBdd(dd, e); | |
| 	    Cudd_IterDerefBdd(dd, t); | |
| 	    return(NULL); | |
| 	} | |
| 	r = Cudd_Not(r); | |
|     } else { | |
| 	r = (t == e) ? t : cuddUniqueInter(dd, index, t, e); | |
| 	if (r == NULL) { | |
| 	    Cudd_IterDerefBdd(dd, e); | |
| 	    Cudd_IterDerefBdd(dd, t); | |
| 	    return(NULL); | |
| 	} | |
|     } | |
|     cuddDeref(t); | |
|     cuddDeref(e); | |
| 
 | |
|     cuddCacheInsert2(dd, Cudd_bddRestrict, f, c, r); | |
|     return(Cudd_NotCond(r,comple)); | |
| 
 | |
| } /* end of cuddBddRestrictRecur */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Implements the recursive step of Cudd_bddAnd. | |
|  | |
|   @return a pointer to the result is successful; NULL otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
|   @see Cudd_bddNPAnd | |
|  | |
| */ | |
| DdNode * | |
| cuddBddNPAndRecur( | |
|   DdManager * manager, | |
|   DdNode * f, | |
|   DdNode * g) | |
| { | |
|     DdNode *F, *ft, *fe, *G, *gt, *ge; | |
|     DdNode *one, *r, *t, *e; | |
|     int topf, topg; | |
|     unsigned int index; | |
| 
 | |
|     statLine(manager); | |
|     one = DD_ONE(manager); | |
| 
 | |
|     /* Terminal cases. */ | |
|     F = Cudd_Regular(f); | |
|     G = Cudd_Regular(g); | |
|     if (F == G) { | |
| 	if (f == g) return(one); | |
| 	else return(Cudd_Not(one)); | |
|     } | |
|     if (G == one) { | |
| 	if (g == one) return(f); | |
| 	else return(g); | |
|     } | |
|     if (F == one) { | |
| 	return(f); | |
|     } | |
| 
 | |
|     /* At this point f and g are not constant. */ | |
|     /* Check cache. */ | |
|     if (F->ref != 1 || G->ref != 1) { | |
| 	r = cuddCacheLookup2(manager, Cudd_bddNPAnd, f, g); | |
| 	if (r != NULL) return(r); | |
|     } | |
| 
 | |
|     checkWhetherToGiveUp(manager); | |
| 
 | |
|     /* Here we can skip the use of cuddI, because the operands are known | |
|     ** to be non-constant. | |
|     */ | |
|     topf = manager->perm[F->index]; | |
|     topg = manager->perm[G->index]; | |
| 
 | |
|     if (topg < topf) {	/* abstract top variable from g */ | |
| 	DdNode *d; | |
| 
 | |
| 	/* Find complements of cofactors of g. */ | |
| 	if (Cudd_IsComplement(g)) { | |
| 	    gt = cuddT(G); | |
| 	    ge = cuddE(G); | |
| 	} else { | |
| 	    gt = Cudd_Not(cuddT(g)); | |
| 	    ge = Cudd_Not(cuddE(g)); | |
| 	} | |
| 	/* Take the OR by applying DeMorgan. */ | |
| 	d = cuddBddAndRecur(manager, gt, ge); | |
| 	if (d == NULL) return(NULL); | |
| 	d = Cudd_Not(d); | |
| 	cuddRef(d); | |
| 	r = cuddBddNPAndRecur(manager, f, d); | |
| 	if (r == NULL) { | |
| 	    Cudd_IterDerefBdd(manager, d); | |
| 	    return(NULL); | |
| 	} | |
| 	cuddRef(r); | |
| 	Cudd_IterDerefBdd(manager, d); | |
| 	cuddCacheInsert2(manager, Cudd_bddNPAnd, f, g, r); | |
| 	cuddDeref(r); | |
| 	return(r); | |
|     } | |
| 
 | |
|     /* Compute cofactors. */ | |
|     index = F->index; | |
|     ft = cuddT(F); | |
|     fe = cuddE(F); | |
|     if (Cudd_IsComplement(f)) { | |
|       ft = Cudd_Not(ft); | |
|       fe = Cudd_Not(fe); | |
|     } | |
| 
 | |
|     if (topg == topf) { | |
| 	gt = cuddT(G); | |
| 	ge = cuddE(G); | |
| 	if (Cudd_IsComplement(g)) { | |
| 	    gt = Cudd_Not(gt); | |
| 	    ge = Cudd_Not(ge); | |
| 	} | |
|     } else { | |
| 	gt = ge = g; | |
|     } | |
| 
 | |
|     t = cuddBddAndRecur(manager, ft, gt); | |
|     if (t == NULL) return(NULL); | |
|     cuddRef(t); | |
| 
 | |
|     e = cuddBddAndRecur(manager, fe, ge); | |
|     if (e == NULL) { | |
| 	Cudd_IterDerefBdd(manager, t); | |
| 	return(NULL); | |
|     } | |
|     cuddRef(e); | |
| 
 | |
|     if (t == e) { | |
| 	r = t; | |
|     } else { | |
| 	if (Cudd_IsComplement(t)) { | |
| 	    r = cuddUniqueInter(manager,(int)index,Cudd_Not(t),Cudd_Not(e)); | |
| 	    if (r == NULL) { | |
| 		Cudd_IterDerefBdd(manager, t); | |
| 		Cudd_IterDerefBdd(manager, e); | |
| 		return(NULL); | |
| 	    } | |
| 	    r = Cudd_Not(r); | |
| 	} else { | |
| 	    r = cuddUniqueInter(manager,(int)index,t,e); | |
| 	    if (r == NULL) { | |
| 		Cudd_IterDerefBdd(manager, t); | |
| 		Cudd_IterDerefBdd(manager, e); | |
| 		return(NULL); | |
| 	    } | |
| 	} | |
|     } | |
|     cuddDeref(e); | |
|     cuddDeref(t); | |
|     if (F->ref != 1 || G->ref != 1) | |
| 	cuddCacheInsert2(manager, Cudd_bddNPAnd, f, g, r); | |
|     return(r); | |
| 
 | |
| } /* end of cuddBddNPAndRecur */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Performs the recursive step of Cudd_addConstrain. | |
|  | |
|   @return a pointer to the result if successful; NULL otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
|   @see Cudd_addConstrain | |
|  | |
| */ | |
| DdNode * | |
| cuddAddConstrainRecur( | |
|   DdManager * dd, | |
|   DdNode * f, | |
|   DdNode * c) | |
| { | |
|     DdNode       *Fv, *Fnv, *Cv, *Cnv, *t, *e, *r; | |
|     DdNode	 *one, *zero; | |
|     int		 topf, topc; | |
|     unsigned int index; | |
| 
 | |
|     statLine(dd); | |
|     one = DD_ONE(dd); | |
|     zero = DD_ZERO(dd); | |
| 
 | |
|     /* Trivial cases. */ | |
|     if (c == one)		return(f); | |
|     if (c == zero)		return(zero); | |
|     if (cuddIsConstant(f))	return(f); | |
|     if (f == c)			return(one); | |
| 
 | |
|     /* Now f and c are non-constant. */ | |
| 
 | |
|     /* Check the cache. */ | |
|     r = cuddCacheLookup2(dd, Cudd_addConstrain, f, c); | |
|     if (r != NULL) { | |
| 	return(r); | |
|     } | |
| 
 | |
|     checkWhetherToGiveUp(dd); | |
| 
 | |
|     /* Recursive step. */ | |
|     topf = dd->perm[f->index]; | |
|     topc = dd->perm[c->index]; | |
|     if (topf <= topc) { | |
| 	index = f->index; | |
| 	Fv = cuddT(f); Fnv = cuddE(f); | |
|     } else { | |
| 	index = c->index; | |
| 	Fv = Fnv = f; | |
|     } | |
|     if (topc <= topf) { | |
| 	Cv = cuddT(c); Cnv = cuddE(c); | |
|     } else { | |
| 	Cv = Cnv = c; | |
|     } | |
| 
 | |
|     if (!cuddIsConstant(Cv)) { | |
| 	t = cuddAddConstrainRecur(dd, Fv, Cv); | |
| 	if (t == NULL) | |
| 	    return(NULL); | |
|     } else if (Cv == one) { | |
| 	t = Fv; | |
|     } else {		/* Cv == zero: return Fnv @ Cnv */ | |
| 	if (Cnv == one) { | |
| 	    r = Fnv; | |
| 	} else { | |
| 	    r = cuddAddConstrainRecur(dd, Fnv, Cnv); | |
| 	    if (r == NULL) | |
| 		return(NULL); | |
| 	} | |
| 	return(r); | |
|     } | |
|     cuddRef(t); | |
| 
 | |
|     if (!cuddIsConstant(Cnv)) { | |
| 	e = cuddAddConstrainRecur(dd, Fnv, Cnv); | |
| 	if (e == NULL) { | |
| 	    Cudd_RecursiveDeref(dd, t); | |
| 	    return(NULL); | |
| 	} | |
|     } else if (Cnv == one) { | |
| 	e = Fnv; | |
|     } else {		/* Cnv == zero: return Fv @ Cv previously computed */ | |
| 	cuddDeref(t); | |
| 	return(t); | |
|     } | |
|     cuddRef(e); | |
| 
 | |
|     r = (t == e) ? t : cuddUniqueInter(dd, index, t, e); | |
|     if (r == NULL) { | |
| 	Cudd_RecursiveDeref(dd, e); | |
| 	Cudd_RecursiveDeref(dd, t); | |
| 	return(NULL); | |
|     } | |
|     cuddDeref(t); | |
|     cuddDeref(e); | |
| 
 | |
|     cuddCacheInsert2(dd, Cudd_addConstrain, f, c, r); | |
|     return(r); | |
| 
 | |
| } /* end of cuddAddConstrainRecur */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Performs the recursive step of Cudd_addRestrict. | |
|  | |
|   @return the restricted %ADD if successful; otherwise NULL. | |
|  | |
|   @sideeffect None | |
|  | |
|   @see Cudd_addRestrict | |
|  | |
| */ | |
| DdNode * | |
| cuddAddRestrictRecur( | |
|   DdManager * dd, | |
|   DdNode * f, | |
|   DdNode * c) | |
| { | |
|     DdNode	 *Fv, *Fnv, *Cv, *Cnv, *t, *e, *r, *one, *zero; | |
|     int		 topf, topc; | |
|     unsigned int index; | |
| 
 | |
|     statLine(dd); | |
|     one = DD_ONE(dd); | |
|     zero = DD_ZERO(dd); | |
| 
 | |
|     /* Trivial cases */ | |
|     if (c == one)		return(f); | |
|     if (c == zero)		return(zero); | |
|     if (cuddIsConstant(f))	return(f); | |
|     if (f == c)			return(one); | |
| 
 | |
|     /* Now f and c are non-constant. */ | |
| 
 | |
|     /* Check the cache. */ | |
|     r = cuddCacheLookup2(dd, Cudd_addRestrict, f, c); | |
|     if (r != NULL) { | |
| 	return(r); | |
|     } | |
| 
 | |
|     checkWhetherToGiveUp(dd); | |
| 
 | |
|     topf = dd->perm[f->index]; | |
|     topc = dd->perm[c->index]; | |
| 
 | |
|     if (topc < topf) {	/* abstract top variable from c */ | |
| 	DdNode *d, *s1, *s2; | |
| 
 | |
| 	/* Find cofactors of c. */ | |
| 	s1 = cuddT(c); | |
| 	s2 = cuddE(c); | |
| 	/* Take the OR by applying DeMorgan. */ | |
| 	d = cuddAddApplyRecur(dd, Cudd_addOr, s1, s2); | |
| 	if (d == NULL) return(NULL); | |
| 	cuddRef(d); | |
| 	r = cuddAddRestrictRecur(dd, f, d); | |
| 	if (r == NULL) { | |
| 	    Cudd_RecursiveDeref(dd, d); | |
| 	    return(NULL); | |
| 	} | |
| 	cuddRef(r); | |
| 	Cudd_RecursiveDeref(dd, d); | |
| 	cuddCacheInsert2(dd, Cudd_addRestrict, f, c, r); | |
| 	cuddDeref(r); | |
| 	return(r); | |
|     } | |
| 
 | |
|     /* Recursive step. Here topf <= topc. */ | |
|     index = f->index; | |
|     Fv = cuddT(f); Fnv = cuddE(f); | |
|     if (topc == topf) { | |
| 	Cv = cuddT(c); Cnv = cuddE(c); | |
|     } else { | |
| 	Cv = Cnv = c; | |
|     } | |
| 
 | |
|     if (!Cudd_IsConstantInt(Cv)) { | |
| 	t = cuddAddRestrictRecur(dd, Fv, Cv); | |
| 	if (t == NULL) return(NULL); | |
|     } else if (Cv == one) { | |
| 	t = Fv; | |
|     } else {		/* Cv == zero: return(Fnv @ Cnv) */ | |
| 	if (Cnv == one) { | |
| 	    r = Fnv; | |
| 	} else { | |
| 	    r = cuddAddRestrictRecur(dd, Fnv, Cnv); | |
| 	    if (r == NULL) return(NULL); | |
| 	} | |
| 	return(r); | |
|     } | |
|     cuddRef(t); | |
| 
 | |
|     if (!cuddIsConstant(Cnv)) { | |
| 	e = cuddAddRestrictRecur(dd, Fnv, Cnv); | |
| 	if (e == NULL) { | |
| 	    Cudd_RecursiveDeref(dd, t); | |
| 	    return(NULL); | |
| 	} | |
|     } else if (Cnv == one) { | |
| 	e = Fnv; | |
|     } else {		/* Cnv == zero: return (Fv @ Cv) previously computed */ | |
| 	cuddDeref(t); | |
| 	return(t); | |
|     } | |
|     cuddRef(e); | |
| 
 | |
|     r = (t == e) ? t : cuddUniqueInter(dd, index, t, e); | |
|     if (r == NULL) { | |
| 	Cudd_RecursiveDeref(dd, e); | |
| 	Cudd_RecursiveDeref(dd, t); | |
| 	return(NULL); | |
|     } | |
|     cuddDeref(t); | |
|     cuddDeref(e); | |
| 
 | |
|     cuddCacheInsert2(dd, Cudd_addRestrict, f, c, r); | |
|     return(r); | |
| 
 | |
| } /* end of cuddAddRestrictRecur */ | |
| 
 | |
| 
 | |
| 
 | |
| /** | |
|   @brief Performs safe minimization of a %BDD. | |
|  | |
|   @details Given the %BDD `f` of a function to be minimized and a %BDD | |
|   `c` representing the care set, Cudd_bddLICompaction produces the | |
|   %BDD of a function that agrees with `f` wherever `c` is 1.  Safe | |
|   minimization means that the size of the result is guaranteed not to | |
|   exceed the size of `f`. This function is based on the DAC97 paper by | |
|   Hong et al.. | |
|  | |
|   @return a pointer to the result if successful; NULL otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
|   @see Cudd_bddLICompaction | |
|  | |
| */ | |
| DdNode * | |
| cuddBddLICompaction( | |
|   DdManager * dd /**< manager */, | |
|   DdNode * f /**< function to be minimized */, | |
|   DdNode * c /**< constraint (care set) */) | |
| { | |
|     st_table *marktable, *markcache, *buildcache; | |
|     DdNode *res, *zero; | |
| 
 | |
|     zero = Cudd_Not(DD_ONE(dd)); | |
|     if (c == zero) return(zero); | |
| 
 | |
|     /* We need to use local caches for both steps of this operation. | |
|     ** The results of the edge marking step are only valid as long as the | |
|     ** edge markings themselves are available. However, the edge markings | |
|     ** are lost at the end of one invocation of Cudd_bddLICompaction. | |
|     ** Hence, the cache entries for the edge marking step must be | |
|     ** invalidated at the end of this function. | |
|     ** For the result of the building step we argue as follows. The result | |
|     ** for a node and a given constrain depends on the BDD in which the node | |
|     ** appears. Hence, the same node and constrain may give different results | |
|     ** in successive invocations. | |
|     */ | |
|     marktable = st_init_table(st_ptrcmp,st_ptrhash); | |
|     if (marktable == NULL) { | |
| 	return(NULL); | |
|     } | |
|     markcache = st_init_table(MarkCacheCompare,MarkCacheHash); | |
|     if (markcache == NULL) { | |
| 	st_free_table(marktable); | |
| 	return(NULL); | |
|     } | |
|     if (cuddBddLICMarkEdges(dd,f,c,marktable,markcache) == CUDD_OUT_OF_MEM) { | |
| 	st_foreach(markcache, MarkCacheCleanUp, NULL); | |
| 	st_free_table(marktable); | |
| 	st_free_table(markcache); | |
| 	return(NULL); | |
|     } | |
|     st_foreach(markcache, MarkCacheCleanUp, NULL); | |
|     st_free_table(markcache); | |
|     buildcache = st_init_table(st_ptrcmp,st_ptrhash); | |
|     if (buildcache == NULL) { | |
| 	st_free_table(marktable); | |
| 	return(NULL); | |
|     } | |
|     res = cuddBddLICBuildResult(dd,f,buildcache,marktable); | |
|     st_free_table(buildcache); | |
|     st_free_table(marktable); | |
|     return(res); | |
| 
 | |
| } /* end of cuddBddLICompaction */ | |
| 
 | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Definition of static functions                                            */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Performs the recursive step of Cudd_bddConstrainDecomp. | |
|  | |
|   @return f super (i) if successful; otherwise NULL. | |
|  | |
|   @sideeffect None | |
|  | |
|   @see Cudd_bddConstrainDecomp | |
|  | |
| */ | |
| static int | |
| cuddBddConstrainDecomp( | |
|   DdManager * dd, | |
|   DdNode * f, | |
|   DdNode ** decomp) | |
| { | |
|     DdNode *F, *fv, *fvn; | |
|     DdNode *fAbs; | |
|     DdNode *result; | |
|     int ok; | |
| 
 | |
|     if (Cudd_IsConstantInt(f)) return(1); | |
|     /* Compute complements of cofactors. */ | |
|     F = Cudd_Regular(f); | |
|     fv = cuddT(F); | |
|     fvn = cuddE(F); | |
|     if (F == f) { | |
| 	fv = Cudd_Not(fv); | |
| 	fvn = Cudd_Not(fvn); | |
|     } | |
|     /* Compute abstraction of top variable. */ | |
|     fAbs = cuddBddAndRecur(dd, fv, fvn); | |
|     if (fAbs == NULL) { | |
| 	return(0); | |
|     } | |
|     cuddRef(fAbs); | |
|     fAbs = Cudd_Not(fAbs); | |
|     /* Recursively find the next abstraction and the components of the | |
|     ** decomposition. */ | |
|     ok = cuddBddConstrainDecomp(dd, fAbs, decomp); | |
|     if (ok == 0) { | |
| 	Cudd_IterDerefBdd(dd,fAbs); | |
| 	return(0); | |
|     } | |
|     /* Compute the component of the decomposition corresponding to the | |
|     ** top variable and store it in the decomposition array. */ | |
|     result = cuddBddConstrainRecur(dd, f, fAbs); | |
|     if (result == NULL) { | |
| 	Cudd_IterDerefBdd(dd,fAbs); | |
| 	return(0); | |
|     } | |
|     cuddRef(result); | |
|     decomp[F->index] = result; | |
|     Cudd_IterDerefBdd(dd, fAbs); | |
|     return(1); | |
| 
 | |
| } /* end of cuddBddConstrainDecomp */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Performs the recursive step of Cudd_bddCharToVect. | |
|  | |
|   @details This function maintains the invariant that f is non-zero. | |
|  | |
|   @return the i-th component of the vector if successful; otherwise NULL. | |
|  | |
|   @sideeffect None | |
|  | |
|   @see Cudd_bddCharToVect | |
|  | |
| */ | |
| static DdNode * | |
| cuddBddCharToVect( | |
|   DdManager * dd, | |
|   DdNode * f, | |
|   DdNode * x) | |
| { | |
|     int topf; | |
|     int level; | |
|     int comple; | |
| 
 | |
|     DdNode *one, *zero, *res, *F, *fT, *fE, *T, *E; | |
| 
 | |
|     statLine(dd); | |
|     /* Check the cache. */ | |
|     res = cuddCacheLookup2(dd, cuddBddCharToVect, f, x); | |
|     if (res != NULL) { | |
| 	return(res); | |
|     } | |
| 
 | |
|     checkWhetherToGiveUp(dd); | |
| 
 | |
|     F = Cudd_Regular(f); | |
| 
 | |
|     topf = cuddI(dd,F->index); | |
|     level = dd->perm[x->index]; | |
| 
 | |
|     if (topf > level) return(x); | |
| 
 | |
|     one = DD_ONE(dd); | |
|     zero = Cudd_Not(one); | |
| 
 | |
|     comple = F != f; | |
|     fT = Cudd_NotCond(cuddT(F),comple); | |
|     fE = Cudd_NotCond(cuddE(F),comple); | |
| 
 | |
|     if (topf == level) { | |
| 	if (fT == zero) return(zero); | |
| 	if (fE == zero) return(one); | |
| 	return(x); | |
|     } | |
| 
 | |
|     /* Here topf < level. */ | |
|     if (fT == zero) return(cuddBddCharToVect(dd, fE, x)); | |
|     if (fE == zero) return(cuddBddCharToVect(dd, fT, x)); | |
| 
 | |
|     T = cuddBddCharToVect(dd, fT, x); | |
|     if (T == NULL) { | |
| 	return(NULL); | |
|     } | |
|     cuddRef(T); | |
|     E = cuddBddCharToVect(dd, fE, x); | |
|     if (E == NULL) { | |
| 	Cudd_IterDerefBdd(dd,T); | |
| 	return(NULL); | |
|     } | |
|     cuddRef(E); | |
|     res = cuddBddIteRecur(dd, dd->vars[F->index], T, E); | |
|     if (res == NULL) { | |
| 	Cudd_IterDerefBdd(dd,T); | |
| 	Cudd_IterDerefBdd(dd,E); | |
| 	return(NULL); | |
|     } | |
|     cuddDeref(T); | |
|     cuddDeref(E); | |
|     cuddCacheInsert2(dd, cuddBddCharToVect, f, x, res); | |
|     return(res); | |
| 
 | |
| } /* end of cuddBddCharToVect */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Performs the edge marking step of Cudd_bddLICompaction. | |
|  | |
|   @return the LUB of the markings of the two outgoing edges of | |
|   <code>f</code> if successful; otherwise CUDD_OUT_OF_MEM. | |
|  | |
|   @sideeffect None | |
|  | |
|   @see Cudd_bddLICompaction cuddBddLICBuildResult | |
|  | |
| */ | |
| static int | |
| cuddBddLICMarkEdges( | |
|   DdManager * dd, | |
|   DdNode * f, | |
|   DdNode * c, | |
|   st_table * table, | |
|   st_table * cache) | |
| { | |
|     DdNode *Fv, *Fnv, *Cv, *Cnv; | |
|     DdNode *one, *zero; | |
|     int topf, topc; | |
|     int comple; | |
|     int resT, resE, res, retval; | |
|     void **slot; | |
|     MarkCacheKey *key; | |
| 
 | |
|     one = DD_ONE(dd); | |
|     zero = Cudd_Not(one); | |
| 
 | |
|     /* Terminal cases. */ | |
|     if (c == zero) return(DD_LIC_DC); | |
|     if (f == one)  return(DD_LIC_1); | |
|     if (f == zero) return(DD_LIC_0); | |
| 
 | |
|     /* Make canonical to increase the utilization of the cache. */ | |
|     comple = Cudd_IsComplement(f); | |
|     f = Cudd_Regular(f); | |
|     /* Now f is a regular pointer to a non-constant node; c may be | |
|     ** constant, or it may be complemented. | |
|     */ | |
| 
 | |
|     /* Check the cache. */ | |
|     key = ALLOC(MarkCacheKey, 1); | |
|     if (key == NULL) { | |
| 	dd->errorCode = CUDD_MEMORY_OUT; | |
| 	return(CUDD_OUT_OF_MEM); | |
|     } | |
|     key->f = f; key->c = c; | |
|     if (st_lookup_int(cache, key, &res)) { | |
| 	FREE(key); | |
| 	if (comple) { | |
| 	    if (res == DD_LIC_0) res = DD_LIC_1; | |
| 	    else if (res == DD_LIC_1) res = DD_LIC_0; | |
| 	} | |
| 	return(res); | |
|     } | |
| 
 | |
|     /* Recursive step. */ | |
|     topf = dd->perm[f->index]; | |
|     topc = cuddI(dd,Cudd_Regular(c)->index); | |
|     if (topf <= topc) { | |
| 	Fv = cuddT(f); Fnv = cuddE(f); | |
|     } else { | |
| 	Fv = Fnv = f; | |
|     } | |
|     if (topc <= topf) { | |
| 	/* We know that c is not constant because f is not. */ | |
| 	Cv = cuddT(Cudd_Regular(c)); Cnv = cuddE(Cudd_Regular(c)); | |
| 	if (Cudd_IsComplement(c)) { | |
| 	    Cv = Cudd_Not(Cv); | |
| 	    Cnv = Cudd_Not(Cnv); | |
| 	} | |
|     } else { | |
| 	Cv = Cnv = c; | |
|     } | |
| 
 | |
|     resT = cuddBddLICMarkEdges(dd, Fv, Cv, table, cache); | |
|     if (resT == CUDD_OUT_OF_MEM) { | |
| 	FREE(key); | |
| 	return(CUDD_OUT_OF_MEM); | |
|     } | |
|     resE = cuddBddLICMarkEdges(dd, Fnv, Cnv, table, cache); | |
|     if (resE == CUDD_OUT_OF_MEM) { | |
| 	FREE(key); | |
| 	return(CUDD_OUT_OF_MEM); | |
|     } | |
| 
 | |
|     /* Update edge markings. */ | |
|     if (topf <= topc) { | |
| 	retval = st_find_or_add(table, f, &slot); | |
| 	if (retval == 0) { | |
| 	    *slot = (void **) (ptrint)((resT << 2) | resE); | |
| 	} else if (retval == 1) { | |
| 	    *slot = (void **) (ptrint)((int)((ptrint) *slot) | (resT << 2) | resE); | |
| 	} else { | |
| 	    FREE(key); | |
| 	    return(CUDD_OUT_OF_MEM); | |
| 	} | |
|     } | |
| 
 | |
|     /* Cache result. */ | |
|     res = resT | resE; | |
|     if (st_insert(cache, key, (void *)(ptrint)res) == ST_OUT_OF_MEM) { | |
| 	FREE(key); | |
| 	return(CUDD_OUT_OF_MEM); | |
|     } | |
| 
 | |
|     /* Take into account possible complementation. */ | |
|     if (comple) { | |
| 	if (res == DD_LIC_0) res = DD_LIC_1; | |
| 	else if (res == DD_LIC_1) res = DD_LIC_0; | |
|     } | |
|     return(res); | |
| 
 | |
| } /* end of cuddBddLICMarkEdges */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Builds the result of Cudd_bddLICompaction. | |
|  | |
|   @return a pointer to the minimized %BDD if successful; otherwise NULL. | |
|  | |
|   @sideeffect None | |
|  | |
|   @see Cudd_bddLICompaction cuddBddLICMarkEdges | |
|  | |
| */ | |
| static DdNode * | |
| cuddBddLICBuildResult( | |
|   DdManager * dd, | |
|   DdNode * f, | |
|   st_table * cache, | |
|   st_table * table) | |
| { | |
|     DdNode *Fv, *Fnv, *r, *t, *e; | |
|     DdNode *one, *zero; | |
|     unsigned int index; | |
|     int comple; | |
|     int markT, markE, markings; | |
| 
 | |
|     one = DD_ONE(dd); | |
|     zero = Cudd_Not(one); | |
| 
 | |
|     if (Cudd_IsConstantInt(f)) return(f); | |
|     /* Make canonical to increase the utilization of the cache. */ | |
|     comple = Cudd_IsComplement(f); | |
|     f = Cudd_Regular(f); | |
| 
 | |
|     /* Check the cache. */ | |
|     if (st_lookup(cache, f, (void **) &r)) { | |
| 	return(Cudd_NotCond(r,comple)); | |
|     } | |
| 
 | |
|     /* Retrieve the edge markings. */ | |
|     if (st_lookup_int(table, f, &markings) == 0) | |
| 	return(NULL); | |
|     markT = markings >> 2; | |
|     markE = markings & 3; | |
| 
 | |
|     index = f->index; | |
|     Fv = cuddT(f); Fnv = cuddE(f); | |
| 
 | |
|     if (markT == DD_LIC_NL) { | |
| 	t = cuddBddLICBuildResult(dd,Fv,cache,table); | |
| 	if (t == NULL) { | |
| 	    return(NULL); | |
| 	} | |
|     } else if (markT == DD_LIC_1) { | |
| 	t = one; | |
|     } else { | |
| 	t = zero; | |
|     } | |
|     cuddRef(t); | |
|     if (markE == DD_LIC_NL) { | |
| 	e = cuddBddLICBuildResult(dd,Fnv,cache,table); | |
| 	if (e == NULL) { | |
| 	    Cudd_IterDerefBdd(dd,t); | |
| 	    return(NULL); | |
| 	} | |
|     } else if (markE == DD_LIC_1) { | |
| 	e = one; | |
|     } else { | |
| 	e = zero; | |
|     } | |
|     cuddRef(e); | |
| 
 | |
|     if (markT == DD_LIC_DC && markE != DD_LIC_DC) { | |
| 	r = e; | |
|     } else if (markT != DD_LIC_DC && markE == DD_LIC_DC) { | |
| 	r = t; | |
|     } else { | |
| 	if (Cudd_IsComplement(t)) { | |
| 	    t = Cudd_Not(t); | |
| 	    e = Cudd_Not(e); | |
| 	    r = (t == e) ? t : cuddUniqueInter(dd, index, t, e); | |
| 	    if (r == NULL) { | |
| 		Cudd_IterDerefBdd(dd, e); | |
| 		Cudd_IterDerefBdd(dd, t); | |
| 		return(NULL); | |
| 	    } | |
| 	    r = Cudd_Not(r); | |
| 	} else { | |
| 	    r = (t == e) ? t : cuddUniqueInter(dd, index, t, e); | |
| 	    if (r == NULL) { | |
| 		Cudd_IterDerefBdd(dd, e); | |
| 		Cudd_IterDerefBdd(dd, t); | |
| 		return(NULL); | |
| 	    } | |
| 	} | |
|     } | |
|     cuddDeref(t); | |
|     cuddDeref(e); | |
| 
 | |
|     if (st_insert(cache, f, r) == ST_OUT_OF_MEM) { | |
| 	cuddRef(r); | |
| 	Cudd_IterDerefBdd(dd,r); | |
| 	return(NULL); | |
|     } | |
| 
 | |
|     return(Cudd_NotCond(r,comple)); | |
| 
 | |
| } /* end of cuddBddLICBuildResult */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Hash function for the computed table of cuddBddLICMarkEdges. | |
|  | |
|   @return the bucket number. | |
|  | |
|   @sideeffect None | |
|  | |
|   @see Cudd_bddLICompaction | |
|  | |
| */ | |
| static int | |
| MarkCacheHash( | |
|   void const * ptr, | |
|   int  modulus) | |
| { | |
|     int val = 0; | |
|     MarkCacheKey const *entry = (MarkCacheKey const *) ptr; | |
| 
 | |
|     val = (int) (ptrint) entry->f; | |
|     val = val * 997 + (int) (ptrint) entry->c; | |
| 
 | |
|     return ((val < 0) ? -val : val) % modulus; | |
| 
 | |
| } /* end of MarkCacheHash */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Comparison function for the computed table of | |
|   cuddBddLICMarkEdges. | |
|  | |
|   @return 0 if the two nodes of the key are equal; 1 otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
|   @see Cudd_bddLICompaction | |
|  | |
| */ | |
| static int | |
| MarkCacheCompare( | |
|   const void * ptr1, | |
|   const void * ptr2) | |
| { | |
|     MarkCacheKey const *entry1 = (MarkCacheKey const *) ptr1; | |
|     MarkCacheKey const *entry2 = (MarkCacheKey const *) ptr2; | |
|      | |
|     return((entry1->f != entry2->f) || (entry1->c != entry2->c)); | |
| 
 | |
| } /* end of MarkCacheCompare */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Frees memory associated with computed table of | |
|   cuddBddLICMarkEdges. | |
|  | |
|   @return ST_CONTINUE. | |
|  | |
|   @sideeffect None | |
|  | |
|   @see Cudd_bddLICompaction | |
|  | |
| */ | |
| static enum st_retval | |
| MarkCacheCleanUp( | |
|   void * key, | |
|   void * value, | |
|   void * arg) | |
| { | |
|     MarkCacheKey *entry = (MarkCacheKey *) key; | |
| 
 | |
|     (void) value; /* avoid warning */ | |
|     (void) arg;   /* avoid warning */ | |
|     FREE(entry); | |
|     return ST_CONTINUE; | |
| 
 | |
| } /* end of MarkCacheCleanUp */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Performs the recursive step of Cudd_bddSqueeze. | |
|  | |
|   @details This procedure exploits the fact that if we complement and | |
|   swap the bounds of the interval we obtain a valid solution by taking | |
|   the complement of the solution to the original problem. Therefore, | |
|   we can enforce the condition that the upper bound is always regular. | |
|  | |
|   @return a pointer to the result if successful; NULL otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
|   @see Cudd_bddSqueeze | |
|  | |
| */ | |
| static DdNode * | |
| cuddBddSqueeze( | |
|   DdManager * dd, | |
|   DdNode * l, | |
|   DdNode * u) | |
| { | |
|     DdNode *one, *zero, *r, *lt, *le, *ut, *ue, *t, *e; | |
| #if 0 | |
|     DdNode *ar; | |
| #endif | |
|     int comple = 0; | |
|     int topu, topl; | |
|     unsigned int index; | |
| 
 | |
|     statLine(dd); | |
|     if (l == u) { | |
| 	return(l); | |
|     } | |
|     one = DD_ONE(dd); | |
|     zero = Cudd_Not(one); | |
|     /* The only case when l == zero && u == one is at the top level, | |
|     ** where returning either one or zero is OK. In all other cases | |
|     ** the procedure will detect such a case and will perform | |
|     ** remapping. Therefore the order in which we test l and u at this | |
|     ** point is immaterial. */ | |
|     if (l == zero) return(l); | |
|     if (u == one)  return(u); | |
| 
 | |
|     /* Make canonical to increase the utilization of the cache. */ | |
|     if (Cudd_IsComplement(u)) { | |
| 	DdNode *temp; | |
| 	temp = Cudd_Not(l); | |
| 	l = Cudd_Not(u); | |
| 	u = temp; | |
| 	comple = 1; | |
|     } | |
|     /* At this point u is regular and non-constant; l is non-constant, but | |
|     ** may be complemented. */ | |
| 
 | |
|     /* Here we could check the relative sizes. */ | |
| 
 | |
|     /* Check the cache. */ | |
|     r = cuddCacheLookup2(dd, Cudd_bddSqueeze, l, u); | |
|     if (r != NULL) { | |
| 	return(Cudd_NotCond(r,comple)); | |
|     } | |
| 
 | |
|     checkWhetherToGiveUp(dd); | |
| 
 | |
|     /* Recursive step. */ | |
|     topu = dd->perm[u->index]; | |
|     topl = dd->perm[Cudd_Regular(l)->index]; | |
|     if (topu <= topl) { | |
| 	index = u->index; | |
| 	ut = cuddT(u); ue = cuddE(u); | |
|     } else { | |
| 	index = Cudd_Regular(l)->index; | |
| 	ut = ue = u; | |
|     } | |
|     if (topl <= topu) { | |
| 	lt = cuddT(Cudd_Regular(l)); le = cuddE(Cudd_Regular(l)); | |
| 	if (Cudd_IsComplement(l)) { | |
| 	    lt = Cudd_Not(lt); | |
| 	    le = Cudd_Not(le); | |
| 	} | |
|     } else { | |
| 	lt = le = l; | |
|     } | |
| 
 | |
|     /* If one interval is contained in the other, use the smaller | |
|     ** interval. This corresponds to one-sided matching. */ | |
|     if ((lt == zero || Cudd_bddLeq(dd,lt,le)) && | |
| 	(ut == one  || Cudd_bddLeq(dd,ue,ut))) { /* remap */ | |
| 	r = cuddBddSqueeze(dd, le, ue); | |
| 	if (r == NULL) | |
| 	    return(NULL); | |
| 	return(Cudd_NotCond(r,comple)); | |
|     } else if ((le == zero || Cudd_bddLeq(dd,le,lt)) && | |
| 	       (ue == one  || Cudd_bddLeq(dd,ut,ue))) { /* remap */ | |
| 	r = cuddBddSqueeze(dd, lt, ut); | |
| 	if (r == NULL) | |
| 	    return(NULL); | |
| 	return(Cudd_NotCond(r,comple)); | |
|     } else if ((le == zero || Cudd_bddLeq(dd,le,Cudd_Not(ut))) && | |
| 	       (ue == one  || Cudd_bddLeq(dd,Cudd_Not(lt),ue))) { /* c-remap */ | |
| 	t = cuddBddSqueeze(dd, lt, ut); | |
| 	cuddRef(t); | |
| 	if (Cudd_IsComplement(t)) { | |
| 	    r = cuddUniqueInter(dd, index, Cudd_Not(t), t); | |
| 	    if (r == NULL) { | |
| 		Cudd_IterDerefBdd(dd, t); | |
| 		return(NULL); | |
| 	    } | |
| 	    r = Cudd_Not(r); | |
| 	} else { | |
| 	    r = cuddUniqueInter(dd, index, t, Cudd_Not(t)); | |
| 	    if (r == NULL) { | |
| 		Cudd_IterDerefBdd(dd, t); | |
| 		return(NULL); | |
| 	    } | |
| 	} | |
| 	cuddDeref(t); | |
| 	if (r == NULL) | |
| 	    return(NULL); | |
| 	cuddCacheInsert2(dd, Cudd_bddSqueeze, l, u, r); | |
| 	return(Cudd_NotCond(r,comple)); | |
|     } else if ((lt == zero || Cudd_bddLeq(dd,lt,Cudd_Not(ue))) && | |
| 	       (ut == one  || Cudd_bddLeq(dd,Cudd_Not(le),ut))) { /* c-remap */ | |
| 	e = cuddBddSqueeze(dd, le, ue); | |
| 	cuddRef(e); | |
| 	if (Cudd_IsComplement(e)) { | |
| 	    r = cuddUniqueInter(dd, index, Cudd_Not(e), e); | |
| 	    if (r == NULL) { | |
| 		Cudd_IterDerefBdd(dd, e); | |
| 		return(NULL); | |
| 	    } | |
| 	} else { | |
| 	    r = cuddUniqueInter(dd, index, e, Cudd_Not(e)); | |
| 	    if (r == NULL) { | |
| 		Cudd_IterDerefBdd(dd, e); | |
| 		return(NULL); | |
| 	    } | |
| 	    r = Cudd_Not(r); | |
| 	} | |
| 	cuddDeref(e); | |
| 	if (r == NULL) | |
| 	    return(NULL); | |
| 	cuddCacheInsert2(dd, Cudd_bddSqueeze, l, u, r); | |
| 	return(Cudd_NotCond(r,comple)); | |
|     } | |
| 
 | |
| #if 0 | |
|     /* If the two intervals intersect, take a solution from | |
|     ** the intersection of the intervals. This guarantees that the | |
|     ** splitting variable will not appear in the result. | |
|     ** This approach corresponds to two-sided matching, and is very | |
|     ** expensive. */ | |
|     if (Cudd_bddLeq(dd,lt,ue) && Cudd_bddLeq(dd,le,ut)) { | |
| 	DdNode *au, *al; | |
| 	au = cuddBddAndRecur(dd,ut,ue); | |
| 	if (au == NULL) | |
| 	    return(NULL); | |
| 	cuddRef(au); | |
| 	al = cuddBddAndRecur(dd,Cudd_Not(lt),Cudd_Not(le)); | |
| 	if (al == NULL) { | |
| 	    Cudd_IterDerefBdd(dd,au); | |
| 	    return(NULL); | |
| 	} | |
| 	cuddRef(al); | |
| 	al = Cudd_Not(al); | |
| 	ar = cuddBddSqueeze(dd, al, au); | |
| 	if (ar == NULL) { | |
| 	    Cudd_IterDerefBdd(dd,au); | |
| 	    Cudd_IterDerefBdd(dd,al); | |
| 	    return(NULL); | |
| 	} | |
| 	cuddRef(ar); | |
| 	Cudd_IterDerefBdd(dd,au); | |
| 	Cudd_IterDerefBdd(dd,al); | |
|     } else { | |
| 	ar = NULL; | |
|     } | |
| #endif | |
|  | |
|     t = cuddBddSqueeze(dd, lt, ut); | |
|     if (t == NULL) { | |
| 	return(NULL); | |
|     } | |
|     cuddRef(t); | |
|     e = cuddBddSqueeze(dd, le, ue); | |
|     if (e == NULL) { | |
| 	Cudd_IterDerefBdd(dd,t); | |
| 	return(NULL); | |
|     } | |
|     cuddRef(e); | |
| 
 | |
|     if (Cudd_IsComplement(t)) { | |
| 	t = Cudd_Not(t); | |
| 	e = Cudd_Not(e); | |
| 	r = (t == e) ? t : cuddUniqueInter(dd, index, t, e); | |
| 	if (r == NULL) { | |
| 	    Cudd_IterDerefBdd(dd, e); | |
| 	    Cudd_IterDerefBdd(dd, t); | |
| 	    return(NULL); | |
| 	} | |
| 	r = Cudd_Not(r); | |
|     } else { | |
| 	r = (t == e) ? t : cuddUniqueInter(dd, index, t, e); | |
| 	if (r == NULL) { | |
| 	    Cudd_IterDerefBdd(dd, e); | |
| 	    Cudd_IterDerefBdd(dd, t); | |
| 	    return(NULL); | |
| 	} | |
|     } | |
|     cuddDeref(t); | |
|     cuddDeref(e); | |
| 
 | |
| #if 0 | |
|     /* Check whether there is a result obtained by abstraction and whether | |
|     ** it is better than the one obtained by recursion. */ | |
|     cuddRef(r); | |
|     if (ar != NULL) { | |
| 	if (Cudd_DagSize(ar) <= Cudd_DagSize(r)) { | |
| 	    Cudd_IterDerefBdd(dd, r); | |
| 	    r = ar; | |
| 	} else { | |
| 	    Cudd_IterDerefBdd(dd, ar); | |
| 	} | |
|     } | |
|     cuddDeref(r); | |
| #endif | |
|  | |
|     cuddCacheInsert2(dd, Cudd_bddSqueeze, l, u, r); | |
|     return(Cudd_NotCond(r,comple)); | |
| 
 | |
| } /* end of cuddBddSqueeze */ | |
| 
 | |
| 
 | |
| /** | |
|   @brief Performs the recursive step of Cudd_bddInterpolate. | |
|  | |
|   @details This procedure exploits the fact that if we complement and | |
|   swap the bounds of the interval we obtain a valid solution by taking | |
|   the complement of the solution to the original problem. Therefore, | |
|   we can enforce the condition that the upper bound is always regular. | |
|  | |
|   @return a pointer to the result if successful; NULL otherwise. | |
|  | |
|   @sideeffect None | |
|  | |
|   @see Cudd_bddInterpolate | |
|  | |
| */ | |
| static DdNode * | |
| cuddBddInterpolate( | |
|   DdManager * dd, | |
|   DdNode * l, | |
|   DdNode * u) | |
| { | |
|     DdNode *one, *zero, *r, *lt, *le, *ut, *ue, *t, *e; | |
| #if 0 | |
|     DdNode *ar; | |
| #endif | |
|     int comple = 0; | |
|     int topu, topl; | |
|     unsigned int index; | |
| 
 | |
|     statLine(dd); | |
|     if (l == u) { | |
| 	return(l); | |
|     } | |
|     one = DD_ONE(dd); | |
|     zero = Cudd_Not(one); | |
|     if (l == zero) return(l); | |
|     if (u == one)  return(u); | |
| 
 | |
|     /* Make canonical to increase the utilization of the cache. */ | |
|     if (Cudd_IsComplement(u)) { | |
| 	DdNode *temp; | |
| 	temp = Cudd_Not(l); | |
| 	l = Cudd_Not(u); | |
| 	u = temp; | |
| 	comple = 1; | |
|     } | |
|     /* At this point u is regular and non-constant; l is non-constant, but | |
|     ** may be complemented. */ | |
| 
 | |
|     /* Check the cache. */ | |
|     r = cuddCacheLookup2(dd, Cudd_bddInterpolate, l, u); | |
|     if (r != NULL) { | |
| 	return(Cudd_NotCond(r,comple)); | |
|     } | |
| 
 | |
|     checkWhetherToGiveUp(dd); | |
| 
 | |
|     /* Recursive step. */ | |
|     topu = dd->perm[u->index]; | |
|     topl = dd->perm[Cudd_Regular(l)->index]; | |
|     if (topu < topl) { | |
|         /* Universally quantify top variable from upper bound. */ | |
|         DdNode *qu; | |
| 	ut = cuddT(u); ue = cuddE(u); | |
|         qu = cuddBddAndRecur(dd, ut, ue); | |
|         if (qu == NULL) return(NULL); | |
|         cuddRef(qu); | |
|         r = cuddBddInterpolate(dd, l, qu); | |
|         if (r == NULL) { | |
|             Cudd_IterDerefBdd(dd, qu); | |
|             return(NULL); | |
|         } | |
|         cuddRef(r); | |
|         Cudd_IterDerefBdd(dd, qu); | |
|         cuddCacheInsert2(dd, Cudd_bddInterpolate, l, u, r); | |
|         cuddDeref(r); | |
|         return(Cudd_NotCond(r, comple)); | |
|     } else if (topl < topu) { | |
|         /* Existentially quantify top variable from lower bound. */ | |
|         DdNode *ql; | |
|         /* Find complements of cofactors of c. */ | |
|         if (Cudd_IsComplement(l)) { | |
|             lt = cuddT(Cudd_Regular(l)); | |
|             le = cuddE(Cudd_Regular(l)); | |
|         } else { | |
|             lt = Cudd_Not(cuddT(l)); | |
|             le = Cudd_Not(cuddE(l)); | |
|         } | |
|         /* Disjoin cofactors by applying DeMorgan. */ | |
|         ql = cuddBddAndRecur(dd, lt, le); | |
|         if (ql == NULL) return (NULL); | |
|         cuddRef(ql); | |
|         ql = Cudd_Not(ql); | |
|         r = cuddBddInterpolate(dd, ql, u); | |
|         if (r == NULL) { | |
|             Cudd_IterDerefBdd(dd, ql); | |
|             return(NULL); | |
|         } | |
|         cuddRef(r); | |
|         Cudd_IterDerefBdd(dd, ql); | |
|         cuddCacheInsert2(dd, Cudd_bddInterpolate, l, u, r); | |
|         cuddDeref(r); | |
|         return(Cudd_NotCond(r, comple)); | |
|     } | |
| 
 | |
|     /* Both bounds depend on the top variable: split and recur. */ | |
|     index = u->index; | |
|     ut = cuddT(u); ue = cuddE(u); | |
|     lt = cuddT(Cudd_Regular(l)); le = cuddE(Cudd_Regular(l)); | |
|     if (Cudd_IsComplement(l)) { | |
|         lt = Cudd_Not(lt); | |
|         le = Cudd_Not(le); | |
|     } | |
| 
 | |
|     t = cuddBddInterpolate(dd, lt, ut); | |
|     if (t == NULL) { | |
| 	return(NULL); | |
|     } | |
|     cuddRef(t); | |
|     e = cuddBddInterpolate(dd, le, ue); | |
|     if (e == NULL) { | |
| 	Cudd_IterDerefBdd(dd,t); | |
| 	return(NULL); | |
|     } | |
|     cuddRef(e); | |
| 
 | |
|     if (Cudd_IsComplement(t)) { | |
| 	t = Cudd_Not(t); | |
| 	e = Cudd_Not(e); | |
| 	r = (t == e) ? t : cuddUniqueInter(dd, index, t, e); | |
| 	if (r == NULL) { | |
| 	    Cudd_IterDerefBdd(dd, e); | |
| 	    Cudd_IterDerefBdd(dd, t); | |
| 	    return(NULL); | |
| 	} | |
| 	r = Cudd_Not(r); | |
|     } else { | |
| 	r = (t == e) ? t : cuddUniqueInter(dd, index, t, e); | |
| 	if (r == NULL) { | |
| 	    Cudd_IterDerefBdd(dd, e); | |
| 	    Cudd_IterDerefBdd(dd, t); | |
| 	    return(NULL); | |
| 	} | |
|     } | |
|     cuddDeref(t); | |
|     cuddDeref(e); | |
| 
 | |
|     cuddCacheInsert2(dd, Cudd_bddInterpolate, l, u, r); | |
|     return(Cudd_NotCond(r,comple)); | |
| 
 | |
| } /* end of cuddBddInterpolate */
 |