/*
* Copyright (c) 1999-2014, Ecole des Mines de Nantes
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Ecole des Mines de Nantes nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package solver.constraints.nary.alldifferent.algo;
import gnu.trove.map.hash.TIntIntHashMap;
import solver.ICause;
import solver.exception.ContradictionException;
import solver.variables.IntVar;
import util.graphOperations.connectivity.StrongConnectivityFinder;
import util.objects.graphs.DirectedGraph;
import util.objects.setDataStructures.ISet;
import util.objects.setDataStructures.SetType;
import java.util.BitSet;
/**
* Algorithm of Alldifferent with AC
*/
public class AlgoAllDiffAC {
//***********************************************************************************
// VARIABLES
//***********************************************************************************
protected int n, n2;
protected DirectedGraph digraph;
private int[] matching;
private int[] nodeSCC;
protected BitSet free;
private StrongConnectivityFinder SCCfinder;
// for augmenting matching (BFS)
private int[] father;
private BitSet in;
private TIntIntHashMap map;
int[] fifo;
IntVar[] vars;
ICause aCause;
//***********************************************************************************
// CONSTRUCTORS
//***********************************************************************************
public AlgoAllDiffAC(IntVar[] variables, ICause cause) {
this.vars = variables;
aCause = cause;
n = vars.length;
matching = new int[n];
for (int i = 0; i < n; i++) {
matching[i] = -1;
}
map = new TIntIntHashMap();
IntVar v;
int ub;
int idx = n;
for (int i = 0; i < n; i++) {
v = vars[i];
ub = v.getUB();
for (int j = v.getLB(); j <= ub; j = v.nextValue(j)) {
if (!map.containsKey(j)) {
map.put(j, idx);
idx++;
}
}
}
n2 = idx;
fifo = new int[n2];
digraph = new DirectedGraph(n2 + 1, SetType.BITSET, false);
free = new BitSet(n2);
father = new int[n2];
in = new BitSet(n2);
SCCfinder = new StrongConnectivityFinder(digraph);
}
//***********************************************************************************
// PROPAGATION
//***********************************************************************************
public void propagate() throws ContradictionException {
findMaximumMatching();
filter();
}
//***********************************************************************************
// Initialization
//***********************************************************************************
protected void findMaximumMatching() throws ContradictionException {
for (int i = 0; i < n2; i++) {
digraph.getSuccOf(i).clear();
digraph.getPredOf(i).clear();
}
free.set(0, n2);
int k, ub;
IntVar v;
for (int i = 0; i < n; i++) {
v = vars[i];
ub = v.getUB();
int mate = matching[i];
for (k = v.getLB(); k <= ub; k = v.nextValue(k)) {
int j = map.get(k);
if (mate == j) {
assert free.get(i) && free.get(j);
digraph.addArc(j, i);
free.clear(i);
free.clear(j);
} else {
digraph.addArc(i, j);
}
}
}
for (int i = free.nextSetBit(0); i >= 0 && i < n; i = free.nextSetBit(i + 1)) {
tryToMatch(i);
}
int p;
for (int i = 0; i < n; i++) {
p = digraph.getPredOf(i).getFirstElement();
matching[i] = p;
}
}
private void tryToMatch(int i) throws ContradictionException {
int mate = augmentPath_BFS(i);
if (mate != -1) {
free.clear(mate);
free.clear(i);
int tmp = mate;
while (tmp != i) {
digraph.removeArc(father[tmp], tmp);
digraph.addArc(tmp, father[tmp]);
tmp = father[tmp];
}
} else {
vars[0].instantiateTo(vars[0].getLB()-1,aCause);
}
}
private int augmentPath_BFS(int root) {
in.clear();
int indexFirst = 0, indexLast = 0;
fifo[indexLast++] = root;
int x, y;
ISet succs;
while (indexFirst != indexLast) {
x = fifo[indexFirst++];
succs = digraph.getSuccOf(x);
for (y = succs.getFirstElement(); y >= 0; y = succs.getNextElement()) {
if (!in.get(y)) {
father[y] = x;
fifo[indexLast++] = y;
in.set(y);
if (free.get(y)) {
return y;
}
}
}
}
return -1;
}
//***********************************************************************************
// PRUNING
//***********************************************************************************
private void buildSCC() {
if (n2 > n * 2) {
digraph.removeNode(n2);
digraph.addNode(n2);
for (int i = n; i < n2; i++) {
if (free.get(i)) {
digraph.addArc(i, n2);
} else {
digraph.addArc(n2, i);
}
}
}
SCCfinder.findAllSCC();
nodeSCC = SCCfinder.getNodesSCC();
digraph.removeNode(n2);
}
protected void filter() throws ContradictionException {
buildSCC();
int j, ub;
IntVar v;
for (int i = 0; i < n; i++) {
v = vars[i];
ub = v.getUB();
for (int k = v.getLB(); k <= ub; k = v.nextValue(k)) {
j = map.get(k);
if (nodeSCC[i] != nodeSCC[j]) {
if (matching[i] == j) {
v.instantiateTo(k, aCause);
} else {
v.removeValue(k, aCause);
digraph.removeArc(i, j);
}
}
}
}
for (int i = 0; i < n; i++) {
v = vars[i];
if (!v.hasEnumeratedDomain()) {
ub = v.getUB();
for (int k = v.getLB(); k <= ub; k++) {
j = map.get(k);
if (!(digraph.arcExists(i, j) || digraph.arcExists(j, i))) {
v.removeValue(k, aCause);
}
}
int lb = v.getLB();
for (int k = v.getUB(); k >= lb; k--) {
j = map.get(k);
if (!(digraph.arcExists(i, j) || digraph.arcExists(j, i))) {
v.removeValue(k, aCause);
}
}
}
}
}
}