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.SetVar;
008import solver.variables.delta.monitor.SetDeltaMonitor;
009import util.ESat;
010import util.procedure.IntProcedure;
011
012/**
013 * More efficient than the provided PropAllEqual.
014 *
015 * @author jimmy
016 */
017public class PropSetEqual extends Propagator<SetVar> {
018
019    private final SetVar s1, s2;
020    private SetDeltaMonitor s1D, s2D;
021
022    public PropSetEqual(SetVar s1, SetVar s2) {
023        super(new SetVar[]{s1, s2}, PropagatorPriority.LINEAR, true);
024        this.s1 = s1;
025        this.s1D = s1.monitorDelta(aCause);
026        this.s2 = s2;
027        this.s2D = s2.monitorDelta(aCause);
028    }
029
030    private boolean isS1Var(int idx) {
031        return idx == 0;
032    }
033
034    private boolean isS2Var(int idx) {
035        return idx == 1;
036    }
037
038    @Override
039    public int getPropagationConditions(int vIdx) {
040        return EventType.ADD_TO_KER.mask + EventType.REMOVE_FROM_ENVELOPE.mask;
041    }
042
043    @Override
044    public void propagate(int evtmask) throws ContradictionException {
045        PropUtil.envSubsetEnv(s1, s2, aCause);
046        PropUtil.envSubsetEnv(s2, s1, aCause);
047        PropUtil.kerSubsetKer(s1, s2, aCause);
048        PropUtil.kerSubsetKer(s2, s1, aCause);
049    }
050
051    @Override
052    public void propagate(int idxVarInProp, int mask) throws ContradictionException {
053        if (isS1Var(idxVarInProp)) {
054            s1D.freeze();
055            s1D.forEach(pruneS2OnS1Env, EventType.REMOVE_FROM_ENVELOPE);
056            s1D.forEach(pickS2OnS1Ker, EventType.ADD_TO_KER);
057            s1D.unfreeze();
058        } else {
059            assert isS2Var(idxVarInProp);
060            s2D.freeze();
061            s2D.forEach(pruneS1OnS2Env, EventType.REMOVE_FROM_ENVELOPE);
062            s2D.forEach(pickS1OnS2Ker, EventType.ADD_TO_KER);
063            s2D.unfreeze();
064        }
065    }
066    private final IntProcedure pruneS2OnS1Env = new IntProcedure() {
067        @Override
068        public void execute(int s1Env) throws ContradictionException {
069            s2.removeFromEnvelope(s1Env, aCause);
070        }
071    };
072    private final IntProcedure pickS2OnS1Ker = new IntProcedure() {
073        @Override
074        public void execute(int s1Ker) throws ContradictionException {
075            s2.addToKernel(s1Ker, aCause);
076        }
077    };
078    private final IntProcedure pruneS1OnS2Env = new IntProcedure() {
079        @Override
080        public void execute(int s2Env) throws ContradictionException {
081            s1.removeFromEnvelope(s2Env, aCause);
082        }
083    };
084    private final IntProcedure pickS1OnS2Ker = new IntProcedure() {
085        @Override
086        public void execute(int s2Ker) throws ContradictionException {
087            s1.addToKernel(s2Ker, aCause);
088        }
089    };
090
091    @Override
092    public ESat isEntailed() {
093        if (!PropUtil.isKerSubsetEnv(s1, s2) || !PropUtil.isKerSubsetEnv(s2, s1)) {
094            return ESat.FALSE;
095        }
096        return s1.instantiated() && s2.instantiated() ? ESat.TRUE : ESat.UNDEFINED;
097    }
098
099    @Override
100    public String toString() {
101        return s1 + " = " + s2;
102    }
103}