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.
1127 lines
30 KiB
1127 lines
30 KiB
/**
|
|
@file
|
|
|
|
@ingroup cudd
|
|
|
|
@brief Utility functions for ZDDs.
|
|
|
|
@author Hyong-Kyoon Shin, In-Ho Moon, Fabio Somenzi
|
|
|
|
@copyright@parblock
|
|
Copyright (c) 1995-2015, Regents of the University of Colorado
|
|
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions
|
|
are met:
|
|
|
|
Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
|
|
Neither the name of the University of Colorado nor the names of its
|
|
contributors may be used to endorse or promote products derived from
|
|
this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
POSSIBILITY OF SUCH DAMAGE.
|
|
@endparblock
|
|
|
|
*/
|
|
|
|
#include "util.h"
|
|
#include "cuddInt.h"
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Constant declarations */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Stucture declarations */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Type declarations */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Variable declarations */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Macro declarations */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
/** \cond */
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Static function prototypes */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static int zp2 (DdManager *zdd, DdNode *f, st_table *t);
|
|
static void zdd_print_minterm_aux (DdManager *zdd, DdNode *node, int level, int *list);
|
|
static void zddPrintCoverAux (DdManager *zdd, DdNode *node, int level, int *list);
|
|
static void zddSupportStep(DdNode * f, int * support);
|
|
static void zddClearFlag(DdNode * f);
|
|
|
|
/** \endcond */
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Definition of exported functions */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
|
/**
|
|
@brief Prints a disjoint sum of product form for a %ZDD.
|
|
|
|
@return 1 if successful; 0 otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_zddPrintDebug Cudd_zddPrintCover
|
|
|
|
*/
|
|
int
|
|
Cudd_zddPrintMinterm(
|
|
DdManager * zdd,
|
|
DdNode * node)
|
|
{
|
|
int i, size;
|
|
int *list;
|
|
|
|
size = (int)zdd->sizeZ;
|
|
list = ALLOC(int, size);
|
|
if (list == NULL) {
|
|
zdd->errorCode = CUDD_MEMORY_OUT;
|
|
return(0);
|
|
}
|
|
for (i = 0; i < size; i++) list[i] = 3; /* bogus value should disappear */
|
|
zdd_print_minterm_aux(zdd, node, 0, list);
|
|
FREE(list);
|
|
return(1);
|
|
|
|
} /* end of Cudd_zddPrintMinterm */
|
|
|
|
|
|
/**
|
|
@brief Prints a sum of products from a %ZDD representing a cover.
|
|
|
|
@return 1 if successful; 0 otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_zddPrintMinterm
|
|
|
|
*/
|
|
int
|
|
Cudd_zddPrintCover(
|
|
DdManager * zdd,
|
|
DdNode * node)
|
|
{
|
|
int i, size;
|
|
int *list;
|
|
|
|
size = (int)zdd->sizeZ;
|
|
if (size % 2 != 0) return(0); /* number of variables should be even */
|
|
list = ALLOC(int, size);
|
|
if (list == NULL) {
|
|
zdd->errorCode = CUDD_MEMORY_OUT;
|
|
return(0);
|
|
}
|
|
for (i = 0; i < size; i++) list[i] = 3; /* bogus value should disappear */
|
|
zddPrintCoverAux(zdd, node, 0, list);
|
|
FREE(list);
|
|
return(1);
|
|
|
|
} /* end of Cudd_zddPrintCover */
|
|
|
|
|
|
/**
|
|
@brief Prints to the standard output a %ZDD and its statistics.
|
|
|
|
@details The statistics include the number of nodes and the number of
|
|
minterms. (The number of minterms is also the number of combinations
|
|
in the set.) 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 products
|
|
<li> pr = 3 : prints counts + list of nodes
|
|
<li> pr > 3 : prints counts + disjoint sum of products + list of nodes
|
|
</ul>
|
|
|
|
@return 1 if successful; 0 otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
int
|
|
Cudd_zddPrintDebug(
|
|
DdManager * zdd,
|
|
DdNode * f,
|
|
int n,
|
|
int pr)
|
|
{
|
|
DdNode *empty = DD_ZERO(zdd);
|
|
int nodes;
|
|
double minterms;
|
|
int retval = 1;
|
|
|
|
if (f == empty && pr > 0) {
|
|
(void) fprintf(zdd->out,": is the empty ZDD\n");
|
|
(void) fflush(zdd->out);
|
|
return(1);
|
|
}
|
|
|
|
if (pr > 0) {
|
|
nodes = Cudd_zddDagSize(f);
|
|
if (nodes == CUDD_OUT_OF_MEM) retval = 0;
|
|
minterms = Cudd_zddCountMinterm(zdd, f, n);
|
|
if (minterms == (double)CUDD_OUT_OF_MEM) retval = 0;
|
|
(void) fprintf(zdd->out,": %d nodes %g minterms\n",
|
|
nodes, minterms);
|
|
if (pr > 2)
|
|
if (!cuddZddP(zdd, f)) retval = 0;
|
|
if (pr == 2 || pr > 3) {
|
|
if (!Cudd_zddPrintMinterm(zdd, f)) retval = 0;
|
|
(void) fprintf(zdd->out,"\n");
|
|
}
|
|
(void) fflush(zdd->out);
|
|
}
|
|
return(retval);
|
|
|
|
} /* end of Cudd_zddPrintDebug */
|
|
|
|
|
|
|
|
/**
|
|
@brief Finds the first path of a %ZDD.
|
|
|
|
@details Defines an iterator on the paths of a %ZDD and finds its first
|
|
path.<p>
|
|
A path is represented as an array of literals, which are integers in
|
|
{0, 1, 2}; 0 represents an else arc out of a node, 1 represents a then arc
|
|
out of a node, and 2 stands for the absence of a node.
|
|
The size of the array equals the number of variables in the manager at
|
|
the time Cudd_zddFirstCube is called.<p>
|
|
The paths that end in the empty terminal are not enumerated.
|
|
|
|
@return a generator that contains the information necessary to
|
|
continue the enumeration if successful; NULL otherwise.
|
|
|
|
@sideeffect The first path is returned as a side effect.
|
|
|
|
@see Cudd_zddForeachPath Cudd_zddNextPath Cudd_GenFree
|
|
Cudd_IsGenEmpty
|
|
|
|
*/
|
|
DdGen *
|
|
Cudd_zddFirstPath(
|
|
DdManager * zdd,
|
|
DdNode * f,
|
|
int ** path)
|
|
{
|
|
DdGen *gen;
|
|
DdNode *top, *next, *prev;
|
|
int i;
|
|
int nvars;
|
|
|
|
/* Sanity Check. */
|
|
if (zdd == NULL || f == NULL) return(NULL);
|
|
|
|
/* Allocate generator an initialize it. */
|
|
gen = ALLOC(DdGen,1);
|
|
if (gen == NULL) {
|
|
zdd->errorCode = CUDD_MEMORY_OUT;
|
|
return(NULL);
|
|
}
|
|
|
|
gen->manager = zdd;
|
|
gen->type = CUDD_GEN_ZDD_PATHS;
|
|
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 = zdd->sizeZ;
|
|
gen->gen.cubes.cube = ALLOC(int,nvars);
|
|
if (gen->gen.cubes.cube == NULL) {
|
|
zdd->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) {
|
|
zdd->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 path of the ZDD. */
|
|
gen->stack.stack[gen->stack.sp] = f; gen->stack.sp++;
|
|
|
|
while (1) {
|
|
top = gen->stack.stack[gen->stack.sp-1];
|
|
if (!cuddIsConstant(Cudd_Regular(top))) {
|
|
/* Take the else branch first. */
|
|
gen->gen.cubes.cube[Cudd_Regular(top)->index] = 0;
|
|
next = cuddE(Cudd_Regular(top));
|
|
gen->stack.stack[gen->stack.sp] = Cudd_Not(next); gen->stack.sp++;
|
|
} else if (Cudd_Regular(top) == DD_ZERO(zdd)) {
|
|
/* 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 = Cudd_Regular(gen->stack.stack[gen->stack.sp-2]);
|
|
next = cuddT(prev);
|
|
if (next != top) { /* follow the then branch next */
|
|
gen->gen.cubes.cube[prev->index] = 1;
|
|
gen->stack.stack[gen->stack.sp-1] = next;
|
|
break;
|
|
}
|
|
/* Pop the stack and try again. */
|
|
gen->gen.cubes.cube[prev->index] = 2;
|
|
gen->stack.sp--;
|
|
top = gen->stack.stack[gen->stack.sp-1];
|
|
}
|
|
} else {
|
|
gen->status = CUDD_GEN_NONEMPTY;
|
|
gen->gen.cubes.value = cuddV(Cudd_Regular(top));
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
done:
|
|
*path = gen->gen.cubes.cube;
|
|
return(gen);
|
|
|
|
} /* end of Cudd_zddFirstPath */
|
|
|
|
|
|
/**
|
|
@brief Generates the next path of a %ZDD.
|
|
|
|
@details Generates the next path of a %ZDD onset, using generator
|
|
gen.
|
|
|
|
@return 0 if the enumeration is completed; 1 otherwise.
|
|
|
|
@sideeffect The path is returned as a side effect. The generator is
|
|
modified.
|
|
|
|
@see Cudd_zddForeachPath Cudd_zddFirstPath Cudd_GenFree
|
|
Cudd_IsGenEmpty
|
|
|
|
*/
|
|
int
|
|
Cudd_zddNextPath(
|
|
DdGen * gen,
|
|
int ** path)
|
|
{
|
|
DdNode *top, *next, *prev;
|
|
DdManager *zdd = 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 = Cudd_Regular(gen->stack.stack[gen->stack.sp-2]);
|
|
next = cuddT(prev);
|
|
if (next != top) { /* follow the then branch next */
|
|
gen->gen.cubes.cube[prev->index] = 1;
|
|
gen->stack.stack[gen->stack.sp-1] = next;
|
|
break;
|
|
}
|
|
/* Pop the stack and try again. */
|
|
gen->gen.cubes.cube[prev->index] = 2;
|
|
gen->stack.sp--;
|
|
}
|
|
|
|
while (1) {
|
|
top = gen->stack.stack[gen->stack.sp-1];
|
|
if (!cuddIsConstant(Cudd_Regular(top))) {
|
|
/* Take the else branch first. */
|
|
gen->gen.cubes.cube[Cudd_Regular(top)->index] = 0;
|
|
next = cuddE(Cudd_Regular(top));
|
|
gen->stack.stack[gen->stack.sp] = Cudd_Not(next); gen->stack.sp++;
|
|
} else if (Cudd_Regular(top) == DD_ZERO(zdd)) {
|
|
/* 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 = Cudd_Regular(gen->stack.stack[gen->stack.sp-2]);
|
|
next = cuddT(prev);
|
|
if (next != top) { /* follow the then branch next */
|
|
gen->gen.cubes.cube[prev->index] = 1;
|
|
gen->stack.stack[gen->stack.sp-1] = next;
|
|
break;
|
|
}
|
|
/* Pop the stack and try again. */
|
|
gen->gen.cubes.cube[prev->index] = 2;
|
|
gen->stack.sp--;
|
|
top = gen->stack.stack[gen->stack.sp-1];
|
|
}
|
|
} else {
|
|
gen->status = CUDD_GEN_NONEMPTY;
|
|
gen->gen.cubes.value = cuddV(Cudd_Regular(top));
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
done:
|
|
if (gen->status == CUDD_GEN_EMPTY) return(0);
|
|
*path = gen->gen.cubes.cube;
|
|
return(1);
|
|
|
|
} /* end of Cudd_zddNextPath */
|
|
|
|
|
|
/**
|
|
@brief Converts a path of a %ZDD representing a cover to a string.
|
|
|
|
@details The string represents an implicant of the cover. The path
|
|
is typically produced by Cudd_zddForeachPath. If the str input is
|
|
NULL, it allocates a new string. The string passed to this function
|
|
must have enough room for all variables and for the terminator.
|
|
|
|
@return a pointer to the string if successful; NULL otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_zddForeachPath
|
|
|
|
*/
|
|
char *
|
|
Cudd_zddCoverPathToString(
|
|
DdManager *zdd /**< %DD manager */,
|
|
int *path /**< path of %ZDD representing a cover */,
|
|
char *str /**< pointer to string to use if != NULL */
|
|
)
|
|
{
|
|
int nvars = zdd->sizeZ;
|
|
int i;
|
|
char *res;
|
|
|
|
if (nvars & 1) return(NULL);
|
|
nvars >>= 1;
|
|
if (str == NULL) {
|
|
res = ALLOC(char, nvars+1);
|
|
if (res == NULL) return(NULL);
|
|
} else {
|
|
res = str;
|
|
}
|
|
for (i = 0; i < nvars; i++) {
|
|
int v = (path[2*i] << 2) | path[2*i+1];
|
|
switch (v) {
|
|
case 0:
|
|
case 2:
|
|
case 8:
|
|
case 10:
|
|
res[i] = '-';
|
|
break;
|
|
case 1:
|
|
case 9:
|
|
res[i] = '0';
|
|
break;
|
|
case 4:
|
|
case 6:
|
|
res[i] = '1';
|
|
break;
|
|
default:
|
|
res[i] = '?';
|
|
}
|
|
}
|
|
res[nvars] = 0;
|
|
|
|
return(res);
|
|
|
|
} /* end of Cudd_zddCoverPathToString */
|
|
|
|
|
|
/**
|
|
@brief Finds the variables on which a %ZDD depends.
|
|
|
|
@return a %BDD consisting of the product of the variables if
|
|
successful; NULL otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_Support
|
|
|
|
*/
|
|
DdNode *
|
|
Cudd_zddSupport(
|
|
DdManager * dd /**< manager */,
|
|
DdNode * f /**< %ZDD whose support is sought */)
|
|
{
|
|
int *support;
|
|
DdNode *res, *tmp, *var;
|
|
int i,j;
|
|
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. */
|
|
zddSupportStep(Cudd_Regular(f),support);
|
|
zddClearFlag(Cudd_Regular(f));
|
|
|
|
/* Transform support from array to cube. */
|
|
do {
|
|
dd->reordered = 0;
|
|
res = DD_ONE(dd);
|
|
cuddRef(res);
|
|
for (j = size - 1; j >= 0; j--) { /* for each level bottom-up */
|
|
i = (j >= dd->size) ? j : dd->invperm[j];
|
|
if (support[i] == 1) {
|
|
/* The following call to cuddUniqueInter is guaranteed
|
|
** not to trigger reordering because the node we look up
|
|
** already exists. */
|
|
var = cuddUniqueInter(dd,i,dd->one,Cudd_Not(dd->one));
|
|
cuddRef(var);
|
|
tmp = cuddBddAndRecur(dd,res,var);
|
|
if (tmp == NULL) {
|
|
Cudd_RecursiveDeref(dd,res);
|
|
Cudd_RecursiveDeref(dd,var);
|
|
res = NULL;
|
|
break;
|
|
}
|
|
cuddRef(tmp);
|
|
Cudd_RecursiveDeref(dd,res);
|
|
Cudd_RecursiveDeref(dd,var);
|
|
res = tmp;
|
|
}
|
|
}
|
|
} while (dd->reordered == 1);
|
|
|
|
FREE(support);
|
|
if (res != NULL) cuddDeref(res);
|
|
if (dd->errorCode == CUDD_TIMEOUT_EXPIRED && dd->timeoutHandler) {
|
|
dd->timeoutHandler(dd, dd->tohArg);
|
|
}
|
|
return(res);
|
|
|
|
} /* end of Cudd_zddSupport */
|
|
|
|
|
|
/**
|
|
@brief Writes a dot file representing the argument ZDDs.
|
|
|
|
@details Writes a file representing the argument ZDDs in a format
|
|
suitable for the graph drawing program dot.
|
|
Cudd_zddDumpDot does not close the file: This is the caller
|
|
responsibility. Cudd_zddDumpDot uses a minimal unique subset of the
|
|
hexadecimal address of a node as name for it.
|
|
If the argument inames is non-null, it is assumed to hold the pointers
|
|
to the names of the inputs. Similarly for onames.
|
|
Cudd_zddDumpDot uses the following convention to draw arcs:
|
|
<ul>
|
|
<li> solid line: THEN arcs;
|
|
<li> dashed line: ELSE arcs.
|
|
</ul>
|
|
The dot options are chosen so that the drawing fits on a letter-size
|
|
sheet.
|
|
|
|
@return 1 in case of success; 0 otherwise (e.g., out-of-memory, file
|
|
system full).
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_DumpDot Cudd_zddPrintDebug
|
|
|
|
*/
|
|
int
|
|
Cudd_zddDumpDot(
|
|
DdManager * dd /**< manager */,
|
|
int n /**< number of output nodes to be dumped */,
|
|
DdNode ** f /**< array of output nodes to be dumped */,
|
|
char const * const * inames /**< array of input names (or NULL) */,
|
|
char const * const * onames /**< array of output names (or NULL) */,
|
|
FILE * fp /**< pointer to the dump file */)
|
|
{
|
|
DdNode *support = NULL;
|
|
DdNode *scan;
|
|
int *sorted = NULL;
|
|
int nvars = dd->sizeZ;
|
|
st_table *visited = NULL;
|
|
st_generator *gen;
|
|
int retval;
|
|
int i, j;
|
|
int slots;
|
|
DdNodePtr *nodelist;
|
|
ptruint refAddr, diff, mask = 0;
|
|
|
|
/* Build a bit array with the support of f. */
|
|
sorted = ALLOC(int,nvars);
|
|
if (sorted == NULL) {
|
|
dd->errorCode = CUDD_MEMORY_OUT;
|
|
goto failure;
|
|
}
|
|
for (i = 0; i < nvars; i++) sorted[i] = 0;
|
|
|
|
/* Take the union of the supports of each output function. */
|
|
for (i = 0; i < n; i++) {
|
|
support = Cudd_zddSupport(dd,f[i]);
|
|
if (support == NULL) goto failure;
|
|
cuddRef(support);
|
|
scan = support;
|
|
while (!cuddIsConstant(scan)) {
|
|
sorted[scan->index] = 1;
|
|
scan = cuddT(scan);
|
|
}
|
|
Cudd_RecursiveDeref(dd,support);
|
|
}
|
|
support = NULL; /* so that we do not try to free it in case of failure */
|
|
|
|
/* Initialize symbol table for visited nodes. */
|
|
visited = st_init_table(st_ptrcmp, st_ptrhash);
|
|
if (visited == NULL) goto failure;
|
|
|
|
/* Collect all the nodes of this DD in the symbol table. */
|
|
for (i = 0; i < n; i++) {
|
|
retval = cuddCollectNodes(f[i],visited);
|
|
if (retval == 0) goto failure;
|
|
}
|
|
|
|
/* Find how many most significant hex digits are identical
|
|
** in the addresses of all the nodes. Build a mask based
|
|
** on this knowledge, so that digits that carry no information
|
|
** will not be printed. This is done in two steps.
|
|
** 1. We scan the symbol table to find the bits that differ
|
|
** in at least 2 addresses.
|
|
** 2. We choose one of the possible masks. There are 8 possible
|
|
** masks for 32-bit integer, and 16 possible masks for 64-bit
|
|
** integers.
|
|
*/
|
|
|
|
/* Find the bits that are different. */
|
|
refAddr = (ptruint) f[0];
|
|
diff = 0;
|
|
gen = st_init_gen(visited);
|
|
while (st_gen(gen, (void **) &scan, NULL)) {
|
|
diff |= refAddr ^ (ptruint) scan;
|
|
}
|
|
st_free_gen(gen);
|
|
|
|
/* Choose the mask. */
|
|
for (i = 0; (unsigned) i < 8 * sizeof(ptruint); i += 4) {
|
|
mask = ((ptruint) 1 << i) - 1;
|
|
if (diff <= mask) break;
|
|
}
|
|
|
|
/* Write the header and the global attributes. */
|
|
retval = fprintf(fp,"digraph \"ZDD\" {\n");
|
|
if (retval == EOF) return(0);
|
|
retval = fprintf(fp,
|
|
"size = \"7.5,10\"\ncenter = true;\nedge [dir = none];\n");
|
|
if (retval == EOF) return(0);
|
|
|
|
/* Write the input name subgraph by scanning the support array. */
|
|
retval = fprintf(fp,"{ node [shape = plaintext];\n");
|
|
if (retval == EOF) goto failure;
|
|
retval = fprintf(fp," edge [style = invis];\n");
|
|
if (retval == EOF) goto failure;
|
|
/* We use a name ("CONST NODES") with an embedded blank, because
|
|
** it is unlikely to appear as an input name.
|
|
*/
|
|
retval = fprintf(fp," \"CONST NODES\" [style = invis];\n");
|
|
if (retval == EOF) goto failure;
|
|
for (i = 0; i < nvars; i++) {
|
|
if (sorted[dd->invpermZ[i]]) {
|
|
if (inames == NULL) {
|
|
retval = fprintf(fp,"\" %d \" -> ", dd->invpermZ[i]);
|
|
} else {
|
|
retval = fprintf(fp,"\" %s \" -> ", inames[dd->invpermZ[i]]);
|
|
}
|
|
if (retval == EOF) goto failure;
|
|
}
|
|
}
|
|
retval = fprintf(fp,"\"CONST NODES\"; \n}\n");
|
|
if (retval == EOF) goto failure;
|
|
|
|
/* Write the output node subgraph. */
|
|
retval = fprintf(fp,"{ rank = same; node [shape = box]; edge [style = invis];\n");
|
|
if (retval == EOF) goto failure;
|
|
for (i = 0; i < n; i++) {
|
|
if (onames == NULL) {
|
|
retval = fprintf(fp,"\"F%d\"", i);
|
|
} else {
|
|
retval = fprintf(fp,"\" %s \"", onames[i]);
|
|
}
|
|
if (retval == EOF) goto failure;
|
|
if (i == n - 1) {
|
|
retval = fprintf(fp,"; }\n");
|
|
} else {
|
|
retval = fprintf(fp," -> ");
|
|
}
|
|
if (retval == EOF) goto failure;
|
|
}
|
|
|
|
/* Write rank info: All nodes with the same index have the same rank. */
|
|
for (i = 0; i < nvars; i++) {
|
|
if (sorted[dd->invpermZ[i]]) {
|
|
retval = fprintf(fp,"{ rank = same; ");
|
|
if (retval == EOF) goto failure;
|
|
if (inames == NULL) {
|
|
retval = fprintf(fp,"\" %d \";\n", dd->invpermZ[i]);
|
|
} else {
|
|
retval = fprintf(fp,"\" %s \";\n", inames[dd->invpermZ[i]]);
|
|
}
|
|
if (retval == EOF) goto failure;
|
|
nodelist = dd->subtableZ[i].nodelist;
|
|
slots = dd->subtableZ[i].slots;
|
|
for (j = 0; j < slots; j++) {
|
|
scan = nodelist[j];
|
|
while (scan != NULL) {
|
|
if (st_is_member(visited,scan)) {
|
|
retval = fprintf(fp,"\"%#" PRIxPTR "\";\n",
|
|
((mask & (ptruint) scan) /
|
|
sizeof(DdNode)));
|
|
if (retval == EOF) goto failure;
|
|
}
|
|
scan = scan->next;
|
|
}
|
|
}
|
|
retval = fprintf(fp,"}\n");
|
|
if (retval == EOF) goto failure;
|
|
}
|
|
}
|
|
|
|
/* All constants have the same rank. */
|
|
retval = fprintf(fp,
|
|
"{ rank = same; \"CONST NODES\";\n{ node [shape = box]; ");
|
|
if (retval == EOF) goto failure;
|
|
nodelist = dd->constants.nodelist;
|
|
slots = dd->constants.slots;
|
|
for (j = 0; j < slots; j++) {
|
|
scan = nodelist[j];
|
|
while (scan != NULL) {
|
|
if (st_is_member(visited,scan)) {
|
|
retval = fprintf(fp,"\"%#" PRIxPTR "\";\n",
|
|
((mask & (ptruint) scan) / sizeof(DdNode)));
|
|
if (retval == EOF) goto failure;
|
|
}
|
|
scan = scan->next;
|
|
}
|
|
}
|
|
retval = fprintf(fp,"}\n}\n");
|
|
if (retval == EOF) goto failure;
|
|
|
|
/* Write edge info. */
|
|
/* Edges from the output nodes. */
|
|
for (i = 0; i < n; i++) {
|
|
if (onames == NULL) {
|
|
retval = fprintf(fp,"\"F%d\"", i);
|
|
} else {
|
|
retval = fprintf(fp,"\" %s \"", onames[i]);
|
|
}
|
|
if (retval == EOF) goto failure;
|
|
retval = fprintf(fp," -> \"%#" PRIxPTR "\" [style = solid];\n",
|
|
((mask & (ptruint) f[i]) / sizeof(DdNode)));
|
|
if (retval == EOF) goto failure;
|
|
}
|
|
|
|
/* Edges from internal nodes. */
|
|
for (i = 0; i < nvars; i++) {
|
|
if (sorted[dd->invpermZ[i]]) {
|
|
nodelist = dd->subtableZ[i].nodelist;
|
|
slots = dd->subtableZ[i].slots;
|
|
for (j = 0; j < slots; j++) {
|
|
scan = nodelist[j];
|
|
while (scan != NULL) {
|
|
if (st_is_member(visited,scan)) {
|
|
retval = fprintf(fp,
|
|
"\"%#" PRIxPTR "\" -> \"%#" PRIxPTR "\";\n",
|
|
((mask & (ptruint) scan) / sizeof(DdNode)),
|
|
((mask & (ptruint) cuddT(scan)) / sizeof(DdNode)));
|
|
if (retval == EOF) goto failure;
|
|
retval = fprintf(fp,
|
|
"\"%#" PRIxPTR "\" -> \"%#" PRIxPTR
|
|
"\" [style = dashed];\n",
|
|
((mask & (ptruint) scan) /
|
|
sizeof(DdNode)),
|
|
((mask & (ptruint) cuddE(scan)) /
|
|
sizeof(DdNode)));
|
|
if (retval == EOF) goto failure;
|
|
}
|
|
scan = scan->next;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Write constant labels. */
|
|
nodelist = dd->constants.nodelist;
|
|
slots = dd->constants.slots;
|
|
for (j = 0; j < slots; j++) {
|
|
scan = nodelist[j];
|
|
while (scan != NULL) {
|
|
if (st_is_member(visited,scan)) {
|
|
retval = fprintf(fp,"\"%#" PRIxPTR "\" [label = \"%g\"];\n",
|
|
((mask & (ptruint) scan) / sizeof(DdNode)),
|
|
cuddV(scan));
|
|
if (retval == EOF) goto failure;
|
|
}
|
|
scan = scan->next;
|
|
}
|
|
}
|
|
|
|
/* Write trailer and return. */
|
|
retval = fprintf(fp,"}\n");
|
|
if (retval == EOF) goto failure;
|
|
|
|
st_free_table(visited);
|
|
FREE(sorted);
|
|
return(1);
|
|
|
|
failure:
|
|
if (sorted != NULL) FREE(sorted);
|
|
if (visited != NULL) st_free_table(visited);
|
|
return(0);
|
|
|
|
} /* end of Cudd_zddDumpBlif */
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Definition of internal functions */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
|
/**
|
|
@brief Prints a %ZDD to the standard output. One line per node is
|
|
printed.
|
|
|
|
@return 1 if successful; 0 otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
@see Cudd_zddPrintDebug
|
|
|
|
*/
|
|
int
|
|
cuddZddP(
|
|
DdManager * zdd,
|
|
DdNode * f)
|
|
{
|
|
int retval;
|
|
st_table *table = st_init_table(st_ptrcmp, st_ptrhash);
|
|
|
|
if (table == NULL) return(0);
|
|
|
|
retval = zp2(zdd, f, table);
|
|
st_free_table(table);
|
|
(void) fputc('\n', zdd->out);
|
|
return(retval);
|
|
|
|
} /* end of cuddZddP */
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* Definition of static functions */
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
|
|
/**
|
|
@brief Performs the recursive step of cuddZddP.
|
|
|
|
@return 1 in case of success; 0 otherwise.
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
static int
|
|
zp2(
|
|
DdManager * zdd,
|
|
DdNode * f,
|
|
st_table * t)
|
|
{
|
|
DdNode *n;
|
|
int T, E;
|
|
DdNode *base = DD_ONE(zdd);
|
|
|
|
if (f == NULL)
|
|
return(0);
|
|
|
|
if (Cudd_IsConstantInt(f)) {
|
|
(void)fprintf(zdd->out, "ID = %d\n", (f == base));
|
|
return(1);
|
|
}
|
|
if (st_is_member(t, f) == 1)
|
|
return(1);
|
|
|
|
if (st_insert(t, f, NULL) == ST_OUT_OF_MEM)
|
|
return(0);
|
|
|
|
(void) fprintf(zdd->out, "ID = 0x%" PRIxPTR "\tindex = %u\tr = %u\t",
|
|
(ptruint)f / (ptruint) sizeof(DdNode), f->index, f->ref);
|
|
|
|
n = cuddT(f);
|
|
if (Cudd_IsConstantInt(n)) {
|
|
(void) fprintf(zdd->out, "T = %d\t\t", (n == base));
|
|
T = 1;
|
|
} else {
|
|
(void) fprintf(zdd->out, "T = 0x%" PRIxPTR "\t", (ptruint) n /
|
|
(ptruint) sizeof(DdNode));
|
|
T = 0;
|
|
}
|
|
|
|
n = cuddE(f);
|
|
if (Cudd_IsConstantInt(n)) {
|
|
(void) fprintf(zdd->out, "E = %d\n", (n == base));
|
|
E = 1;
|
|
} else {
|
|
(void) fprintf(zdd->out, "E = 0x%" PRIxPTR "\n", (ptruint) n /
|
|
(ptruint) sizeof(DdNode));
|
|
E = 0;
|
|
}
|
|
|
|
if (E == 0)
|
|
if (zp2(zdd, cuddE(f), t) == 0) return(0);
|
|
if (T == 0)
|
|
if (zp2(zdd, cuddT(f), t) == 0) return(0);
|
|
return(1);
|
|
|
|
} /* end of zp2 */
|
|
|
|
|
|
/**
|
|
@brief Performs the recursive step of Cudd_zddPrintMinterm.
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
static void
|
|
zdd_print_minterm_aux(
|
|
DdManager * zdd /* manager */,
|
|
DdNode * node /* current node */,
|
|
int level /* depth in the recursion */,
|
|
int * list /* current recursion path */)
|
|
{
|
|
DdNode *Nv, *Nnv;
|
|
int i, v;
|
|
DdNode *base = DD_ONE(zdd);
|
|
|
|
if (Cudd_IsConstantInt(node)) {
|
|
if (node == base) {
|
|
/* Check for missing variable. */
|
|
if (level != zdd->sizeZ) {
|
|
list[zdd->invpermZ[level]] = 0;
|
|
zdd_print_minterm_aux(zdd, node, level + 1, list);
|
|
return;
|
|
}
|
|
/* Terminal case: Print one cube based on the current recursion
|
|
** path.
|
|
*/
|
|
for (i = 0; i < zdd->sizeZ; i++) {
|
|
v = list[i];
|
|
if (v == 0)
|
|
(void) fprintf(zdd->out,"0");
|
|
else if (v == 1)
|
|
(void) fprintf(zdd->out,"1");
|
|
else if (v == 3)
|
|
(void) fprintf(zdd->out,"@"); /* should never happen */
|
|
else
|
|
(void) fprintf(zdd->out,"-");
|
|
}
|
|
(void) fprintf(zdd->out," 1\n");
|
|
}
|
|
} else {
|
|
/* Check for missing variable. */
|
|
if (level != cuddIZ(zdd,node->index)) {
|
|
list[zdd->invpermZ[level]] = 0;
|
|
zdd_print_minterm_aux(zdd, node, level + 1, list);
|
|
return;
|
|
}
|
|
|
|
Nnv = cuddE(node);
|
|
Nv = cuddT(node);
|
|
if (Nv == Nnv) {
|
|
list[node->index] = 2;
|
|
zdd_print_minterm_aux(zdd, Nnv, level + 1, list);
|
|
return;
|
|
}
|
|
|
|
list[node->index] = 1;
|
|
zdd_print_minterm_aux(zdd, Nv, level + 1, list);
|
|
list[node->index] = 0;
|
|
zdd_print_minterm_aux(zdd, Nnv, level + 1, list);
|
|
}
|
|
return;
|
|
|
|
} /* end of zdd_print_minterm_aux */
|
|
|
|
|
|
/**
|
|
@brief Performs the recursive step of Cudd_zddPrintCover.
|
|
|
|
@sideeffect None
|
|
|
|
*/
|
|
static void
|
|
zddPrintCoverAux(
|
|
DdManager * zdd /* manager */,
|
|
DdNode * node /* current node */,
|
|
int level /* depth in the recursion */,
|
|
int * list /* current recursion path */)
|
|
{
|
|
DdNode *Nv, *Nnv;
|
|
int i, v;
|
|
DdNode *base = DD_ONE(zdd);
|
|
|
|
if (Cudd_IsConstantInt(node)) {
|
|
if (node == base) {
|
|
/* Check for missing variable. */
|
|
if (level != zdd->sizeZ) {
|
|
list[zdd->invpermZ[level]] = 0;
|
|
zddPrintCoverAux(zdd, node, level + 1, list);
|
|
return;
|
|
}
|
|
/* Terminal case: Print one cube based on the current recursion
|
|
** path.
|
|
*/
|
|
for (i = 0; i < zdd->sizeZ; i += 2) {
|
|
v = list[i] * 4 + list[i+1];
|
|
if (v == 0)
|
|
(void) putc('-',zdd->out);
|
|
else if (v == 4)
|
|
(void) putc('1',zdd->out);
|
|
else if (v == 1)
|
|
(void) putc('0',zdd->out);
|
|
else
|
|
(void) putc('@',zdd->out); /* should never happen */
|
|
}
|
|
(void) fprintf(zdd->out," 1\n");
|
|
}
|
|
} else {
|
|
/* Check for missing variable. */
|
|
if (level != cuddIZ(zdd,node->index)) {
|
|
list[zdd->invpermZ[level]] = 0;
|
|
zddPrintCoverAux(zdd, node, level + 1, list);
|
|
return;
|
|
}
|
|
|
|
Nnv = cuddE(node);
|
|
Nv = cuddT(node);
|
|
if (Nv == Nnv) {
|
|
list[node->index] = 2;
|
|
zddPrintCoverAux(zdd, Nnv, level + 1, list);
|
|
return;
|
|
}
|
|
|
|
list[node->index] = 1;
|
|
zddPrintCoverAux(zdd, Nv, level + 1, list);
|
|
list[node->index] = 0;
|
|
zddPrintCoverAux(zdd, Nnv, level + 1, list);
|
|
}
|
|
return;
|
|
|
|
} /* end of zddPrintCoverAux */
|
|
|
|
|
|
/**
|
|
@brief Performs the recursive step of Cudd_zddSupport.
|
|
|
|
@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 zddClearFlag
|
|
|
|
*/
|
|
static void
|
|
zddSupportStep(
|
|
DdNode * f,
|
|
int * support)
|
|
{
|
|
if (cuddIsConstant(f) || Cudd_IsComplement(f->next)) {
|
|
return;
|
|
}
|
|
|
|
support[f->index] = 1;
|
|
zddSupportStep(cuddT(f),support);
|
|
zddSupportStep(Cudd_Regular(cuddE(f)),support);
|
|
/* Mark as visited. */
|
|
f->next = Cudd_Not(f->next);
|
|
return;
|
|
|
|
} /* end of zddSupportStep */
|
|
|
|
|
|
/**
|
|
@brief Performs a DFS from f, clearing the LSB of the next
|
|
pointers.
|
|
|
|
@sideeffect None
|
|
|
|
@see zddSupportStep
|
|
|
|
*/
|
|
static void
|
|
zddClearFlag(
|
|
DdNode * f)
|
|
{
|
|
if (!Cudd_IsComplement(f->next)) {
|
|
return;
|
|
}
|
|
/* Clear visited flag. */
|
|
f->next = Cudd_Regular(f->next);
|
|
if (cuddIsConstant(f)) {
|
|
return;
|
|
}
|
|
zddClearFlag(cuddT(f));
|
|
zddClearFlag(Cudd_Regular(cuddE(f)));
|
|
return;
|
|
|
|
} /* end of zddClearFlag */
|
|
|