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.
4233 lines
101 KiB
4233 lines
101 KiB
/**
|
|
@file
|
|
|
|
@ingroup cudd
|
|
|
|
@brief Utility functions.
|
|
|
|
@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 <stddef.h>
|
|
#include <float.h>
|
|
#include "util.h"
|
|
#include "epdInt.h"
|
|
#include "cuddInt.h"
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Constant declarations */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/* Random generator constants. */
|
|
#define MODULUS1 2147483563
|
|
#define LEQA1 40014
|
|
#define LEQQ1 53668
|
|
#define LEQR1 12211
|
|
#define MODULUS2 2147483399
|
|
#define LEQA2 40692
|
|
#define LEQQ2 52774
|
|
#define LEQR2 3791
|
|
#define STAB_DIV (1 + (MODULUS1 - 1) / STAB_SIZE)
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Stucture declarations */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Type declarations */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Variable declarations */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Macro declarations */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
#define bang(f) ((Cudd_IsComplement(f)) ? '!' : ' ')
|
|
|
|
/** \cond */
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Static function prototypes */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static int dp2 (DdManager *dd, DdNode *f, st_table *t);
|
|
static void ddPrintMintermAux (DdManager *dd, DdNode *node, int *list);
|
|
static int ddDagInt (DdNode *n);
|
|
static int cuddNodeArrayRecur (DdNode *f, DdNodePtr *table, int index);
|
|
static int cuddEstimateCofactor (DdManager *dd, st_table *table, DdNode * node, int i, int phase, DdNode ** ptr);
|
|
static DdNode * cuddUniqueLookup (DdManager * unique, int index, DdNode * T, DdNode * E);
|
|
static int cuddEstimateCofactorSimple (DdNode * node, int i);
|
|
static double ddCountMintermAux (DdManager *dd, DdNode *node, double max, DdHashTable *table);
|
|
static int ddEpdCountMintermAux (DdManager const *dd, DdNode *node, EpDouble *max, EpDouble *epd, st_table *table);
|
|
static long double ddLdblCountMintermAux(DdManager const *manager, DdNode *node, long double max, st_table *table);
|
|
static double ddCountPathAux (DdNode *node, st_table *table);
|
|
static double ddCountPathsToNonZero (DdNode * N, st_table * table);
|
|
static void ddSupportStep (DdNode *f, int *support);
|
|
static void ddClearFlag (DdNode *f);
|
|
static int ddLeavesInt (DdNode *n);
|
|
static int ddPickArbitraryMinterms (DdManager *dd, DdNode *node, int nvars, int nminterms, char **string);
|
|
static int ddPickRepresentativeCube (DdManager *dd, DdNode *node, double *weight, char *string);
|
|
static enum st_retval ddEpdFree (void * key, void * value, void * arg);
|
|
static void ddFindSupport(DdManager *dd, DdNode *f, int *SP);
|
|
static void ddClearVars(DdManager *dd, int SP);
|
|
static int indexCompare(const void *a, const void *b);
|
|
static enum st_retval ddLdblFree(void * key, void * value, void * arg);
|
|
#if HAVE_POWL != 1
|
|
static long double powl(long double base, long double exponent);
|
|
#endif
|
|
/** \endcond */
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Definition of exported functions */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
|
/**
|
|
@brief Prints a disjoint sum of products.
|
|
|
|
@details Prints a disjoint sum of product cover for the function
|
|
rooted at node. Each product corresponds to a path from node to a
|
|
leaf node different from the logical zero, and different from the
|
|
background value. Uses the package default output file.
|
|
|
|
@return 1 if successful; 0 otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_PrintDebug Cudd_bddPrintCover
|
|
|
|
*/
|
|
int
|
|
Cudd_PrintMinterm(
|
|
DdManager * manager,
|
|
DdNode * node)
|
|
{
|
|
int i, *list;
|
|
|
|
list = ALLOC(int,manager->size);
|
|
if (list == NULL) {
|
|
manager->errorCode = CUDD_MEMORY_OUT;
|
|
return(0);
|
|
}
|
|
for (i = 0; i < manager->size; i++) list[i] = 2;
|
|
ddPrintMintermAux(manager,node,list);
|
|
FREE(list);
|
|
return(1);
|
|
|
|
} /* end of Cudd_PrintMinterm */
|
|
|
|
|
|
/**
|
|
@brief Prints a sum of prime implicants of a %BDD.
|
|
|
|
@details Prints a sum of product cover for an incompletely
|
|
specified function given by a lower bound and an upper bound. Each
|
|
product is a prime implicant obtained by expanding the product
|
|
corresponding to a path from node to the constant one. Uses the
|
|
package default output file.
|
|
|
|
@return 1 if successful; 0 otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_PrintMinterm
|
|
|
|
*/
|
|
int
|
|
Cudd_bddPrintCover(
|
|
DdManager *dd,
|
|
DdNode *l,
|
|
DdNode *u)
|
|
{
|
|
int *array;
|
|
int q, result;
|
|
DdNode *lb;
|
|
#ifdef DD_DEBUG
|
|
DdNode *cover;
|
|
#endif
|
|
|
|
array = ALLOC(int, Cudd_ReadSize(dd));
|
|
if (array == NULL) return(0);
|
|
lb = l;
|
|
cuddRef(lb);
|
|
#ifdef DD_DEBUG
|
|
cover = Cudd_ReadLogicZero(dd);
|
|
cuddRef(cover);
|
|
#endif
|
|
while (lb != Cudd_ReadLogicZero(dd)) {
|
|
DdNode *implicant, *prime, *tmp;
|
|
int length;
|
|
implicant = Cudd_LargestCube(dd,lb,&length);
|
|
if (implicant == NULL) {
|
|
Cudd_RecursiveDeref(dd,lb);
|
|
FREE(array);
|
|
return(0);
|
|
}
|
|
cuddRef(implicant);
|
|
prime = Cudd_bddMakePrime(dd,implicant,u);
|
|
if (prime == NULL) {
|
|
Cudd_RecursiveDeref(dd,lb);
|
|
Cudd_RecursiveDeref(dd,implicant);
|
|
FREE(array);
|
|
return(0);
|
|
}
|
|
cuddRef(prime);
|
|
Cudd_RecursiveDeref(dd,implicant);
|
|
tmp = Cudd_bddAnd(dd,lb,Cudd_Not(prime));
|
|
if (tmp == NULL) {
|
|
Cudd_RecursiveDeref(dd,lb);
|
|
Cudd_RecursiveDeref(dd,prime);
|
|
FREE(array);
|
|
return(0);
|
|
}
|
|
cuddRef(tmp);
|
|
Cudd_RecursiveDeref(dd,lb);
|
|
lb = tmp;
|
|
result = Cudd_BddToCubeArray(dd,prime,array);
|
|
if (result == 0) {
|
|
Cudd_RecursiveDeref(dd,lb);
|
|
Cudd_RecursiveDeref(dd,prime);
|
|
FREE(array);
|
|
return(0);
|
|
}
|
|
for (q = 0; q < dd->size; q++) {
|
|
switch (array[q]) {
|
|
case 0:
|
|
(void) fprintf(dd->out, "0");
|
|
break;
|
|
case 1:
|
|
(void) fprintf(dd->out, "1");
|
|
break;
|
|
case 2:
|
|
(void) fprintf(dd->out, "-");
|
|
break;
|
|
default:
|
|
(void) fprintf(dd->out, "?");
|
|
}
|
|
}
|
|
(void) fprintf(dd->out, " 1\n");
|
|
#ifdef DD_DEBUG
|
|
tmp = Cudd_bddOr(dd,prime,cover);
|
|
if (tmp == NULL) {
|
|
Cudd_RecursiveDeref(dd,cover);
|
|
Cudd_RecursiveDeref(dd,lb);
|
|
Cudd_RecursiveDeref(dd,prime);
|
|
FREE(array);
|
|
return(0);
|
|
}
|
|
cuddRef(tmp);
|
|
Cudd_RecursiveDeref(dd,cover);
|
|
cover = tmp;
|
|
#endif
|
|
Cudd_RecursiveDeref(dd,prime);
|
|
}
|
|
(void) fprintf(dd->out, "\n");
|
|
Cudd_RecursiveDeref(dd,lb);
|
|
FREE(array);
|
|
#ifdef DD_DEBUG
|
|
if (!Cudd_bddLeq(dd,cover,u) || !Cudd_bddLeq(dd,l,cover)) {
|
|
Cudd_RecursiveDeref(dd,cover);
|
|
return(0);
|
|
}
|
|
Cudd_RecursiveDeref(dd,cover);
|
|
#endif
|
|
return(1);
|
|
|
|
} /* end of Cudd_bddPrintCover */
|
|
|
|
|
|
/**
|
|
@brief Prints to the manager standard output a %DD and its statistics.
|
|
|
|
@details The statistics include the number of nodes, the number of leaves,
|
|
and the number of minterms. (The number of minterms is the number of
|
|
assignments to the variables that cause the function to be different
|
|
from the logical zero (for BDDs) and from the background value (for
|
|
ADDs.) The statistics are printed if pr > 0. Specifically:
|
|
<ul>
|
|
<li> pr = 0 : prints nothing
|
|
<li> pr = 1 : prints counts of nodes and minterms
|
|
<li> pr = 2 : prints counts + disjoint sum of product
|
|
<li> pr = 3 : prints counts + list of nodes
|
|
<li> pr > 3 : prints counts + disjoint sum of product + list of nodes
|
|
</ul>
|
|
For the purpose of counting the number of minterms, the function is
|
|
supposed to depend on n variables.
|
|
|
|
@return 1 if successful; 0 otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_DagSize Cudd_CountLeaves Cudd_CountMinterm
|
|
Cudd_PrintMinterm
|
|
|
|
*/
|
|
int
|
|
Cudd_PrintDebug(
|
|
DdManager * dd,
|
|
DdNode * f,
|
|
int n,
|
|
int pr)
|
|
{
|
|
DdNode *azero, *bzero;
|
|
int nodes;
|
|
int leaves;
|
|
double minterms;
|
|
int retval = 1;
|
|
|
|
if (dd == NULL) {
|
|
return(0);
|
|
}
|
|
if (f == NULL) {
|
|
(void) fprintf(dd->out,": is the NULL DD\n");
|
|
(void) fflush(dd->out);
|
|
dd->errorCode = CUDD_INVALID_ARG;
|
|
return(0);
|
|
}
|
|
azero = DD_ZERO(dd);
|
|
bzero = Cudd_Not(DD_ONE(dd));
|
|
if ((f == azero || f == bzero) && pr > 0){
|
|
(void) fprintf(dd->out,": is the zero DD\n");
|
|
(void) fflush(dd->out);
|
|
return(1);
|
|
}
|
|
if (pr > 0) {
|
|
nodes = Cudd_DagSize(f);
|
|
if (nodes == CUDD_OUT_OF_MEM) retval = 0;
|
|
leaves = Cudd_CountLeaves(f);
|
|
if (leaves == CUDD_OUT_OF_MEM) retval = 0;
|
|
minterms = Cudd_CountMinterm(dd, f, n);
|
|
if (minterms == (double)CUDD_OUT_OF_MEM) {
|
|
retval = 0;
|
|
(void) fprintf(dd->out,": %d nodes %d leaves unknown minterms\n",
|
|
nodes, leaves);
|
|
} else {
|
|
(void) fprintf(dd->out,": %d nodes %d leaves %g minterms\n",
|
|
nodes, leaves, minterms);
|
|
}
|
|
if (pr > 2) {
|
|
if (!cuddP(dd, f)) retval = 0;
|
|
}
|
|
if (pr == 2 || pr > 3) {
|
|
if (!Cudd_PrintMinterm(dd,f)) retval = 0;
|
|
(void) fprintf(dd->out,"\n");
|
|
}
|
|
(void) fflush(dd->out);
|
|
}
|
|
return(retval);
|
|
|
|
} /* end of Cudd_PrintDebug */
|
|
|
|
|
|
/**
|
|
@brief Prints a one-line summary of an %ADD or %BDD to the manager stdout.
|
|
|
|
@details The summary includes the number of nodes, the number of leaves,
|
|
and the number of minterms. The number of minterms is computed with
|
|
arbitrary precision unlike Cudd_PrintDebug(). For the purpose of counting
|
|
minterms, the function `f` is supposed to depend on `n` variables.
|
|
|
|
@return 1 if successful; 0 otherwise.
|
|
|
|
@see Cudd_PrintDebug Cudd_ApaPrintMinterm Cudd_ApaPrintMintermExp
|
|
*/
|
|
int
|
|
Cudd_PrintSummary(
|
|
DdManager * dd /**< manager */,
|
|
DdNode * f /**< %DD to be summarized */,
|
|
int n /**< number of variables for minterm computation */,
|
|
int mode /**< integer (0) or exponential (1) format */)
|
|
{
|
|
DdNode *azero, *bzero;
|
|
int nodes, leaves, digits;
|
|
int retval = 1;
|
|
DdApaNumber count;
|
|
|
|
if (dd == NULL) {
|
|
return(0);
|
|
}
|
|
if (f == NULL) {
|
|
(void) fprintf(dd->out,": is the NULL DD\n");
|
|
(void) fflush(dd->out);
|
|
dd->errorCode = CUDD_INVALID_ARG;
|
|
return(0);
|
|
}
|
|
azero = DD_ZERO(dd);
|
|
bzero = Cudd_Not(DD_ONE(dd));
|
|
if (f == azero || f == bzero){
|
|
(void) fprintf(dd->out,": is the zero DD\n");
|
|
(void) fflush(dd->out);
|
|
return(1);
|
|
}
|
|
nodes = Cudd_DagSize(f);
|
|
if (nodes == CUDD_OUT_OF_MEM) retval = 0;
|
|
leaves = Cudd_CountLeaves(f);
|
|
if (leaves == CUDD_OUT_OF_MEM) retval = 0;
|
|
(void) fprintf(dd->out,": %d nodes %d leaves ", nodes, leaves);
|
|
count = Cudd_ApaCountMinterm(dd, f, n, &digits);
|
|
if (count == NULL) {
|
|
retval = 0;
|
|
} else if (mode) {
|
|
if (!Cudd_ApaPrintExponential(dd->out, digits, count, 6))
|
|
retval = 0;
|
|
} else {
|
|
if (!Cudd_ApaPrintDecimal(dd->out, digits, count))
|
|
retval = 0;
|
|
}
|
|
FREE(count);
|
|
(void) fprintf(dd->out, " minterms\n");
|
|
(void) fflush(dd->out);
|
|
return(retval);
|
|
|
|
} /* end of Cudd_PrintSummary */
|
|
|
|
|
|
/**
|
|
@brief Counts the number of nodes in a %DD.
|
|
|
|
@return the number of nodes in the graph rooted at node.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_SharingSize Cudd_PrintDebug
|
|
|
|
*/
|
|
int
|
|
Cudd_DagSize(
|
|
DdNode * node)
|
|
{
|
|
int i;
|
|
|
|
i = ddDagInt(Cudd_Regular(node));
|
|
ddClearFlag(Cudd_Regular(node));
|
|
|
|
return(i);
|
|
|
|
} /* end of Cudd_DagSize */
|
|
|
|
|
|
/**
|
|
@brief Estimates the number of nodes in a cofactor of a %DD.
|
|
|
|
@details This function uses a refinement of the algorithm of Cabodi
|
|
et al. (ICCAD96). The refinement allows the procedure to account
|
|
for part of the recombination that may occur in the part of the
|
|
cofactor above the cofactoring variable. This procedure does not
|
|
create any new node. It does keep a small table of results;
|
|
therefore it may run out of memory. If this is a concern, one
|
|
should use Cudd_EstimateCofactorSimple, which is faster, does not
|
|
allocate any memory, but is less accurate.
|
|
|
|
@return an estimate of the number of nodes in a cofactor of the
|
|
graph rooted at node with respect to the variable whose index is i.
|
|
In case of failure, returns CUDD_OUT_OF_MEM.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_DagSize Cudd_EstimateCofactorSimple
|
|
|
|
*/
|
|
int
|
|
Cudd_EstimateCofactor(
|
|
DdManager *dd /**< manager */,
|
|
DdNode * f /**< function */,
|
|
int i /**< index of variable */,
|
|
int phase /**< 1: positive; 0: negative */
|
|
)
|
|
{
|
|
int val;
|
|
DdNode *ptr;
|
|
st_table *table;
|
|
|
|
table = st_init_table(st_ptrcmp,st_ptrhash);
|
|
if (table == NULL) {
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
return(CUDD_OUT_OF_MEM);
|
|
}
|
|
val = cuddEstimateCofactor(dd,table,Cudd_Regular(f),i,phase,&ptr);
|
|
ddClearFlag(Cudd_Regular(f));
|
|
st_free_table(table);
|
|
if (val == CUDD_OUT_OF_MEM)
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
|
|
return(val);
|
|
|
|
} /* end of Cudd_EstimateCofactor */
|
|
|
|
|
|
/**
|
|
@brief Estimates the number of nodes in a cofactor of a %DD.
|
|
|
|
@details Returns an estimate of the number of nodes in the positive
|
|
cofactor of the graph rooted at node with respect to the variable
|
|
whose index is i. This procedure implements with minor changes the
|
|
algorithm of Cabodi et al. (ICCAD96). It does not allocate any
|
|
memory, it does not change the state of the manager, and it is
|
|
fast. However, it has been observed to overestimate the size of the
|
|
cofactor by as much as a factor of 2.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_DagSize
|
|
|
|
*/
|
|
int
|
|
Cudd_EstimateCofactorSimple(
|
|
DdNode * node,
|
|
int i)
|
|
{
|
|
int val;
|
|
|
|
val = cuddEstimateCofactorSimple(Cudd_Regular(node),i);
|
|
ddClearFlag(Cudd_Regular(node));
|
|
|
|
return(val);
|
|
|
|
} /* end of Cudd_EstimateCofactorSimple */
|
|
|
|
|
|
/**
|
|
@brief Counts the number of nodes in an array of DDs.
|
|
|
|
@details Shared nodes are counted only once.
|
|
|
|
@return the total number of nodes.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_DagSize
|
|
|
|
*/
|
|
int
|
|
Cudd_SharingSize(
|
|
DdNode ** nodeArray,
|
|
int n)
|
|
{
|
|
int i,j;
|
|
|
|
i = 0;
|
|
for (j = 0; j < n; j++) {
|
|
i += ddDagInt(Cudd_Regular(nodeArray[j]));
|
|
}
|
|
for (j = 0; j < n; j++) {
|
|
ddClearFlag(Cudd_Regular(nodeArray[j]));
|
|
}
|
|
return(i);
|
|
|
|
} /* end of Cudd_SharingSize */
|
|
|
|
|
|
/**
|
|
@brief Counts the minterms of an %ADD or %BDD.
|
|
|
|
@details The function is assumed to depend on `nvars` variables. The
|
|
minterm count is represented as a double; hence overflow is
|
|
possible. For functions with many variables (more than 1023 if
|
|
floating point conforms to IEEE 754), one should consider
|
|
Cudd_ApaCountMinterm() or Cudd_EpdCountMinterm().
|
|
|
|
@return the number of minterms of the function rooted at node if
|
|
successful; +infinity if the number of minterms is known to be larger
|
|
than the maximum value representable as a double; `(double) CUDD_OUT_OF_MEM`
|
|
otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_ApaCountMinterm Cudd_EpdCountMinterm Cudd_LdblCountMinterm
|
|
Cudd_PrintDebug Cudd_CountPath
|
|
|
|
*/
|
|
//double
|
|
//Cudd_CountMinterm(
|
|
// DdManager * manager,
|
|
// DdNode * node,
|
|
// int nvars)
|
|
//{
|
|
// double max;
|
|
// DdHashTable *table;
|
|
// double res;
|
|
// CUDD_VALUE_TYPE epsilon;
|
|
//
|
|
// /* Scale the maximum number of minterm. This is done in an attempt
|
|
// * to deal with functions that depend on more than 1023, but less
|
|
// * than 2044 variables and don't have too many minterms.
|
|
// */
|
|
// printf("vars: %i, min_exp: %.400f, %i\n", nvars, DBL_MIN_EXP, DBL_MIN_EXP);
|
|
// printf("1: %f, 2: %i \n", (double)(nvars + DBL_MIN_EXP), (nvars + DBL_MIN_EXP));
|
|
// max = pow(2.0,(double)(nvars + DBL_MIN_EXP));
|
|
// printf("max: %f, DD_PLUS_INF_VAL: %.50f\n", max, DD_PLUS_INF_VAL);
|
|
// if (max >= DD_PLUS_INF_VAL) {
|
|
// return((double)CUDD_OUT_OF_MEM);
|
|
// }
|
|
// table = cuddHashTableInit(manager,1,2);
|
|
// if (table == NULL) {
|
|
// return((double)CUDD_OUT_OF_MEM);
|
|
// }
|
|
// /* Temporarily set epsilon to 0 to avoid rounding errors. */
|
|
// epsilon = Cudd_ReadEpsilon(manager);
|
|
// Cudd_SetEpsilon(manager,(CUDD_VALUE_TYPE)0.0);
|
|
// res = ddCountMintermAux(manager,node,max,table);
|
|
// cuddHashTableQuit(table);
|
|
// Cudd_SetEpsilon(manager,epsilon);
|
|
// if (res == (double)CUDD_OUT_OF_MEM) {
|
|
// return((double)CUDD_OUT_OF_MEM);
|
|
// } else if (res >= pow(2.0,(double)(DBL_MAX_EXP + DBL_MIN_EXP))) {
|
|
// /* Minterm count is too large to be scaled back. */
|
|
// return(DD_PLUS_INF_VAL);
|
|
// } else {
|
|
// printf("in third case: %.400f\n", res);
|
|
// /* Undo the scaling. */
|
|
// res *= pow(2.0,(double)-DBL_MIN_EXP);
|
|
// printf("after rescaling: %f\n", res);
|
|
// return(res);
|
|
// }
|
|
//
|
|
//} /* end of Cudd_CountMinterm */
|
|
|
|
/**Function********************************************************************
|
|
|
|
Synopsis [Counts the number of minterms of a DD.]
|
|
|
|
Description [Counts the number of minterms of a DD. The function is
|
|
assumed to depend on nvars variables. The minterm count is
|
|
represented as a double, to allow for a larger number of variables.
|
|
Returns the number of minterms of the function rooted at node if
|
|
successful; (double) CUDD_OUT_OF_MEM otherwise.]
|
|
|
|
SideEffects [None]
|
|
|
|
SeeAlso [Cudd_PrintDebug Cudd_CountPath]
|
|
|
|
******************************************************************************/
|
|
double
|
|
Cudd_CountMinterm(
|
|
DdManager * manager,
|
|
DdNode * node,
|
|
int nvars)
|
|
{
|
|
double max;
|
|
DdHashTable *table;
|
|
double res;
|
|
CUDD_VALUE_TYPE epsilon;
|
|
|
|
max = pow(2.0,(double)nvars);
|
|
table = cuddHashTableInit(manager,1,2);
|
|
if (table == NULL) {
|
|
return((double)CUDD_OUT_OF_MEM);
|
|
}
|
|
epsilon = Cudd_ReadEpsilon(manager);
|
|
Cudd_SetEpsilon(manager,(CUDD_VALUE_TYPE)0.0);
|
|
res = ddCountMintermAux(manager,node,max,table);
|
|
cuddHashTableQuit(table);
|
|
Cudd_SetEpsilon(manager,epsilon);
|
|
|
|
return(res);
|
|
|
|
} /* end of Cudd_CountMinterm */
|
|
|
|
/**
|
|
@brief Counts the paths of a %DD.
|
|
|
|
@details Paths to all terminal nodes are counted. The path count is
|
|
represented as a double; hence overflow is possible.
|
|
|
|
@return the number of paths of the function rooted at node if
|
|
successful; `(double) CUDD_OUT_OF_MEM` otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_CountMinterm
|
|
|
|
*/
|
|
double
|
|
Cudd_CountPath(
|
|
DdNode * node)
|
|
{
|
|
|
|
st_table *table;
|
|
double i;
|
|
|
|
table = st_init_table(st_ptrcmp,st_ptrhash);
|
|
if (table == NULL) {
|
|
return((double)CUDD_OUT_OF_MEM);
|
|
}
|
|
i = ddCountPathAux(Cudd_Regular(node),table);
|
|
st_foreach(table, cuddStCountfree, NULL);
|
|
st_free_table(table);
|
|
return(i);
|
|
|
|
} /* end of Cudd_CountPath */
|
|
|
|
|
|
/**
|
|
@brief Counts the minterms of an %ADD or %BDD with extended range.
|
|
|
|
@details The function is assumed to depend on `nvars` variables. The
|
|
minterm count is represented as an `EpDouble`, to allow for any
|
|
number of variables.
|
|
|
|
@return 0 if successful; `CUDD_OUT_OF_MEM` otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_CountMinterm Cudd_LdblCountMinterm Cudd_ApaCountMinterm
|
|
Cudd_PrintDebug Cudd_CountPath
|
|
|
|
*/
|
|
int
|
|
Cudd_EpdCountMinterm(
|
|
DdManager const * manager,
|
|
DdNode * node,
|
|
int nvars,
|
|
EpDouble * epd)
|
|
{
|
|
EpDouble max, tmp;
|
|
st_table *table;
|
|
int status;
|
|
|
|
EpdPow2(nvars, &max);
|
|
table = st_init_table(st_ptrcmp, st_ptrhash);
|
|
if (table == NULL) {
|
|
EpdMakeZero(epd, 0);
|
|
return(CUDD_OUT_OF_MEM);
|
|
}
|
|
status = ddEpdCountMintermAux(manager,Cudd_Regular(node),&max,epd,table);
|
|
st_foreach(table, ddEpdFree, NULL);
|
|
st_free_table(table);
|
|
if (status == CUDD_OUT_OF_MEM) {
|
|
EpdMakeZero(epd, 0);
|
|
return(CUDD_OUT_OF_MEM);
|
|
}
|
|
if (Cudd_IsComplement(node)) {
|
|
EpdSubtract3(&max, epd, &tmp);
|
|
EpdCopy(&tmp, epd);
|
|
}
|
|
return(0);
|
|
|
|
} /* end of Cudd_EpdCountMinterm */
|
|
|
|
|
|
/**
|
|
@brief Returns the number of minterms of aa %ADD or %BDD as a long double.
|
|
|
|
@details On systems where double and long double are the same type,
|
|
Cudd_CountMinterm() is preferable. On systems where long double values
|
|
have 15-bit exponents, this function avoids overflow for up to 16383
|
|
variables. It applies scaling to try to avoid overflow when the number of
|
|
variables is larger than 16383, but smaller than 32764.
|
|
|
|
@return The nimterm count if successful; +infinity if the number is known to
|
|
be too large for representation as a long double;
|
|
`(long double)CUDD_OUT_OF_MEM` otherwise.
|
|
|
|
@see Cudd_CountMinterm Cudd_EpdCountMinterm Cudd_ApaCountMinterm
|
|
*/
|
|
long double
|
|
Cudd_LdblCountMinterm(
|
|
DdManager const *manager,
|
|
DdNode *node,
|
|
int nvars)
|
|
{
|
|
long double max, count;
|
|
st_table *table;
|
|
|
|
max = powl(2.0L, (long double) (nvars+LDBL_MIN_EXP));
|
|
if (max == HUGE_VALL) {
|
|
return((long double)CUDD_OUT_OF_MEM);
|
|
}
|
|
table = st_init_table(st_ptrcmp, st_ptrhash);
|
|
if (table == NULL) {
|
|
return((long double)CUDD_OUT_OF_MEM);
|
|
}
|
|
count = ddLdblCountMintermAux(manager, Cudd_Regular(node), max, table);
|
|
st_foreach(table, ddLdblFree, NULL);
|
|
st_free_table(table);
|
|
if (count == (long double)CUDD_OUT_OF_MEM) {
|
|
return((long double)CUDD_OUT_OF_MEM);
|
|
}
|
|
if (Cudd_IsComplement(node)) {
|
|
count = max - count;
|
|
}
|
|
if (count >= powl(2.0L, (long double)(LDBL_MAX_EXP + LDBL_MIN_EXP))) {
|
|
/* Minterm count is too large to be scaled back. */
|
|
return(HUGE_VALL);
|
|
} else {
|
|
/* Undo the scaling. */
|
|
count *= powl(2.0L,(long double)-LDBL_MIN_EXP);
|
|
return(count);
|
|
}
|
|
|
|
} /* end of Cudd_LdlbCountMinterm */
|
|
|
|
|
|
/**
|
|
@brief Prints the number of minterms of an %ADD or %BDD with extended range.
|
|
|
|
@return 1 if successful; 0 otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_EpdCountMinterm Cudd_ApaPrintMintermExp
|
|
|
|
*/
|
|
int
|
|
Cudd_EpdPrintMinterm(
|
|
DdManager const * dd,
|
|
DdNode * node,
|
|
int nvars)
|
|
{
|
|
EpDouble epd;
|
|
int ret;
|
|
char pstring[128];
|
|
|
|
ret = Cudd_EpdCountMinterm(dd, node, nvars, &epd);
|
|
if (ret !=0) return(0);
|
|
EpdGetString(&epd, pstring);
|
|
fprintf(dd->out, "%s", pstring);
|
|
return(1);
|
|
|
|
} /* end of Cudd_EpdPrintMinterm */
|
|
|
|
|
|
/**
|
|
@brief Counts the paths to a non-zero terminal of a %DD.
|
|
|
|
@details The path count is represented as a double; hence overflow is
|
|
possible.
|
|
|
|
@return the number of paths of the function rooted at node.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_CountMinterm Cudd_CountPath
|
|
|
|
*/
|
|
double
|
|
Cudd_CountPathsToNonZero(
|
|
DdNode * node)
|
|
{
|
|
|
|
st_table *table;
|
|
double i;
|
|
|
|
table = st_init_table(st_ptrcmp,st_ptrhash);
|
|
if (table == NULL) {
|
|
return((double)CUDD_OUT_OF_MEM);
|
|
}
|
|
i = ddCountPathsToNonZero(node,table);
|
|
st_foreach(table, cuddStCountfree, NULL);
|
|
st_free_table(table);
|
|
return(i);
|
|
|
|
} /* end of Cudd_CountPathsToNonZero */
|
|
|
|
|
|
/**
|
|
@brief Finds the variables on which a %DD depends.
|
|
|
|
@return the number of variables if successful; CUDD_OUT_OF_MEM
|
|
otherwise.
|
|
|
|
@sideeffect The indices of the support variables are returned as
|
|
side effects. If the function is constant, no array is allocated.
|
|
|
|
@see Cudd_Support Cudd_SupportIndex Cudd_VectorSupportIndices
|
|
|
|
*/
|
|
int
|
|
Cudd_SupportIndices(
|
|
DdManager * dd /**< manager */,
|
|
DdNode * f /**< %DD whose support is sought */,
|
|
int **indices /**< array containing (on return) the indices */)
|
|
{
|
|
int SP = 0;
|
|
|
|
ddFindSupport(dd, Cudd_Regular(f), &SP);
|
|
ddClearFlag(Cudd_Regular(f));
|
|
ddClearVars(dd, SP);
|
|
if (SP > 0) {
|
|
int i;
|
|
*indices = ALLOC(int, SP);
|
|
if (*indices == NULL) {
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
return(CUDD_OUT_OF_MEM);
|
|
}
|
|
|
|
for (i = 0; i < SP; i++)
|
|
(*indices)[i] = (int) (ptrint) dd->stack[i];
|
|
|
|
util_qsort(*indices, SP, sizeof(int), indexCompare);
|
|
} else {
|
|
*indices = NULL;
|
|
}
|
|
|
|
return(SP);
|
|
|
|
} /* end of Cudd_SupportIndices */
|
|
|
|
|
|
/**
|
|
@brief Finds the variables on which a %DD depends.
|
|
|
|
@return a %BDD consisting of the product of the variables if
|
|
successful; NULL otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_VectorSupport Cudd_ClassifySupport
|
|
|
|
*/
|
|
DdNode *
|
|
Cudd_Support(
|
|
DdManager * dd /**< manager */,
|
|
DdNode * f /**< %DD whose support is sought */)
|
|
{
|
|
int *support;
|
|
DdNode *res;
|
|
int j;
|
|
|
|
int size = Cudd_SupportIndices(dd, f, &support);
|
|
if (size == CUDD_OUT_OF_MEM)
|
|
return(NULL);
|
|
|
|
/* Transform support from array of indices to cube. */
|
|
res = DD_ONE(dd);
|
|
cuddRef(res);
|
|
|
|
for (j = size - 1; j >= 0; j--) { /* for each index bottom-up (almost) */
|
|
int index = support[j];
|
|
DdNode *var = dd->vars[index];
|
|
DdNode *tmp = Cudd_bddAnd(dd,res,var);
|
|
if (tmp == NULL) {
|
|
Cudd_RecursiveDeref(dd,res);
|
|
FREE(support);
|
|
return(NULL);
|
|
}
|
|
cuddRef(tmp);
|
|
Cudd_RecursiveDeref(dd,res);
|
|
res = tmp;
|
|
}
|
|
|
|
FREE(support);
|
|
cuddDeref(res);
|
|
return(res);
|
|
|
|
} /* end of Cudd_Support */
|
|
|
|
|
|
/**
|
|
@brief Finds the variables on which a %DD depends.
|
|
|
|
@return an index array of the variables if successful; NULL
|
|
otherwise. The size of the array equals the number of variables in
|
|
the manager. Each entry of the array is 1 if the corresponding
|
|
variable is in the support of the %DD and 0 otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_Support Cudd_SupportIndices Cudd_ClassifySupport
|
|
|
|
*/
|
|
int *
|
|
Cudd_SupportIndex(
|
|
DdManager * dd /**< manager */,
|
|
DdNode * f /**< %DD whose support is sought */)
|
|
{
|
|
int *support;
|
|
int i;
|
|
int size;
|
|
|
|
/* Allocate and initialize support array for ddSupportStep. */
|
|
size = ddMax(dd->size, dd->sizeZ);
|
|
support = ALLOC(int,size);
|
|
if (support == NULL) {
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
return(NULL);
|
|
}
|
|
for (i = 0; i < size; i++) {
|
|
support[i] = 0;
|
|
}
|
|
|
|
/* Compute support and clean up markers. */
|
|
ddSupportStep(Cudd_Regular(f),support);
|
|
ddClearFlag(Cudd_Regular(f));
|
|
|
|
return(support);
|
|
|
|
} /* end of Cudd_SupportIndex */
|
|
|
|
|
|
/**
|
|
@brief Counts the variables on which a %DD depends.
|
|
|
|
@return the variables on which a %DD depends.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_Support Cudd_SupportIndices
|
|
|
|
*/
|
|
int
|
|
Cudd_SupportSize(
|
|
DdManager * dd /**< manager */,
|
|
DdNode * f /**< %DD whose support size is sought */)
|
|
{
|
|
int SP = 0;
|
|
|
|
ddFindSupport(dd, Cudd_Regular(f), &SP);
|
|
ddClearFlag(Cudd_Regular(f));
|
|
ddClearVars(dd, SP);
|
|
|
|
return(SP);
|
|
|
|
} /* end of Cudd_SupportSize */
|
|
|
|
|
|
/**
|
|
@brief Finds the variables on which a set of DDs depends.
|
|
|
|
@details The set must contain either BDDs and ADDs, or ZDDs.
|
|
|
|
@return the number of variables if successful; CUDD_OUT_OF_MEM
|
|
otherwise.
|
|
|
|
@sideeffect The indices of the support variables are returned as
|
|
side effects. If the function is constant, no array is allocated.
|
|
|
|
@see Cudd_Support Cudd_SupportIndex Cudd_VectorSupportIndices
|
|
|
|
*/
|
|
int
|
|
Cudd_VectorSupportIndices(
|
|
DdManager * dd /**< manager */,
|
|
DdNode ** F /**< %DD whose support is sought */,
|
|
int n /**< size of the array */,
|
|
int **indices /**< array containing (on return) the indices */)
|
|
{
|
|
int i;
|
|
int SP = 0;
|
|
|
|
/* Compute support and clean up markers. */
|
|
for (i = 0; i < n; i++) {
|
|
ddFindSupport(dd, Cudd_Regular(F[i]), &SP);
|
|
}
|
|
for (i = 0; i < n; i++) {
|
|
ddClearFlag(Cudd_Regular(F[i]));
|
|
}
|
|
ddClearVars(dd, SP);
|
|
|
|
if (SP > 0) {
|
|
*indices = ALLOC(int, SP);
|
|
if (*indices == NULL) {
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
return(CUDD_OUT_OF_MEM);
|
|
}
|
|
|
|
for (i = 0; i < SP; i++)
|
|
(*indices)[i] = (int) (ptrint) dd->stack[i];
|
|
|
|
util_qsort(*indices, SP, sizeof(int), indexCompare);
|
|
} else {
|
|
*indices = NULL;
|
|
}
|
|
|
|
return(SP);
|
|
|
|
} /* end of Cudd_VectorSupportIndices */
|
|
|
|
|
|
/**
|
|
@brief Finds the variables on which a set of DDs depends.
|
|
|
|
@details The set must contain either BDDs and ADDs, or ZDDs.
|
|
|
|
@return a %BDD consisting of the product of the variables if
|
|
successful; NULL otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_Support Cudd_ClassifySupport
|
|
|
|
*/
|
|
DdNode *
|
|
Cudd_VectorSupport(
|
|
DdManager * dd /**< manager */,
|
|
DdNode ** F /**< array of DDs whose support is sought */,
|
|
int n /**< size of the array */)
|
|
{
|
|
int *support;
|
|
DdNode *res;
|
|
int j;
|
|
int size = Cudd_VectorSupportIndices(dd, F, n, &support);
|
|
if (size == CUDD_OUT_OF_MEM)
|
|
return(NULL);
|
|
|
|
/* Transform support from array of indices to cube. */
|
|
res = DD_ONE(dd);
|
|
cuddRef(res);
|
|
|
|
for (j = size - 1; j >= 0; j--) { /* for each index bottom-up (almost) */
|
|
int index = support[j];
|
|
DdNode *var = dd->vars[index];
|
|
DdNode *tmp = Cudd_bddAnd(dd,res,var);
|
|
if (tmp == NULL) {
|
|
Cudd_RecursiveDeref(dd,res);
|
|
FREE(support);
|
|
return(NULL);
|
|
}
|
|
cuddRef(tmp);
|
|
Cudd_RecursiveDeref(dd,res);
|
|
res = tmp;
|
|
}
|
|
|
|
FREE(support);
|
|
cuddDeref(res);
|
|
return(res);
|
|
|
|
} /* end of Cudd_VectorSupport */
|
|
|
|
|
|
/**
|
|
@brief Finds the variables on which a set of DDs depends.
|
|
|
|
@details The set must contain either BDDs and ADDs, or ZDDs.
|
|
|
|
@return an index array of the variables if successful; NULL
|
|
otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_SupportIndex Cudd_VectorSupport Cudd_VectorSupportIndices
|
|
|
|
*/
|
|
int *
|
|
Cudd_VectorSupportIndex(
|
|
DdManager * dd /**< manager */,
|
|
DdNode ** F /**< array of DDs whose support is sought */,
|
|
int n /**< size of the array */)
|
|
{
|
|
int *support;
|
|
int i;
|
|
int size;
|
|
|
|
/* Allocate and initialize support array for ddSupportStep. */
|
|
size = ddMax(dd->size, dd->sizeZ);
|
|
support = ALLOC(int,size);
|
|
if (support == NULL) {
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
return(NULL);
|
|
}
|
|
for (i = 0; i < size; i++) {
|
|
support[i] = 0;
|
|
}
|
|
|
|
/* Compute support and clean up markers. */
|
|
for (i = 0; i < n; i++) {
|
|
ddSupportStep(Cudd_Regular(F[i]),support);
|
|
}
|
|
for (i = 0; i < n; i++) {
|
|
ddClearFlag(Cudd_Regular(F[i]));
|
|
}
|
|
|
|
return(support);
|
|
|
|
} /* end of Cudd_VectorSupportIndex */
|
|
|
|
|
|
/**
|
|
@brief Counts the variables on which a set of DDs depends.
|
|
|
|
@details The set must contain either BDDs and ADDs, or ZDDs.
|
|
|
|
@return the number of variables on which a set of DDs depends.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_VectorSupport Cudd_SupportSize
|
|
|
|
*/
|
|
int
|
|
Cudd_VectorSupportSize(
|
|
DdManager * dd /**< manager */,
|
|
DdNode ** F /**< array of DDs whose support is sought */,
|
|
int n /**< size of the array */)
|
|
{
|
|
int i;
|
|
int SP = 0;
|
|
|
|
/* Compute support and clean up markers. */
|
|
for (i = 0; i < n; i++) {
|
|
ddFindSupport(dd, Cudd_Regular(F[i]), &SP);
|
|
}
|
|
for (i = 0; i < n; i++) {
|
|
ddClearFlag(Cudd_Regular(F[i]));
|
|
}
|
|
ddClearVars(dd, SP);
|
|
|
|
return(SP);
|
|
|
|
} /* end of Cudd_VectorSupportSize */
|
|
|
|
|
|
/**
|
|
@brief Classifies the variables in the support of two DDs.
|
|
|
|
@details Classifies the variables in the support of two DDs
|
|
<code>f</code> and <code>g</code>, depending on whether they appear
|
|
in both DDs, only in <code>f</code>, or only in <code>g</code>.
|
|
|
|
@return 1 if successful; 0 otherwise.
|
|
|
|
@sideeffect The cubes of the three classes of variables are
|
|
returned as side effects.
|
|
|
|
@see Cudd_Support Cudd_VectorSupport
|
|
|
|
*/
|
|
int
|
|
Cudd_ClassifySupport(
|
|
DdManager * dd /**< manager */,
|
|
DdNode * f /**< first %DD */,
|
|
DdNode * g /**< second %DD */,
|
|
DdNode ** common /**< cube of shared variables */,
|
|
DdNode ** onlyF /**< cube of variables only in f */,
|
|
DdNode ** onlyG /**< cube of variables only in g */)
|
|
{
|
|
int *supportF, *supportG;
|
|
int fi, gi;
|
|
int sizeF, sizeG;
|
|
|
|
sizeF = Cudd_SupportIndices(dd, f, &supportF);
|
|
if (sizeF == CUDD_OUT_OF_MEM)
|
|
return(0);
|
|
|
|
sizeG = Cudd_SupportIndices(dd, g, &supportG);
|
|
if (sizeG == CUDD_OUT_OF_MEM) {
|
|
FREE(supportF);
|
|
return(0);
|
|
}
|
|
|
|
/* Classify variables and create cubes. This part of the procedure
|
|
** relies on the sorting of the indices in the two support arrays.
|
|
*/
|
|
*common = *onlyF = *onlyG = DD_ONE(dd);
|
|
cuddRef(*common); cuddRef(*onlyF); cuddRef(*onlyG);
|
|
fi = sizeF - 1;
|
|
gi = sizeG - 1;
|
|
while (fi >= 0 || gi >= 0) {
|
|
int indexF = fi >= 0 ? supportF[fi] : -1;
|
|
int indexG = gi >= 0 ? supportG[gi] : -1;
|
|
int index = ddMax(indexF, indexG);
|
|
DdNode *var = dd->vars[index];
|
|
#ifdef DD_DEBUG
|
|
assert(index >= 0);
|
|
#endif
|
|
if (indexF == indexG) {
|
|
DdNode *tmp = Cudd_bddAnd(dd,*common,var);
|
|
if (tmp == NULL) {
|
|
Cudd_RecursiveDeref(dd,*common);
|
|
Cudd_RecursiveDeref(dd,*onlyF);
|
|
Cudd_RecursiveDeref(dd,*onlyG);
|
|
FREE(supportF); FREE(supportG);
|
|
return(0);
|
|
}
|
|
cuddRef(tmp);
|
|
Cudd_RecursiveDeref(dd,*common);
|
|
*common = tmp;
|
|
fi--;
|
|
gi--;
|
|
} else if (index == indexF) {
|
|
DdNode *tmp = Cudd_bddAnd(dd,*onlyF,var);
|
|
if (tmp == NULL) {
|
|
Cudd_RecursiveDeref(dd,*common);
|
|
Cudd_RecursiveDeref(dd,*onlyF);
|
|
Cudd_RecursiveDeref(dd,*onlyG);
|
|
FREE(supportF); FREE(supportG);
|
|
return(0);
|
|
}
|
|
cuddRef(tmp);
|
|
Cudd_RecursiveDeref(dd,*onlyF);
|
|
*onlyF = tmp;
|
|
fi--;
|
|
} else { /* index == indexG */
|
|
DdNode *tmp = Cudd_bddAnd(dd,*onlyG,var);
|
|
if (tmp == NULL) {
|
|
Cudd_RecursiveDeref(dd,*common);
|
|
Cudd_RecursiveDeref(dd,*onlyF);
|
|
Cudd_RecursiveDeref(dd,*onlyG);
|
|
FREE(supportF); FREE(supportG);
|
|
return(0);
|
|
}
|
|
cuddRef(tmp);
|
|
Cudd_RecursiveDeref(dd,*onlyG);
|
|
*onlyG = tmp;
|
|
gi--;
|
|
}
|
|
}
|
|
|
|
FREE(supportF); FREE(supportG);
|
|
cuddDeref(*common); cuddDeref(*onlyF); cuddDeref(*onlyG);
|
|
return(1);
|
|
|
|
} /* end of Cudd_ClassifySupport */
|
|
|
|
|
|
/**
|
|
@brief Counts the number of leaves in a %DD.
|
|
|
|
@return the number of leaves in the %DD rooted at node if successful;
|
|
CUDD_OUT_OF_MEM otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_PrintDebug
|
|
|
|
*/
|
|
int
|
|
Cudd_CountLeaves(
|
|
DdNode * node)
|
|
{
|
|
int i;
|
|
|
|
i = ddLeavesInt(Cudd_Regular(node));
|
|
ddClearFlag(Cudd_Regular(node));
|
|
return(i);
|
|
|
|
} /* end of Cudd_CountLeaves */
|
|
|
|
|
|
/**
|
|
@brief Picks one on-set cube randomly from the given %DD.
|
|
|
|
@details The cube is written into an array of characters. The array
|
|
must have at least as many entries as there are variables.
|
|
|
|
@return 1 if successful; 0 otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_bddPickOneMinterm
|
|
|
|
*/
|
|
int
|
|
Cudd_bddPickOneCube(
|
|
DdManager * ddm,
|
|
DdNode * node,
|
|
char * string)
|
|
{
|
|
DdNode *N, *T, *E;
|
|
DdNode *one, *bzero;
|
|
char dir;
|
|
int i;
|
|
|
|
if (string == NULL || node == NULL) return(0);
|
|
|
|
/* The constant 0 function has no on-set cubes. */
|
|
one = DD_ONE(ddm);
|
|
bzero = Cudd_Not(one);
|
|
if (node == bzero) {
|
|
ddm->errorCode = CUDD_INVALID_ARG;
|
|
return(0);
|
|
}
|
|
|
|
for (i = 0; i < ddm->size; i++) string[i] = 2;
|
|
|
|
for (;;) {
|
|
|
|
if (node == one) break;
|
|
|
|
N = Cudd_Regular(node);
|
|
|
|
T = cuddT(N); E = cuddE(N);
|
|
if (Cudd_IsComplement(node)) {
|
|
T = Cudd_Not(T); E = Cudd_Not(E);
|
|
}
|
|
if (T == bzero) {
|
|
string[N->index] = 0;
|
|
node = E;
|
|
} else if (E == bzero) {
|
|
string[N->index] = 1;
|
|
node = T;
|
|
} else {
|
|
dir = (char) ((Cudd_Random(ddm) & 0x2000) >> 13);
|
|
string[N->index] = dir;
|
|
node = dir ? T : E;
|
|
}
|
|
}
|
|
return(1);
|
|
|
|
} /* end of Cudd_bddPickOneCube */
|
|
|
|
|
|
/**
|
|
@brief Picks one on-set minterm randomly from the given %DD.
|
|
|
|
@details The minterm is in terms of <code>vars</code>. The array
|
|
<code>vars</code> should contain at least all variables in the
|
|
support of <code>f</code>; if this condition is not met the minterm
|
|
built by this procedure may not be contained in <code>f</code>.
|
|
|
|
@return a pointer to the %BDD for the minterm if successful; NULL otherwise.
|
|
There are three reasons why the procedure may fail:
|
|
<ul>
|
|
<li> It may run out of memory;
|
|
<li> the function <code>f</code> may be the constant 0;
|
|
<li> the minterm may not be contained in <code>f</code>.
|
|
</ul>
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_bddPickOneCube
|
|
|
|
*/
|
|
DdNode *
|
|
Cudd_bddPickOneMinterm(
|
|
DdManager * dd /**< manager */,
|
|
DdNode * f /**< function from which to pick one minterm */,
|
|
DdNode ** vars /**< array of variables */,
|
|
int n /**< size of <code>vars</code> */)
|
|
{
|
|
char *string;
|
|
int i, size;
|
|
int *indices;
|
|
int result;
|
|
DdNode *old, *neW;
|
|
|
|
size = dd->size;
|
|
string = ALLOC(char, size);
|
|
if (string == NULL) {
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
return(NULL);
|
|
}
|
|
indices = ALLOC(int,n);
|
|
if (indices == NULL) {
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
FREE(string);
|
|
return(NULL);
|
|
}
|
|
|
|
for (i = 0; i < n; i++) {
|
|
indices[i] = vars[i]->index;
|
|
}
|
|
|
|
result = Cudd_bddPickOneCube(dd,f,string);
|
|
if (result == 0) {
|
|
FREE(string);
|
|
FREE(indices);
|
|
return(NULL);
|
|
}
|
|
|
|
/* Randomize choice for don't cares. */
|
|
for (i = 0; i < n; i++) {
|
|
if (string[indices[i]] == 2)
|
|
string[indices[i]] = (char) ((Cudd_Random(dd) & 0x20) >> 5);
|
|
}
|
|
|
|
/* Build result BDD. */
|
|
old = Cudd_ReadOne(dd);
|
|
cuddRef(old);
|
|
|
|
for (i = n-1; i >= 0; i--) {
|
|
neW = Cudd_bddAnd(dd,old,Cudd_NotCond(vars[i],string[indices[i]]==0));
|
|
if (neW == NULL) {
|
|
FREE(string);
|
|
FREE(indices);
|
|
Cudd_RecursiveDeref(dd,old);
|
|
return(NULL);
|
|
}
|
|
cuddRef(neW);
|
|
Cudd_RecursiveDeref(dd,old);
|
|
old = neW;
|
|
}
|
|
|
|
#ifdef DD_DEBUG
|
|
/* Test. */
|
|
if (Cudd_bddLeq(dd,old,f)) {
|
|
cuddDeref(old);
|
|
} else {
|
|
Cudd_RecursiveDeref(dd,old);
|
|
old = NULL;
|
|
}
|
|
#else
|
|
cuddDeref(old);
|
|
#endif
|
|
|
|
FREE(string);
|
|
FREE(indices);
|
|
return(old);
|
|
|
|
} /* end of Cudd_bddPickOneMinterm */
|
|
|
|
|
|
/**
|
|
@brief Picks k on-set minterms evenly distributed from given %DD.
|
|
|
|
@details The minterms are in terms of <code>vars</code>. The array
|
|
<code>vars</code> should contain at least all variables in the
|
|
support of <code>f</code>; if this condition is not met the minterms
|
|
built by this procedure may not be contained in <code>f</code>.
|
|
|
|
@return an array of BDDs for the minterms if successful; NULL otherwise.
|
|
There are three reasons why the procedure may fail:
|
|
<ul>
|
|
<li> It may run out of memory;
|
|
<li> the function <code>f</code> may be the constant 0;
|
|
<li> the minterms may not be contained in <code>f</code>.
|
|
</ul>
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_bddPickOneMinterm Cudd_bddPickOneCube
|
|
|
|
*/
|
|
DdNode **
|
|
Cudd_bddPickArbitraryMinterms(
|
|
DdManager * dd /**< manager */,
|
|
DdNode * f /**< function from which to pick k minterms */,
|
|
DdNode ** vars /**< array of variables */,
|
|
int n /**< size of <code>vars</code> */,
|
|
int k /**< number of minterms to find */)
|
|
{
|
|
char **string;
|
|
int i, j, l, size;
|
|
int *indices;
|
|
int result;
|
|
DdNode **old, *neW;
|
|
double minterms;
|
|
char *saveString;
|
|
int saveFlag, savePoint = 0, isSame;
|
|
|
|
minterms = Cudd_CountMinterm(dd,f,n);
|
|
if ((double)k > minterms) {
|
|
return(NULL);
|
|
}
|
|
|
|
size = dd->size;
|
|
string = ALLOC(char *, k);
|
|
if (string == NULL) {
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
return(NULL);
|
|
}
|
|
for (i = 0; i < k; i++) {
|
|
string[i] = ALLOC(char, size + 1);
|
|
if (string[i] == NULL) {
|
|
for (j = 0; j < i; j++)
|
|
FREE(string[i]);
|
|
FREE(string);
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
return(NULL);
|
|
}
|
|
for (j = 0; j < size; j++) string[i][j] = '2';
|
|
string[i][size] = '\0';
|
|
}
|
|
indices = ALLOC(int,n);
|
|
if (indices == NULL) {
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
for (i = 0; i < k; i++)
|
|
FREE(string[i]);
|
|
FREE(string);
|
|
return(NULL);
|
|
}
|
|
|
|
for (i = 0; i < n; i++) {
|
|
indices[i] = vars[i]->index;
|
|
}
|
|
|
|
result = ddPickArbitraryMinterms(dd,f,n,k,string);
|
|
if (result == 0) {
|
|
for (i = 0; i < k; i++)
|
|
FREE(string[i]);
|
|
FREE(string);
|
|
FREE(indices);
|
|
return(NULL);
|
|
}
|
|
|
|
old = ALLOC(DdNode *, k);
|
|
if (old == NULL) {
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
for (i = 0; i < k; i++)
|
|
FREE(string[i]);
|
|
FREE(string);
|
|
FREE(indices);
|
|
return(NULL);
|
|
}
|
|
saveString = ALLOC(char, size + 1);
|
|
if (saveString == NULL) {
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
for (i = 0; i < k; i++)
|
|
FREE(string[i]);
|
|
FREE(string);
|
|
FREE(indices);
|
|
FREE(old);
|
|
return(NULL);
|
|
}
|
|
saveFlag = 0;
|
|
|
|
/* Build result BDD array. */
|
|
for (i = 0; i < k; i++) {
|
|
isSame = 0;
|
|
if (!saveFlag) {
|
|
for (j = i + 1; j < k; j++) {
|
|
if (strcmp(string[i], string[j]) == 0) {
|
|
savePoint = i;
|
|
strcpy(saveString, string[i]);
|
|
saveFlag = 1;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
if (strcmp(string[i], saveString) == 0) {
|
|
isSame = 1;
|
|
} else {
|
|
saveFlag = 0;
|
|
for (j = i + 1; j < k; j++) {
|
|
if (strcmp(string[i], string[j]) == 0) {
|
|
savePoint = i;
|
|
strcpy(saveString, string[i]);
|
|
saveFlag = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* Randomize choice for don't cares. */
|
|
for (j = 0; j < n; j++) {
|
|
if (string[i][indices[j]] == '2')
|
|
string[i][indices[j]] =
|
|
(char) ((Cudd_Random(dd) & 0x20) ? '1' : '0');
|
|
}
|
|
|
|
while (isSame) {
|
|
isSame = 0;
|
|
for (j = savePoint; j < i; j++) {
|
|
if (strcmp(string[i], string[j]) == 0) {
|
|
isSame = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (isSame) {
|
|
strcpy(string[i], saveString);
|
|
/* Randomize choice for don't cares. */
|
|
for (j = 0; j < n; j++) {
|
|
if (string[i][indices[j]] == '2')
|
|
string[i][indices[j]] =
|
|
(char) ((Cudd_Random(dd) & 0x20) ? '1' : '0');
|
|
}
|
|
}
|
|
}
|
|
|
|
old[i] = Cudd_ReadOne(dd);
|
|
cuddRef(old[i]);
|
|
|
|
for (j = 0; j < n; j++) {
|
|
if (string[i][indices[j]] == '0') {
|
|
neW = Cudd_bddAnd(dd,old[i],Cudd_Not(vars[j]));
|
|
} else {
|
|
neW = Cudd_bddAnd(dd,old[i],vars[j]);
|
|
}
|
|
if (neW == NULL) {
|
|
FREE(saveString);
|
|
for (l = 0; l < k; l++)
|
|
FREE(string[l]);
|
|
FREE(string);
|
|
FREE(indices);
|
|
for (l = 0; l <= i; l++)
|
|
Cudd_RecursiveDeref(dd,old[l]);
|
|
FREE(old);
|
|
return(NULL);
|
|
}
|
|
cuddRef(neW);
|
|
Cudd_RecursiveDeref(dd,old[i]);
|
|
old[i] = neW;
|
|
}
|
|
|
|
/* Test. */
|
|
if (!Cudd_bddLeq(dd,old[i],f)) {
|
|
FREE(saveString);
|
|
for (l = 0; l < k; l++)
|
|
FREE(string[l]);
|
|
FREE(string);
|
|
FREE(indices);
|
|
for (l = 0; l <= i; l++)
|
|
Cudd_RecursiveDeref(dd,old[l]);
|
|
FREE(old);
|
|
return(NULL);
|
|
}
|
|
}
|
|
|
|
FREE(saveString);
|
|
for (i = 0; i < k; i++) {
|
|
cuddDeref(old[i]);
|
|
FREE(string[i]);
|
|
}
|
|
FREE(string);
|
|
FREE(indices);
|
|
return(old);
|
|
|
|
} /* end of Cudd_bddPickArbitraryMinterms */
|
|
|
|
|
|
/**
|
|
@brief Extracts a subset from a %BDD.
|
|
|
|
@details Extracts a subset from a %BDD in the following procedure.
|
|
1. Compute the weight for each mask variable by counting the number of
|
|
minterms for both positive and negative cofactors of the %BDD with
|
|
respect to each mask variable. (weight = # positive - # negative)
|
|
2. Find a representative cube of the %BDD by using the weight. From the
|
|
top variable of the %BDD, for each variable, if the weight is greater
|
|
than 0.0, choose THEN branch, othereise ELSE branch, until meeting
|
|
the constant 1.
|
|
3. Quantify out the variables not in maskVars from the representative
|
|
cube and if a variable in maskVars is don't care, replace the
|
|
variable with a constant(1 or 0) depending on the weight.
|
|
4. Make a subset of the %BDD by multiplying with the modified cube.
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
DdNode *
|
|
Cudd_SubsetWithMaskVars(
|
|
DdManager * dd /**< manager */,
|
|
DdNode * f /**< function from which to pick a cube */,
|
|
DdNode ** vars /**< array of variables */,
|
|
int nvars /**< size of <code>vars</code> */,
|
|
DdNode ** maskVars /**< array of variables */,
|
|
int mvars /**< size of <code>maskVars</code> */)
|
|
{
|
|
double *weight;
|
|
char *string;
|
|
int i, size;
|
|
int *indices, *mask;
|
|
int result;
|
|
DdNode *cube, *newCube, *subset;
|
|
DdNode *cof;
|
|
DdNode *support;
|
|
DdNode *zero;
|
|
|
|
support = Cudd_Support(dd,f);
|
|
cuddRef(support);
|
|
Cudd_RecursiveDeref(dd,support);
|
|
|
|
size = dd->size;
|
|
|
|
weight = ALLOC(double,size);
|
|
if (weight == NULL) {
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
return(NULL);
|
|
}
|
|
for (i = 0; i < size; i++) {
|
|
weight[i] = 0.0;
|
|
}
|
|
for (i = 0; i < mvars; i++) {
|
|
cof = Cudd_Cofactor(dd, f, maskVars[i]);
|
|
cuddRef(cof);
|
|
weight[i] = Cudd_CountMinterm(dd, cof, nvars);
|
|
Cudd_RecursiveDeref(dd,cof);
|
|
|
|
cof = Cudd_Cofactor(dd, f, Cudd_Not(maskVars[i]));
|
|
cuddRef(cof);
|
|
weight[i] -= Cudd_CountMinterm(dd, cof, nvars);
|
|
Cudd_RecursiveDeref(dd,cof);
|
|
}
|
|
|
|
string = ALLOC(char, size + 1);
|
|
if (string == NULL) {
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
FREE(weight);
|
|
return(NULL);
|
|
}
|
|
mask = ALLOC(int, size);
|
|
if (mask == NULL) {
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
FREE(weight);
|
|
FREE(string);
|
|
return(NULL);
|
|
}
|
|
for (i = 0; i < size; i++) {
|
|
string[i] = '2';
|
|
mask[i] = 0;
|
|
}
|
|
string[size] = '\0';
|
|
indices = ALLOC(int,nvars);
|
|
if (indices == NULL) {
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
FREE(weight);
|
|
FREE(string);
|
|
FREE(mask);
|
|
return(NULL);
|
|
}
|
|
for (i = 0; i < nvars; i++) {
|
|
indices[i] = vars[i]->index;
|
|
}
|
|
|
|
result = ddPickRepresentativeCube(dd,f,weight,string);
|
|
if (result == 0) {
|
|
FREE(weight);
|
|
FREE(string);
|
|
FREE(mask);
|
|
FREE(indices);
|
|
return(NULL);
|
|
}
|
|
|
|
cube = Cudd_ReadOne(dd);
|
|
cuddRef(cube);
|
|
zero = Cudd_Not(Cudd_ReadOne(dd));
|
|
for (i = 0; i < nvars; i++) {
|
|
if (string[indices[i]] == '0') {
|
|
newCube = Cudd_bddIte(dd,cube,Cudd_Not(vars[i]),zero);
|
|
} else if (string[indices[i]] == '1') {
|
|
newCube = Cudd_bddIte(dd,cube,vars[i],zero);
|
|
} else
|
|
continue;
|
|
if (newCube == NULL) {
|
|
FREE(weight);
|
|
FREE(string);
|
|
FREE(mask);
|
|
FREE(indices);
|
|
Cudd_RecursiveDeref(dd,cube);
|
|
return(NULL);
|
|
}
|
|
cuddRef(newCube);
|
|
Cudd_RecursiveDeref(dd,cube);
|
|
cube = newCube;
|
|
}
|
|
Cudd_RecursiveDeref(dd,cube);
|
|
|
|
for (i = 0; i < mvars; i++) {
|
|
mask[maskVars[i]->index] = 1;
|
|
}
|
|
for (i = 0; i < nvars; i++) {
|
|
if (mask[indices[i]]) {
|
|
if (string[indices[i]] == '2') {
|
|
if (weight[indices[i]] >= 0.0)
|
|
string[indices[i]] = '1';
|
|
else
|
|
string[indices[i]] = '0';
|
|
}
|
|
} else {
|
|
string[indices[i]] = '2';
|
|
}
|
|
}
|
|
|
|
cube = Cudd_ReadOne(dd);
|
|
cuddRef(cube);
|
|
zero = Cudd_Not(Cudd_ReadOne(dd));
|
|
|
|
/* Build result BDD. */
|
|
for (i = 0; i < nvars; i++) {
|
|
if (string[indices[i]] == '0') {
|
|
newCube = Cudd_bddIte(dd,cube,Cudd_Not(vars[i]),zero);
|
|
} else if (string[indices[i]] == '1') {
|
|
newCube = Cudd_bddIte(dd,cube,vars[i],zero);
|
|
} else
|
|
continue;
|
|
if (newCube == NULL) {
|
|
FREE(weight);
|
|
FREE(string);
|
|
FREE(mask);
|
|
FREE(indices);
|
|
Cudd_RecursiveDeref(dd,cube);
|
|
return(NULL);
|
|
}
|
|
cuddRef(newCube);
|
|
Cudd_RecursiveDeref(dd,cube);
|
|
cube = newCube;
|
|
}
|
|
|
|
subset = Cudd_bddAnd(dd,f,cube);
|
|
cuddRef(subset);
|
|
Cudd_RecursiveDeref(dd,cube);
|
|
|
|
/* Test. */
|
|
if (Cudd_bddLeq(dd,subset,f)) {
|
|
cuddDeref(subset);
|
|
} else {
|
|
Cudd_RecursiveDeref(dd,subset);
|
|
subset = NULL;
|
|
}
|
|
|
|
FREE(weight);
|
|
FREE(string);
|
|
FREE(mask);
|
|
FREE(indices);
|
|
return(subset);
|
|
|
|
} /* end of Cudd_SubsetWithMaskVars */
|
|
|
|
|
|
/**
|
|
@brief Finds the first cube of a decision diagram.
|
|
|
|
@details Defines an iterator on the onset of a decision diagram
|
|
and finds its first cube.<p>
|
|
A cube is represented as an array of literals, which are integers in
|
|
{0, 1, 2}; 0 represents a complemented literal, 1 represents an
|
|
uncomplemented literal, and 2 stands for don't care. The enumeration
|
|
produces a disjoint cover of the function associated with the diagram.
|
|
The size of the array equals the number of variables in the manager at
|
|
the time Cudd_FirstCube is called.<p>
|
|
For each cube, a value is also returned. This value is always 1 for a
|
|
%BDD, while it may be different from 1 for an %ADD.
|
|
For BDDs, the offset is the set of cubes whose value is the logical zero.
|
|
For ADDs, the offset is the set of cubes whose value is the
|
|
background value. The cubes of the offset are not enumerated.
|
|
|
|
@return a generator that contains the information necessary to
|
|
continue the enumeration if successful; NULL otherwise.
|
|
|
|
@sideeffect The first cube and its value are returned as side effects.
|
|
|
|
@see Cudd_ForeachCube Cudd_NextCube Cudd_GenFree Cudd_IsGenEmpty
|
|
Cudd_FirstNode
|
|
|
|
*/
|
|
DdGen *
|
|
Cudd_FirstCube(
|
|
DdManager * dd,
|
|
DdNode * f,
|
|
int ** cube,
|
|
CUDD_VALUE_TYPE * value)
|
|
{
|
|
DdGen *gen;
|
|
DdNode *top, *treg, *next, *nreg, *prev, *preg;
|
|
int i;
|
|
int nvars;
|
|
|
|
/* Sanity Check. */
|
|
if (dd == NULL || f == NULL) return(NULL);
|
|
|
|
/* Allocate generator an initialize it. */
|
|
gen = ALLOC(DdGen,1);
|
|
if (gen == NULL) {
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
return(NULL);
|
|
}
|
|
|
|
gen->manager = dd;
|
|
gen->type = CUDD_GEN_CUBES;
|
|
gen->status = CUDD_GEN_EMPTY;
|
|
gen->gen.cubes.cube = NULL;
|
|
gen->gen.cubes.value = DD_ZERO_VAL;
|
|
gen->stack.sp = 0;
|
|
gen->stack.stack = NULL;
|
|
gen->node = NULL;
|
|
|
|
nvars = dd->size;
|
|
gen->gen.cubes.cube = ALLOC(int,nvars);
|
|
if (gen->gen.cubes.cube == NULL) {
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
FREE(gen);
|
|
return(NULL);
|
|
}
|
|
for (i = 0; i < nvars; i++) gen->gen.cubes.cube[i] = 2;
|
|
|
|
/* The maximum stack depth is one plus the number of variables.
|
|
** because a path may have nodes at all levels, including the
|
|
** constant level.
|
|
*/
|
|
gen->stack.stack = ALLOC(DdNodePtr, nvars+1);
|
|
if (gen->stack.stack == NULL) {
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
FREE(gen->gen.cubes.cube);
|
|
FREE(gen);
|
|
return(NULL);
|
|
}
|
|
for (i = 0; i <= nvars; i++) gen->stack.stack[i] = NULL;
|
|
|
|
/* Find the first cube of the onset. */
|
|
gen->stack.stack[gen->stack.sp] = f; gen->stack.sp++;
|
|
|
|
while (1) {
|
|
top = gen->stack.stack[gen->stack.sp-1];
|
|
treg = Cudd_Regular(top);
|
|
if (!cuddIsConstant(treg)) {
|
|
/* Take the else branch first. */
|
|
gen->gen.cubes.cube[treg->index] = 0;
|
|
next = cuddE(treg);
|
|
if (top != treg) next = Cudd_Not(next);
|
|
gen->stack.stack[gen->stack.sp] = next; gen->stack.sp++;
|
|
} else if (top == Cudd_Not(DD_ONE(dd)) || top == dd->background) {
|
|
/* Backtrack */
|
|
while (1) {
|
|
if (gen->stack.sp == 1) {
|
|
/* The current node has no predecessor. */
|
|
gen->status = CUDD_GEN_EMPTY;
|
|
gen->stack.sp--;
|
|
goto done;
|
|
}
|
|
prev = gen->stack.stack[gen->stack.sp-2];
|
|
preg = Cudd_Regular(prev);
|
|
nreg = cuddT(preg);
|
|
if (prev != preg) {next = Cudd_Not(nreg);} else {next = nreg;}
|
|
if (next != top) { /* follow the then branch next */
|
|
gen->gen.cubes.cube[preg->index] = 1;
|
|
gen->stack.stack[gen->stack.sp-1] = next;
|
|
break;
|
|
}
|
|
/* Pop the stack and try again. */
|
|
gen->gen.cubes.cube[preg->index] = 2;
|
|
gen->stack.sp--;
|
|
top = gen->stack.stack[gen->stack.sp-1];
|
|
}
|
|
} else {
|
|
gen->status = CUDD_GEN_NONEMPTY;
|
|
gen->gen.cubes.value = cuddV(top);
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
done:
|
|
*cube = gen->gen.cubes.cube;
|
|
*value = gen->gen.cubes.value;
|
|
return(gen);
|
|
|
|
} /* end of Cudd_FirstCube */
|
|
|
|
|
|
/**
|
|
@brief Generates the next cube of a decision diagram onset.
|
|
|
|
@return 0 if the enumeration is completed; 1 otherwise.
|
|
|
|
@sideeffect The cube and its value are returned as side effects. The
|
|
generator is modified.
|
|
|
|
@see Cudd_ForeachCube Cudd_FirstCube Cudd_GenFree Cudd_IsGenEmpty
|
|
Cudd_NextNode
|
|
|
|
*/
|
|
int
|
|
Cudd_NextCube(
|
|
DdGen * gen,
|
|
int ** cube,
|
|
CUDD_VALUE_TYPE * value)
|
|
{
|
|
DdNode *top, *treg, *next, *nreg, *prev, *preg;
|
|
DdManager *dd = gen->manager;
|
|
|
|
/* Backtrack from previously reached terminal node. */
|
|
while (1) {
|
|
if (gen->stack.sp == 1) {
|
|
/* The current node has no predecessor. */
|
|
gen->status = CUDD_GEN_EMPTY;
|
|
gen->stack.sp--;
|
|
goto done;
|
|
}
|
|
top = gen->stack.stack[gen->stack.sp-1];
|
|
prev = gen->stack.stack[gen->stack.sp-2];
|
|
preg = Cudd_Regular(prev);
|
|
nreg = cuddT(preg);
|
|
if (prev != preg) {next = Cudd_Not(nreg);} else {next = nreg;}
|
|
if (next != top) { /* follow the then branch next */
|
|
gen->gen.cubes.cube[preg->index] = 1;
|
|
gen->stack.stack[gen->stack.sp-1] = next;
|
|
break;
|
|
}
|
|
/* Pop the stack and try again. */
|
|
gen->gen.cubes.cube[preg->index] = 2;
|
|
gen->stack.sp--;
|
|
}
|
|
|
|
while (1) {
|
|
top = gen->stack.stack[gen->stack.sp-1];
|
|
treg = Cudd_Regular(top);
|
|
if (!cuddIsConstant(treg)) {
|
|
/* Take the else branch first. */
|
|
gen->gen.cubes.cube[treg->index] = 0;
|
|
next = cuddE(treg);
|
|
if (top != treg) next = Cudd_Not(next);
|
|
gen->stack.stack[gen->stack.sp] = next; gen->stack.sp++;
|
|
} else if (top == Cudd_Not(DD_ONE(dd)) || top == dd->background) {
|
|
/* Backtrack */
|
|
while (1) {
|
|
if (gen->stack.sp == 1) {
|
|
/* The current node has no predecessor. */
|
|
gen->status = CUDD_GEN_EMPTY;
|
|
gen->stack.sp--;
|
|
goto done;
|
|
}
|
|
prev = gen->stack.stack[gen->stack.sp-2];
|
|
preg = Cudd_Regular(prev);
|
|
nreg = cuddT(preg);
|
|
if (prev != preg) {next = Cudd_Not(nreg);} else {next = nreg;}
|
|
if (next != top) { /* follow the then branch next */
|
|
gen->gen.cubes.cube[preg->index] = 1;
|
|
gen->stack.stack[gen->stack.sp-1] = next;
|
|
break;
|
|
}
|
|
/* Pop the stack and try again. */
|
|
gen->gen.cubes.cube[preg->index] = 2;
|
|
gen->stack.sp--;
|
|
top = gen->stack.stack[gen->stack.sp-1];
|
|
}
|
|
} else {
|
|
gen->status = CUDD_GEN_NONEMPTY;
|
|
gen->gen.cubes.value = cuddV(top);
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
done:
|
|
if (gen->status == CUDD_GEN_EMPTY) return(0);
|
|
*cube = gen->gen.cubes.cube;
|
|
*value = gen->gen.cubes.value;
|
|
return(1);
|
|
|
|
} /* end of Cudd_NextCube */
|
|
|
|
|
|
/**
|
|
@brief Finds the first prime of a Boolean function.
|
|
|
|
@details@parblock
|
|
Defines an iterator on a pair of BDDs describing a
|
|
(possibly incompletely specified) Boolean functions and finds the
|
|
first cube of a cover of the function.
|
|
|
|
The two argument BDDs are the lower and upper bounds of an interval.
|
|
It is a mistake to call this function with a lower bound that is not
|
|
less than or equal to the upper bound.
|
|
|
|
A cube is represented as an array of literals, which are integers in
|
|
{0, 1, 2}; 0 represents a complemented literal, 1 represents an
|
|
uncomplemented literal, and 2 stands for don't care. The enumeration
|
|
produces a prime and irredundant cover of the function associated
|
|
with the two BDDs. The size of the array equals the number of
|
|
variables in the manager at the time Cudd_FirstCube is called.
|
|
|
|
This iterator can only be used on BDDs.
|
|
@endparblock
|
|
|
|
@return a generator that contains the information necessary to
|
|
continue the enumeration if successful; NULL otherwise.
|
|
|
|
@sideeffect The first cube is returned as side effect.
|
|
|
|
@see Cudd_ForeachPrime Cudd_NextPrime Cudd_GenFree Cudd_IsGenEmpty
|
|
Cudd_FirstCube Cudd_FirstNode
|
|
|
|
*/
|
|
DdGen *
|
|
Cudd_FirstPrime(
|
|
DdManager *dd,
|
|
DdNode *l,
|
|
DdNode *u,
|
|
int **cube)
|
|
{
|
|
DdGen *gen;
|
|
DdNode *implicant, *prime, *tmp;
|
|
int length, result;
|
|
|
|
/* Sanity Check. */
|
|
if (dd == NULL || l == NULL || u == NULL) return(NULL);
|
|
|
|
/* Allocate generator an initialize it. */
|
|
gen = ALLOC(DdGen,1);
|
|
if (gen == NULL) {
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
return(NULL);
|
|
}
|
|
|
|
gen->manager = dd;
|
|
gen->type = CUDD_GEN_PRIMES;
|
|
gen->status = CUDD_GEN_EMPTY;
|
|
gen->gen.primes.cube = NULL;
|
|
gen->gen.primes.ub = u;
|
|
gen->stack.sp = 0;
|
|
gen->stack.stack = NULL;
|
|
gen->node = l;
|
|
cuddRef(l);
|
|
|
|
gen->gen.primes.cube = ALLOC(int,dd->size);
|
|
if (gen->gen.primes.cube == NULL) {
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
FREE(gen);
|
|
return(NULL);
|
|
}
|
|
|
|
if (gen->node == Cudd_ReadLogicZero(dd)) {
|
|
gen->status = CUDD_GEN_EMPTY;
|
|
} else {
|
|
implicant = Cudd_LargestCube(dd,gen->node,&length);
|
|
if (implicant == NULL) {
|
|
Cudd_RecursiveDeref(dd,gen->node);
|
|
FREE(gen->gen.primes.cube);
|
|
FREE(gen);
|
|
return(NULL);
|
|
}
|
|
cuddRef(implicant);
|
|
prime = Cudd_bddMakePrime(dd,implicant,gen->gen.primes.ub);
|
|
if (prime == NULL) {
|
|
Cudd_RecursiveDeref(dd,gen->node);
|
|
Cudd_RecursiveDeref(dd,implicant);
|
|
FREE(gen->gen.primes.cube);
|
|
FREE(gen);
|
|
return(NULL);
|
|
}
|
|
cuddRef(prime);
|
|
Cudd_RecursiveDeref(dd,implicant);
|
|
tmp = Cudd_bddAnd(dd,gen->node,Cudd_Not(prime));
|
|
if (tmp == NULL) {
|
|
Cudd_RecursiveDeref(dd,gen->node);
|
|
Cudd_RecursiveDeref(dd,prime);
|
|
FREE(gen->gen.primes.cube);
|
|
FREE(gen);
|
|
return(NULL);
|
|
}
|
|
cuddRef(tmp);
|
|
Cudd_RecursiveDeref(dd,gen->node);
|
|
gen->node = tmp;
|
|
result = Cudd_BddToCubeArray(dd,prime,gen->gen.primes.cube);
|
|
if (result == 0) {
|
|
Cudd_RecursiveDeref(dd,gen->node);
|
|
Cudd_RecursiveDeref(dd,prime);
|
|
FREE(gen->gen.primes.cube);
|
|
FREE(gen);
|
|
return(NULL);
|
|
}
|
|
Cudd_RecursiveDeref(dd,prime);
|
|
gen->status = CUDD_GEN_NONEMPTY;
|
|
}
|
|
*cube = gen->gen.primes.cube;
|
|
return(gen);
|
|
|
|
} /* end of Cudd_FirstPrime */
|
|
|
|
|
|
/**
|
|
@brief Generates the next prime of a Boolean function.
|
|
|
|
@return 0 if the enumeration is completed; 1 otherwise.
|
|
|
|
@sideeffect The cube and is returned as side effects. The
|
|
generator is modified.
|
|
|
|
@see Cudd_ForeachPrime Cudd_FirstPrime Cudd_GenFree Cudd_IsGenEmpty
|
|
Cudd_NextCube Cudd_NextNode
|
|
|
|
*/
|
|
int
|
|
Cudd_NextPrime(
|
|
DdGen *gen,
|
|
int **cube)
|
|
{
|
|
DdNode *implicant, *prime, *tmp;
|
|
DdManager *dd = gen->manager;
|
|
int length, result;
|
|
|
|
if (gen->node == Cudd_ReadLogicZero(dd)) {
|
|
gen->status = CUDD_GEN_EMPTY;
|
|
} else {
|
|
implicant = Cudd_LargestCube(dd,gen->node,&length);
|
|
if (implicant == NULL) {
|
|
gen->status = CUDD_GEN_EMPTY;
|
|
return(0);
|
|
}
|
|
cuddRef(implicant);
|
|
prime = Cudd_bddMakePrime(dd,implicant,gen->gen.primes.ub);
|
|
if (prime == NULL) {
|
|
Cudd_RecursiveDeref(dd,implicant);
|
|
gen->status = CUDD_GEN_EMPTY;
|
|
return(0);
|
|
}
|
|
cuddRef(prime);
|
|
Cudd_RecursiveDeref(dd,implicant);
|
|
tmp = Cudd_bddAnd(dd,gen->node,Cudd_Not(prime));
|
|
if (tmp == NULL) {
|
|
Cudd_RecursiveDeref(dd,prime);
|
|
gen->status = CUDD_GEN_EMPTY;
|
|
return(0);
|
|
}
|
|
cuddRef(tmp);
|
|
Cudd_RecursiveDeref(dd,gen->node);
|
|
gen->node = tmp;
|
|
result = Cudd_BddToCubeArray(dd,prime,gen->gen.primes.cube);
|
|
if (result == 0) {
|
|
Cudd_RecursiveDeref(dd,prime);
|
|
gen->status = CUDD_GEN_EMPTY;
|
|
return(0);
|
|
}
|
|
Cudd_RecursiveDeref(dd,prime);
|
|
gen->status = CUDD_GEN_NONEMPTY;
|
|
}
|
|
if (gen->status == CUDD_GEN_EMPTY) return(0);
|
|
*cube = gen->gen.primes.cube;
|
|
return(1);
|
|
|
|
} /* end of Cudd_NextPrime */
|
|
|
|
|
|
/**
|
|
@brief Computes the cube of an array of %BDD variables.
|
|
|
|
@details If non-null, the phase argument indicates which literal of
|
|
each variable should appear in the cube. If phase\[i\] is nonzero,
|
|
then the positive literal is used. If phase is NULL, the cube is
|
|
positive unate.
|
|
|
|
@return a pointer to the result if successful; NULL otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_addComputeCube Cudd_IndicesToCube Cudd_CubeArrayToBdd
|
|
|
|
*/
|
|
DdNode *
|
|
Cudd_bddComputeCube(
|
|
DdManager * dd,
|
|
DdNode ** vars,
|
|
int * phase,
|
|
int n)
|
|
{
|
|
DdNode *cube;
|
|
DdNode *fn;
|
|
int i;
|
|
|
|
cube = DD_ONE(dd);
|
|
cuddRef(cube);
|
|
|
|
for (i = n - 1; i >= 0; i--) {
|
|
if (phase == NULL || phase[i] != 0) {
|
|
fn = Cudd_bddAnd(dd,vars[i],cube);
|
|
} else {
|
|
fn = Cudd_bddAnd(dd,Cudd_Not(vars[i]),cube);
|
|
}
|
|
if (fn == NULL) {
|
|
Cudd_RecursiveDeref(dd,cube);
|
|
return(NULL);
|
|
}
|
|
cuddRef(fn);
|
|
Cudd_RecursiveDeref(dd,cube);
|
|
cube = fn;
|
|
}
|
|
cuddDeref(cube);
|
|
|
|
return(cube);
|
|
|
|
} /* end of Cudd_bddComputeCube */
|
|
|
|
|
|
/**
|
|
@brief Computes the cube of an array of %ADD variables.
|
|
|
|
@details If non-null, the phase argument indicates which literal of
|
|
each variable should appear in the cube. If phase\[i\] is nonzero,
|
|
then the positive literal is used. If phase is NULL, the cube is
|
|
positive unate.
|
|
|
|
@return a pointer to the result if successful; NULL otherwise.
|
|
|
|
@sideeffect none
|
|
|
|
@see Cudd_bddComputeCube
|
|
|
|
*/
|
|
DdNode *
|
|
Cudd_addComputeCube(
|
|
DdManager * dd,
|
|
DdNode ** vars,
|
|
int * phase,
|
|
int n)
|
|
{
|
|
DdNode *cube, *azero;
|
|
DdNode *fn;
|
|
int i;
|
|
|
|
cube = DD_ONE(dd);
|
|
cuddRef(cube);
|
|
azero = DD_ZERO(dd);
|
|
|
|
for (i = n - 1; i >= 0; i--) {
|
|
if (phase == NULL || phase[i] != 0) {
|
|
fn = Cudd_addIte(dd,vars[i],cube,azero);
|
|
} else {
|
|
fn = Cudd_addIte(dd,vars[i],azero,cube);
|
|
}
|
|
if (fn == NULL) {
|
|
Cudd_RecursiveDeref(dd,cube);
|
|
return(NULL);
|
|
}
|
|
cuddRef(fn);
|
|
Cudd_RecursiveDeref(dd,cube);
|
|
cube = fn;
|
|
}
|
|
cuddDeref(cube);
|
|
|
|
return(cube);
|
|
|
|
} /* end of Cudd_addComputeCube */
|
|
|
|
|
|
/**
|
|
@brief Builds the %BDD of a cube from a positional array.
|
|
|
|
@details The array must have one integer entry for each %BDD
|
|
variable. If the i-th entry is 1, the variable of index i appears
|
|
in true form in the cube; If the i-th entry is 0, the variable of
|
|
index i appears complemented in the cube; otherwise the variable
|
|
does not appear in the cube.
|
|
|
|
@return a pointer to the %BDD for the cube if successful; NULL
|
|
otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_bddComputeCube Cudd_IndicesToCube Cudd_BddToCubeArray
|
|
|
|
*/
|
|
DdNode *
|
|
Cudd_CubeArrayToBdd(
|
|
DdManager *dd,
|
|
int *array)
|
|
{
|
|
DdNode *cube, *var, *tmp;
|
|
int i;
|
|
int size = Cudd_ReadSize(dd);
|
|
|
|
cube = DD_ONE(dd);
|
|
cuddRef(cube);
|
|
for (i = size - 1; i >= 0; i--) {
|
|
if ((array[i] & ~1) == 0) {
|
|
var = Cudd_bddIthVar(dd,i);
|
|
tmp = Cudd_bddAnd(dd,cube,Cudd_NotCond(var,array[i]==0));
|
|
if (tmp == NULL) {
|
|
Cudd_RecursiveDeref(dd,cube);
|
|
return(NULL);
|
|
}
|
|
cuddRef(tmp);
|
|
Cudd_RecursiveDeref(dd,cube);
|
|
cube = tmp;
|
|
}
|
|
}
|
|
cuddDeref(cube);
|
|
return(cube);
|
|
|
|
} /* end of Cudd_CubeArrayToBdd */
|
|
|
|
|
|
/**
|
|
@brief Builds a positional array from the %BDD of a cube.
|
|
|
|
@details Array must have one entry for each %BDD variable. The
|
|
positional array has 1 in i-th position if the variable of index i
|
|
appears in true form in the cube; it has 0 in i-th position if the
|
|
variable of index i appears in complemented form in the cube;
|
|
finally, it has 2 in i-th position if the variable of index i does
|
|
not appear in the cube.
|
|
|
|
@return 1 if successful (the %BDD is indeed a cube); 0 otherwise.
|
|
|
|
@sideeffect The result is in the array passed by reference.
|
|
|
|
@see Cudd_CubeArrayToBdd
|
|
|
|
*/
|
|
int
|
|
Cudd_BddToCubeArray(
|
|
DdManager *dd,
|
|
DdNode *cube,
|
|
int *array)
|
|
{
|
|
DdNode *scan, *t, *e;
|
|
int i;
|
|
int size = Cudd_ReadSize(dd);
|
|
DdNode *lzero = Cudd_Not(DD_ONE(dd));
|
|
|
|
for (i = size-1; i >= 0; i--) {
|
|
array[i] = 2;
|
|
}
|
|
scan = cube;
|
|
while (!Cudd_IsConstantInt(scan)) {
|
|
unsigned int index = Cudd_Regular(scan)->index;
|
|
cuddGetBranches(scan,&t,&e);
|
|
if (t == lzero) {
|
|
array[index] = 0;
|
|
scan = e;
|
|
} else if (e == lzero) {
|
|
array[index] = 1;
|
|
scan = t;
|
|
} else {
|
|
return(0); /* cube is not a cube */
|
|
}
|
|
}
|
|
if (scan == lzero) {
|
|
return(0);
|
|
} else {
|
|
return(1);
|
|
}
|
|
|
|
} /* end of Cudd_BddToCubeArray */
|
|
|
|
|
|
/**
|
|
@brief Finds the first node of a decision diagram.
|
|
|
|
@details Defines an iterator on the nodes of a decision diagram and
|
|
finds its first node. The nodes are enumerated in a reverse
|
|
topological order, so that a node is always preceded in the
|
|
enumeration by its descendants.
|
|
|
|
@return a generator that contains the information necessary to
|
|
continue the enumeration if successful; NULL otherwise.
|
|
|
|
@sideeffect The first node is returned as a side effect.
|
|
|
|
@see Cudd_ForeachNode Cudd_NextNode Cudd_GenFree Cudd_IsGenEmpty
|
|
Cudd_FirstCube
|
|
|
|
*/
|
|
DdGen *
|
|
Cudd_FirstNode(
|
|
DdManager * dd,
|
|
DdNode * f,
|
|
DdNode ** node)
|
|
{
|
|
DdGen *gen;
|
|
int size;
|
|
|
|
/* Sanity Check. */
|
|
if (dd == NULL || f == NULL) return(NULL);
|
|
|
|
/* Allocate generator an initialize it. */
|
|
gen = ALLOC(DdGen,1);
|
|
if (gen == NULL) {
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
return(NULL);
|
|
}
|
|
|
|
gen->manager = dd;
|
|
gen->type = CUDD_GEN_NODES;
|
|
gen->status = CUDD_GEN_EMPTY;
|
|
gen->stack.sp = 0;
|
|
gen->node = NULL;
|
|
|
|
/* Collect all the nodes on the generator stack for later perusal. */
|
|
gen->stack.stack = cuddNodeArray(Cudd_Regular(f), &size);
|
|
if (gen->stack.stack == NULL) {
|
|
FREE(gen);
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
return(NULL);
|
|
}
|
|
gen->gen.nodes.size = size;
|
|
|
|
/* Find the first node. */
|
|
if (gen->stack.sp < gen->gen.nodes.size) {
|
|
gen->status = CUDD_GEN_NONEMPTY;
|
|
gen->node = gen->stack.stack[gen->stack.sp];
|
|
*node = gen->node;
|
|
}
|
|
|
|
return(gen);
|
|
|
|
} /* end of Cudd_FirstNode */
|
|
|
|
|
|
/**
|
|
@brief Finds the next node of a decision diagram.
|
|
|
|
@return 0 if the enumeration is completed; 1 otherwise.
|
|
|
|
@sideeffect The next node is returned as a side effect.
|
|
|
|
@see Cudd_ForeachNode Cudd_FirstNode Cudd_GenFree Cudd_IsGenEmpty
|
|
Cudd_NextCube
|
|
|
|
*/
|
|
int
|
|
Cudd_NextNode(
|
|
DdGen * gen,
|
|
DdNode ** node)
|
|
{
|
|
/* Find the next node. */
|
|
gen->stack.sp++;
|
|
if (gen->stack.sp < gen->gen.nodes.size) {
|
|
gen->node = gen->stack.stack[gen->stack.sp];
|
|
*node = gen->node;
|
|
return(1);
|
|
} else {
|
|
gen->status = CUDD_GEN_EMPTY;
|
|
return(0);
|
|
}
|
|
|
|
} /* end of Cudd_NextNode */
|
|
|
|
|
|
/**
|
|
@brief Frees a CUDD generator.
|
|
|
|
@return always 0.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_ForeachCube Cudd_ForeachNode Cudd_FirstCube Cudd_NextCube
|
|
Cudd_FirstNode Cudd_NextNode Cudd_IsGenEmpty
|
|
|
|
*/
|
|
int
|
|
Cudd_GenFree(
|
|
DdGen * gen)
|
|
{
|
|
if (gen == NULL) return(0);
|
|
switch (gen->type) {
|
|
case CUDD_GEN_CUBES:
|
|
case CUDD_GEN_ZDD_PATHS:
|
|
FREE(gen->gen.cubes.cube);
|
|
FREE(gen->stack.stack);
|
|
break;
|
|
case CUDD_GEN_PRIMES:
|
|
FREE(gen->gen.primes.cube);
|
|
Cudd_RecursiveDeref(gen->manager,gen->node);
|
|
break;
|
|
case CUDD_GEN_NODES:
|
|
FREE(gen->stack.stack);
|
|
break;
|
|
default:
|
|
return(0);
|
|
}
|
|
FREE(gen);
|
|
return(0);
|
|
|
|
} /* end of Cudd_GenFree */
|
|
|
|
|
|
/**
|
|
@brief Queries the status of a generator.
|
|
|
|
@return 1 if the generator is empty or NULL; 0 otherswise.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_ForeachCube Cudd_ForeachNode Cudd_FirstCube Cudd_NextCube
|
|
Cudd_FirstNode Cudd_NextNode Cudd_GenFree
|
|
|
|
*/
|
|
int
|
|
Cudd_IsGenEmpty(
|
|
DdGen * gen)
|
|
{
|
|
if (gen == NULL) return(1);
|
|
return(gen->status == CUDD_GEN_EMPTY);
|
|
|
|
} /* end of Cudd_IsGenEmpty */
|
|
|
|
|
|
/**
|
|
@brief Builds a cube of %BDD variables from an array of indices.
|
|
|
|
@return a pointer to the result if successful; NULL otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_bddComputeCube Cudd_CubeArrayToBdd
|
|
|
|
*/
|
|
DdNode *
|
|
Cudd_IndicesToCube(
|
|
DdManager * dd,
|
|
int * array,
|
|
int n)
|
|
{
|
|
DdNode *cube, *tmp;
|
|
int i;
|
|
|
|
cube = DD_ONE(dd);
|
|
cuddRef(cube);
|
|
for (i = n - 1; i >= 0; i--) {
|
|
tmp = Cudd_bddAnd(dd,Cudd_bddIthVar(dd,array[i]),cube);
|
|
if (tmp == NULL) {
|
|
Cudd_RecursiveDeref(dd,cube);
|
|
return(NULL);
|
|
}
|
|
cuddRef(tmp);
|
|
Cudd_RecursiveDeref(dd,cube);
|
|
cube = tmp;
|
|
}
|
|
|
|
cuddDeref(cube);
|
|
return(cube);
|
|
|
|
} /* end of Cudd_IndicesToCube */
|
|
|
|
|
|
/**
|
|
@brief Prints the package version number.
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
void
|
|
Cudd_PrintVersion(
|
|
FILE * fp)
|
|
{
|
|
(void) fprintf(fp, "%s\n", CUDD_VERSION);
|
|
|
|
} /* end of Cudd_PrintVersion */
|
|
|
|
|
|
/**
|
|
@brief Computes the average distance between adjacent nodes in the manager.
|
|
|
|
@details Adjacent nodes are node pairs such that the second node
|
|
is the then child, else child, or next node in the collision list.
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
double
|
|
Cudd_AverageDistance(
|
|
DdManager * dd)
|
|
{
|
|
double tetotal, nexttotal;
|
|
double tesubtotal, nextsubtotal;
|
|
double temeasured, nextmeasured;
|
|
int i, j;
|
|
int slots, nvars;
|
|
ptrdiff_t diff;
|
|
DdNode *scan;
|
|
DdNodePtr *nodelist;
|
|
DdNode *sentinel = &(dd->sentinel);
|
|
|
|
nvars = dd->size;
|
|
if (nvars == 0) return(0.0);
|
|
|
|
/* Initialize totals. */
|
|
tetotal = 0.0;
|
|
nexttotal = 0.0;
|
|
temeasured = 0.0;
|
|
nextmeasured = 0.0;
|
|
|
|
/* Scan the variable subtables. */
|
|
for (i = 0; i < nvars; i++) {
|
|
nodelist = dd->subtables[i].nodelist;
|
|
tesubtotal = 0.0;
|
|
nextsubtotal = 0.0;
|
|
slots = dd->subtables[i].slots;
|
|
for (j = 0; j < slots; j++) {
|
|
scan = nodelist[j];
|
|
while (scan != sentinel) {
|
|
diff = (ptrint) scan - (ptrint) cuddT(scan);
|
|
tesubtotal += (double) ddAbs(diff);
|
|
diff = (ptrint) scan - (ptrint) Cudd_Regular(cuddE(scan));
|
|
tesubtotal += (double) ddAbs(diff);
|
|
temeasured += 2.0;
|
|
if (scan->next != sentinel) {
|
|
diff = (ptrint) scan - (ptrint) scan->next;
|
|
nextsubtotal += (double) ddAbs(diff);
|
|
nextmeasured += 1.0;
|
|
}
|
|
scan = scan->next;
|
|
}
|
|
}
|
|
tetotal += tesubtotal;
|
|
nexttotal += nextsubtotal;
|
|
}
|
|
|
|
/* Scan the constant table. */
|
|
nodelist = dd->constants.nodelist;
|
|
nextsubtotal = 0.0;
|
|
slots = dd->constants.slots;
|
|
for (j = 0; j < slots; j++) {
|
|
scan = nodelist[j];
|
|
while (scan != NULL) {
|
|
if (scan->next != NULL) {
|
|
diff = (ptrint) scan - (ptrint) scan->next;
|
|
nextsubtotal += (double) ddAbs(diff);
|
|
nextmeasured += 1.0;
|
|
}
|
|
scan = scan->next;
|
|
}
|
|
}
|
|
nexttotal += nextsubtotal;
|
|
|
|
return((tetotal + nexttotal) / (temeasured + nextmeasured));
|
|
|
|
} /* end of Cudd_AverageDistance */
|
|
|
|
|
|
/**
|
|
@brief Portable random number generator.
|
|
|
|
@details Based on ran2 from "Numerical Recipes in C." It is a long
|
|
period (> 2 * 10^18) random number generator of L'Ecuyer with
|
|
Bays-Durham shuffle. The random generator can be explicitly
|
|
initialized by calling Cudd_Srandom. If no explicit initialization
|
|
is performed, then the seed 1 is assumed.
|
|
|
|
@return a long integer uniformly distributed between 0 and
|
|
2147483561 (inclusive of the endpoint values).
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_Srandom
|
|
|
|
*/
|
|
int32_t
|
|
Cudd_Random(DdManager *dd)
|
|
{
|
|
int i; /* index in the shuffle table */
|
|
int32_t w; /* work variable */
|
|
|
|
/* dd->cuddRand == 0 if the geneartor has not been initialized yet. */
|
|
if (dd->cuddRand == 0) Cudd_Srandom(dd,1);
|
|
|
|
/* Compute cuddRand = (cuddRand * LEQA1) % MODULUS1 avoiding
|
|
** overflows by Schrage's method.
|
|
*/
|
|
w = dd->cuddRand / LEQQ1;
|
|
dd->cuddRand = LEQA1 * (dd->cuddRand - w * LEQQ1) - w * LEQR1;
|
|
dd->cuddRand += (dd->cuddRand < 0) * MODULUS1;
|
|
|
|
/* Compute dd->cuddRand2 = (dd->cuddRand2 * LEQA2) % MODULUS2 avoiding
|
|
** overflows by Schrage's method.
|
|
*/
|
|
w = dd->cuddRand2 / LEQQ2;
|
|
dd->cuddRand2 = LEQA2 * (dd->cuddRand2 - w * LEQQ2) - w * LEQR2;
|
|
dd->cuddRand2 += (dd->cuddRand2 < 0) * MODULUS2;
|
|
|
|
/* dd->cuddRand is shuffled with the Bays-Durham algorithm.
|
|
** dd->shuffleSelect and cuddRand2 are combined to generate the output.
|
|
*/
|
|
|
|
/* Pick one element from the shuffle table; "i" will be in the range
|
|
** from 0 to STAB_SIZE-1.
|
|
*/
|
|
i = (int) (dd->shuffleSelect / STAB_DIV);
|
|
/* Mix the element of the shuffle table with the current iterate of
|
|
** the second sub-generator, and replace the chosen element of the
|
|
** shuffle table with the current iterate of the first sub-generator.
|
|
*/
|
|
dd->shuffleSelect = dd->shuffleTable[i] - dd->cuddRand2;
|
|
dd->shuffleTable[i] = dd->cuddRand;
|
|
dd->shuffleSelect += (dd->shuffleSelect < 1) * (MODULUS1 - 1);
|
|
/* Since dd->shuffleSelect != 0, and we want to be able to return 0,
|
|
** here we subtract 1 before returning.
|
|
*/
|
|
return(dd->shuffleSelect - 1);
|
|
|
|
} /* end of Cudd_Random */
|
|
|
|
|
|
/**
|
|
@brief Initializer for the portable random number generator.
|
|
|
|
@details Based on ran2 in "Numerical Recipes in C." The input is the
|
|
seed for the generator. If it is negative, its absolute value is
|
|
taken as seed. If it is 0, then 1 is taken as seed. The initialized
|
|
sets up the two recurrences used to generate a long-period stream,
|
|
and sets up the shuffle table.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_Random
|
|
|
|
*/
|
|
void
|
|
Cudd_Srandom(
|
|
DdManager *dd,
|
|
int32_t seed)
|
|
{
|
|
int32_t i;
|
|
|
|
if (seed < 0) dd->cuddRand = -seed;
|
|
else if (seed == 0) dd->cuddRand = 1;
|
|
else dd->cuddRand = seed;
|
|
dd->cuddRand2 = dd->cuddRand;
|
|
/* Load the shuffle table (after 11 warm-ups). */
|
|
for (i = 0; i < STAB_SIZE + 11; i++) {
|
|
int32_t w;
|
|
w = dd->cuddRand / LEQQ1;
|
|
dd->cuddRand = LEQA1 * (dd->cuddRand - w * LEQQ1) - w * LEQR1;
|
|
dd->cuddRand += (dd->cuddRand < 0) * MODULUS1;
|
|
dd->shuffleTable[i % STAB_SIZE] = dd->cuddRand;
|
|
}
|
|
dd->shuffleSelect = dd->shuffleTable[1 % STAB_SIZE];
|
|
|
|
} /* end of Cudd_Srandom */
|
|
|
|
|
|
/**
|
|
@brief Computes the density of a %BDD or %ADD.
|
|
|
|
@details The density is the ratio of the number of minterms to the
|
|
number of nodes. If 0 is passed as number of variables, the number
|
|
of variables existing in the manager is used.
|
|
|
|
@return the density if successful; (double) CUDD_OUT_OF_MEM
|
|
otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_CountMinterm Cudd_DagSize
|
|
|
|
*/
|
|
double
|
|
Cudd_Density(
|
|
DdManager * dd /**< manager */,
|
|
DdNode * f /**< function whose density is sought */,
|
|
int nvars /**< size of the support of f */)
|
|
{
|
|
double minterms;
|
|
int nodes;
|
|
double density;
|
|
|
|
if (nvars == 0) nvars = dd->size;
|
|
minterms = Cudd_CountMinterm(dd,f,nvars);
|
|
if (minterms == (double) CUDD_OUT_OF_MEM) return(minterms);
|
|
nodes = Cudd_DagSize(f);
|
|
density = minterms / (double) nodes;
|
|
return(density);
|
|
|
|
} /* end of Cudd_Density */
|
|
|
|
|
|
/**
|
|
@brief Warns that a memory allocation failed.
|
|
|
|
@details This function can be used as replacement of MMout_of_memory
|
|
to prevent the safe_mem functions of the util package from exiting
|
|
when malloc returns NULL. One possible use is in case of
|
|
discretionary allocations; for instance, an allocation of memory to
|
|
enlarge the computed table.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_OutOfMemSilent Cudd_RegisterOutOfMemoryCallback
|
|
|
|
*/
|
|
void
|
|
Cudd_OutOfMem(
|
|
size_t size /**< size of the allocation that failed */)
|
|
{
|
|
(void) fflush(stdout);
|
|
(void) fprintf(stderr, "\nCUDD: unable to allocate %" PRIszt " bytes\n",
|
|
size);
|
|
|
|
} /* end of Cudd_OutOfMem */
|
|
|
|
|
|
/**
|
|
@brief Doesn not warn that a memory allocation failed.
|
|
|
|
@details This function can be used as replacement of MMout_of_memory
|
|
to prevent the safe_mem functions of the util package from exiting
|
|
when malloc returns NULL. One possible use is in case of
|
|
discretionary allocations; for instance, an allocation of memory to
|
|
enlarge the computed table.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_OutOfMem Cudd_RegisterOutOfMemoryCallback
|
|
|
|
*/
|
|
void
|
|
Cudd_OutOfMemSilent(
|
|
size_t size /**< size of the allocation that failed */)
|
|
{
|
|
(void) size; /* suppress warning */
|
|
|
|
} /* end of Cudd_OutOfMem */
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Definition of internal functions */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
|
/**
|
|
@brief Prints a %DD to the standard output. One line per node is
|
|
printed.
|
|
|
|
@return 1 if successful; 0 otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_PrintDebug
|
|
|
|
*/
|
|
int
|
|
cuddP(
|
|
DdManager * dd,
|
|
DdNode * f)
|
|
{
|
|
int retval;
|
|
st_table *table = st_init_table(st_ptrcmp,st_ptrhash);
|
|
|
|
if (table == NULL) return(0);
|
|
|
|
retval = dp2(dd,f,table);
|
|
st_free_table(table);
|
|
(void) fputc('\n',dd->out);
|
|
return(retval);
|
|
|
|
} /* end of cuddP */
|
|
|
|
|
|
/**
|
|
@brief Frees the memory used to store the minterm counts recorded
|
|
in the visited table.
|
|
|
|
@return ST_CONTINUE.
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
enum st_retval
|
|
cuddStCountfree(
|
|
void * key,
|
|
void * value,
|
|
void * arg)
|
|
{
|
|
double *d = (double *)value;
|
|
|
|
(void) key; /* avoid warning */
|
|
(void) arg; /* avoid warning */
|
|
FREE(d);
|
|
return(ST_CONTINUE);
|
|
|
|
} /* end of cuddStCountfree */
|
|
|
|
|
|
/**
|
|
@brief Recursively collects all the nodes of a %DD in a symbol
|
|
table.
|
|
|
|
@details Traverses the %DD f and collects all its nodes in a
|
|
symbol table. f is assumed to be a regular pointer and
|
|
cuddCollectNodes guarantees this assumption in the recursive calls.
|
|
|
|
@return 1 in case of success; 0 otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
int
|
|
cuddCollectNodes(
|
|
DdNode * f,
|
|
st_table * visited)
|
|
{
|
|
DdNode *T, *E;
|
|
int retval;
|
|
|
|
#ifdef DD_DEBUG
|
|
assert(!Cudd_IsComplement(f));
|
|
#endif
|
|
|
|
/* If already visited, nothing to do. */
|
|
if (st_is_member(visited, f) == 1)
|
|
return(1);
|
|
|
|
/* Check for abnormal condition that should never happen. */
|
|
if (f == NULL)
|
|
return(0);
|
|
|
|
/* Mark node as visited. */
|
|
if (st_add_direct(visited, f, NULL) == ST_OUT_OF_MEM)
|
|
return(0);
|
|
|
|
/* Check terminal case. */
|
|
if (cuddIsConstant(f))
|
|
return(1);
|
|
|
|
/* Recursive calls. */
|
|
T = cuddT(f);
|
|
retval = cuddCollectNodes(T,visited);
|
|
if (retval != 1) return(retval);
|
|
E = Cudd_Regular(cuddE(f));
|
|
retval = cuddCollectNodes(E,visited);
|
|
return(retval);
|
|
|
|
} /* end of cuddCollectNodes */
|
|
|
|
|
|
/**
|
|
@brief Recursively collects all the nodes of a %DD in an array.
|
|
|
|
@details Traverses the %DD f and collects all its nodes in an array.
|
|
The caller should free the array returned by cuddNodeArray. The
|
|
nodes are collected in reverse topological order, so that a node is
|
|
always preceded in the array by all its descendants.
|
|
|
|
@return a pointer to the array of nodes in case of success; NULL
|
|
otherwise.
|
|
|
|
@sideeffect The number of nodes is returned as a side effect.
|
|
|
|
@see Cudd_FirstNode
|
|
|
|
*/
|
|
DdNodePtr *
|
|
cuddNodeArray(
|
|
DdNode *f,
|
|
int *n)
|
|
{
|
|
DdNodePtr *table;
|
|
int size, retval;
|
|
|
|
size = ddDagInt(Cudd_Regular(f));
|
|
table = ALLOC(DdNodePtr, size);
|
|
if (table == NULL) {
|
|
ddClearFlag(Cudd_Regular(f));
|
|
return(NULL);
|
|
}
|
|
|
|
retval = cuddNodeArrayRecur(f, table, 0);
|
|
assert(retval == size);
|
|
|
|
*n = size;
|
|
return(table);
|
|
|
|
} /* cuddNodeArray */
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Definition of static functions */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
|
/**
|
|
@brief Performs the recursive step of cuddP.
|
|
|
|
@return 1 in case of success; 0 otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
static int
|
|
dp2(
|
|
DdManager *dd,
|
|
DdNode * f,
|
|
st_table * t)
|
|
{
|
|
DdNode *g, *n, *N;
|
|
int T,E;
|
|
|
|
if (f == NULL) {
|
|
return(0);
|
|
}
|
|
g = Cudd_Regular(f);
|
|
if (cuddIsConstant(g)) {
|
|
(void) fprintf(dd->out,"ID = %c0x%" PRIxPTR "\tvalue = %-9g\n", bang(f),
|
|
(ptruint) g / (ptruint) sizeof(DdNode),cuddV(g));
|
|
return(1);
|
|
}
|
|
if (st_is_member(t,g) == 1) {
|
|
return(1);
|
|
}
|
|
if (st_add_direct(t,g,NULL) == ST_OUT_OF_MEM)
|
|
return(0);
|
|
#ifdef DD_STATS
|
|
(void) fprintf(dd->out,"ID = %c0x%"PRIxPTR"\tindex = %d\tr = %d\t", bang(f),
|
|
(ptruint) g / (ptruint) sizeof(DdNode), g->index, g->ref);
|
|
#else
|
|
(void) fprintf(dd->out,"ID = %c0x%" PRIxPTR "\tindex = %u\t", bang(f),
|
|
(ptruint) g / (ptruint) sizeof(DdNode),g->index);
|
|
#endif
|
|
n = cuddT(g);
|
|
if (cuddIsConstant(n)) {
|
|
(void) fprintf(dd->out,"T = %-9g\t",cuddV(n));
|
|
T = 1;
|
|
} else {
|
|
(void) fprintf(dd->out,"T = 0x%" PRIxPTR "\t",
|
|
(ptruint) n / (ptruint) sizeof(DdNode));
|
|
T = 0;
|
|
}
|
|
|
|
n = cuddE(g);
|
|
N = Cudd_Regular(n);
|
|
if (cuddIsConstant(N)) {
|
|
(void) fprintf(dd->out,"E = %c%-9g\n",bang(n),cuddV(N));
|
|
E = 1;
|
|
} else {
|
|
(void) fprintf(dd->out,"E = %c0x%" PRIxPTR "\n",
|
|
bang(n), (ptruint) N/(ptruint) sizeof(DdNode));
|
|
E = 0;
|
|
}
|
|
if (E == 0) {
|
|
if (dp2(dd,N,t) == 0)
|
|
return(0);
|
|
}
|
|
if (T == 0) {
|
|
if (dp2(dd,cuddT(g),t) == 0)
|
|
return(0);
|
|
}
|
|
return(1);
|
|
|
|
} /* end of dp2 */
|
|
|
|
|
|
/**
|
|
@brief Performs the recursive step of Cudd_PrintMinterm.
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
static void
|
|
ddPrintMintermAux(
|
|
DdManager * dd /**< manager */,
|
|
DdNode * node /**< current node */,
|
|
int * list /**< current recursion path */)
|
|
{
|
|
DdNode *N,*Nv,*Nnv;
|
|
int i,v;
|
|
unsigned int index;
|
|
|
|
N = Cudd_Regular(node);
|
|
|
|
if (cuddIsConstant(N)) {
|
|
/* Terminal case: Print one cube based on the current recursion
|
|
** path, unless we have reached the background value (ADDs) or
|
|
** the logical zero (BDDs).
|
|
*/
|
|
if (node != dd->background && node != Cudd_Not(dd->one)) {
|
|
for (i = 0; i < dd->size; i++) {
|
|
v = list[i];
|
|
if (v == 0) (void) fprintf(dd->out,"0");
|
|
else if (v == 1) (void) fprintf(dd->out,"1");
|
|
else (void) fprintf(dd->out,"-");
|
|
}
|
|
(void) fprintf(dd->out," % g\n", cuddV(node));
|
|
}
|
|
} else {
|
|
Nv = cuddT(N);
|
|
Nnv = cuddE(N);
|
|
if (Cudd_IsComplement(node)) {
|
|
Nv = Cudd_Not(Nv);
|
|
Nnv = Cudd_Not(Nnv);
|
|
}
|
|
index = N->index;
|
|
list[index] = 0;
|
|
ddPrintMintermAux(dd,Nnv,list);
|
|
list[index] = 1;
|
|
ddPrintMintermAux(dd,Nv,list);
|
|
list[index] = 2;
|
|
}
|
|
return;
|
|
|
|
} /* end of ddPrintMintermAux */
|
|
|
|
|
|
/**
|
|
@brief Performs the recursive step of Cudd_DagSize.
|
|
|
|
@return the number of nodes in the graph rooted at n.
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
static int
|
|
ddDagInt(
|
|
DdNode * n)
|
|
{
|
|
int tval, eval;
|
|
|
|
if (Cudd_IsComplement(n->next)) {
|
|
return(0);
|
|
}
|
|
n->next = Cudd_Not(n->next);
|
|
if (cuddIsConstant(n)) {
|
|
return(1);
|
|
}
|
|
tval = ddDagInt(cuddT(n));
|
|
eval = ddDagInt(Cudd_Regular(cuddE(n)));
|
|
return(1 + tval + eval);
|
|
|
|
} /* end of ddDagInt */
|
|
|
|
|
|
/**
|
|
@brief Performs the recursive step of cuddNodeArray.
|
|
|
|
@details node is supposed to be regular; the invariant is maintained
|
|
by this procedure.
|
|
|
|
@return an the number of nodes in the %DD.
|
|
|
|
@sideeffect Clears the least significant bit of the next field that
|
|
was used as visited flag by cuddNodeArrayRecur when counting the
|
|
nodes.
|
|
|
|
*/
|
|
static int
|
|
cuddNodeArrayRecur(
|
|
DdNode *f,
|
|
DdNodePtr *table,
|
|
int index)
|
|
{
|
|
int tindex, eindex;
|
|
|
|
if (!Cudd_IsComplement(f->next)) {
|
|
return(index);
|
|
}
|
|
/* Clear visited flag. */
|
|
f->next = Cudd_Regular(f->next);
|
|
if (cuddIsConstant(f)) {
|
|
table[index] = f;
|
|
return(index + 1);
|
|
}
|
|
tindex = cuddNodeArrayRecur(cuddT(f), table, index);
|
|
eindex = cuddNodeArrayRecur(Cudd_Regular(cuddE(f)), table, tindex);
|
|
table[eindex] = f;
|
|
return(eindex + 1);
|
|
|
|
} /* end of cuddNodeArrayRecur */
|
|
|
|
|
|
/**
|
|
@brief Performs the recursive step of Cudd_CofactorEstimate.
|
|
|
|
@details Uses the least significant bit of the next field as visited
|
|
flag. node is supposed to be regular; the invariant is maintained by
|
|
this procedure.
|
|
|
|
@return an estimate of the number of nodes in the %DD of a cofactor
|
|
of node.
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
static int
|
|
cuddEstimateCofactor(
|
|
DdManager *dd,
|
|
st_table *table,
|
|
DdNode * node,
|
|
int i,
|
|
int phase,
|
|
DdNode ** ptr)
|
|
{
|
|
int tval, eval, val;
|
|
DdNode *ptrT, *ptrE;
|
|
|
|
#ifdef DD_DEBUG
|
|
assert(!Cudd_IsComplement(node));
|
|
#endif
|
|
if (Cudd_IsComplement(node->next)) {
|
|
if (!st_lookup(table, node, (void **)ptr)) {
|
|
if (st_add_direct(table, node, node) == ST_OUT_OF_MEM)
|
|
return(CUDD_OUT_OF_MEM);
|
|
*ptr = node;
|
|
}
|
|
return(0);
|
|
}
|
|
node->next = Cudd_Not(node->next);
|
|
if (cuddIsConstant(node)) {
|
|
*ptr = node;
|
|
if (st_add_direct(table, node, node) == ST_OUT_OF_MEM)
|
|
return(CUDD_OUT_OF_MEM);
|
|
return(1);
|
|
}
|
|
if ((int) node->index == i) {
|
|
if (phase == 1) {
|
|
*ptr = cuddT(node);
|
|
val = ddDagInt(cuddT(node));
|
|
} else {
|
|
*ptr = cuddE(node);
|
|
val = ddDagInt(Cudd_Regular(cuddE(node)));
|
|
}
|
|
if (node->ref > 1) {
|
|
if (st_add_direct(table,node,*ptr) == ST_OUT_OF_MEM)
|
|
return(CUDD_OUT_OF_MEM);
|
|
}
|
|
return(val);
|
|
}
|
|
if (dd->perm[node->index] > dd->perm[i]) {
|
|
*ptr = node;
|
|
if (node->ref > 1) {
|
|
if (st_add_direct(table,node,node) == ST_OUT_OF_MEM)
|
|
return(CUDD_OUT_OF_MEM);
|
|
}
|
|
val = 1 + ddDagInt(cuddT(node)) + ddDagInt(Cudd_Regular(cuddE(node)));
|
|
return(val);
|
|
}
|
|
tval = cuddEstimateCofactor(dd,table,cuddT(node),i,phase,&ptrT);
|
|
if (tval == CUDD_OUT_OF_MEM) return(CUDD_OUT_OF_MEM);
|
|
eval = cuddEstimateCofactor(dd,table,Cudd_Regular(cuddE(node)),i,
|
|
phase,&ptrE);
|
|
if (eval == CUDD_OUT_OF_MEM) return(CUDD_OUT_OF_MEM);
|
|
ptrE = Cudd_NotCond(ptrE,Cudd_IsComplement(cuddE(node)));
|
|
if (ptrT == ptrE) { /* recombination */
|
|
*ptr = ptrT;
|
|
val = tval;
|
|
if (node->ref > 1) {
|
|
if (st_add_direct(table,node,*ptr) == ST_OUT_OF_MEM)
|
|
return(CUDD_OUT_OF_MEM);
|
|
}
|
|
} else {
|
|
int complement = Cudd_IsComplement(ptrT);
|
|
if (complement) {
|
|
ptrT = Cudd_Regular(ptrT);
|
|
ptrE = Cudd_Complement(ptrE);
|
|
}
|
|
if ((ptrT != cuddT(node) || ptrE != cuddE(node)) &&
|
|
(*ptr = cuddUniqueLookup(dd,node->index,ptrT,ptrE)) != NULL) {
|
|
if (Cudd_IsComplement((*ptr)->next)) {
|
|
val = 0;
|
|
} else {
|
|
val = 1 + tval + eval;
|
|
}
|
|
if (node->ref > 1) {
|
|
if (st_add_direct(table,node,*ptr) == ST_OUT_OF_MEM)
|
|
return(CUDD_OUT_OF_MEM);
|
|
}
|
|
if (complement) {
|
|
*ptr = Cudd_Complement(*ptr);
|
|
}
|
|
} else {
|
|
*ptr = node;
|
|
val = 1 + tval + eval;
|
|
}
|
|
}
|
|
return(val);
|
|
|
|
} /* end of cuddEstimateCofactor */
|
|
|
|
|
|
/**
|
|
@brief Checks the unique table for the existence of an internal node.
|
|
|
|
@return a pointer to the node if it is in the table; NULL otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see cuddUniqueInter
|
|
|
|
*/
|
|
static DdNode *
|
|
cuddUniqueLookup(
|
|
DdManager * unique,
|
|
int index,
|
|
DdNode * T,
|
|
DdNode * E)
|
|
{
|
|
unsigned int posn;
|
|
int level;
|
|
DdNodePtr *nodelist;
|
|
DdNode *looking;
|
|
DdSubtable *subtable;
|
|
|
|
if (index >= unique->size) {
|
|
return(NULL);
|
|
}
|
|
|
|
level = unique->perm[index];
|
|
subtable = &(unique->subtables[level]);
|
|
|
|
#ifdef DD_DEBUG
|
|
assert(level < cuddI(unique,T->index));
|
|
assert(level < cuddI(unique,Cudd_Regular(E)->index));
|
|
#endif
|
|
|
|
posn = ddHash(T, E, subtable->shift);
|
|
nodelist = subtable->nodelist;
|
|
looking = nodelist[posn];
|
|
|
|
while (T < cuddT(looking)) {
|
|
looking = Cudd_Regular(looking->next);
|
|
}
|
|
while (T == cuddT(looking) && E < cuddE(looking)) {
|
|
looking = Cudd_Regular(looking->next);
|
|
}
|
|
if (cuddT(looking) == T && cuddE(looking) == E) {
|
|
return(looking);
|
|
}
|
|
|
|
return(NULL);
|
|
|
|
} /* end of cuddUniqueLookup */
|
|
|
|
|
|
/**
|
|
@brief Performs the recursive step of Cudd_CofactorEstimateSimple.
|
|
|
|
@details Uses the least significant bit of the next field as visited
|
|
flag. node is supposed to be regular; the invariant is maintained by
|
|
this procedure.
|
|
|
|
@return an estimate of the number of nodes in the %DD of the positive
|
|
cofactor of node.
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
static int
|
|
cuddEstimateCofactorSimple(
|
|
DdNode * node,
|
|
int i)
|
|
{
|
|
int tval, eval;
|
|
|
|
if (Cudd_IsComplement(node->next)) {
|
|
return(0);
|
|
}
|
|
node->next = Cudd_Not(node->next);
|
|
if (cuddIsConstant(node)) {
|
|
return(1);
|
|
}
|
|
tval = cuddEstimateCofactorSimple(cuddT(node),i);
|
|
if ((int) node->index == i) return(tval);
|
|
eval = cuddEstimateCofactorSimple(Cudd_Regular(cuddE(node)),i);
|
|
return(1 + tval + eval);
|
|
|
|
} /* end of cuddEstimateCofactorSimple */
|
|
|
|
|
|
/**
|
|
@brief Performs the recursive step of Cudd_CountMinterm.
|
|
|
|
@details It is based on the following identity. Let |f| be the
|
|
number of minterms of f. Then:
|
|
|
|
|f| = (|f0|+|f1|)/2
|
|
|
|
where f0 and f1 are the two cofactors of f. Does not use the
|
|
identity |f'| = max - |f|, to minimize loss of accuracy due to
|
|
roundoff.
|
|
|
|
@return the number of minterms of the function rooted at node.
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
static double
|
|
ddCountMintermAux(
|
|
DdManager * dd,
|
|
DdNode * node,
|
|
double max,
|
|
DdHashTable * table)
|
|
{
|
|
DdNode *N, *Nt, *Ne;
|
|
double min, minT, minE;
|
|
DdNode *res;
|
|
|
|
N = Cudd_Regular(node);
|
|
|
|
if (cuddIsConstant(N)) {
|
|
if (node == dd->background || node == Cudd_Not(dd->one)) {
|
|
return(0.0);
|
|
} else {
|
|
return(max);
|
|
}
|
|
}
|
|
if (N->ref != 1 && (res = cuddHashTableLookup1(table,node)) != NULL) {
|
|
min = cuddV(res);
|
|
if (res->ref == 0) {
|
|
table->manager->dead++;
|
|
table->manager->constants.dead++;
|
|
}
|
|
return(min);
|
|
}
|
|
|
|
Nt = cuddT(N); Ne = cuddE(N);
|
|
if (Cudd_IsComplement(node)) {
|
|
Nt = Cudd_Not(Nt); Ne = Cudd_Not(Ne);
|
|
}
|
|
|
|
minT = ddCountMintermAux(dd,Nt,max,table);
|
|
if (minT == (double)CUDD_OUT_OF_MEM) return((double)CUDD_OUT_OF_MEM);
|
|
minT *= 0.5;
|
|
minE = ddCountMintermAux(dd,Ne,max,table);
|
|
if (minE == (double)CUDD_OUT_OF_MEM) return((double)CUDD_OUT_OF_MEM);
|
|
minE *= 0.5;
|
|
min = minT + minE;
|
|
|
|
if (N->ref != 1) {
|
|
ptrint fanout = (ptrint) N->ref;
|
|
cuddSatDec(fanout);
|
|
res = cuddUniqueConst(table->manager,min);
|
|
if (!res) {
|
|
return((double)CUDD_OUT_OF_MEM);
|
|
}
|
|
if (!cuddHashTableInsert1(table,node,res,fanout)) {
|
|
cuddRef(res); Cudd_RecursiveDeref(table->manager, res);
|
|
return((double)CUDD_OUT_OF_MEM);
|
|
}
|
|
}
|
|
|
|
return(min);
|
|
|
|
} /* end of ddCountMintermAux */
|
|
|
|
|
|
/**
|
|
@brief Performs the recursive step of Cudd_CountPath.
|
|
|
|
@details It is based on the following identity. Let |f| be the
|
|
number of paths of f. Then:
|
|
|
|
|f| = |f0|+|f1|
|
|
|
|
where f0 and f1 are the two cofactors of f. Uses the
|
|
identity |f'| = |f|, to improve the utilization of the (local) cache.
|
|
|
|
@return the number of paths of the function rooted at node.
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
static double
|
|
ddCountPathAux(
|
|
DdNode * node,
|
|
st_table * table)
|
|
{
|
|
|
|
DdNode *Nv, *Nnv;
|
|
double paths, *ppaths, paths1, paths2;
|
|
void *dummy;
|
|
|
|
|
|
if (cuddIsConstant(node)) {
|
|
return(1.0);
|
|
}
|
|
if (st_lookup(table, node, &dummy)) {
|
|
paths = *(double *) dummy;
|
|
return(paths);
|
|
}
|
|
|
|
Nv = cuddT(node); Nnv = cuddE(node);
|
|
|
|
paths1 = ddCountPathAux(Nv,table);
|
|
if (paths1 == (double)CUDD_OUT_OF_MEM) return((double)CUDD_OUT_OF_MEM);
|
|
paths2 = ddCountPathAux(Cudd_Regular(Nnv),table);
|
|
if (paths2 == (double)CUDD_OUT_OF_MEM) return((double)CUDD_OUT_OF_MEM);
|
|
paths = paths1 + paths2;
|
|
|
|
ppaths = ALLOC(double,1);
|
|
if (ppaths == NULL) {
|
|
return((double)CUDD_OUT_OF_MEM);
|
|
}
|
|
|
|
*ppaths = paths;
|
|
|
|
if (st_add_direct(table, node, ppaths) == ST_OUT_OF_MEM) {
|
|
FREE(ppaths);
|
|
return((double)CUDD_OUT_OF_MEM);
|
|
}
|
|
return(paths);
|
|
|
|
} /* end of ddCountPathAux */
|
|
|
|
|
|
/**
|
|
@brief Performs the recursive step of Cudd_EpdCountMinterm.
|
|
|
|
@details It is based on the following identity. Let |f| be the
|
|
number of minterms of f. Then:
|
|
|
|
|f| = (|f0|+|f1|)/2
|
|
|
|
where f0 and f1 are the two cofactors of f. Does not use the
|
|
identity |f'| = max - |f|, to minimize loss of accuracy due to
|
|
roundoff.
|
|
|
|
@return the number of minterms of the function rooted at node.
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
static int
|
|
ddEpdCountMintermAux(
|
|
DdManager const * dd,
|
|
DdNode * node,
|
|
EpDouble * max,
|
|
EpDouble * epd,
|
|
st_table * table)
|
|
{
|
|
DdNode *Nt, *Ne;
|
|
EpDouble *min, minT, minE;
|
|
EpDouble *res;
|
|
int status;
|
|
|
|
/* node is assumed to be regular */
|
|
if (cuddIsConstant(node)) {
|
|
if (node == dd->background) {
|
|
EpdMakeZero(epd, 0);
|
|
} else {
|
|
EpdCopy(max, epd);
|
|
}
|
|
return(0);
|
|
}
|
|
if (node->ref != 1 && st_lookup(table, node, (void **) &res)) {
|
|
EpdCopy(res, epd);
|
|
return(0);
|
|
}
|
|
|
|
Nt = cuddT(node); Ne = cuddE(node);
|
|
|
|
status = ddEpdCountMintermAux(dd,Nt,max,&minT,table);
|
|
if (status == CUDD_OUT_OF_MEM) return(CUDD_OUT_OF_MEM);
|
|
EpdMultiply(&minT, (double)0.5);
|
|
status = ddEpdCountMintermAux(dd,Cudd_Regular(Ne),max,&minE,table);
|
|
if (status == CUDD_OUT_OF_MEM) return(CUDD_OUT_OF_MEM);
|
|
if (Cudd_IsComplement(Ne)) {
|
|
EpdSubtract3(max, &minE, epd);
|
|
EpdCopy(epd, &minE);
|
|
}
|
|
EpdMultiply(&minE, (double)0.5);
|
|
EpdAdd3(&minT, &minE, epd);
|
|
|
|
if (node->ref > 1) {
|
|
min = EpdAlloc();
|
|
if (!min)
|
|
return(CUDD_OUT_OF_MEM);
|
|
EpdCopy(epd, min);
|
|
if (st_insert(table, node, min) == ST_OUT_OF_MEM) {
|
|
EpdFree(min);
|
|
return(CUDD_OUT_OF_MEM);
|
|
}
|
|
}
|
|
|
|
return(0);
|
|
|
|
} /* end of ddEpdCountMintermAux */
|
|
|
|
|
|
/**
|
|
@brief Performs the recursive step of Cudd_LdblCountMinterm.
|
|
|
|
@details It is based on the following identity. Let |f| be the
|
|
number of minterms of f. Then:
|
|
|
|
|f| = (|f0|+|f1|)/2
|
|
|
|
where f0 and f1 are the two cofactors of f. Does not use the
|
|
identity |f'| = max - |f|, to minimize loss of accuracy due to
|
|
roundoff.
|
|
|
|
@return the number of minterms of the function rooted at node.
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
static long double
|
|
ddLdblCountMintermAux(
|
|
DdManager const *manager,
|
|
DdNode *node,
|
|
long double max,
|
|
st_table *table)
|
|
{
|
|
DdNode *t, *e;
|
|
long double min, minT, minE;
|
|
long double *res;
|
|
if (cuddIsConstant(node)) {
|
|
if (node == manager->background) {
|
|
return 0.0L;
|
|
} else {
|
|
return max;
|
|
}
|
|
}
|
|
if (node->ref != 1 && st_lookup(table, node, (void **) &res)) {
|
|
return *res;
|
|
}
|
|
|
|
t = cuddT(node); e = cuddE(node);
|
|
|
|
minT = ddLdblCountMintermAux(manager, t, max, table);
|
|
if (minT == (long double) CUDD_OUT_OF_MEM)
|
|
return((long double) CUDD_OUT_OF_MEM);
|
|
minT *= 0.5L;
|
|
minE = ddLdblCountMintermAux(manager, Cudd_Regular(e), max, table);
|
|
if (minE == (long double) CUDD_OUT_OF_MEM)
|
|
return((long double) CUDD_OUT_OF_MEM);
|
|
if (Cudd_IsComplement(e)) {
|
|
minE = max - minE;
|
|
}
|
|
minE *= 0.5L;
|
|
min = minT + minE;
|
|
if (node->ref != 1) {
|
|
res = ALLOC(long double, 1);
|
|
if (res == NULL)
|
|
return((long double) CUDD_OUT_OF_MEM);
|
|
*res = min;
|
|
if (st_insert(table, node, res) == ST_OUT_OF_MEM) {
|
|
FREE(res);
|
|
return((long double) CUDD_OUT_OF_MEM);
|
|
}
|
|
}
|
|
return(min);
|
|
|
|
} /* end of ddLdblCountMintermAux */
|
|
|
|
|
|
/**
|
|
@brief Performs the recursive step of Cudd_CountPathsToNonZero.
|
|
|
|
@details It is based on the following identity. Let |f| be the
|
|
number of paths of f. Then:
|
|
|
|
|f| = |f0|+|f1|
|
|
|
|
where f0 and f1 are the two cofactors of f.
|
|
|
|
@return the number of paths of the function rooted at node.
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
static double
|
|
ddCountPathsToNonZero(
|
|
DdNode * N,
|
|
st_table * table)
|
|
{
|
|
|
|
DdNode *node, *Nt, *Ne;
|
|
double paths, *ppaths, paths1, paths2;
|
|
void *dummy;
|
|
|
|
node = Cudd_Regular(N);
|
|
if (cuddIsConstant(node)) {
|
|
return((double) !(Cudd_IsComplement(N) || cuddV(node)==DD_ZERO_VAL));
|
|
}
|
|
if (st_lookup(table, N, &dummy)) {
|
|
paths = *(double *) dummy;
|
|
return(paths);
|
|
}
|
|
|
|
Nt = cuddT(node); Ne = cuddE(node);
|
|
if (node != N) {
|
|
Nt = Cudd_Not(Nt); Ne = Cudd_Not(Ne);
|
|
}
|
|
|
|
paths1 = ddCountPathsToNonZero(Nt,table);
|
|
if (paths1 == (double)CUDD_OUT_OF_MEM) return((double)CUDD_OUT_OF_MEM);
|
|
paths2 = ddCountPathsToNonZero(Ne,table);
|
|
if (paths2 == (double)CUDD_OUT_OF_MEM) return((double)CUDD_OUT_OF_MEM);
|
|
paths = paths1 + paths2;
|
|
|
|
ppaths = ALLOC(double,1);
|
|
if (ppaths == NULL) {
|
|
return((double)CUDD_OUT_OF_MEM);
|
|
}
|
|
|
|
*ppaths = paths;
|
|
|
|
if (st_add_direct(table, N, ppaths) == ST_OUT_OF_MEM) {
|
|
FREE(ppaths);
|
|
return((double)CUDD_OUT_OF_MEM);
|
|
}
|
|
return(paths);
|
|
|
|
} /* end of ddCountPathsToNonZero */
|
|
|
|
|
|
/**
|
|
@brief Performs the recursive step of Cudd_Support.
|
|
|
|
@details Performs a DFS from f. The support is accumulated in supp
|
|
as a side effect. Uses the LSB of the then pointer as visited flag.
|
|
|
|
@sideeffect None
|
|
|
|
@see ddClearFlag
|
|
|
|
*/
|
|
static void
|
|
ddSupportStep(
|
|
DdNode * f,
|
|
int * support)
|
|
{
|
|
if (cuddIsConstant(f) || Cudd_IsComplement(f->next))
|
|
return;
|
|
|
|
support[f->index] = 1;
|
|
ddSupportStep(cuddT(f),support);
|
|
ddSupportStep(Cudd_Regular(cuddE(f)),support);
|
|
/* Mark as visited. */
|
|
f->next = Cudd_Complement(f->next);
|
|
|
|
} /* end of ddSupportStep */
|
|
|
|
|
|
/**
|
|
@brief Performs a DFS from f, clearing the LSB of the next pointers.
|
|
|
|
@sideeffect None
|
|
|
|
@see ddSupportStep ddFindSupport ddLeavesInt ddDagInt
|
|
|
|
*/
|
|
static void
|
|
ddClearFlag(
|
|
DdNode * f)
|
|
{
|
|
if (!Cudd_IsComplement(f->next)) {
|
|
return;
|
|
}
|
|
/* Clear visited flag. */
|
|
f->next = Cudd_Regular(f->next);
|
|
if (cuddIsConstant(f)) {
|
|
return;
|
|
}
|
|
ddClearFlag(cuddT(f));
|
|
ddClearFlag(Cudd_Regular(cuddE(f)));
|
|
return;
|
|
|
|
} /* end of ddClearFlag */
|
|
|
|
|
|
/**
|
|
@brief Performs the recursive step of Cudd_CountLeaves.
|
|
|
|
@return the number of leaves in the %DD rooted at n.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_CountLeaves
|
|
|
|
*/
|
|
static int
|
|
ddLeavesInt(
|
|
DdNode * n)
|
|
{
|
|
int tval, eval;
|
|
|
|
if (Cudd_IsComplement(n->next)) {
|
|
return(0);
|
|
}
|
|
n->next = Cudd_Not(n->next);
|
|
if (cuddIsConstant(n)) {
|
|
return(1);
|
|
}
|
|
tval = ddLeavesInt(cuddT(n));
|
|
eval = ddLeavesInt(Cudd_Regular(cuddE(n)));
|
|
return(tval + eval);
|
|
|
|
} /* end of ddLeavesInt */
|
|
|
|
|
|
/**
|
|
@brief Performs the recursive step of Cudd_bddPickArbitraryMinterms.
|
|
|
|
@return 1 if successful; 0 otherwise.
|
|
|
|
@sideeffect none
|
|
|
|
@see Cudd_bddPickArbitraryMinterms
|
|
|
|
*/
|
|
static int
|
|
ddPickArbitraryMinterms(
|
|
DdManager *dd,
|
|
DdNode *node,
|
|
int nvars,
|
|
int nminterms,
|
|
char **string)
|
|
{
|
|
DdNode *N, *T, *E;
|
|
DdNode *one, *bzero;
|
|
int i, t, result;
|
|
double min1, min2;
|
|
|
|
if (string == NULL || node == NULL) return(0);
|
|
|
|
/* The constant 0 function has no on-set cubes. */
|
|
one = DD_ONE(dd);
|
|
bzero = Cudd_Not(one);
|
|
if (nminterms == 0 || node == bzero) return(1);
|
|
if (node == one) {
|
|
return(1);
|
|
}
|
|
|
|
N = Cudd_Regular(node);
|
|
T = cuddT(N); E = cuddE(N);
|
|
if (Cudd_IsComplement(node)) {
|
|
T = Cudd_Not(T); E = Cudd_Not(E);
|
|
}
|
|
|
|
min1 = Cudd_CountMinterm(dd, T, nvars) / 2.0;
|
|
if (min1 == (double)CUDD_OUT_OF_MEM) return(0);
|
|
min2 = Cudd_CountMinterm(dd, E, nvars) / 2.0;
|
|
if (min2 == (double)CUDD_OUT_OF_MEM) return(0);
|
|
|
|
t = (int)((double)nminterms * min1 / (min1 + min2) + 0.5);
|
|
for (i = 0; i < t; i++)
|
|
string[i][N->index] = '1';
|
|
for (i = t; i < nminterms; i++)
|
|
string[i][N->index] = '0';
|
|
|
|
result = ddPickArbitraryMinterms(dd,T,nvars,t,&string[0]);
|
|
if (result == 0)
|
|
return(0);
|
|
result = ddPickArbitraryMinterms(dd,E,nvars,nminterms-t,&string[t]);
|
|
return(result);
|
|
|
|
} /* end of ddPickArbitraryMinterms */
|
|
|
|
|
|
/**
|
|
@brief Finds a representative cube of a %BDD.
|
|
|
|
@details Finds a representative cube of a %BDD with the weight of
|
|
each variable. From the top variable, if the weight is greater than or
|
|
equal to 0.0, choose THEN branch unless the child is the constant 0.
|
|
Otherwise, choose ELSE branch unless the child is the constant 0.
|
|
|
|
@sideeffect Cudd_SubsetWithMaskVars Cudd_bddPickOneCube
|
|
|
|
*/
|
|
static int
|
|
ddPickRepresentativeCube(
|
|
DdManager *dd,
|
|
DdNode *node,
|
|
double *weight,
|
|
char *string)
|
|
{
|
|
DdNode *N, *T, *E;
|
|
DdNode *one, *bzero;
|
|
|
|
if (string == NULL || node == NULL) return(0);
|
|
|
|
/* The constant 0 function has no on-set cubes. */
|
|
one = DD_ONE(dd);
|
|
bzero = Cudd_Not(one);
|
|
if (node == bzero) return(0);
|
|
|
|
if (node == DD_ONE(dd)) return(1);
|
|
|
|
for (;;) {
|
|
N = Cudd_Regular(node);
|
|
if (N == one)
|
|
break;
|
|
T = cuddT(N);
|
|
E = cuddE(N);
|
|
if (Cudd_IsComplement(node)) {
|
|
T = Cudd_Not(T);
|
|
E = Cudd_Not(E);
|
|
}
|
|
if (weight[N->index] >= 0.0) {
|
|
if (T == bzero) {
|
|
node = E;
|
|
string[N->index] = '0';
|
|
} else {
|
|
node = T;
|
|
string[N->index] = '1';
|
|
}
|
|
} else {
|
|
if (E == bzero) {
|
|
node = T;
|
|
string[N->index] = '1';
|
|
} else {
|
|
node = E;
|
|
string[N->index] = '0';
|
|
}
|
|
}
|
|
}
|
|
return(1);
|
|
|
|
} /* end of ddPickRepresentativeCube */
|
|
|
|
|
|
/**
|
|
@brief Frees the memory used to store the minterm counts recorded
|
|
in the visited table.
|
|
|
|
@return ST_CONTINUE.
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
static enum st_retval
|
|
ddEpdFree(
|
|
void * key,
|
|
void * value,
|
|
void * arg)
|
|
{
|
|
EpDouble *epd = (EpDouble *) value;
|
|
|
|
(void) key; /* avoid warning */
|
|
(void) arg; /* avoid warning */
|
|
EpdFree(epd);
|
|
return(ST_CONTINUE);
|
|
|
|
} /* end of ddEpdFree */
|
|
|
|
|
|
/**
|
|
@brief Recursively find the support of f.
|
|
|
|
@details This function uses the LSB of the next field of the nodes
|
|
of f as visited flag. It also uses the LSB of the next field of the
|
|
variables as flag to remember whether a certain index has already
|
|
been seen. Finally, it uses the manager stack to record all seen
|
|
indices.
|
|
|
|
@sideeffect The stack pointer SP is modified by side-effect. The next
|
|
fields are changed and need to be reset.
|
|
|
|
*/
|
|
static void
|
|
ddFindSupport(
|
|
DdManager *dd,
|
|
DdNode *f,
|
|
int *SP)
|
|
{
|
|
unsigned int index;
|
|
DdNode *var;
|
|
|
|
if (cuddIsConstant(f) || Cudd_IsComplement(f->next)) {
|
|
return;
|
|
}
|
|
|
|
index = f->index;
|
|
var = dd->vars[index];
|
|
/* It is possible that var is embedded in f. That causes no problem,
|
|
** though, because if we see it after encountering another node with
|
|
** the same index, nothing is supposed to happen.
|
|
*/
|
|
if (!Cudd_IsComplement(var->next)) {
|
|
var->next = Cudd_Complement(var->next);
|
|
dd->stack[*SP] = (DdNode *)(ptruint) index;
|
|
(*SP)++;
|
|
}
|
|
ddFindSupport(dd, cuddT(f), SP);
|
|
ddFindSupport(dd, Cudd_Regular(cuddE(f)), SP);
|
|
/* Mark as visited. */
|
|
f->next = Cudd_Complement(f->next);
|
|
|
|
} /* end of ddFindSupport */
|
|
|
|
|
|
/**
|
|
@brief Clears visited flags for variables.
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
static void
|
|
ddClearVars(
|
|
DdManager *dd,
|
|
int SP)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < SP; i++) {
|
|
int index = (int) (ptrint) dd->stack[i];
|
|
DdNode *var = dd->vars[index];
|
|
var->next = Cudd_Regular(var->next);
|
|
}
|
|
|
|
} /* end of ddClearVars */
|
|
|
|
|
|
/**
|
|
@brief Compares indices for qsort.
|
|
|
|
@details Subtracting these integers cannot produce overflow, because
|
|
they are non-negative.
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
static int
|
|
indexCompare(
|
|
const void *a,
|
|
const void *b)
|
|
{
|
|
int ia = *(int const *) a;
|
|
int ib = *(int const *) b;
|
|
return(ia - ib);
|
|
|
|
} /* end of indexCompare */
|
|
|
|
|
|
/**
|
|
@brief Frees the memory used to store the minterm counts recorded in the
|
|
visited table by Cudd_LdblCountMinterm.
|
|
|
|
@returns ST_CONTINUE.
|
|
|
|
@sideeffect None
|
|
*/
|
|
static enum st_retval
|
|
ddLdblFree(
|
|
void * key,
|
|
void * value,
|
|
void * arg)
|
|
{
|
|
long double * ld = (long double *) value;
|
|
|
|
(void) key; /* avoid warning */
|
|
(void) arg; /* avoid warning */
|
|
FREE(ld);
|
|
return(ST_CONTINUE);
|
|
|
|
} /* end of ddLdblFree */
|
|
|
|
|
|
#if HAVE_POWL != 1
|
|
/**
|
|
@brief Replacement for standard library powl.
|
|
|
|
@details Some systems' C libraries, notably Cygwin as of 2015,
|
|
lack an implementation of powl. This simple-minded replacement
|
|
works for integral powers. It is based on iterative squaring.
|
|
|
|
@return base raised to the exponent.
|
|
*/
|
|
static long double
|
|
powl(
|
|
long double base,
|
|
long double exponent)
|
|
{
|
|
long exp;
|
|
long double power = 1.0L, square = base;
|
|
if (exponent < 0.0L) {
|
|
exp = (long) -exponent;
|
|
} else {
|
|
exp = (long) exponent;
|
|
}
|
|
/* Compute base^exponent by iterative squaring.
|
|
* The loop invariant is power * square^exp = base^exponent.
|
|
*/
|
|
while (exp > 0) {
|
|
if (exp & 1L)
|
|
power *= square;
|
|
square *= square;
|
|
exp >>= 1L;
|
|
}
|
|
if (exponent < 0.0L) {
|
|
power = 1.0L / power;
|
|
}
|
|
return(power);
|
|
|
|
} /* end of powl */
|
|
#endif
|