#include "espresso.h"


static void dump_irredundant(pset_family E, pset_family Rt, pset_family Rp, sm_matrix *table);
static pcover do_minimize(pset_family F, pset_family D, pset_family R, int exact_cover, int weighted);


/*
 *  minimize_exact -- main entry point for exact minimization
 *
 *  Global flags which affect this routine are:
 *
 *      debug
 *      skip_make_sparse
 */
/** MM: pri chybe vrati NULL a dealokuje F */
pcover
minimize_exact(pset_family F, pset_family D, pset_family R, int exact_cover)
{
    return do_minimize(F, D, R, exact_cover, /*weighted*/ 0);
}

/** MM: tato funkcia nie je nikdy volana - ponechana len pre ukazku */
/** MM: pri chybe vrati NULL a dealokuje F */
pcover
minimize_exact_literals(pset_family F, pset_family D, pset_family R, int exact_cover)
{
    return do_minimize(F, D, R, exact_cover, /*weighted*/ 1);
}


/** MM: pri chybe vrati NULL a dealokuje F */
/** MM: kontroluje make_sparse a sm_minimum_cover */
static pcover
do_minimize(pset_family F, pset_family D, pset_family R, int exact_cover, int weighted)
{
    pcover newF, E, Rt, Rp, Ftmp;
    pset p, last;
    int heur, level, *weights;
    sm_matrix *table;
    sm_row *cover;
    sm_element *pe;
    int debug_save = debug; /** MM: ulozenie povodneho debugu */

    if (debug & EXACT) {
    	debug |= (IRRED | MINCOV);
    }
    
    level = (debug & MINCOV) ? 4 : 0;
    heur = ! exact_cover;

    /* Generate all prime implicants */
    /** MM: upravene kvoli vyskytujucemu sa memory leaku */
    EXEC(Ftmp = primes_consensus(cube2list(F, D)), "PRIMES     ", F);
	sf_free(F);
	F = Ftmp;

    /* Setup the prime implicant table */
    EXEC(irred_split_cover(F, D, &E, &Rt, &Rp), "ESSENTIALS ", E); /** MM: vrati E, Rt a Rp */
    EXEC(table = irred_derive_table(D, E, Rp),  "PI-TABLE   ", Rp);

    /* Solve either a weighted or nonweighted covering problem */
    if (weighted) {
    	/* correct only for all 2-valued variables */
    	weights = ALLOC(int, F->count);
    	foreach_set(Rp, last, p) {
    	    weights[SIZE(p)] = cube->size - set_ord(p);
    	}
    } else {
    	weights = NIL(int);
    }
    EXEC(cover=sm_minimum_cover(table,weights,heur,level), "MINCOV     ", F);
    if (weights != 0) {
    	FREE(weights);
    }
    if (cover==NULL){ /** MM: ak chyba v sm_minimum_cover */
        free_cover(E);
        free_cover(Rt);
        free_cover(Rp);
        sm_free(table);
        free_cover(F);
        debug = debug_save;
        return NULL;
    }

    if (debug & EXACT) {
    	dump_irredundant(E, Rt, Rp, table);
    }

    /* Form the result cover */
    newF = new_cover(100);
    foreach_set(E, last, p) {
    	newF = sf_addset(newF, p);
    }
    sm_foreach_row_element(cover, pe) {
    	newF = sf_addset(newF, GETSET(F, pe->col_num));
    }

    free_cover(E);
    free_cover(Rt);
    free_cover(Rp);
    sm_free(table);
    sm_row_free(cover);
    free_cover(F);

    /* Attempt to make the results more sparse */
    debug &= ~ (IRRED | SHARP | MINCOV);
    if (! skip_make_sparse && R != 0) {
    	newF = make_sparse(newF, D, R); /** MM: chybu netreba kontrolovat - osetri si ju volajuci */
    }

    debug = debug_save;
    return newF;
}

/** MM: upravena, aby sa zatvoril len subor a nie hocijaky iny stream okrem standardoutput */
/** MM: pridany aj retazec "(stream)" */
static void
dump_irredundant(pset_family E, pset_family Rt, pset_family Rp, sm_matrix *table)
{
    FILE *fp_pi_table, *fp_primes; /* filepointre na subory s naznom nazov_suboru.primes a nazov_suboru.pi */
    /* vypisy sa snazi presmerovat do suborov */
    pPLA PLA;
    pset last, p;
    char *file;
    bool3 primes_file = FALSE3; /** MM: na indikaciu, ci boli otvorene subory alebo boli pouzite streamy */
    bool3 pi_file = FALSE3;
    
    if (filename == 0 || (strcmp(filename, "(string)") == 0) || (strcmp(filename, "(stream)") == 0) ) {
    	fp_pi_table = fp_primes = standardoutput;
    } else {
    	file = ALLOC(char, strlen(filename)+20);
    	primes_file = TRUE3; pi_file = TRUE3; /** MM: predpoklada sa, ze subory sa podari vytvorit */
    	(void) snprintf(file, strlen(filename)+20, "%s.primes", filename); /** MM: snaha otvorit subor s koncovkou .primes */ /** MM: zmenene na secure version */
    	if ((fp_primes = fopen(file, "w")) == NULL) {
    	    if (erroroutput!=NULL) fprintf(erroroutput, "espresso: Unable to open %s\n", file);
    	    fp_primes = standardoutput; primes_file = FALSE3;
    	}
    	(void) snprintf(file, strlen(filename)+20, "%s.pi", filename); /* snaha otvorit subor s koncovkou .pi */ /** MM: zmenene na secure version */
    	if ((fp_pi_table = fopen(file, "w")) == NULL) {
    	    if (erroroutput!=NULL) fprintf(erroroutput, "espresso: Unable to open %s\n", file);
    	    fp_pi_table = standardoutput; pi_file = FALSE3;
    	}
    	FREE(file);
    }

    if (fp_primes!=NULL) { /** MM: vykona sa, len ak existuje validny fp na fp_primes */
        PLA = new_PLA();
    
        PLA_labels(PLA);
    
        fpr_header(fp_primes, PLA, F_type);
        free_PLA(PLA);
   
        fprintf(fp_primes, "# Essential primes are\n");
        foreach_set(E, last, p) {
        	(void) fprintf(fp_primes, "%s\n", pc1(p));
        }
        fprintf(fp_primes, "# Totally redundant primes are\n");
        foreach_set(Rt, last, p) {
        	(void) fprintf(fp_primes, "%s\n", pc1(p));
        }
        fprintf(fp_primes, "# Partially redundant primes are\n");
        foreach_set(Rp, last, p) {
        	(void) fprintf(fp_primes, "%s\n", pc1(p));
        }
        if (primes_file == TRUE3) { /** MM: stream sa zatvori, len ak bol vystup subor */
        	(void) fclose(fp_primes);
        }
    }
	
	if (fp_pi_table!=NULL) { /** MM: vykona sa, len ak existuje validny fp na fp_pi_table */
        sm_write(fp_pi_table, table); /* jedine tu sa zapisuje do fp_pi_table */
        if (pi_file == TRUE3) {
        	(void) fclose(fp_pi_table);
        }
    }
}
