summaryrefslogtreecommitdiffstats
path: root/src/bdd/cudd/cuddSymmetry.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/bdd/cudd/cuddSymmetry.c')
-rw-r--r--src/bdd/cudd/cuddSymmetry.c1668
1 files changed, 1668 insertions, 0 deletions
diff --git a/src/bdd/cudd/cuddSymmetry.c b/src/bdd/cudd/cuddSymmetry.c
new file mode 100644
index 00000000..e5488b17
--- /dev/null
+++ b/src/bdd/cudd/cuddSymmetry.c
@@ -0,0 +1,1668 @@
+/**CFile***********************************************************************
+
+ FileName [cuddSymmetry.c]
+
+ PackageName [cudd]
+
+ Synopsis [Functions for symmetry-based variable reordering.]
+
+ Description [External procedures included in this file:
+ <ul>
+ <li> Cudd_SymmProfile()
+ </ul>
+ Internal procedures included in this module:
+ <ul>
+ <li> cuddSymmCheck()
+ <li> cuddSymmSifting()
+ <li> cuddSymmSiftingConv()
+ </ul>
+ Static procedures included in this module:
+ <ul>
+ <li> ddSymmUniqueCompare()
+ <li> ddSymmSiftingAux()
+ <li> ddSymmSiftingConvAux()
+ <li> ddSymmSiftingUp()
+ <li> ddSymmSiftingDown()
+ <li> ddSymmGroupMove()
+ <li> ddSymmGroupMoveBackward()
+ <li> ddSymmSiftingBackward()
+ <li> ddSymmSummary()
+ </ul>]
+
+ Author [Shipra Panda, Fabio Somenzi]
+
+ Copyright [This file was created at the University of Colorado at
+ Boulder. The University of Colorado at Boulder makes no warranty
+ about the suitability of this software for any purpose. It is
+ presented on an AS IS basis.]
+
+******************************************************************************/
+
+#include "util_hack.h"
+#include "cuddInt.h"
+
+/*---------------------------------------------------------------------------*/
+/* Constant declarations */
+/*---------------------------------------------------------------------------*/
+
+#define MV_OOM (Move *)1
+
+/*---------------------------------------------------------------------------*/
+/* Stucture declarations */
+/*---------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------*/
+/* Type declarations */
+/*---------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------*/
+/* Variable declarations */
+/*---------------------------------------------------------------------------*/
+
+#ifndef lint
+static char rcsid[] DD_UNUSED = "$Id: cuddSymmetry.c,v 1.1.1.1 2003/02/24 22:23:53 wjiang Exp $";
+#endif
+
+static int *entry;
+
+extern int ddTotalNumberSwapping;
+#ifdef DD_STATS
+extern int ddTotalNISwaps;
+#endif
+
+/*---------------------------------------------------------------------------*/
+/* Macro declarations */
+/*---------------------------------------------------------------------------*/
+
+/**AutomaticStart*************************************************************/
+
+/*---------------------------------------------------------------------------*/
+/* Static function prototypes */
+/*---------------------------------------------------------------------------*/
+
+static int ddSymmUniqueCompare ARGS((int *ptrX, int *ptrY));
+static int ddSymmSiftingAux ARGS((DdManager *table, int x, int xLow, int xHigh));
+static int ddSymmSiftingConvAux ARGS((DdManager *table, int x, int xLow, int xHigh));
+static Move * ddSymmSiftingUp ARGS((DdManager *table, int y, int xLow));
+static Move * ddSymmSiftingDown ARGS((DdManager *table, int x, int xHigh));
+static int ddSymmGroupMove ARGS((DdManager *table, int x, int y, Move **moves));
+static int ddSymmGroupMoveBackward ARGS((DdManager *table, int x, int y));
+static int ddSymmSiftingBackward ARGS((DdManager *table, Move *moves, int size));
+static void ddSymmSummary ARGS((DdManager *table, int lower, int upper, int *symvars, int *symgroups));
+
+/**AutomaticEnd***************************************************************/
+
+
+/*---------------------------------------------------------------------------*/
+/* Definition of exported functions */
+/*---------------------------------------------------------------------------*/
+
+
+/**Function********************************************************************
+
+ Synopsis [Prints statistics on symmetric variables.]
+
+ Description []
+
+ SideEffects [None]
+
+******************************************************************************/
+void
+Cudd_SymmProfile(
+ DdManager * table,
+ int lower,
+ int upper)
+{
+ int i,x,gbot;
+ int TotalSymm = 0;
+ int TotalSymmGroups = 0;
+
+ for (i = lower; i <= upper; i++) {
+ if (table->subtables[i].next != (unsigned) i) {
+ x = i;
+ (void) fprintf(table->out,"Group:");
+ do {
+ (void) fprintf(table->out," %d",table->invperm[x]);
+ TotalSymm++;
+ gbot = x;
+ x = table->subtables[x].next;
+ } while (x != i);
+ TotalSymmGroups++;
+#ifdef DD_DEBUG
+ assert(table->subtables[gbot].next == (unsigned) i);
+#endif
+ i = gbot;
+ (void) fprintf(table->out,"\n");
+ }
+ }
+ (void) fprintf(table->out,"Total Symmetric = %d\n",TotalSymm);
+ (void) fprintf(table->out,"Total Groups = %d\n",TotalSymmGroups);
+
+} /* end of Cudd_SymmProfile */
+
+
+/*---------------------------------------------------------------------------*/
+/* Definition of internal functions */
+/*---------------------------------------------------------------------------*/
+
+
+/**Function********************************************************************
+
+ Synopsis [Checks for symmetry of x and y.]
+
+ Description [Checks for symmetry of x and y. Ignores projection
+ functions, unless they are isolated. Returns 1 in case of symmetry; 0
+ otherwise.]
+
+ SideEffects [None]
+
+******************************************************************************/
+int
+cuddSymmCheck(
+ DdManager * table,
+ int x,
+ int y)
+{
+ DdNode *f,*f0,*f1,*f01,*f00,*f11,*f10;
+ int comple; /* f0 is complemented */
+ int xsymmy; /* x and y may be positively symmetric */
+ int xsymmyp; /* x and y may be negatively symmetric */
+ int arccount; /* number of arcs from layer x to layer y */
+ int TotalRefCount; /* total reference count of layer y minus 1 */
+ int yindex;
+ int i;
+ DdNodePtr *list;
+ int slots;
+ DdNode *sentinel = &(table->sentinel);
+#ifdef DD_DEBUG
+ int xindex;
+#endif
+
+ /* Checks that x and y are not the projection functions.
+ ** For x it is sufficient to check whether there is only one
+ ** node; indeed, if there is one node, it is the projection function
+ ** and it cannot point to y. Hence, if y isn't just the projection
+ ** function, it has one arc coming from a layer different from x.
+ */
+ if (table->subtables[x].keys == 1) {
+ return(0);
+ }
+ yindex = table->invperm[y];
+ if (table->subtables[y].keys == 1) {
+ if (table->vars[yindex]->ref == 1)
+ return(0);
+ }
+
+ xsymmy = xsymmyp = 1;
+ arccount = 0;
+ slots = table->subtables[x].slots;
+ list = table->subtables[x].nodelist;
+ for (i = 0; i < slots; i++) {
+ f = list[i];
+ while (f != sentinel) {
+ /* Find f1, f0, f11, f10, f01, f00. */
+ f1 = cuddT(f);
+ f0 = Cudd_Regular(cuddE(f));
+ comple = Cudd_IsComplement(cuddE(f));
+ if ((int) f1->index == yindex) {
+ arccount++;
+ f11 = cuddT(f1); f10 = cuddE(f1);
+ } else {
+ if ((int) f0->index != yindex) {
+ /* If f is an isolated projection function it is
+ ** allowed to bypass layer y.
+ */
+ if (f1 != DD_ONE(table) || f0 != DD_ONE(table) || f->ref != 1)
+ return(0); /* f bypasses layer y */
+ }
+ f11 = f10 = f1;
+ }
+ if ((int) f0->index == yindex) {
+ arccount++;
+ f01 = cuddT(f0); f00 = cuddE(f0);
+ } else {
+ f01 = f00 = f0;
+ }
+ if (comple) {
+ f01 = Cudd_Not(f01);
+ f00 = Cudd_Not(f00);
+ }
+
+ if (f1 != DD_ONE(table) || f0 != DD_ONE(table) || f->ref != 1) {
+ xsymmy &= f01 == f10;
+ xsymmyp &= f11 == f00;
+ if ((xsymmy == 0) && (xsymmyp == 0))
+ return(0);
+ }
+
+ f = f->next;
+ } /* while */
+ } /* for */
+
+ /* Calculate the total reference counts of y */
+ TotalRefCount = -1; /* -1 for projection function */
+ slots = table->subtables[y].slots;
+ list = table->subtables[y].nodelist;
+ for (i = 0; i < slots; i++) {
+ f = list[i];
+ while (f != sentinel) {
+ TotalRefCount += f->ref;
+ f = f->next;
+ }
+ }
+
+#if defined(DD_DEBUG) && defined(DD_VERBOSE)
+ if (arccount == TotalRefCount) {
+ xindex = table->invperm[x];
+ (void) fprintf(table->out,
+ "Found symmetry! x =%d\ty = %d\tPos(%d,%d)\n",
+ xindex,yindex,x,y);
+ }
+#endif
+
+ return(arccount == TotalRefCount);
+
+} /* end of cuddSymmCheck */
+
+
+/**Function********************************************************************
+
+ Synopsis [Symmetric sifting algorithm.]
+
+ Description [Symmetric sifting algorithm.
+ Assumes that no dead nodes are present.
+ <ol>
+ <li> Order all the variables according to the number of entries in
+ each unique subtable.
+ <li> Sift the variable up and down, remembering each time the total
+ size of the DD heap and grouping variables that are symmetric.
+ <li> Select the best permutation.
+ <li> Repeat 3 and 4 for all variables.
+ </ol>
+ Returns 1 plus the number of symmetric variables if successful; 0
+ otherwise.]
+
+ SideEffects [None]
+
+ SeeAlso [cuddSymmSiftingConv]
+
+******************************************************************************/
+int
+cuddSymmSifting(
+ DdManager * table,
+ int lower,
+ int upper)
+{
+ int i;
+ int *var;
+ int size;
+ int x;
+ int result;
+ int symvars;
+ int symgroups;
+#ifdef DD_STATS
+ int previousSize;
+#endif
+
+ size = table->size;
+
+ /* Find order in which to sift variables. */
+ var = NULL;
+ entry = ALLOC(int,size);
+ if (entry == NULL) {
+ table->errorCode = CUDD_MEMORY_OUT;
+ goto ddSymmSiftingOutOfMem;
+ }
+ var = ALLOC(int,size);
+ if (var == NULL) {
+ table->errorCode = CUDD_MEMORY_OUT;
+ goto ddSymmSiftingOutOfMem;
+ }
+
+ for (i = 0; i < size; i++) {
+ x = table->perm[i];
+ entry[i] = table->subtables[x].keys;
+ var[i] = i;
+ }
+
+ qsort((void *)var,size,sizeof(int),(int (*)(const void *, const void *))ddSymmUniqueCompare);
+
+ /* Initialize the symmetry of each subtable to itself. */
+ for (i = lower; i <= upper; i++) {
+ table->subtables[i].next = i;
+ }
+
+ for (i = 0; i < ddMin(table->siftMaxVar,size); i++) {
+ if (ddTotalNumberSwapping >= table->siftMaxSwap)
+ break;
+ x = table->perm[var[i]];
+#ifdef DD_STATS
+ previousSize = table->keys - table->isolated;
+#endif
+ if (x < lower || x > upper) continue;
+ if (table->subtables[x].next == (unsigned) x) {
+ result = ddSymmSiftingAux(table,x,lower,upper);
+ if (!result) goto ddSymmSiftingOutOfMem;
+#ifdef DD_STATS
+ if (table->keys < (unsigned) previousSize + table->isolated) {
+ (void) fprintf(table->out,"-");
+ } else if (table->keys > (unsigned) previousSize +
+ table->isolated) {
+ (void) fprintf(table->out,"+"); /* should never happen */
+ } else {
+ (void) fprintf(table->out,"=");
+ }
+ fflush(table->out);
+#endif
+ }
+ }
+
+ FREE(var);
+ FREE(entry);
+
+ ddSymmSummary(table, lower, upper, &symvars, &symgroups);
+
+#ifdef DD_STATS
+ (void) fprintf(table->out, "\n#:S_SIFTING %8d: symmetric variables\n",
+ symvars);
+ (void) fprintf(table->out, "#:G_SIFTING %8d: symmetric groups",
+ symgroups);
+#endif
+
+ return(1+symvars);
+
+ddSymmSiftingOutOfMem:
+
+ if (entry != NULL) FREE(entry);
+ if (var != NULL) FREE(var);
+
+ return(0);
+
+} /* end of cuddSymmSifting */
+
+
+/**Function********************************************************************
+
+ Synopsis [Symmetric sifting to convergence algorithm.]
+
+ Description [Symmetric sifting to convergence algorithm.
+ Assumes that no dead nodes are present.
+ <ol>
+ <li> Order all the variables according to the number of entries in
+ each unique subtable.
+ <li> Sift the variable up and down, remembering each time the total
+ size of the DD heap and grouping variables that are symmetric.
+ <li> Select the best permutation.
+ <li> Repeat 3 and 4 for all variables.
+ <li> Repeat 1-4 until no further improvement.
+ </ol>
+ Returns 1 plus the number of symmetric variables if successful; 0
+ otherwise.]
+
+ SideEffects [None]
+
+ SeeAlso [cuddSymmSifting]
+
+******************************************************************************/
+int
+cuddSymmSiftingConv(
+ DdManager * table,
+ int lower,
+ int upper)
+{
+ int i;
+ int *var;
+ int size;
+ int x;
+ int result;
+ int symvars;
+ int symgroups;
+ int classes;
+ int initialSize;
+#ifdef DD_STATS
+ int previousSize;
+#endif
+
+ initialSize = table->keys - table->isolated;
+
+ size = table->size;
+
+ /* Find order in which to sift variables. */
+ var = NULL;
+ entry = ALLOC(int,size);
+ if (entry == NULL) {
+ table->errorCode = CUDD_MEMORY_OUT;
+ goto ddSymmSiftingConvOutOfMem;
+ }
+ var = ALLOC(int,size);
+ if (var == NULL) {
+ table->errorCode = CUDD_MEMORY_OUT;
+ goto ddSymmSiftingConvOutOfMem;
+ }
+
+ for (i = 0; i < size; i++) {
+ x = table->perm[i];
+ entry[i] = table->subtables[x].keys;
+ var[i] = i;
+ }
+
+ qsort((void *)var,size,sizeof(int),(int (*)(const void *, const void *))ddSymmUniqueCompare);
+
+ /* Initialize the symmetry of each subtable to itself
+ ** for first pass of converging symmetric sifting.
+ */
+ for (i = lower; i <= upper; i++) {
+ table->subtables[i].next = i;
+ }
+
+ for (i = 0; i < ddMin(table->siftMaxVar, table->size); i++) {
+ if (ddTotalNumberSwapping >= table->siftMaxSwap)
+ break;
+ x = table->perm[var[i]];
+ if (x < lower || x > upper) continue;
+ /* Only sift if not in symmetry group already. */
+ if (table->subtables[x].next == (unsigned) x) {
+#ifdef DD_STATS
+ previousSize = table->keys - table->isolated;
+#endif
+ result = ddSymmSiftingAux(table,x,lower,upper);
+ if (!result) goto ddSymmSiftingConvOutOfMem;
+#ifdef DD_STATS
+ if (table->keys < (unsigned) previousSize + table->isolated) {
+ (void) fprintf(table->out,"-");
+ } else if (table->keys > (unsigned) previousSize +
+ table->isolated) {
+ (void) fprintf(table->out,"+");
+ } else {
+ (void) fprintf(table->out,"=");
+ }
+ fflush(table->out);
+#endif
+ }
+ }
+
+ /* Sifting now until convergence. */
+ while ((unsigned) initialSize > table->keys - table->isolated) {
+ initialSize = table->keys - table->isolated;
+#ifdef DD_STATS
+ (void) fprintf(table->out,"\n");
+#endif
+ /* Here we consider only one representative for each symmetry class. */
+ for (x = lower, classes = 0; x <= upper; x++, classes++) {
+ while ((unsigned) x < table->subtables[x].next) {
+ x = table->subtables[x].next;
+ }
+ /* Here x is the largest index in a group.
+ ** Groups consist of adjacent variables.
+ ** Hence, the next increment of x will move it to a new group.
+ */
+ i = table->invperm[x];
+ entry[i] = table->subtables[x].keys;
+ var[classes] = i;
+ }
+
+ qsort((void *)var,classes,sizeof(int),(int (*)(const void *, const void *))ddSymmUniqueCompare);
+
+ /* Now sift. */
+ for (i = 0; i < ddMin(table->siftMaxVar,classes); i++) {
+ if (ddTotalNumberSwapping >= table->siftMaxSwap)
+ break;
+ x = table->perm[var[i]];
+ if ((unsigned) x >= table->subtables[x].next) {
+#ifdef DD_STATS
+ previousSize = table->keys - table->isolated;
+#endif
+ result = ddSymmSiftingConvAux(table,x,lower,upper);
+ if (!result ) goto ddSymmSiftingConvOutOfMem;
+#ifdef DD_STATS
+ if (table->keys < (unsigned) previousSize + table->isolated) {
+ (void) fprintf(table->out,"-");
+ } else if (table->keys > (unsigned) previousSize +
+ table->isolated) {
+ (void) fprintf(table->out,"+");
+ } else {
+ (void) fprintf(table->out,"=");
+ }
+ fflush(table->out);
+#endif
+ }
+ } /* for */
+ }
+
+ ddSymmSummary(table, lower, upper, &symvars, &symgroups);
+
+#ifdef DD_STATS
+ (void) fprintf(table->out, "\n#:S_SIFTING %8d: symmetric variables\n",
+ symvars);
+ (void) fprintf(table->out, "#:G_SIFTING %8d: symmetric groups",
+ symgroups);
+#endif
+
+ FREE(var);
+ FREE(entry);
+
+ return(1+symvars);
+
+ddSymmSiftingConvOutOfMem:
+
+ if (entry != NULL) FREE(entry);
+ if (var != NULL) FREE(var);
+
+ return(0);
+
+} /* end of cuddSymmSiftingConv */
+
+
+/*---------------------------------------------------------------------------*/
+/* Definition of static functions */
+/*---------------------------------------------------------------------------*/
+
+
+/**Function********************************************************************
+
+ Synopsis [Comparison function used by qsort.]
+
+ Description [Comparison function used by qsort to order the variables
+ according to the number of keys in the subtables.
+ Returns the difference in number of keys between the two
+ variables being compared.]
+
+ SideEffects [None]
+
+******************************************************************************/
+static int
+ddSymmUniqueCompare(
+ int * ptrX,
+ int * ptrY)
+{
+#if 0
+ if (entry[*ptrY] == entry[*ptrX]) {
+ return((*ptrX) - (*ptrY));
+ }
+#endif
+ return(entry[*ptrY] - entry[*ptrX]);
+
+} /* end of ddSymmUniqueCompare */
+
+
+/**Function********************************************************************
+
+ Synopsis [Given xLow <= x <= xHigh moves x up and down between the
+ boundaries.]
+
+ Description [Given xLow <= x <= xHigh moves x up and down between the
+ boundaries. Finds the best position and does the required changes.
+ Assumes that x is not part of a symmetry group. Returns 1 if
+ successful; 0 otherwise.]
+
+ SideEffects [None]
+
+******************************************************************************/
+static int
+ddSymmSiftingAux(
+ DdManager * table,
+ int x,
+ int xLow,
+ int xHigh)
+{
+ Move *move;
+ Move *moveUp; /* list of up moves */
+ Move *moveDown; /* list of down moves */
+ int initialSize;
+ int result;
+ int i;
+ int topbot; /* index to either top or bottom of symmetry group */
+ int initGroupSize, finalGroupSize;
+
+
+#ifdef DD_DEBUG
+ /* check for previously detected symmetry */
+ assert(table->subtables[x].next == (unsigned) x);
+#endif
+
+ initialSize = table->keys - table->isolated;
+
+ moveDown = NULL;
+ moveUp = NULL;
+
+ if ((x - xLow) > (xHigh - x)) {
+ /* Will go down first, unless x == xHigh:
+ ** Look for consecutive symmetries above x.
+ */
+ for (i = x; i > xLow; i--) {
+ if (!cuddSymmCheck(table,i-1,i))
+ break;
+ topbot = table->subtables[i-1].next; /* find top of i-1's group */
+ table->subtables[i-1].next = i;
+ table->subtables[x].next = topbot; /* x is bottom of group so its */
+ /* next is top of i-1's group */
+ i = topbot + 1; /* add 1 for i--; new i is top of symm group */
+ }
+ } else {
+ /* Will go up first unless x == xlow:
+ ** Look for consecutive symmetries below x.
+ */
+ for (i = x; i < xHigh; i++) {
+ if (!cuddSymmCheck(table,i,i+1))
+ break;
+ /* find bottom of i+1's symm group */
+ topbot = i + 1;
+ while ((unsigned) topbot < table->subtables[topbot].next) {
+ topbot = table->subtables[topbot].next;
+ }
+ table->subtables[topbot].next = table->subtables[i].next;
+ table->subtables[i].next = i + 1;
+ i = topbot - 1; /* subtract 1 for i++; new i is bottom of group */
+ }
+ }
+
+ /* Now x may be in the middle of a symmetry group.
+ ** Find bottom of x's symm group.
+ */
+ while ((unsigned) x < table->subtables[x].next)
+ x = table->subtables[x].next;
+
+ if (x == xLow) { /* Sift down */
+
+#ifdef DD_DEBUG
+ /* x must be a singleton */
+ assert((unsigned) x == table->subtables[x].next);
+#endif
+ if (x == xHigh) return(1); /* just one variable */
+
+ initGroupSize = 1;
+
+ moveDown = ddSymmSiftingDown(table,x,xHigh);
+ /* after this point x --> xHigh, unless early term */
+ if (moveDown == MV_OOM) goto ddSymmSiftingAuxOutOfMem;
+ if (moveDown == NULL) return(1);
+
+ x = moveDown->y;
+ /* Find bottom of x's group */
+ i = x;
+ while ((unsigned) i < table->subtables[i].next) {
+ i = table->subtables[i].next;
+ }
+#ifdef DD_DEBUG
+ /* x should be the top of the symmetry group and i the bottom */
+ assert((unsigned) i >= table->subtables[i].next);
+ assert((unsigned) x == table->subtables[i].next);
+#endif
+ finalGroupSize = i - x + 1;
+
+ if (initGroupSize == finalGroupSize) {
+ /* No new symmetry groups detected, return to best position */
+ result = ddSymmSiftingBackward(table,moveDown,initialSize);
+ } else {
+ initialSize = table->keys - table->isolated;
+ moveUp = ddSymmSiftingUp(table,x,xLow);
+ result = ddSymmSiftingBackward(table,moveUp,initialSize);
+ }
+ if (!result) goto ddSymmSiftingAuxOutOfMem;
+
+ } else if (cuddNextHigh(table,x) > xHigh) { /* Sift up */
+ /* Find top of x's symm group */
+ i = x; /* bottom */
+ x = table->subtables[x].next; /* top */
+
+ if (x == xLow) return(1); /* just one big group */
+
+ initGroupSize = i - x + 1;
+
+ moveUp = ddSymmSiftingUp(table,x,xLow);
+ /* after this point x --> xLow, unless early term */
+ if (moveUp == MV_OOM) goto ddSymmSiftingAuxOutOfMem;
+ if (moveUp == NULL) return(1);
+
+ x = moveUp->x;
+ /* Find top of x's group */
+ i = table->subtables[x].next;
+#ifdef DD_DEBUG
+ /* x should be the bottom of the symmetry group and i the top */
+ assert((unsigned) x >= table->subtables[x].next);
+ assert((unsigned) i == table->subtables[x].next);
+#endif
+ finalGroupSize = x - i + 1;
+
+ if (initGroupSize == finalGroupSize) {
+ /* No new symmetry groups detected, return to best position */
+ result = ddSymmSiftingBackward(table,moveUp,initialSize);
+ } else {
+ initialSize = table->keys - table->isolated;
+ moveDown = ddSymmSiftingDown(table,x,xHigh);
+ result = ddSymmSiftingBackward(table,moveDown,initialSize);
+ }
+ if (!result) goto ddSymmSiftingAuxOutOfMem;
+
+ } else if ((x - xLow) > (xHigh - x)) { /* must go down first: shorter */
+
+ moveDown = ddSymmSiftingDown(table,x,xHigh);
+ /* at this point x == xHigh, unless early term */
+ if (moveDown == MV_OOM) goto ddSymmSiftingAuxOutOfMem;
+
+ if (moveDown != NULL) {
+ x = moveDown->y; /* x is top here */
+ i = x;
+ while ((unsigned) i < table->subtables[i].next) {
+ i = table->subtables[i].next;
+ }
+ } else {
+ i = x;
+ while ((unsigned) i < table->subtables[i].next) {
+ i = table->subtables[i].next;
+ }
+ x = table->subtables[i].next;
+ }
+#ifdef DD_DEBUG
+ /* x should be the top of the symmetry group and i the bottom */
+ assert((unsigned) i >= table->subtables[i].next);
+ assert((unsigned) x == table->subtables[i].next);
+#endif
+ initGroupSize = i - x + 1;
+
+ moveUp = ddSymmSiftingUp(table,x,xLow);
+ if (moveUp == MV_OOM) goto ddSymmSiftingAuxOutOfMem;
+
+ if (moveUp != NULL) {
+ x = moveUp->x;
+ i = table->subtables[x].next;
+ } else {
+ i = x;
+ while ((unsigned) x < table->subtables[x].next)
+ x = table->subtables[x].next;
+ }
+#ifdef DD_DEBUG
+ /* x should be the bottom of the symmetry group and i the top */
+ assert((unsigned) x >= table->subtables[x].next);
+ assert((unsigned) i == table->subtables[x].next);
+#endif
+ finalGroupSize = x - i + 1;
+
+ if (initGroupSize == finalGroupSize) {
+ /* No new symmetry groups detected, return to best position */
+ result = ddSymmSiftingBackward(table,moveUp,initialSize);
+ } else {
+ while (moveDown != NULL) {
+ move = moveDown->next;
+ cuddDeallocNode(table, (DdNode *) moveDown);
+ moveDown = move;
+ }
+ initialSize = table->keys - table->isolated;
+ moveDown = ddSymmSiftingDown(table,x,xHigh);
+ result = ddSymmSiftingBackward(table,moveDown,initialSize);
+ }
+ if (!result) goto ddSymmSiftingAuxOutOfMem;
+
+ } else { /* moving up first: shorter */
+ /* Find top of x's symmetry group */
+ x = table->subtables[x].next;
+
+ moveUp = ddSymmSiftingUp(table,x,xLow);
+ /* at this point x == xHigh, unless early term */
+ if (moveUp == MV_OOM) goto ddSymmSiftingAuxOutOfMem;
+
+ if (moveUp != NULL) {
+ x = moveUp->x;
+ i = table->subtables[x].next;
+ } else {
+ while ((unsigned) x < table->subtables[x].next)
+ x = table->subtables[x].next;
+ i = table->subtables[x].next;
+ }
+#ifdef DD_DEBUG
+ /* x is bottom of the symmetry group and i is top */
+ assert((unsigned) x >= table->subtables[x].next);
+ assert((unsigned) i == table->subtables[x].next);
+#endif
+ initGroupSize = x - i + 1;
+
+ moveDown = ddSymmSiftingDown(table,x,xHigh);
+ if (moveDown == MV_OOM) goto ddSymmSiftingAuxOutOfMem;
+
+ if (moveDown != NULL) {
+ x = moveDown->y;
+ i = x;
+ while ((unsigned) i < table->subtables[i].next) {
+ i = table->subtables[i].next;
+ }
+ } else {
+ i = x;
+ x = table->subtables[x].next;
+ }
+#ifdef DD_DEBUG
+ /* x should be the top of the symmetry group and i the bottom */
+ assert((unsigned) i >= table->subtables[i].next);
+ assert((unsigned) x == table->subtables[i].next);
+#endif
+ finalGroupSize = i - x + 1;
+
+ if (initGroupSize == finalGroupSize) {
+ /* No new symmetries detected, go back to best position */
+ result = ddSymmSiftingBackward(table,moveDown,initialSize);
+ } else {
+ while (moveUp != NULL) {
+ move = moveUp->next;
+ cuddDeallocNode(table, (DdNode *) moveUp);
+ moveUp = move;
+ }
+ initialSize = table->keys - table->isolated;
+ moveUp = ddSymmSiftingUp(table,x,xLow);
+ result = ddSymmSiftingBackward(table,moveUp,initialSize);
+ }
+ if (!result) goto ddSymmSiftingAuxOutOfMem;
+ }
+
+ while (moveDown != NULL) {
+ move = moveDown->next;
+ cuddDeallocNode(table, (DdNode *) moveDown);
+ moveDown = move;
+ }
+ while (moveUp != NULL) {
+ move = moveUp->next;
+ cuddDeallocNode(table, (DdNode *) moveUp);
+ moveUp = move;
+ }
+
+ return(1);
+
+ddSymmSiftingAuxOutOfMem:
+ if (moveDown != MV_OOM) {
+ while (moveDown != NULL) {
+ move = moveDown->next;
+ cuddDeallocNode(table, (DdNode *) moveDown);
+ moveDown = move;
+ }
+ }
+ if (moveUp != MV_OOM) {
+ while (moveUp != NULL) {
+ move = moveUp->next;
+ cuddDeallocNode(table, (DdNode *) moveUp);
+ moveUp = move;
+ }
+ }
+
+ return(0);
+
+} /* end of ddSymmSiftingAux */
+
+
+/**Function********************************************************************
+
+ Synopsis [Given xLow <= x <= xHigh moves x up and down between the
+ boundaries.]
+
+ Description [Given xLow <= x <= xHigh moves x up and down between the
+ boundaries. Finds the best position and does the required changes.
+ Assumes that x is either an isolated variable, or it is the bottom of
+ a symmetry group. All symmetries may not have been found, because of
+ exceeded growth limit. Returns 1 if successful; 0 otherwise.]
+
+ SideEffects [None]
+
+******************************************************************************/
+static int
+ddSymmSiftingConvAux(
+ DdManager * table,
+ int x,
+ int xLow,
+ int xHigh)
+{
+ Move *move;
+ Move *moveUp; /* list of up moves */
+ Move *moveDown; /* list of down moves */
+ int initialSize;
+ int result;
+ int i;
+ int initGroupSize, finalGroupSize;
+
+
+ initialSize = table->keys - table->isolated;
+
+ moveDown = NULL;
+ moveUp = NULL;
+
+ if (x == xLow) { /* Sift down */
+#ifdef DD_DEBUG
+ /* x is bottom of symmetry group */
+ assert((unsigned) x >= table->subtables[x].next);
+#endif
+ i = table->subtables[x].next;
+ initGroupSize = x - i + 1;
+
+ moveDown = ddSymmSiftingDown(table,x,xHigh);
+ /* at this point x == xHigh, unless early term */
+ if (moveDown == MV_OOM) goto ddSymmSiftingConvAuxOutOfMem;
+ if (moveDown == NULL) return(1);
+
+ x = moveDown->y;
+ i = x;
+ while ((unsigned) i < table->subtables[i].next) {
+ i = table->subtables[i].next;
+ }
+#ifdef DD_DEBUG
+ /* x should be the top of the symmetric group and i the bottom */
+ assert((unsigned) i >= table->subtables[i].next);
+ assert((unsigned) x == table->subtables[i].next);
+#endif
+ finalGroupSize = i - x + 1;
+
+ if (initGroupSize == finalGroupSize) {
+ /* No new symmetries detected, go back to best position */
+ result = ddSymmSiftingBackward(table,moveDown,initialSize);
+ } else {
+ initialSize = table->keys - table->isolated;
+ moveUp = ddSymmSiftingUp(table,x,xLow);
+ result = ddSymmSiftingBackward(table,moveUp,initialSize);
+ }
+ if (!result) goto ddSymmSiftingConvAuxOutOfMem;
+
+ } else if (cuddNextHigh(table,x) > xHigh) { /* Sift up */
+ /* Find top of x's symm group */
+ while ((unsigned) x < table->subtables[x].next)
+ x = table->subtables[x].next;
+ i = x; /* bottom */
+ x = table->subtables[x].next; /* top */
+
+ if (x == xLow) return(1);
+
+ initGroupSize = i - x + 1;
+
+ moveUp = ddSymmSiftingUp(table,x,xLow);
+ /* at this point x == xLow, unless early term */
+ if (moveUp == MV_OOM) goto ddSymmSiftingConvAuxOutOfMem;
+ if (moveUp == NULL) return(1);
+
+ x = moveUp->x;
+ i = table->subtables[x].next;
+#ifdef DD_DEBUG
+ /* x should be the bottom of the symmetry group and i the top */
+ assert((unsigned) x >= table->subtables[x].next);
+ assert((unsigned) i == table->subtables[x].next);
+#endif
+ finalGroupSize = x - i + 1;
+
+ if (initGroupSize == finalGroupSize) {
+ /* No new symmetry groups detected, return to best position */
+ result = ddSymmSiftingBackward(table,moveUp,initialSize);
+ } else {
+ initialSize = table->keys - table->isolated;
+ moveDown = ddSymmSiftingDown(table,x,xHigh);
+ result = ddSymmSiftingBackward(table,moveDown,initialSize);
+ }
+ if (!result)
+ goto ddSymmSiftingConvAuxOutOfMem;
+
+ } else if ((x - xLow) > (xHigh - x)) { /* must go down first: shorter */
+ moveDown = ddSymmSiftingDown(table,x,xHigh);
+ /* at this point x == xHigh, unless early term */
+ if (moveDown == MV_OOM) goto ddSymmSiftingConvAuxOutOfMem;
+
+ if (moveDown != NULL) {
+ x = moveDown->y;
+ i = x;
+ while ((unsigned) i < table->subtables[i].next) {
+ i = table->subtables[i].next;
+ }
+ } else {
+ while ((unsigned) x < table->subtables[x].next)
+ x = table->subtables[x].next;
+ i = x;
+ x = table->subtables[x].next;
+ }
+#ifdef DD_DEBUG
+ /* x should be the top of the symmetry group and i the bottom */
+ assert((unsigned) i >= table->subtables[i].next);
+ assert((unsigned) x == table->subtables[i].next);
+#endif
+ initGroupSize = i - x + 1;
+
+ moveUp = ddSymmSiftingUp(table,x,xLow);
+ if (moveUp == MV_OOM) goto ddSymmSiftingConvAuxOutOfMem;
+
+ if (moveUp != NULL) {
+ x = moveUp->x;
+ i = table->subtables[x].next;
+ } else {
+ i = x;
+ while ((unsigned) x < table->subtables[x].next)
+ x = table->subtables[x].next;
+ }
+#ifdef DD_DEBUG
+ /* x should be the bottom of the symmetry group and i the top */
+ assert((unsigned) x >= table->subtables[x].next);
+ assert((unsigned) i == table->subtables[x].next);
+#endif
+ finalGroupSize = x - i + 1;
+
+ if (initGroupSize == finalGroupSize) {
+ /* No new symmetry groups detected, return to best position */
+ result = ddSymmSiftingBackward(table,moveUp,initialSize);
+ } else {
+ while (moveDown != NULL) {
+ move = moveDown->next;
+ cuddDeallocNode(table, (DdNode *) moveDown);
+ moveDown = move;
+ }
+ initialSize = table->keys - table->isolated;
+ moveDown = ddSymmSiftingDown(table,x,xHigh);
+ result = ddSymmSiftingBackward(table,moveDown,initialSize);
+ }
+ if (!result) goto ddSymmSiftingConvAuxOutOfMem;
+
+ } else { /* moving up first: shorter */
+ /* Find top of x's symmetry group */
+ x = table->subtables[x].next;
+
+ moveUp = ddSymmSiftingUp(table,x,xLow);
+ /* at this point x == xHigh, unless early term */
+ if (moveUp == MV_OOM) goto ddSymmSiftingConvAuxOutOfMem;
+
+ if (moveUp != NULL) {
+ x = moveUp->x;
+ i = table->subtables[x].next;
+ } else {
+ i = x;
+ while ((unsigned) x < table->subtables[x].next)
+ x = table->subtables[x].next;
+ }
+#ifdef DD_DEBUG
+ /* x is bottom of the symmetry group and i is top */
+ assert((unsigned) x >= table->subtables[x].next);
+ assert((unsigned) i == table->subtables[x].next);
+#endif
+ initGroupSize = x - i + 1;
+
+ moveDown = ddSymmSiftingDown(table,x,xHigh);
+ if (moveDown == MV_OOM) goto ddSymmSiftingConvAuxOutOfMem;
+
+ if (moveDown != NULL) {
+ x = moveDown->y;
+ i = x;
+ while ((unsigned) i < table->subtables[i].next) {
+ i = table->subtables[i].next;
+ }
+ } else {
+ i = x;
+ x = table->subtables[x].next;
+ }
+#ifdef DD_DEBUG
+ /* x should be the top of the symmetry group and i the bottom */
+ assert((unsigned) i >= table->subtables[i].next);
+ assert((unsigned) x == table->subtables[i].next);
+#endif
+ finalGroupSize = i - x + 1;
+
+ if (initGroupSize == finalGroupSize) {
+ /* No new symmetries detected, go back to best position */
+ result = ddSymmSiftingBackward(table,moveDown,initialSize);
+ } else {
+ while (moveUp != NULL) {
+ move = moveUp->next;
+ cuddDeallocNode(table, (DdNode *) moveUp);
+ moveUp = move;
+ }
+ initialSize = table->keys - table->isolated;
+ moveUp = ddSymmSiftingUp(table,x,xLow);
+ result = ddSymmSiftingBackward(table,moveUp,initialSize);
+ }
+ if (!result) goto ddSymmSiftingConvAuxOutOfMem;
+ }
+
+ while (moveDown != NULL) {
+ move = moveDown->next;
+ cuddDeallocNode(table, (DdNode *) moveDown);
+ moveDown = move;
+ }
+ while (moveUp != NULL) {
+ move = moveUp->next;
+ cuddDeallocNode(table, (DdNode *) moveUp);
+ moveUp = move;
+ }
+
+ return(1);
+
+ddSymmSiftingConvAuxOutOfMem:
+ if (moveDown != MV_OOM) {
+ while (moveDown != NULL) {
+ move = moveDown->next;
+ cuddDeallocNode(table, (DdNode *) moveDown);
+ moveDown = move;
+ }
+ }
+ if (moveUp != MV_OOM) {
+ while (moveUp != NULL) {
+ move = moveUp->next;
+ cuddDeallocNode(table, (DdNode *) moveUp);
+ moveUp = move;
+ }
+ }
+
+ return(0);
+
+} /* end of ddSymmSiftingConvAux */
+
+
+/**Function********************************************************************
+
+ Synopsis [Moves x up until either it reaches the bound (xLow) or
+ the size of the DD heap increases too much.]
+
+ Description [Moves x up until either it reaches the bound (xLow) or
+ the size of the DD heap increases too much. Assumes that x is the top
+ of a symmetry group. Checks x for symmetry to the adjacent
+ variables. If symmetry is found, the symmetry group of x is merged
+ with the symmetry group of the other variable. Returns the set of
+ moves in case of success; MV_OOM if memory is full.]
+
+ SideEffects [None]
+
+******************************************************************************/
+static Move *
+ddSymmSiftingUp(
+ DdManager * table,
+ int y,
+ int xLow)
+{
+ Move *moves;
+ Move *move;
+ int x;
+ int size;
+ int i;
+ int gxtop,gybot;
+ int limitSize;
+ int xindex, yindex;
+ int zindex;
+ int z;
+ int isolated;
+ int L; /* lower bound on DD size */
+#ifdef DD_DEBUG
+ int checkL;
+#endif
+
+
+ moves = NULL;
+ yindex = table->invperm[y];
+
+ /* Initialize the lower bound.
+ ** The part of the DD below the bottom of y' group will not change.
+ ** The part of the DD above y that does not interact with y will not
+ ** change. The rest may vanish in the best case, except for
+ ** the nodes at level xLow, which will not vanish, regardless.
+ */
+ limitSize = L = table->keys - table->isolated;
+ gybot = y;
+ while ((unsigned) gybot < table->subtables[gybot].next)
+ gybot = table->subtables[gybot].next;
+ for (z = xLow + 1; z <= gybot; z++) {
+ zindex = table->invperm[z];
+ if (zindex == yindex || cuddTestInteract(table,zindex,yindex)) {
+ isolated = table->vars[zindex]->ref == 1;
+ L -= table->subtables[z].keys - isolated;
+ }
+ }
+
+ x = cuddNextLow(table,y);
+ while (x >= xLow && L <= limitSize) {
+#ifdef DD_DEBUG
+ gybot = y;
+ while ((unsigned) gybot < table->subtables[gybot].next)
+ gybot = table->subtables[gybot].next;
+ checkL = table->keys - table->isolated;
+ for (z = xLow + 1; z <= gybot; z++) {
+ zindex = table->invperm[z];
+ if (zindex == yindex || cuddTestInteract(table,zindex,yindex)) {
+ isolated = table->vars[zindex]->ref == 1;
+ checkL -= table->subtables[z].keys - isolated;
+ }
+ }
+ assert(L == checkL);
+#endif
+ gxtop = table->subtables[x].next;
+ if (cuddSymmCheck(table,x,y)) {
+ /* Symmetry found, attach symm groups */
+ table->subtables[x].next = y;
+ i = table->subtables[y].next;
+ while (table->subtables[i].next != (unsigned) y)
+ i = table->subtables[i].next;
+ table->subtables[i].next = gxtop;
+ } else if (table->subtables[x].next == (unsigned) x &&
+ table->subtables[y].next == (unsigned) y) {
+ /* x and y have self symmetry */
+ xindex = table->invperm[x];
+ size = cuddSwapInPlace(table,x,y);
+#ifdef DD_DEBUG
+ assert(table->subtables[x].next == (unsigned) x);
+ assert(table->subtables[y].next == (unsigned) y);
+#endif
+ if (size == 0) goto ddSymmSiftingUpOutOfMem;
+ /* Update the lower bound. */
+ if (cuddTestInteract(table,xindex,yindex)) {
+ isolated = table->vars[xindex]->ref == 1;
+ L += table->subtables[y].keys - isolated;
+ }
+ move = (Move *) cuddDynamicAllocNode(table);
+ if (move == NULL) goto ddSymmSiftingUpOutOfMem;
+ move->x = x;
+ move->y = y;
+ move->size = size;
+ move->next = moves;
+ moves = move;
+ if ((double) size > (double) limitSize * table->maxGrowth)
+ return(moves);
+ if (size < limitSize) limitSize = size;
+ } else { /* Group move */
+ size = ddSymmGroupMove(table,x,y,&moves);
+ if (size == 0) goto ddSymmSiftingUpOutOfMem;
+ /* Update the lower bound. */
+ z = moves->y;
+ do {
+ zindex = table->invperm[z];
+ if (cuddTestInteract(table,zindex,yindex)) {
+ isolated = table->vars[zindex]->ref == 1;
+ L += table->subtables[z].keys - isolated;
+ }
+ z = table->subtables[z].next;
+ } while (z != (int) moves->y);
+ if ((double) size > (double) limitSize * table->maxGrowth)
+ return(moves);
+ if (size < limitSize) limitSize = size;
+ }
+ y = gxtop;
+ x = cuddNextLow(table,y);
+ }
+
+ return(moves);
+
+ddSymmSiftingUpOutOfMem:
+ while (moves != NULL) {
+ move = moves->next;
+ cuddDeallocNode(table, (DdNode *) moves);
+ moves = move;
+ }
+ return(MV_OOM);
+
+} /* end of ddSymmSiftingUp */
+
+
+/**Function********************************************************************
+
+ Synopsis [Moves x down until either it reaches the bound (xHigh) or
+ the size of the DD heap increases too much.]
+
+ Description [Moves x down until either it reaches the bound (xHigh)
+ or the size of the DD heap increases too much. Assumes that x is the
+ bottom of a symmetry group. Checks x for symmetry to the adjacent
+ variables. If symmetry is found, the symmetry group of x is merged
+ with the symmetry group of the other variable. Returns the set of
+ moves in case of success; MV_OOM if memory is full.]
+
+ SideEffects [None]
+
+******************************************************************************/
+static Move *
+ddSymmSiftingDown(
+ DdManager * table,
+ int x,
+ int xHigh)
+{
+ Move *moves;
+ Move *move;
+ int y;
+ int size;
+ int limitSize;
+ int gxtop,gybot;
+ int R; /* upper bound on node decrease */
+ int xindex, yindex;
+ int isolated;
+ int z;
+ int zindex;
+#ifdef DD_DEBUG
+ int checkR;
+#endif
+
+ moves = NULL;
+ /* Initialize R */
+ xindex = table->invperm[x];
+ gxtop = table->subtables[x].next;
+ limitSize = size = table->keys - table->isolated;
+ R = 0;
+ for (z = xHigh; z > gxtop; z--) {
+ zindex = table->invperm[z];
+ if (zindex == xindex || cuddTestInteract(table,xindex,zindex)) {
+ isolated = table->vars[zindex]->ref == 1;
+ R += table->subtables[z].keys - isolated;
+ }
+ }
+
+ y = cuddNextHigh(table,x);
+ while (y <= xHigh && size - R < limitSize) {
+#ifdef DD_DEBUG
+ gxtop = table->subtables[x].next;
+ checkR = 0;
+ for (z = xHigh; z > gxtop; z--) {
+ zindex = table->invperm[z];
+ if (zindex == xindex || cuddTestInteract(table,xindex,zindex)) {
+ isolated = table->vars[zindex]->ref == 1;
+ checkR += table->subtables[z].keys - isolated;
+ }
+ }
+ assert(R == checkR);
+#endif
+ gybot = table->subtables[y].next;
+ while (table->subtables[gybot].next != (unsigned) y)
+ gybot = table->subtables[gybot].next;
+ if (cuddSymmCheck(table,x,y)) {
+ /* Symmetry found, attach symm groups */
+ gxtop = table->subtables[x].next;
+ table->subtables[x].next = y;
+ table->subtables[gybot].next = gxtop;
+ } else if (table->subtables[x].next == (unsigned) x &&
+ table->subtables[y].next == (unsigned) y) {
+ /* x and y have self symmetry */
+ /* Update upper bound on node decrease. */
+ yindex = table->invperm[y];
+ if (cuddTestInteract(table,xindex,yindex)) {
+ isolated = table->vars[yindex]->ref == 1;
+ R -= table->subtables[y].keys - isolated;
+ }
+ size = cuddSwapInPlace(table,x,y);
+#ifdef DD_DEBUG
+ assert(table->subtables[x].next == (unsigned) x);
+ assert(table->subtables[y].next == (unsigned) y);
+#endif
+ if (size == 0) goto ddSymmSiftingDownOutOfMem;
+ move = (Move *) cuddDynamicAllocNode(table);
+ if (move == NULL) goto ddSymmSiftingDownOutOfMem;
+ move->x = x;
+ move->y = y;
+ move->size = size;
+ move->next = moves;
+ moves = move;
+ if ((double) size > (double) limitSize * table->maxGrowth)
+ return(moves);
+ if (size < limitSize) limitSize = size;
+ } else { /* Group move */
+ /* Update upper bound on node decrease: first phase. */
+ gxtop = table->subtables[x].next;
+ z = gxtop + 1;
+ do {
+ zindex = table->invperm[z];
+ if (zindex == xindex || cuddTestInteract(table,xindex,zindex)) {
+ isolated = table->vars[zindex]->ref == 1;
+ R -= table->subtables[z].keys - isolated;
+ }
+ z++;
+ } while (z <= gybot);
+ size = ddSymmGroupMove(table,x,y,&moves);
+ if (size == 0) goto ddSymmSiftingDownOutOfMem;
+ if ((double) size > (double) limitSize * table->maxGrowth)
+ return(moves);
+ if (size < limitSize) limitSize = size;
+ /* Update upper bound on node decrease: second phase. */
+ gxtop = table->subtables[gybot].next;
+ for (z = gxtop + 1; z <= gybot; z++) {
+ zindex = table->invperm[z];
+ if (zindex == xindex || cuddTestInteract(table,xindex,zindex)) {
+ isolated = table->vars[zindex]->ref == 1;
+ R += table->subtables[z].keys - isolated;
+ }
+ }
+ }
+ x = gybot;
+ y = cuddNextHigh(table,x);
+ }
+
+ return(moves);
+
+ddSymmSiftingDownOutOfMem:
+ while (moves != NULL) {
+ move = moves->next;
+ cuddDeallocNode(table, (DdNode *) moves);
+ moves = move;
+ }
+ return(MV_OOM);
+
+} /* end of ddSymmSiftingDown */
+
+
+/**Function********************************************************************
+
+ Synopsis [Swaps two groups.]
+
+ Description [Swaps two groups. x is assumed to be the bottom variable
+ of the first group. y is assumed to be the top variable of the second
+ group. Updates the list of moves. Returns the number of keys in the
+ table if successful; 0 otherwise.]
+
+ SideEffects [None]
+
+******************************************************************************/
+static int
+ddSymmGroupMove(
+ DdManager * table,
+ int x,
+ int y,
+ Move ** moves)
+{
+ Move *move;
+ int size;
+ int i,j;
+ int xtop,xbot,xsize,ytop,ybot,ysize,newxtop;
+ int swapx,swapy;
+
+#if DD_DEBUG
+ assert(x < y); /* we assume that x < y */
+#endif
+ /* Find top, bottom, and size for the two groups. */
+ xbot = x;
+ xtop = table->subtables[x].next;
+ xsize = xbot - xtop + 1;
+ ybot = y;
+ while ((unsigned) ybot < table->subtables[ybot].next)
+ ybot = table->subtables[ybot].next;
+ ytop = y;
+ ysize = ybot - ytop + 1;
+
+ /* Sift the variables of the second group up through the first group. */
+ for (i = 1; i <= ysize; i++) {
+ for (j = 1; j <= xsize; j++) {
+ size = cuddSwapInPlace(table,x,y);
+ if (size == 0) return(0);
+ swapx = x; swapy = y;
+ y = x;
+ x = y - 1;
+ }
+ y = ytop + i;
+ x = y - 1;
+ }
+
+ /* fix symmetries */
+ y = xtop; /* ytop is now where xtop used to be */
+ for (i = 0; i < ysize-1 ; i++) {
+ table->subtables[y].next = y + 1;
+ y = y + 1;
+ }
+ table->subtables[y].next = xtop; /* y is bottom of its group, join */
+ /* its symmetry to top of its group */
+ x = y + 1;
+ newxtop = x;
+ for (i = 0; i < xsize - 1 ; i++) {
+ table->subtables[x].next = x + 1;
+ x = x + 1;
+ }
+ table->subtables[x].next = newxtop; /* x is bottom of its group, join */
+ /* its symmetry to top of its group */
+ /* Store group move */
+ move = (Move *) cuddDynamicAllocNode(table);
+ if (move == NULL) return(0);
+ move->x = swapx;
+ move->y = swapy;
+ move->size = size;
+ move->next = *moves;
+ *moves = move;
+
+ return(size);
+
+} /* end of ddSymmGroupMove */
+
+
+/**Function********************************************************************
+
+ Synopsis [Undoes the swap of two groups.]
+
+ Description [Undoes the swap of two groups. x is assumed to be the
+ bottom variable of the first group. y is assumed to be the top
+ variable of the second group. Returns the number of keys in the table
+ if successful; 0 otherwise.]
+
+ SideEffects [None]
+
+******************************************************************************/
+static int
+ddSymmGroupMoveBackward(
+ DdManager * table,
+ int x,
+ int y)
+{
+ int size;
+ int i,j;
+ int xtop,xbot,xsize,ytop,ybot,ysize,newxtop;
+
+#if DD_DEBUG
+ assert(x < y); /* We assume that x < y */
+#endif
+
+ /* Find top, bottom, and size for the two groups. */
+ xbot = x;
+ xtop = table->subtables[x].next;
+ xsize = xbot - xtop + 1;
+ ybot = y;
+ while ((unsigned) ybot < table->subtables[ybot].next)
+ ybot = table->subtables[ybot].next;
+ ytop = y;
+ ysize = ybot - ytop + 1;
+
+ /* Sift the variables of the second group up through the first group. */
+ for (i = 1; i <= ysize; i++) {
+ for (j = 1; j <= xsize; j++) {
+ size = cuddSwapInPlace(table,x,y);
+ if (size == 0) return(0);
+ y = x;
+ x = cuddNextLow(table,y);
+ }
+ y = ytop + i;
+ x = y - 1;
+ }
+
+ /* Fix symmetries. */
+ y = xtop;
+ for (i = 0; i < ysize-1 ; i++) {
+ table->subtables[y].next = y + 1;
+ y = y + 1;
+ }
+ table->subtables[y].next = xtop; /* y is bottom of its group, join */
+ /* its symmetry to top of its group */
+ x = y + 1;
+ newxtop = x;
+ for (i = 0; i < xsize-1 ; i++) {
+ table->subtables[x].next = x + 1;
+ x = x + 1;
+ }
+ table->subtables[x].next = newxtop; /* x is bottom of its group, join */
+ /* its symmetry to top of its group */
+
+ return(size);
+
+} /* end of ddSymmGroupMoveBackward */
+
+
+/**Function********************************************************************
+
+ Synopsis [Given a set of moves, returns the DD heap to the position
+ giving the minimum size.]
+
+ Description [Given a set of moves, returns the DD heap to the
+ position giving the minimum size. In case of ties, returns to the
+ closest position giving the minimum size. Returns 1 in case of
+ success; 0 otherwise.]
+
+ SideEffects [None]
+
+******************************************************************************/
+static int
+ddSymmSiftingBackward(
+ DdManager * table,
+ Move * moves,
+ int size)
+{
+ Move *move;
+ int res;
+
+ for (move = moves; move != NULL; move = move->next) {
+ if (move->size < size) {
+ size = move->size;
+ }
+ }
+
+ for (move = moves; move != NULL; move = move->next) {
+ if (move->size == size) return(1);
+ if (table->subtables[move->x].next == move->x && table->subtables[move->y].next == move->y) {
+ res = cuddSwapInPlace(table,(int)move->x,(int)move->y);
+#ifdef DD_DEBUG
+ assert(table->subtables[move->x].next == move->x);
+ assert(table->subtables[move->y].next == move->y);
+#endif
+ } else { /* Group move necessary */
+ res = ddSymmGroupMoveBackward(table,(int)move->x,(int)move->y);
+ }
+ if (!res) return(0);
+ }
+
+ return(1);
+
+} /* end of ddSymmSiftingBackward */
+
+
+/**Function********************************************************************
+
+ Synopsis [Counts numbers of symmetric variables and symmetry
+ groups.]
+
+ Description []
+
+ SideEffects [None]
+
+******************************************************************************/
+static void
+ddSymmSummary(
+ DdManager * table,
+ int lower,
+ int upper,
+ int * symvars,
+ int * symgroups)
+{
+ int i,x,gbot;
+ int TotalSymm = 0;
+ int TotalSymmGroups = 0;
+
+ for (i = lower; i <= upper; i++) {
+ if (table->subtables[i].next != (unsigned) i) {
+ TotalSymmGroups++;
+ x = i;
+ do {
+ TotalSymm++;
+ gbot = x;
+ x = table->subtables[x].next;
+ } while (x != i);
+#ifdef DD_DEBUG
+ assert(table->subtables[gbot].next == (unsigned) i);
+#endif
+ i = gbot;
+ }
+ }
+ *symvars = TotalSymm;
+ *symgroups = TotalSymmGroups;
+
+ return;
+
+} /* end of ddSymmSummary */