package ifs.extension;

import ifs.model.*;
import ifs.solver.*;
import ifs.util.*;

import java.util.*;

/**
 * Computation of violated initial values (minimal perturbation problem).
 * <br><br>
 * It is using {@link Constraint#isConsistent(Value, Value)} to find out what 
 * initial values (of different variables) cannot be assigned when an arbitrary value is
 * assigned to a variable. This information is computed in advance, before the solver is
 * executed. It is used for better estimation of perturbation penalty (see 
 * {@link ifs.perturbations.PerturbationsCounter}) when a value is to be assigned to a variable.
 *
 * @author <a href="mailto:muller@ktiml.mff.cuni.cz">Tomas Muller</a>
 * @version 1.0
 */
public class ViolatedInitials extends Extension {
    private static org.apache.log4j.Logger sLogger = org.apache.log4j.Logger.getLogger(ViolatedInitials.class);
    private Hashtable iViolatedInitials = new Hashtable();
    
    public ViolatedInitials(Solver solver, DataProperties properties) {
        super(solver, properties);
    }
    
    /** Compute the violations between any value and all other initial values */
    public boolean init() {
        sLogger.info("Computation of violated initials enabled.");
        for (Enumeration i = getModel().variables().elements(); i.hasMoreElements();) {
            Variable variable = (Variable)i.nextElement();
            if (variable.getInitialAssignment() == null) continue;
            for (Enumeration i1 = variable.constraints().elements(); i1.hasMoreElements();) {
                Constraint constraint = (Constraint)i1.nextElement();
                Vector conflicts = conflictValues(constraint, variable.getInitialAssignment());
                for (Enumeration i2 = conflicts.elements(); i2.hasMoreElements(); ) {
                    Value value = (Value)i2.nextElement();
                    addViolatedInitial(value, variable.getInitialAssignment());
                }
            }
        }
        return true;
    }
    
    /** Initial values that cannot be assigned when the given value is assigned */
    public Set getViolatedInitials(Value value) {
        return (Set)iViolatedInitials.get(value);
    }
    
    private void addViolatedInitial(Value value, Value anotherValue) {
        Set violations = (Set)iViolatedInitials.get(value);
        if (violations == null) {
            violations = new HashSet();
            iViolatedInitials.put(value, violations);
        }
        violations.add(anotherValue);
    }
    
    private Vector conflictValues(Constraint constraint, Value aValue) {
        Vector ret = new FastVector();
        for (Enumeration i1 = constraint.variables().elements();i1.hasMoreElements();) {
            Variable variable = (Variable)i1.nextElement();
            if (variable.equals(aValue.variable())) continue;
            if (variable.getAssignment() != null) continue;
            for (Enumeration i2 = variable.values().elements(); i2.hasMoreElements(); ) {
                Value value = (Value)i2.nextElement();
                if (!constraint.isConsistent(aValue, value))
                    ret.addElement(value);
            }
        }
        return ret;
    }
}
