/*
    sharp.c -- perform sharp, disjoint sharp, and intersection
*/

#include "espresso.h"

long start_time;


/* cv_sharp -- form the sharp product between two covers */
/** MM: uz kontroluje parametre, ktore NEdealokuje */
/** MM: vracia novu strukturu, pripadne NULL pri chybe */
pcover cv_sharp(pset_family A, pset_family B)
{
    pcube last, p;
    pcover T;
    
    if ( (A==NULL) || (B==NULL) ) return NULL; /** MM: ak chyba parametra */

    T = new_cover(0);
    if (T==NULL) return NULL;
    foreach_set(A, last, p){
    	T = sf_union(T, cb_sharp(p, B));
    	if (T==NULL) return NULL; /** MM: ak chyba alokacie */
    }
    return T;
}


/* cb_sharp -- form the sharp product between a cube and a cover */
/** MM: uz kontroluje parametre, ktore NEdealokuje */
/** MM: vracia novu strukturu (upravene T), pripadne NULL pri chybe */
pcover cb_sharp(pset c, pset_family T)
{
    if ( (T==NULL) || (c==NULL) ) return NULL; /** MM: ak chyba parametra */
    
    if (T->count == 0) {
    	T = sf_addset(new_cover(1), c); /** MM: toto si osetri volajuci */
    } else {
    	start_time = ptime();
    	T = cb_recur_sharp(c, T, 0, T->count-1, 0);
    }
    return T;
}


/* recursive formulation to provide balanced merging */
/** MM: uz kontroluje parametre, ktore NEdealokuje */
/** MM: vracia novu strukturu , pripadne NULL pri chybe */
pcover cb_recur_sharp(pset c, pset_family T, int first, int last, int level)
{
    pcover temp, left, right;
    int middle;
    
    if ( (c==NULL) || (T==NULL) ) return NULL; /** MM: ak chyba parametrov */

    if (first == last) {
    	temp = sharp(c, GETSET(T, first)); /** MM: tuto chybu si osetri volajuci */
    } else {
    	middle = (first + last) / 2;
    	left = cb_recur_sharp(c, T, first, middle, level+1);
    	right = cb_recur_sharp(c, T, middle+1, last, level+1);
    	temp = cv_intersect(left, right); /** MM: left a right netreba kontrolovat - cv_intersect si s nimi poradi */
    	if (temp==NULL){ /** MM: ak chyba alokacie alebo cb_recur_sharp */
            free_cover(left); free_cover(right); return NULL;
        }
    	if ((debug & SHARP) && level < 4) {
    	    if (debugoutput!=NULL) {
                fprintf(debugoutput, "# SHARP[%d]: %4d = %4d x %4d, time = %s\n",
            		level, temp->count, left->count, right->count,
            		print_time(ptime() - start_time));
                fflush(debugoutput);
            }
    	}
    	free_cover(left);
    	free_cover(right);
    }
    return temp;
}


/* sharp -- form the sharp product between two cubes */
/** MM: uz kontroluje parametre, ktore NEdealokuje */
/** MM: vracia novu strukturu , pripadne NULL pri chybe */
pcover sharp(pset a, pset b)
{
    register int var;
    register pcube d=cube->temp[0], temp=cube->temp[1], temp1=cube->temp[2];
    
    if ( (a==NULL) || (b==NULL) ) return NULL; /** MM: ak chyba parametrov */
    
    pcover r = new_cover(cube->num_vars);
    if (r==NULL) return NULL; /** MM: ak chyba alokacie */

    if (cdist0(a, b)) {
    	set_diff(d, a, b);
    	for(var = 0; var < cube->num_vars; var++) {
    	    if (! setp_empty(set_and(temp, d, cube->var_mask[var]))) {
        		set_diff(temp1, a, cube->var_mask[var]);
        		set_or(GETSET(r, r->count++), temp, temp1);
    	    }
    	}
    } else {
    	r = sf_addset(r, a); /** MM: s chybou si musi poradit volajuci */
    }
    return r;
}

/* make cover disjoint */
/** MM: uz kontroluje parameter, ktory NEdealokuje */
/** MM: pri chybe vrati NULL */
pcover make_disjoint(pset_family A)
{
    pcover R, _new;
    register pset last, p;
    
    if (A==NULL) return NULL; /** MM: ak chyba parametra */

    R = new_cover(0);
    if (R==NULL) return NULL; /** MM: ak chyba alokacie */
    foreach_set(A, last, p) {
    	_new = cb_dsharp(p, R); /** MM: vysledok netreba kontrolovat; sf_append si s nim poradi */
    	R = sf_append(R, _new);
    	if (R==NULL) return NULL; /** MM: ak chyba, tak vsetko je uz dealokovane */
    }
    return R;
}


/* cv_dsharp -- disjoint-sharp product between two covers */
/** MM: uz kontroluje parametre, ktore NEdealokuje */
/** MM: vracia novu strukturu, pripadne NULL */
pcover cv_dsharp(pset_family A, pset_family B)
{
    register pcube last, p;
    pcover T;
    
    if ( (A==NULL) || (B==NULL) ) return NULL; /** MM: ak chyba parametrov */

    T = new_cover(0);
    if (T==NULL) return NULL; /** MM: ak chyba alokacie */
    foreach_set(A, last, p) {
    	T = sf_union(T, cb_dsharp(p, B));
    	if (T==NULL) return NULL; /** MM: ak chyba alokacie */
    }
    return T;
}


/* cb1_dsharp -- disjoint-sharp product between a cover and a cube */
/** kontroluje parametre, ktore NEdealokuje */
/** vracia nuvu strukturu, pripadne NULL */
pcover cb1_dsharp(pset_family T, pset c)
{
    pcube last, p;
    pcover R;
    
    if ( (c==NULL) || (T==NULL) ) return NULL; /** MM: ak chyba parametrov */

    R = new_cover(T->count);
    if (R==NULL) return NULL; /** MM: ak chyba alokacie */
    foreach_set(T, last, p) {
    	R = sf_union(R, dsharp(p, c));
    	if (R==NULL) return NULL; /** MM: ak chyba alokacie pripadne dsharp-u*/
    }
    return R;
}


/* cb_dsharp -- disjoint-sharp product between a cube and a cover */
/** kontroluje parametre, ktore NEdealokuje */
/** vracia nuvu strukturu, pripadne NULL */
pcover cb_dsharp(pset c, pset_family T)
{
    pcube last, p;
    pcover Y, Y1;
    
    if ( (c==NULL) || (T==NULL) ) return NULL; /** MM: ak chyba parametrov */

    if (T->count == 0) {
    	Y = sf_addset(new_cover(1), c);
    } else {
    	Y = new_cover(T->count);
    	if (Y==NULL) return NULL; /** MM: ak chyba alokacie */
    	set_copy(GETSET(Y,Y->count++), c);
    	foreach_set(T, last, p) {
    	    Y1 = cb1_dsharp(Y, p);
    	    free_cover(Y);
    	    Y = Y1;
    	    if (Y1==NULL) return NULL; /** MM: ak chyba alokacie */
    	}
    }
    return Y;
}


/* dsharp -- form the disjoint-sharp product between two cubes */
/** kontroluje parametre, ktore NEdealokuje */
/** vracia nuvu strukturu, pripadne NULL */
pcover dsharp(pset a, pset b)
{
    register pcube mask, diff, _and, temp, temp1 = cube->temp[0];
    int var;
    pcover r;
    
    if ( (a==NULL) || (b==NULL) ) return NULL; /** MM: ak chyba parametrov */

    r = new_cover(cube->num_vars);
    if (r==NULL) return NULL; /** MM: ak chyba alokacie */
    
    if (cdist0(a, b)) {
    	diff = set_diff(new_cube(), a, b);
    	_and = set_and(new_cube(), a, b);
    	mask = new_cube();
    	if ( (diff==NULL) || (_and==NULL) || (mask==NULL) ){ /** MM: ak chyba alokacie */
            free_cube(diff); free_cube(_and); free_cube(mask);
            sf_free(r);
            return NULL;
        }
    	for(var = 0; var < cube->num_vars; var++) {
    	    /* check if position var of "a and not b" is not empty */
    	    if (! setp_disjoint(diff, cube->var_mask[var])) {
        
        		/* coordinate var equals the difference between a and b */
        		temp = GETSET(r, r->count++);
        		(void) set_and(temp, diff, cube->var_mask[var]);
        
        		/* coordinates 0 ... var-1 equal the intersection */
        		INLINEset_and(temp1, _and, mask);
        		INLINEset_or(temp, temp, temp1);
        
        		/* coordinates var+1 .. cube->num_vars equal a */
        		set_or(mask, mask, cube->var_mask[var]);
        		INLINEset_diff(temp1, a, mask);
        		INLINEset_or(temp, temp, temp1);
    	    }
    	}
    	free_cube(diff);
    	free_cube(_and);
    	free_cube(mask);
    } else {
    	r = sf_addset(r, a); /** MM: tuto chybu si osetri volajuci */
    }
    return r;
}

/* cv_intersect -- form the intersection of two covers */

#define MAGIC 500               /* save 500 cubes before containment */

/** MM: uz kontroluje parametre, ktore NEdealokuje */
/** MM: chyba cez NULL */
pcover cv_intersect(pset_family A, pset_family B)
{
    register pcube pi, pj, lasti, lastj, pt;
    pcover T, Tsave = NULL;
    
    if ( (A==NULL) || (B==NULL) ) return NULL; /** MM: ak chyba paramtetrov */

    /* How large should each temporary result cover be ? */
    T = new_cover(MAGIC); /** MM: prirastkova set_family */
    if (T==NULL) return NULL; /** MM: ak chyba alokacie */
    pt = T->data;

    /* Form pairwise intersection of each cube of A with each cube of B */
    foreach_set(A, lasti, pi) {
    	foreach_set(B, lastj, pj) {
    	    if (cdist0(pi, pj)) {
        		(void) set_and(pt, pi, pj);
        		if (++T->count >= T->capacity) { /** MM: ak uz je T zaplnene, spoji sa s Tsave a opat sa vytvori nove T */
        		    if (Tsave == NULL){
            			Tsave = sf_contain(T);
            			if (Tsave==NULL) return NULL; /** MM: ak chyba alokacie; vsetko je uz dealokovane */
                    }
        		    else{
               			Tsave = sf_union(Tsave, sf_contain(T));
               			if (Tsave==NULL) return NULL; /** MM: ak chyba alokacie; vsetko je uz dealokovane */
                    }
        		    T = new_cover(MAGIC);
        		    if (T==NULL){ /** MM: ak chyba alokacie */
                        sf_free(Tsave);
                        return NULL;
                    }
        		    pt = T->data;
        		} else
        		    pt += T->wsize;
    	    }
    	}
    }


    if (Tsave == NULL)
    	Tsave = sf_contain(T);
    else
    	Tsave = sf_union(Tsave, sf_contain(T));
    return Tsave; /** MM: chybu si musi skontrolovat volajuci; vsetko je dealokovane */
}
