001package org.clafer.choco.constraint.propagator;
002
003import solver.constraints.Propagator;
004import solver.constraints.PropagatorPriority;
005import solver.exception.ContradictionException;
006import solver.variables.EventType;
007import solver.variables.IntVar;
008import solver.variables.SetVar;
009import solver.variables.Variable;
010import solver.variables.delta.IIntDeltaMonitor;
011import solver.variables.delta.monitor.SetDeltaMonitor;
012import util.ESat;
013import util.procedure.IntProcedure;
014
015/**
016 *
017 * @author jimmy
018 */
019public class PropSingleton extends Propagator<Variable> {
020
021    private final IntVar i;
022    private final IIntDeltaMonitor iD;
023    private final SetVar s;
024    private final SetDeltaMonitor sD;
025
026    public PropSingleton(IntVar ivar, SetVar svar) {
027        super(new Variable[]{ivar, svar}, PropagatorPriority.UNARY, true);
028        this.i = ivar;
029        this.iD = i.monitorDelta(aCause);
030        this.s = svar;
031        this.sD = s.monitorDelta(aCause);
032    }
033
034    private boolean isIVar(int idx) {
035        return idx == 0;
036    }
037
038    private boolean isSVar(int idx) {
039        return idx == 1;
040    }
041
042    @Override
043    public int getPropagationConditions(int vIdx) {
044        if (isIVar(vIdx)) {
045            return EventType.INT_ALL_MASK();
046        }
047        assert isSVar(vIdx);
048        return EventType.ADD_TO_KER.mask + EventType.REMOVE_FROM_ENVELOPE.mask;
049    }
050
051    @Override
052    public void propagate(int evtmask) throws ContradictionException {
053        if (s.getKernelSize() > 1) {
054            contradiction(s, "Singleton cannot have more than 1 element");
055        } else if (s.getKernelSize() == 1) {
056            int val = s.getKernelFirst();
057            i.instantiateTo(val, aCause);
058            s.instantiateTo(new int[]{val}, aCause);
059        }
060        PropUtil.domSubsetEnv(i, s, aCause);
061        PropUtil.envSubsetDom(s, i, aCause);
062        if (i.instantiated()) {
063            s.instantiateTo(new int[]{i.getValue()}, aCause);
064        } else if (s.getEnvelopeSize() == 1) {
065            int val = s.getEnvelopeFirst();
066            i.instantiateTo(val, aCause);
067            s.instantiateTo(new int[]{val}, aCause);
068        }
069    }
070
071    @Override
072    public void propagate(int idxVarInProp, int mask) throws ContradictionException {
073        if (isIVar(idxVarInProp)) {
074            if (i.instantiated()) {
075                s.instantiateTo(new int[]{i.getValue()}, aCause);
076            } else {
077                iD.freeze();
078                iD.forEach(pruneSOnIRem, EventType.REMOVE);
079                iD.unfreeze();
080            }
081        } else {
082            assert isSVar(idxVarInProp);
083            int sKerSize = s.getKernelSize();
084            if (sKerSize > 1) {
085                contradiction(s, "Singleton cannot have more than 1 element");
086            } else if (sKerSize == 1) {
087                int val = s.getKernelFirst();
088                i.instantiateTo(val, aCause);
089                s.instantiateTo(new int[]{val}, aCause);
090            } else {
091                sD.freeze();
092                sD.forEach(pruneIOnSEnv, EventType.REMOVE_FROM_ENVELOPE);
093                sD.unfreeze();
094                if (i.instantiated()) {
095                    s.instantiateTo(new int[]{i.getValue()}, aCause);
096                }
097            }
098        }
099    }
100    private final IntProcedure pruneSOnIRem = new IntProcedure() {
101        @Override
102        public void execute(int i) throws ContradictionException {
103            s.removeFromEnvelope(i, aCause);
104        }
105    };
106    private final IntProcedure pruneIOnSEnv = new IntProcedure() {
107        @Override
108        public void execute(int sEnv) throws ContradictionException {
109            i.removeValue(sEnv, aCause);
110        }
111    };
112
113    @Override
114    public ESat isEntailed() {
115        if (s.getKernelSize() > 1) {
116            return ESat.FALSE;
117        }
118        if (s.getEnvelopeSize() < 1) {
119            return ESat.FALSE;
120        }
121        if (PropUtil.isDomIntersectEnv(i, s)) {
122            return i.instantiated() && s.instantiated() ? ESat.TRUE : ESat.UNDEFINED;
123        }
124        return ESat.FALSE;
125    }
126
127    @Override
128    public String toString() {
129        return "singleton({" + i + "} = " + s + ")";
130    }
131}