/*
 * Revision Control Information
 *
 * $Source$
 * $Author$
 * $Revision$
 * $Date$
 *
 */
/*
    setc.c -- massive bit-hacking for performing special "cube"-type
    operations on a set

    The basic trick used for binary valued variables is the following:

    If a[w] and b[w] contain a full word of binary variables, then:

     1) to get the full word of their intersection, we use

        x = a[w] & b[w];


     2) to see if the intersection is null in any variables, we examine

        x = ~(x | x >> 1) & DISJOINT;

    this will have a single 1 in each binary variable for which
    the intersection is null.  In particular, if this is zero,
    then there are no disjoint variables; or, if this is nonzero,
    then there is at least one disjoint variable.  A "count_ones"
    over x will tell in how many variables they have an null
    intersection.


     3) to get a mask which selects the disjoint variables, we use

        (x | x << 1)

    this provides a selector which can be used to see where
    they have an null intersection


    cdist       return distance between two cubes
    cdist0      return true if two cubes are distance 0 apart
    cdist01     return distance, or 2 if distance exceeds 1
    consensus   compute consensus of two cubes distance 1 apart
    force_lower expand hack (for now), related to consensus
*/

#include "espresso.h"

ABC_NAMESPACE_IMPL_START


/* see if the cube has a full row of 1's (with respect to cof) */
bool full_row(p, cof)
IN register pcube p, cof;
{
    register int i = LOOP(p);
    do if ((p[i] | cof[i]) != cube.fullset[i]) return FALSE; while (--i > 0);
    return TRUE;
}

/*
    cdist0 -- return TRUE if a and b are distance 0 apart
*/

bool cdist0(a, b)
register pcube a, b;
{
 {  /* Check binary variables */
    register int w, last; register unsigned int x;
    if ((last = cube.inword) != -1) {

    /* Check the partial word of binary variables */
    x = a[last] & b[last];
    if (~(x | x >> 1) & cube.inmask)
        return FALSE;               /* disjoint in some variable */

    /* Check the full words of binary variables */
    for(w = 1; w < last; w++) {
        x = a[w] & b[w];
        if (~(x | x >> 1) & DISJOINT)
        return FALSE;           /* disjoint in some variable */
    }
    }
 }

 {  /* Check the multiple-valued variables */
    register int w, var, last; register pcube mask;
    for(var = cube.num_binary_vars; var < cube.num_vars; var++) {
    mask = cube.var_mask[var]; last = cube.last_word[var];
    for(w = cube.first_word[var]; w <= last; w++)
        if (a[w] & b[w] & mask[w])
        goto nextvar;
    return FALSE;           /* disjoint in this variable */
    nextvar: ;
    }
 }
    return TRUE;
}

/*
    cdist01 -- return the "distance" between two cubes (defined as the
    number of null variables in their intersection).  If the distance
    exceeds 1, the value 2 is returned.
*/

int cdist01(a, b)
register pset a, b;
{
    int dist = 0;

 {  /* Check binary variables */
    register int w, last; register unsigned int x;
    if ((last = cube.inword) != -1) {

    /* Check the partial word of binary variables */
    x = a[last] & b[last];
    if ((x = ~ (x | x >> 1) & cube.inmask))
        if ((dist = count_ones(x)) > 1)
        return 2;

    /* Check the full words of binary variables */
    for(w = 1; w < last; w++) {
        x = a[w] & b[w];
        if ((x = ~ (x | x >> 1) & DISJOINT))
        if (dist == 1 || (dist += count_ones(x)) > 1)
            return 2;
    }
    }
 }

 {  /* Check the multiple-valued variables */
    register int w, var, last; register pcube mask;
    for(var = cube.num_binary_vars; var < cube.num_vars; var++) {
    mask = cube.var_mask[var]; last = cube.last_word[var];
    for(w = cube.first_word[var]; w <= last; w++)
        if (a[w] & b[w] & mask[w])
        goto nextvar;
    if (++dist > 1)
        return 2;
    nextvar: ;
    }
 }
    return dist;
}

/*
    cdist -- return the "distance" between two cubes (defined as the
    number of null variables in their intersection).
*/

int cdist(a, b)
register pset a, b;
{
    int dist = 0;

 {  /* Check binary variables */
    register int w, last; register unsigned int x;
    if ((last = cube.inword) != -1) {

    /* Check the partial word of binary variables */
    x = a[last] & b[last];
    if ((x = ~ (x | x >> 1) & cube.inmask))
        dist = count_ones(x);

    /* Check the full words of binary variables */
    for(w = 1; w < last; w++) {
        x = a[w] & b[w];
        if ((x = ~ (x | x >> 1) & DISJOINT))
        dist += count_ones(x);
    }
    }
 }

 {  /* Check the multiple-valued variables */
    register int w, var, last; register pcube mask;
    for(var = cube.num_binary_vars; var < cube.num_vars; var++) {
    mask = cube.var_mask[var]; last = cube.last_word[var];
    for(w = cube.first_word[var]; w <= last; w++)
        if (a[w] & b[w] & mask[w])
        goto nextvar;
    dist++;
    nextvar: ;
    }
 }
    return dist;
}

/*
    force_lower -- Determine which variables of a do not intersect b.
*/

pset force_lower(xlower, a, b)
INOUT pset xlower;
IN register pset a, b;
{

 {  /* Check binary variables (if any) */
    register int w, last; register unsigned int x;
    if ((last = cube.inword) != -1) {

    /* Check the partial word of binary variables */
    x = a[last] & b[last];
    if ((x = ~(x | x >> 1) & cube.inmask))
        xlower[last] |= (x | (x << 1)) & a[last];

    /* Check the full words of binary variables */
    for(w = 1; w < last; w++) {
        x = a[w] & b[w];
        if ((x = ~(x | x >> 1) & DISJOINT))
        xlower[w] |= (x | (x << 1)) & a[w];
    }
    }
 }

 {  /* Check the multiple-valued variables */
    register int w, var, last; register pcube mask;
    for(var = cube.num_binary_vars; var < cube.num_vars; var++) {
    mask = cube.var_mask[var]; last = cube.last_word[var];
    for(w = cube.first_word[var]; w <= last; w++)
        if (a[w] & b[w] & mask[w])
        goto nextvar;
    for(w = cube.first_word[var]; w <= last; w++)
        xlower[w] |= a[w] & mask[w];
    nextvar: ;
    }
 }
    return xlower;
}

/*
    consensus -- multiple-valued consensus

    Although this looks very messy, the idea is to compute for r the
    "and" of the cubes a and b for each variable, unless the "and" is
    null in a variable, in which case the "or" of a and b is computed
    for this variable.

    Because we don't check how many variables are null in the
    intersection of a and b, the returned value for r really only
    represents the consensus when a and b are distance 1 apart.
*/

void consensus(r, a, b)
INOUT pcube r;
IN register pcube a, b;
{
    INLINEset_clear(r, cube.size);

 {  /* Check binary variables (if any) */
    register int w, last; register unsigned int x;
    if ((last = cube.inword) != -1) {

    /* Check the partial word of binary variables */
    r[last] = x = a[last] & b[last];
    if ((x = ~(x | x >> 1) & cube.inmask))
        r[last] |= (x | (x << 1)) & (a[last] | b[last]);

    /* Check the full words of binary variables */
    for(w = 1; w < last; w++) {
        r[w] = x = a[w] & b[w];
        if ((x = ~(x | x >> 1) & DISJOINT))
        r[w] |= (x | (x << 1)) & (a[w] | b[w]);
    }
    }
 }


 {  /* Check the multiple-valued variables */
    bool empty; int var; unsigned int x;
    register int w, last; register pcube mask;
    for(var = cube.num_binary_vars; var < cube.num_vars; var++) {
    mask = cube.var_mask[var];
    last = cube.last_word[var];
    empty = TRUE;
    for(w = cube.first_word[var]; w <= last; w++)
        if ((x = a[w] & b[w] & mask[w]))
        empty = FALSE, r[w] |= x;
    if (empty)
        for(w = cube.first_word[var]; w <= last; w++)
        r[w] |= mask[w] & (a[w] | b[w]);
    }
 }
}

/*
    cactive -- return the index of the single active variable in
    the cube, or return -1 if there are none or more than 2.
*/

int cactive(a)
register pcube a;
{
    int active = -1, dist = 0, bit_index();

 {  /* Check binary variables */
    register int w, last;
    register unsigned int x;
    if ((last = cube.inword) != -1) {

    /* Check the partial word of binary variables */
    x = a[last];
    if ((x = ~ (x & x >> 1) & cube.inmask)) {
        if ((dist = count_ones(x)) > 1)
        return -1;        /* more than 2 active variables */
        active = (last-1)*(BPI/2) + bit_index(x) / 2;
    }

    /* Check the full words of binary variables */
    for(w = 1; w < last; w++) {
        x = a[w];
        if ((x = ~ (x & x >> 1) & DISJOINT)) {
        if ((dist += count_ones(x)) > 1)
            return -1;        /* more than 2 active variables */
        active = (w-1)*(BPI/2) + bit_index(x) / 2;
        }
    }
    }
 }

 {  /* Check the multiple-valued variables */
    register int w, var, last;
    register pcube mask;
    for(var = cube.num_binary_vars; var < cube.num_vars; var++) {
    mask = cube.var_mask[var];
    last = cube.last_word[var];
    for(w = cube.first_word[var]; w <= last; w++)
        if (mask[w] & ~ a[w]) {
        if (++dist > 1)
            return -1;
        active = var;
        break;
        }
    }
 }
 return active;
}

/*
    ccommon -- return TRUE if a and b are share "active" variables
    active variables include variables that are empty;
*/

bool ccommon(a, b, cof)
register pcube a, b, cof;
{
 {  /* Check binary variables */
    int last;
    register int w;
    register unsigned int x, y;
    if ((last = cube.inword) != -1) {

    /* Check the partial word of binary variables */
    x = a[last] | cof[last];
    y = b[last] | cof[last];
    if (~(x & x>>1) & ~(y & y>>1) & cube.inmask)
        return TRUE;

    /* Check the full words of binary variables */
    for(w = 1; w < last; w++) {
        x = a[w] | cof[w];
        y = b[w] | cof[w];
        if (~(x & x>>1) & ~(y & y>>1) & DISJOINT)
        return TRUE;
    }
    }
 }

 {  /* Check the multiple-valued variables */
    int var;
    register int w, last;
    register pcube mask;
    for(var = cube.num_binary_vars; var < cube.num_vars; var++) {
    mask = cube.var_mask[var]; last = cube.last_word[var];
    /* Check for some part missing from a */
    for(w = cube.first_word[var]; w <= last; w++)
        if (mask[w] & ~a[w] & ~cof[w]) {

        /* If so, check for some part missing from b */
        for(w = cube.first_word[var]; w <= last; w++)
            if (mask[w] & ~b[w] & ~cof[w])
            return TRUE;            /* both active */
        break;
        }
    }
 }
    return FALSE;
}

/*
    These routines compare two sets (cubes) for the qsort() routine and
    return:

    -1 if set a is to precede set b
     0 if set a and set b are equal
     1 if set a is to follow set b

    Usually the SIZE field of the set is assumed to contain the size
    of the set (which will save recomputing the set size during the
    sort).  For distance-1 merging, the global variable cube.temp[0] is
    a mask which mask's-out the merging variable.
*/

/* descend -- comparison for descending sort on set size */
int descend(a, b)
pset *a, *b;
{
    register pset a1 = *a, b1 = *b;
    if (SIZE(a1) > SIZE(b1)) return -1;
    else if (SIZE(a1) < SIZE(b1)) return 1;
    else {
    register int i = LOOP(a1);
    do
        if (a1[i] > b1[i]) return -1; else if (a1[i] < b1[i]) return 1;
    while (--i > 0);
    }
    return 0;
}

/* ascend -- comparison for ascending sort on set size */
int ascend(a, b)
pset *a, *b;
{
    register pset a1 = *a, b1 = *b;
    if (SIZE(a1) > SIZE(b1)) return 1;
    else if (SIZE(a1) < SIZE(b1)) return -1;
    else {
    register int i = LOOP(a1);
    do
        if (a1[i] > b1[i]) return 1; else if (a1[i] < b1[i]) return -1;
    while (--i > 0);
    }
    return 0;
}


/* lex_order -- comparison for "lexical" ordering of cubes */
int lex_order(a, b)
pset *a, *b;
{
    register pset a1 = *a, b1 = *b;
    register int i = LOOP(a1);
    do
    if (a1[i] > b1[i]) return -1; else if (a1[i] < b1[i]) return 1;
    while (--i > 0);
    return 0;
}


/* d1_order -- comparison for distance-1 merge routine */
int d1_order(a, b)
pset *a, *b;
{
    register pset a1 = *a, b1 = *b, c1 = cube.temp[0];
    register int i = LOOP(a1);
    register unsigned int x1, x2;
    do
    if ((x1 = a1[i] | c1[i]) > (x2 = b1[i] | c1[i])) return -1;
    else if (x1 < x2) return 1;
    while (--i > 0);
    return 0;
}


/* desc1 -- comparison (without indirection) for descending sort */
/* also has effect of handling NULL pointers,and a NULL pointer has smallest
order */
int desc1(a, b)
register pset a, b;
{
    if (a == (pset) NULL)
    return (b == (pset) NULL) ? 0 : 1;
    else if (b == (pset) NULL)
    return -1;
    if (SIZE(a) > SIZE(b)) return -1;
    else if (SIZE(a) < SIZE(b)) return 1;
    else {
    register int i = LOOP(a);
    do
        if (a[i] > b[i]) return -1; else if (a[i] < b[i]) return 1;
    while (--i > 0);
    }
    return 0;
}
ABC_NAMESPACE_IMPL_END