001package org.clafer.choco.constraint.propagator;
002
003import java.util.Arrays;
004import solver.constraints.Propagator;
005import solver.constraints.PropagatorPriority;
006import solver.exception.ContradictionException;
007import solver.variables.EventType;
008import solver.variables.IntVar;
009import solver.variables.SetVar;
010import solver.variables.Variable;
011import solver.variables.delta.IIntDeltaMonitor;
012import solver.variables.delta.monitor.SetDeltaMonitor;
013import util.ESat;
014import util.procedure.IntProcedure;
015
016/**
017 * An idempotent and more efficient propagator than the default one. Does not
018 * require an supplementary disjoint propagator like the default one.
019 *
020 * @author jimmy
021 */
022public class PropIntChannel extends Propagator<Variable> {
023
024    private final SetVar[] sets;
025    private SetDeltaMonitor[] setsD;
026    private final IntVar[] ints;
027    private IIntDeltaMonitor[] intsD;
028
029    public PropIntChannel(SetVar[] sets, IntVar[] ints) {
030        super(buildArray(sets, ints), PropagatorPriority.LINEAR, true);
031        this.sets = sets;
032        this.setsD = PropUtil.monitorDeltas(sets, aCause);
033        this.ints = ints;
034        this.intsD = PropUtil.monitorDeltas(ints, aCause);
035    }
036
037    private static Variable[] buildArray(SetVar[] sets, IntVar[] ints) {
038        Variable[] vars = new Variable[sets.length + ints.length];
039        System.arraycopy(sets, 0, vars, 0, sets.length);
040        System.arraycopy(ints, 0, vars, sets.length, ints.length);
041        return vars;
042    }
043
044    private boolean isSetVar(int idx) {
045        return idx < sets.length;
046    }
047
048    private int getSetVarIndex(int idx) {
049        assert isSetVar(idx);
050        return idx;
051    }
052
053    private boolean isIntVar(int idx) {
054        return idx >= sets.length;
055    }
056
057    private int getIntVarIndex(int idx) {
058        assert isIntVar(idx);
059        return idx - sets.length;
060    }
061
062    @Override
063    public int getPropagationConditions(int vIdx) {
064        if (isSetVar(vIdx)) {
065            return EventType.ADD_TO_KER.mask + EventType.REMOVE_FROM_ENVELOPE.mask;
066        }
067        assert isIntVar(vIdx);
068        return EventType.INT_ALL_MASK();
069    }
070
071    @Override
072    public void propagate(int evtmask) throws ContradictionException {
073        for (int i = 0; i < ints.length; i++) {
074            int ub = ints[i].getUB();
075            for (int j = ints[i].getLB(); j <= ub; j = ints[i].nextValue(j)) {
076                if (j < 0 || j >= sets.length || !sets[j].envelopeContains(i)) {
077                    ints[i].removeValue(j, aCause);
078                }
079            }
080            if (ints[i].instantiated()) {
081                sets[ints[i].getValue()].addToKernel(i, aCause);
082            }
083        }
084        for (int i = 0; i < sets.length; i++) {
085            for (int j = sets[i].getEnvelopeFirst(); j != SetVar.END; j = sets[i].getEnvelopeNext()) {
086                if (j < 0 || j >= ints.length || !ints[j].contains(i)) {
087                    sets[i].removeFromEnvelope(j, aCause);
088                }
089            }
090        }
091        for (int i = 0; i < sets.length; i++) {
092            for (int j = sets[i].getKernelFirst(); j != SetVar.END; j = sets[i].getKernelNext()) {
093                ints[j].instantiateTo(i, aCause);
094            }
095        }
096    }
097
098    @Override
099    public void propagate(final int idxVarInProp, final int mask) throws ContradictionException {
100        if (isSetVar(idxVarInProp)) {
101            final int id = getSetVarIndex(idxVarInProp);
102
103            setsD[id].freeze();
104            setsD[id].forEach(new IntProcedure() {
105                @Override
106                public void execute(int setKer) throws ContradictionException {
107                    ints[setKer].instantiateTo(id, aCause);
108                    for (int i = 0; i < sets.length; i++) {
109                        if (i != id) {
110                            sets[i].removeFromEnvelope(setKer, aCause);
111                        }
112                    }
113                }
114            }, EventType.ADD_TO_KER);
115            setsD[id].forEach(new IntProcedure() {
116                @Override
117                public void execute(int setEnv) throws ContradictionException {
118                    if (ints[setEnv].removeValue(id, aCause) && ints[setEnv].instantiated()) {
119                        int val = ints[setEnv].getValue();
120                        sets[val].addToKernel(setEnv, aCause);
121                        for (int i = 0; i < sets.length; i++) {
122                            if (i != val) {
123                                sets[i].removeFromEnvelope(setEnv, aCause);
124                            }
125                        }
126                    }
127                }
128            }, EventType.REMOVE_FROM_ENVELOPE);
129            setsD[id].unfreeze();
130        } else {
131            assert isIntVar(idxVarInProp);
132            final int id = getIntVarIndex(idxVarInProp);
133
134            intsD[id].freeze();
135            intsD[id].forEach(new IntProcedure() {
136                @Override
137                public void execute(int intRem) throws ContradictionException {
138                    sets[intRem].removeFromEnvelope(id, aCause);
139                }
140            }, EventType.REMOVE);
141            if (ints[id].instantiated()) {
142                sets[ints[id].getValue()].addToKernel(id, aCause);
143            }
144            intsD[id].unfreeze();
145        }
146    }
147
148    @Override
149    public ESat isEntailed() {
150        for (int i = 0; i < ints.length; i++) {
151            if (ints[i].instantiated()) {
152                int value = ints[i].getValue();
153                if (value < 0 || value >= sets.length || !sets[value].envelopeContains(i)) {
154                    return ESat.FALSE;
155                }
156            }
157        }
158        for (int i = 0; i < sets.length; i++) {
159            for (int j = sets[i].getKernelFirst(); j != SetVar.END; j = sets[i].getKernelNext()) {
160                if (j < 0 || j >= ints.length || !ints[j].contains(i)) {
161                    return ESat.FALSE;
162                }
163            }
164        }
165        return isCompletelyInstantiated() ? ESat.TRUE : ESat.UNDEFINED;
166    }
167
168    @Override
169    public String toString() {
170        return "intChannel(" + Arrays.toString(sets) + ", " + Arrays.toString(ints) + ")";
171    }
172}