/*
 * Revision Control Information
 *
 * $Source$
 * $Author$
 * $Revision$
 * $Date$
 *
 */
/*
 *  Module: espresso.c
 *  Purpose: The main espresso algorithm
 *
 *  Returns a minimized version of the ON-set of a function
 *
 *  The following global variables affect the operation of Espresso:
 *
 *  MISCELLANEOUS:
 *      trace
 *          print trace information as the minimization progresses
 *
 *      remove_essential
 *          remove essential primes
 *
 *      single_expand
 *          if true, stop after first expand/irredundant
 *
 *  LAST_GASP or SUPER_GASP strategy:
 *      use_super_gasp
 *          uses the super_gasp strategy rather than last_gasp
 *
 *  SETUP strategy:
 *      recompute_onset
 *          recompute onset using the complement before starting
 *
 *      unwrap_onset
 *          unwrap the function output part before first expand
 *
 *  MAKE_SPARSE strategy:
 *      force_irredundant
 *          iterates make_sparse to force a minimal solution (used
 *          indirectly by make_sparse)
 *
 *      skip_make_sparse
 *          skip the make_sparse step (used by opo only)
 */

#include "espresso.h"

pcover espresso(F, D1, R)
pcover F, D1, R;
{
    pcover E, D, Fsave;
    pset last, p;
    cost_t cost, best_cost;

begin:
    Fsave = sf_save(F);        /* save original function */
    D = sf_save(D1);        /* make a scratch copy of D */

    /* Setup has always been a problem */
    if (recompute_onset) {
    EXEC(E = simplify(cube1list(F)),     "SIMPLIFY   ", E);
    free_cover(F);
    F = E;
    }
    cover_cost(F, &cost);
    if (unwrap_onset && (cube.part_size[cube.num_vars - 1] > 1)
      && (cost.out != cost.cubes*cube.part_size[cube.num_vars-1])
      && (cost.out < 5000))
    EXEC(F = sf_contain(unravel(F, cube.num_vars - 1)), "SETUP      ", F);

    /* Initial expand and irredundant */
    foreach_set(F, last, p) {
    RESET(p, PRIME);
    }
    EXECUTE(F = expand(F, R, FALSE), EXPAND_TIME, F, cost);
    EXECUTE(F = irredundant(F, D), IRRED_TIME, F, cost);

    if (! single_expand) {
    if (remove_essential) {
        EXECUTE(E = essential(&F, &D), ESSEN_TIME, E, cost);
    } else {
        E = new_cover(0);
    }

    cover_cost(F, &cost);
    do {

        /* Repeat inner loop until solution becomes "stable" */
        do {
        copy_cost(&cost, &best_cost);
        EXECUTE(F = reduce(F, D), REDUCE_TIME, F, cost);
        EXECUTE(F = expand(F, R, FALSE), EXPAND_TIME, F, cost);
        EXECUTE(F = irredundant(F, D), IRRED_TIME, F, cost);
        } while (cost.cubes < best_cost.cubes);

        /* Perturb solution to see if we can continue to iterate */
        copy_cost(&cost, &best_cost);
        if (use_super_gasp) {
        F = super_gasp(F, D, R, &cost);
        if (cost.cubes >= best_cost.cubes)
            break;
        } else {
        F = last_gasp(F, D, R, &cost);
        }

    } while (cost.cubes < best_cost.cubes ||
        (cost.cubes == best_cost.cubes && cost.total < best_cost.total));

    /* Append the essential cubes to F */
    F = sf_append(F, E);                /* disposes of E */
    if (trace) size_stamp(F, "ADJUST     ");
    }

    /* Free the D which we used */
    free_cover(D);

    /* Attempt to make the PLA matrix sparse */
    if (! skip_make_sparse) {
    F = make_sparse(F, D1, R);
    }

    /*
     *  Check to make sure function is actually smaller !!
     *  This can only happen because of the initial unravel.  If we fail,
     *  then run the whole thing again without the unravel.
     */
    if (Fsave->count < F->count) {
    free_cover(F);
    F = Fsave;
    unwrap_onset = FALSE;
    goto begin;
    } else {
    free_cover(Fsave);
    }

    return F;
}
