#include "espresso.h"

/** MM: int na indikaciu chyby */
/** MM: -1 pri neuspechu */
/** MM: autorove returny boli brane ako "nie chyby" */
int map_dcset(pPLA PLA)
{
    int var, i;
    pcover Tplus=NULL, Tminus=NULL, Tplusbar=NULL, Tminusbar=NULL; /** MM: pridana dodatocna inicializacia kvoli neskorsiemu mazaniu pri chybe */
    pcover newf, term1, term2, dcset, dcsetbar=NULL;
    pcube cplus, cminus, last, p;
    int tmp; /** MM: pre navratove hodnoty */
    
    if (PLA==NULL) return -1; /** MM: ak chyba parametra */

    if (PLA->label == NIL(char *) || PLA->label[0] == NIL(char))
	return 0; /** MM: autorov return */

    /* try to find a binary variable named "DONT_CARE" */
    var = -1;
    for(i = 0; i < cube->num_binary_vars * 2; i++) {
    	if (strncmp(PLA->label[i], "DONT_CARE", 9) == 0 ||
    	  strncmp(PLA->label[i], "DONTCARE", 8) == 0 ||
    	  strncmp(PLA->label[i], "dont_care", 9) == 0 ||
    	  strncmp(PLA->label[i], "dontcare", 8) == 0) {
    	    var = i/2;
    	    break;
    	}
    }
    if (var == -1) {
        return 0; /** MM: autorov return */
    }

    /* form the cofactor cubes for the don't-care variable */
    cplus = set_save(cube->fullset);
    cminus = set_save(cube->fullset);
    if ( (cplus==NULL) || (cminus==NULL) ) { /** MM: ak chyba alokacie */
        set_free(cplus); set_free(cminus);
        return -1;
    }
    set_remove(cplus, var*2);
    set_remove(cminus, var*2 + 1);

    /* form the don't-care set */
    EXEC(tmp = simp_comp(cofactor(cube1list(PLA->F), cplus), &Tplus, &Tplusbar),
	"simpcomp+", Tplus);
	if (tmp==-1){ /** MM: ak chyba v simp_comp */
        free_cover(Tplus); free_cover(Tplusbar);
        return -1;
    }
    EXEC(tmp = simp_comp(cofactor(cube1list(PLA->F), cminus), &Tminus, &Tminusbar),
	"simpcomp-", Tminus);
	if (tmp==-1){ /** MM: ak chyba v simp_comp */
        free_cover(Tplus); free_cover(Tplusbar);
        free_cover(Tminus); free_cover(Tminusbar);
        return -1;
    }
    EXEC(term1 = cv_intersect(Tplus, Tminusbar), "term1    ", term1);
    EXEC(term2 = cv_intersect(Tminus, Tplusbar), "term2    ", term2);
    EXEC(dcset = sf_union(term1, term2), "union     ", dcset); /** MM: term1 a term2 su uz disposed of */
    if (dcset==NULL){ /** MM: ak chyba v predchadzajucich 3 funkciach */
        free_cover(Tplus); free_cover(Tminus);
        free_cover(Tplusbar); free_cover(Tminusbar);
        return -1;
    }
    /** MM: treba zavolat sf_free(PLA->D) */
    sf_free(PLA->D); PLA->D=NULL;
    EXEC(tmp = simp_comp(cube1list(dcset), &PLA->D, &dcsetbar), "simplify", PLA->D);
    if (tmp==-1){ /** MM: ak chyba, nastavia sa hodnoty tak, aby spolocna vetva po else vratila -1 */
        newf = NULL; /** MM: ak je newf NULL, bude NULL aj PLA->F a podmienka vyusti k ukonceniu funkcie */
    } else {
        EXEC(newf = cv_intersect(PLA->F, dcsetbar), "separate  ", PLA->F);
    }
    free_cover(PLA->F);
    PLA->F = newf;
    free_cover(Tplus);
    free_cover(Tminus);
    free_cover(Tplusbar);
    free_cover(Tminusbar);
    free_cover(dcsetbar);
    if (PLA->F==NULL){ /** MM: ak chyba v predchadzajucich funkciach */
        return -1;
    }

    /* remove any cubes dependent on the DONT_CARE variable */
    (void) sf_active(PLA->F);
    foreach_set(PLA->F, last, p) {
    	if (! is_in_set(p, var*2) || ! is_in_set(p, var*2+1)) {
    	    RESET(p, ACTIVE);
    	}
    }
    PLA->F = sf_inactive(PLA->F);

    /* resize the cube and delete the don't-care variable */
    setdown_cube();
    for(i = 2*var+2; i < cube->size; i++) {
    	PLA->label[i-2] = PLA->label[i];
    }
    for(i = var+1; i < cube->num_vars; i++) {
    	cube->part_size[i-1] = cube->part_size[i];
    }
    cube->num_binary_vars--;
    cube->num_vars--;
    tmp = cube_setup(); /** MM: num_binary_vars snad nekleslo na zapornu hodnotu... */
    if (tmp==-1) return -1; /** MM: ak chybne zadane parametre kocky */
    PLA->F = sf_delc(PLA->F, 2*var, 2*var+1);
    PLA->D = sf_delc(PLA->D, 2*var, 2*var+1);
    if ( (PLA->F==NULL) || (PLA->D==NULL) ) return -1; /** MM: ak chyba v sf_delc aka sf_delcol */
    return 0;
}

/** MM: pridana kontrola pre complement */
/** MM: funkcia je volana len z read_pla, takze nie je potrebne uviest PLA do povodneho stavu! :-) */
/** MM: chyba cez -1 */
int map_output_symbolic(pPLA PLA)
{
    pset_family newF, newD, Ftmp;
    pset compress;
    symbolic_t *p1;
    symbolic_list_t *p2;
    int i, bit, tot_size, base, old_size, tmp;

    /* Remove the DC-set from the ON-set (is this necessary ??) */
    if (PLA->D->count > 0) {
    	Ftmp = complement(cube2list(PLA->D, PLA->R));
    	if (Ftmp == NULL){ /** MM: ak chyba alokacie */
            return -1;
        }else {
            sf_free(PLA->F);
            PLA->F = Ftmp;
        }
    }

    /* tot_size = width added for all symbolic variables */
    tot_size = 0;
    for(p1=PLA->symbolic_output; p1!=NIL(symbolic_t); p1=p1->next) {
    	for(p2=p1->symbolic_list; p2!=NIL(symbolic_list_t); p2=p2->next) {
    	    if (p2->pos<0 || p2->pos>=cube->part_size[cube->output]) {
        		fatal2("symbolic-output index out of range"); /** MM: ak nejaka chyba */
        		return -1; /** MM: netreba nic dealokovat */
    	    }
    	}
    	tot_size += 1 << p1->symbolic_list_length;
    }

    /* adjust the indices to skip over new outputs */
    for(p1=PLA->symbolic_output; p1!=NIL(symbolic_t); p1=p1->next) {
    	for(p2=p1->symbolic_list; p2!=NIL(symbolic_list_t); p2=p2->next) {
    	    p2->pos += tot_size;
    	}
    }

    /* resize the cube structure -- add enough for the one-hot outputs */
    old_size = cube->size;
    cube->part_size[cube->output] += tot_size;
    setdown_cube();
    cube_setup();

    /* insert space in the output part for the one-hot output */
    base = cube->first_part[cube->output];
    PLA->F = sf_addcol(PLA->F, base, tot_size); /** MM: da sa povedat, sf_addcol disposes of A */
    PLA->D = sf_addcol(PLA->D, base, tot_size);
    PLA->R = sf_addcol(PLA->R, base, tot_size);
    if ( (PLA->F==NULL) || (PLA->D==NULL) || (PLA->R==NULL) ) return -1; /** MM: ak chyba alokacie */

    /* do the real work */
    for(p1=PLA->symbolic_output; p1!=NIL(symbolic_t); p1=p1->next) {
    	newF = new_cover(100);
    	newD = new_cover(100);
    	if ( (newF==NULL) || (newD==NULL) ) { /** MM: ak chyba alokacie */
            sf_free(newF); sf_free(newD);
            return -1;
        }
    	find_inputs(NIL(set_family_t), PLA, p1->symbolic_list, base, 0,
    			    &newF, &newD);
    
    	free_cover(PLA->F);
    	PLA->F = newF;
    /*
     *  retain OLD DC-set -- but we've lost the don't-care arc information
     *  (it defaults to branch to the zero state)
    	free_cover(PLA->D);
    	PLA->D = newD;
     */
    	free_cover(newD);
    	base += 1 << p1->symbolic_list_length;
    }

    /* delete the old outputs, and resize the cube */
    compress = set_full(newF->sf_size);
    if (compress == NULL) return -1; /** MM: ak chyba alokacie */
    for(p1=PLA->symbolic_output; p1!=NIL(symbolic_t); p1=p1->next) {
    	for(p2=p1->symbolic_list; p2!=NIL(symbolic_list_t); p2=p2->next) {
    	    bit = cube->first_part[cube->output] + p2->pos;
    	    set_remove(compress, bit);
    	}
    }
    cube->part_size[cube->output] -= newF->sf_size - set_ord(compress);
    setdown_cube();
    cube_setup();
    PLA->F = sf_compress(PLA->F, compress);
    PLA->D = sf_compress(PLA->D, compress);
    if ( (PLA->F == NULL) || (PLA->D == NULL) ) { /** MM: ak chyba alokacie */
        set_free(compress); return -1;
    }
    if (cube->size != PLA->F->sf_size){
         fatal2("map_output_symbolic error"); /** MM: ak neznama chyba */
         set_free(compress);
         return -1;
    }

    /* Quick minimization */
    PLA->F = sf_contain(PLA->F);
    PLA->D = sf_contain(PLA->D);
    for(i = 0; i < cube->num_vars; i++) {
    	PLA->F = d1merge(PLA->F, i);
    	PLA->D = d1merge(PLA->D, i);
    }
    PLA->F = sf_contain(PLA->F);
    PLA->D = sf_contain(PLA->D);
    /** MM: detekciu chyby si mozem dovolit odlozit, lebo funkcie uz vedia pracovat NULL */
    if ( (PLA->F == NULL) || (PLA->D == NULL) ) { /** MM: ak chyba alokacie */
        sf_free(PLA->F); sf_free(PLA->D); set_free(compress); return -1;
    }

    free_cover(PLA->R);
    PLA->R = new_cover(0);
    if (PLA->R==NULL) { set_free(compress); return -1; } /** MM: ak chyba alokacie */

    tmp = symbolic_hack_labels(PLA, PLA->symbolic_output,
			    compress, cube->size, old_size, tot_size);
    set_free(compress);
    if (tmp!=0) return -1; /** MM: kontrola chyby v symbolic_hack_labels */
    return 0;
}


void find_inputs(pset_family A, pPLA PLA, symbolic_list_t *list, int base, int value, pset_family *newF, pset_family *newD)
{
    pcover S, S1;
    register pset last, p;

    /*
     *  A represents th 'input' values for which the outputs assume
     *  the integer value 'value
     */
    if (list == NIL(symbolic_list_t)) {
    	/*
    	 *  Simulate these inputs against the on-set; then, insert into the
    	 *  new on-set a 1 in the proper position
    	 */
    	S = cv_intersect(A, PLA->F);
    	foreach_set(S, last, p) {
    	    set_insert(p, base + value);
    	}
    	*newF = sf_append(*newF, S);
    
    	/*
    	 *  'simulate' these inputs against the don't-care set
    	S = cv_intersect(A, PLA->D);
    	*newD = sf_append(*newD, S);
    	 */

    } else {
    	/* intersect and recur with the OFF-set */
    	S = cof_output(PLA->R, cube->first_part[cube->output] + list->pos);
    	if (A != NIL(set_family_t)) {
    	    S1 = cv_intersect(A, S);
    	    free_cover(S);
    	    S = S1;
    	}
    	find_inputs(S, PLA, list->next, base, value*2, newF, newD);
    	free_cover(S);
    
    	/* intersect and recur with the ON-set */
    	S = cof_output(PLA->F, cube->first_part[cube->output] + list->pos);
    	if (A != NIL(set_family_t)) {
    	    S1 = cv_intersect(A, S);
    	    free_cover(S);
    	    S = S1;
    	}
    	find_inputs(S, PLA, list->next, base, value*2 + 1, newF, newD);
    	free_cover(S);
    }
}

/** MM: int na indikaciu chyby */
int map_symbolic(pPLA PLA)
{
    symbolic_t *p1;
    symbolic_list_t *p2;
    int var, base, num_vars, num_binary_vars, *new_part_size;
    int new_size, size_added, num_deleted_vars, num_added_vars, newvar, tmp;
    pset compress;

    /* Verify legal values are in the symbolic lists */
    for(p1 = PLA->symbolic; p1 != NIL(symbolic_t); p1 = p1->next) {
    	for(p2=p1->symbolic_list; p2!=NIL(symbolic_list_t); p2=p2->next) {
    	    if (p2->variable  < 0 || p2->variable >= cube->num_binary_vars) {
        		fatal2(".symbolic requires binary variables");
        		return -1;
    	    }
    	}
    }

    /*
     *  size_added = width added for all symbolic variables
     *  num_deleted_vars = # binary variables to be deleted
     *  num_added_vars = # new mv variables
     *  compress = a cube which will be used to compress the set families
     */
    size_added = 0;
    num_added_vars = 0;
    for(p1 = PLA->symbolic; p1 != NIL(symbolic_t); p1 = p1->next) {
    	size_added += 1 << p1->symbolic_list_length;
    	num_added_vars++;
    }
    compress = set_full(PLA->F->sf_size + size_added);
    if (compress == NULL) return -1; /** MM: ak chyba alokacie */
    for(p1 = PLA->symbolic; p1 != NIL(symbolic_t); p1 = p1->next) {
    	for(p2=p1->symbolic_list; p2!=NIL(symbolic_list_t); p2=p2->next) {
    	    set_remove(compress, p2->variable*2);
    	    set_remove(compress, p2->variable*2+1);
    	}
    }
    num_deleted_vars = ((PLA->F->sf_size + size_added) - set_ord(compress))/2;

    /* compute the new cube constants */
    num_vars = cube->num_vars - num_deleted_vars + num_added_vars;
    num_binary_vars = cube->num_binary_vars - num_deleted_vars;
    new_size = cube->size - num_deleted_vars*2 + size_added;
    new_part_size = ALLOC(int, num_vars);
    if (new_part_size == NULL) { set_free(compress); return -1; }/** MM: ak chyba alokacie */
    new_part_size[num_vars-1] = cube->part_size[cube->num_vars-1];
    for(var = cube->num_binary_vars; var < cube->num_vars-1; var++) {
    	new_part_size[var-num_deleted_vars] = cube->part_size[var];
    }

    /* re-size the covers, opening room for the new mv variables */
    base = cube->first_part[cube->output];
    PLA->F = sf_addcol(PLA->F, base, size_added); /** MM: da sa povedat, sf_addcol disposes of A */
    PLA->D = sf_addcol(PLA->D, base, size_added);
    PLA->R = sf_addcol(PLA->R, base, size_added);
    if ( (PLA->F==NULL) || (PLA->D==NULL) || (PLA->R==NULL) ) { FREE(new_part_size); set_free(compress); return -1; } /** MM: ak chyba alokacie */

    /* compute the values for the new mv variables */
    newvar = (cube->num_vars - 1) - num_deleted_vars;
    for(p1 = PLA->symbolic; p1 != NIL(symbolic_t); p1 = p1->next) {
    	PLA->F = map_symbolic_cover(PLA->F, p1->symbolic_list, base);
    	PLA->D = map_symbolic_cover(PLA->D, p1->symbolic_list, base);
    	PLA->R = map_symbolic_cover(PLA->R, p1->symbolic_list, base);
    	if ( (PLA->F == NULL) || (PLA->D == NULL) || (PLA->R == NULL) ) { /** MM: ak chyba, tak pokrytie bolo dealokovane */
            FREE(new_part_size); set_free(compress); return -1;
        }
    	base += 1 << p1->symbolic_list_length;
    	new_part_size[newvar++] = 1 << p1->symbolic_list_length;
    }

    /* delete the binary variables which disappear */
    PLA->F = sf_compress(PLA->F, compress); /** MM: disposes of A */
    PLA->D = sf_compress(PLA->D, compress);
    PLA->R = sf_compress(PLA->R, compress);
    if ( (PLA->F == NULL) || (PLA->D == NULL) || (PLA->R == NULL) ) { /** MM: ak chyba alokacie */
        FREE(new_part_size); set_free(compress); return -1;
    }

    tmp = symbolic_hack_labels(PLA, PLA->symbolic, compress,
		new_size, cube->size, size_added);
	if (tmp != 0){ /** MM: cube->size musi ostat povodna, inak dojde k padu pro dealokacii PLA->label[i] */
        FREE(new_part_size);
        set_free(compress);
        setdown_cube(); /** MM: len pre istotu... */
        return -1;
    }
    setdown_cube();
    FREE(cube->part_size);
    cube->num_vars = num_vars; /** MM: symbolic snad nikdy neupravi hodnoty, aby to bolo zle */
    cube->num_binary_vars = num_binary_vars;
    cube->part_size = new_part_size;
    cube_setup();
    set_free(compress);
    return 0;
}


/** MM: len upravuje T*/
/** MM: list moze byt aj NULL */
/** pri chybe vrati NULL a dealokuje parameter!!!! */
/** volana len 3x v map_symbolic */
pcover map_symbolic_cover(pset_family T, symbolic_list_t *list, int base)
{
    pset last, p;
    int tmp;
    
    if (T == NULL) return NULL; /** MM: ak chyba parametra */
    
    foreach_set(T, last, p) {
    	tmp = form_bitvector(p, base, 0, list);
    	if (tmp != 0){ /** MM: ak vznikla nejaka chyba */
            sf_free(T);
            return NULL;
        }
    }
    return T;
}

/** MM: int pre indikaciu chyby */
int form_bitvector(pset p, int base, int value, symbolic_list_t *list)
       			/* p = old cube, looking at binary variables */
         		/* base = where in mv cube the new variable starts */
          		/* value = current value for this recursion */
                /* list = current place in the symbolic list */
{   
    int ret = 0;
    
    if (list == NIL(symbolic_list_t)) {
    	set_insert(p, base + value);
    } else {
    	switch(GETINPUT(p, list->variable)) {
    	    case ZERO:
        		ret = form_bitvector(p, base, value*2, list->next);
        		break;
    	    case ONE:
        		ret = form_bitvector(p, base, value*2+1, list->next);
        		break;
    	    case TWO:
        		ret = form_bitvector(p, base, value*2, list->next);
        		ret += form_bitvector(p, base, value*2+1, list->next);
        		break;
    	    default:
        		fatal2("bad cube in form_bitvector");
        		return -1;
    	}
    }
    return ret;
}

/** MM: int kvoli indikacii chybyô -1 pre chybu */
/** MM: volana len dvomi funkciami v read_pla */
/** MM: zmaze PLA->label[x], lebo moze dojst k zmene velkosti kocky */
int symbolic_hack_labels(pPLA PLA, symbolic_t *list, pset compress, int new_size, int old_size, int size_added)
{
    int i, base;
    char **oldlabel;
    symbolic_t *p1;
    symbolic_label_t *p3;
    
    if ( (PLA==NULL) || (list==NULL) ) return -1; /** MM: ak chyba parametra */

    /* hack with the labels */
    if ((oldlabel = PLA->label) == NIL(char *))
    	return 0;
    PLA->label = ALLOC(char *, new_size);
    if ( (PLA->label==NULL) && (new_size != 0) ) { /** MM: ak chyba alokacie */
        for ( int i = 0; i < old_size; i++)
            FREE(oldlabel[i]);
        FREE(oldlabel);
        return -1; /** MM: PLA->label je NULL */
    }
    for(i = 0; i < new_size; i++) {
    	PLA->label[i] = NIL(char);
    }

    /* copy the binary variable labels and unchanged mv variable labels */
    base = 0;
    for(i = 0; i < cube->first_part[cube->output]; i++) {
    	if (is_in_set(compress, i)) {
    	    PLA->label[base++] = oldlabel[i];
    	} else {
    	    if (oldlabel[i] != NIL(char)) {
        		FREE(oldlabel[i]);
    	    }
    	}
    }

    /* add the user-defined labels for the symbolic outputs */
    for(p1 = list; p1 != NIL(symbolic_t); p1 = p1->next) {
    	p3 = p1->symbolic_label;
    	for(i = 0; i < (1 << p1->symbolic_list_length); i++) {
    	    if (p3 == NIL(symbolic_label_t)) { /** MM: ak PLA nema zoznam symbolic_labels, tak sa alokuje nova pamat */
        		PLA->label[base+i] = ALLOC(char, 10);
        		if (PLA->label[base+i] == NULL) goto cleanup; /** MM: ak chyba alokacie */
        		(void) snprintf(PLA->label[base+i], 10, "X%d", i); /** MM: zmenene na secure version */
    	    } else {
        		PLA->label[base+i] = p3->label; /** MM: ak ma PLA zoznam symbolic_labels, len sa priradia ukazatele na tieto retazce */
        		p3 = p3->next; /** MM: Cize po uvolneni labels budu retazce v symbolic_labels neplatne a vo free_PLA treba na to davat pozor!!! */
    	    }
    	}
    	base += 1 << p1->symbolic_list_length;
    }

    /* copy the labels for the binary outputs which remain */
    for(i = cube->first_part[cube->output]; i < old_size; i++) {
    	if (is_in_set(compress, i + size_added)) {
    	    PLA->label[base++] = oldlabel[i];
    	} else {
    	    if (oldlabel[i] != NIL(char)) {
        		FREE(oldlabel[i]);
    	    }
    	}
    }
    FREE(oldlabel);
    return 0;
    
/** MM: ak chyba v cykle, treba zmazat zbytky oldlabels */
cleanup:
    /** MM: dealokacia starych labelov */
    for (int i = 0; i<old_size; i++){
        FREE(oldlabel[i]); /** MM: FREE zaberie, len ak oldlabel[i] nie je NULL */
    }
    FREE(oldlabel);
    /** MM: dealokacia novych labelov */
    for ( int i = 0; i < new_size; i++)
        FREE(PLA->label[i]);
    FREE(PLA->label);
    return -1;
}
