package ifs.example.csp;

import ifs.model.*;
import java.util.*;

/**
 * CSP binary constraint.
 * <br><br>
 * This class only implements the generation of a binary CSP constraint and the consistency check.
 * 
 * @author <a href="mailto:muller@ktiml.mff.cuni.cz">Tomas Muller</a>
 * @version 1.0
 */
public class CSPBinaryConstraint extends BinaryConstraint {
    private int iId = 0;
    private boolean iIsConsistent[][] = null;
    private int iNrCompatiblePairs;
    
    /** Constructor
     * @param nrCompatiblePairs number of compatible pairs of values in the constraint
     */
    public CSPBinaryConstraint(int id, int nrCompatiblePairs) {
    	super();
    	iId = id;
        iNrCompatiblePairs = nrCompatiblePairs;
    }
    
    private void swap(int[][] allPairs, int first, int second) {
        int[] a = allPairs[first];
        allPairs[first] = allPairs[second];
        allPairs[second] = a;
    }
    
    /**
     * Initializes the constraint. Randomly generates the given number of compatible pairs of values.
     * @param rndNumGen random number generator
     */
    public void init(Random rndNumGen) {
        int numberOfAllPairs = first().values().size() * second().values().size();
        int[][] allPairs = new int[numberOfAllPairs][];
        int idx = 0;
        
        iIsConsistent = new boolean[first().values().size()][second().values().size()];
        
        for (Enumeration i1=first().values().elements();
        i1.hasMoreElements();) {
            CSPValue v1 = (CSPValue)i1.nextElement();
            for (Enumeration i2=second().values().elements();
            i2.hasMoreElements();) {
                CSPValue v2 = (CSPValue)i2.nextElement();
                iIsConsistent[v1.toInt()][v2.toInt()] = false;
                allPairs[idx++] = new int[] {v1.toInt(), v2.toInt()};
            }
        }
        
        for (int i=0; i<iNrCompatiblePairs; i++) {
            swap(allPairs, i, i+(int)(rndNumGen.nextDouble()*(numberOfAllPairs-i)));
            iIsConsistent[allPairs[i][0]][allPairs[i][1]] = true;
        }
    }
    
    /**
     * True if the pair of given values is compatible.
     */
    public boolean isConsistent(Value value1, Value value2) {
        if (value1==null || value2==null) return true;
        if (isFirst(value1.variable())) {
            return iIsConsistent[value1.toInt()][value2.toInt()];
        } else {
            return iIsConsistent[value2.toInt()][value1.toInt()];
        }
    }

    /**
     * Add the other variable to the set of conflicts, if it is not compatible with the given value.
     */
    public void computeConflicts(Value aValue, Set conflicts) {
        if (isFirst(aValue.variable())) {
            if (!isConsistent(aValue, second().getAssignment())) {
                conflicts.add(second().getAssignment());
            }
        } else {
            if (!isConsistent(first().getAssignment(), aValue)) {
                conflicts.add(first().getAssignment());
            }
        }
    }
    
    public String getName() { return "C"+getId(); }
}