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}