001package org.clafer.choco.constraint.propagator;
002
003import org.clafer.common.Util;
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 *
018 * @author jimmy
019 */
020public class PropArrayToSet extends Propagator<Variable> {
021
022    private final IntVar[] as;
023    private final IIntDeltaMonitor[] asD;
024    private final SetVar s;
025    private final SetDeltaMonitor sD;
026
027    public PropArrayToSet(IntVar[] as, SetVar s) {
028        super(buildArray(as, s), PropagatorPriority.TERNARY, true);
029        if (as.length == 0) {
030            throw new IllegalArgumentException();
031        }
032        this.as = as;
033        this.asD = PropUtil.monitorDeltas(as, aCause);
034        this.s = s;
035        this.sD = s.monitorDelta(aCause);
036    }
037
038    private static Variable[] buildArray(IntVar[] as, SetVar s) {
039        Variable[] array = new Variable[as.length + 1];
040        array[0] = s;
041        System.arraycopy(as, 0, array, 1, as.length);
042        return array;
043    }
044
045    private boolean isAVar(int idx) {
046        return idx >= 1;
047    }
048
049    private int getAVarIndex(int idx) {
050        assert isAVar(idx);
051        return idx - 1;
052    }
053
054    private boolean isSVar(int idx) {
055        return idx == 0;
056    }
057
058    @Override
059    public int getPropagationConditions(int vIdx) {
060        if (isAVar(vIdx)) {
061            return EventType.INT_ALL_MASK();
062        }
063        assert isSVar(vIdx);
064        return EventType.REMOVE_FROM_ENVELOPE.mask + EventType.ADD_TO_KER.mask;
065    }
066
067    private boolean findMate(int sEnv) throws ContradictionException {
068        assert s.envelopeContains(sEnv);
069        boolean inKer = s.kernelContains(sEnv);
070        int mate = -1;
071        for (int i = 0; i < as.length; i++) {
072            if (as[i].contains(sEnv)) {
073                // Found a second mate.
074                if (mate != -1 || !inKer) {
075                    mate = -2;
076                    break;
077                }
078                mate = i;
079            }
080        }
081        if (mate == -1) {
082            // No mates.
083            s.removeFromEnvelope(sEnv, aCause);
084        } else if (mate != -2 && inKer) {
085            // One mate.
086            return as[mate].instantiateTo(sEnv, aCause);
087        }
088        return false;
089    }
090
091    private void findMates() throws ContradictionException {
092        for (int i = s.getEnvelopeFirst(); i != SetVar.END; i = s.getEnvelopeNext()) {
093            if (findMate(i)) {
094                findMates();
095                return;
096            }
097        }
098    }
099
100    @Override
101    public void propagate(int evtmask) throws ContradictionException {
102        // Prune as
103        for (IntVar a : as) {
104            PropUtil.domSubsetEnv(a, s, aCause);
105        }
106        // Prune s
107        findMates();
108        // Pick s
109        for (IntVar a : as) {
110            if (a.instantiated()) {
111                s.addToKernel(a.getValue(), aCause);
112            }
113        }
114    }
115
116    @Override
117    public void propagate(int idxVarInProp, int mask) throws ContradictionException {
118        if (isSVar(idxVarInProp)) {
119            sD.freeze();
120            sD.forEach(pruneAOnSEnv, EventType.REMOVE_FROM_ENVELOPE);
121            sD.forEach(pickAOnSEnv, EventType.ADD_TO_KER);
122            sD.unfreeze();
123        } else {
124            assert isAVar(idxVarInProp);
125
126            int id = getAVarIndex(idxVarInProp);
127            if (EventType.isRemove(mask)
128                    || (EventType.isInclow(mask) && as[id].getLB() > s.getEnvelopeFirst())
129                    || EventType.isDecupp(mask)) {
130                asD[id].freeze();
131                asD[id].forEach(pruneSOnARem, EventType.REMOVE);
132                asD[id].unfreeze();
133            }
134            if (as[id].instantiated()) {
135                s.addToKernel(as[id].getValue(), aCause);
136            }
137        }
138    }
139    private final IntProcedure pruneAOnSEnv = new IntProcedure() {
140        @Override
141        public void execute(int sEnv) throws ContradictionException {
142            for (IntVar a : as) {
143                if (a.removeValue(sEnv, aCause) && a.instantiated()) {
144                    s.addToKernel(a.getValue(), aCause);
145                }
146            }
147        }
148    };
149    private final IntProcedure pickAOnSEnv = new IntProcedure() {
150        @Override
151        public void execute(int sKer) throws ContradictionException {
152            if (findMate(sKer)) {
153                findMates();
154            }
155        }
156    };
157    private final IntProcedure pruneSOnARem = new IntProcedure() {
158        @Override
159        public void execute(int aRem) throws ContradictionException {
160            if (s.envelopeContains(aRem)) {
161                if (findMate(aRem)) {
162                    findMates();
163                }
164            }
165        }
166    };
167
168    @Override
169    public ESat isEntailed() {
170        if (s.getKernelSize() > as.length) {
171            return ESat.FALSE;
172        }
173        for (IntVar a : as) {
174            if (!PropUtil.isDomIntersectEnv(a, s)) {
175                return ESat.FALSE;
176            }
177        }
178        for (int i = s.getKernelFirst(); i != SetVar.END; i = s.getKernelNext()) {
179            if (!PropUtil.domsContain(as, i)) {
180                return ESat.FALSE;
181            }
182        }
183        return isCompletelyInstantiated() ? ESat.TRUE : ESat.UNDEFINED;
184    }
185
186    @Override
187    public String toString() {
188        return "arrayToSet({" + Util.commaSeparate(as) + "} = " + s + ")";
189    }
190}