public Goal execute() throws Failure {
    class ObserverEqualValue extends Observer {
      @Override
      public Object master() {
        return ConstraintExpEqualsValue.this;
      }

      @Override
      public int subscriberMask() {
        return EventOfInterest.VALUE | EventOfInterest.MIN | EventOfInterest.MAX;
      }

      @Override
      public String toString() {
        return "ObserverEqualValue";
      }

      @Override
      public void update(Subject exp, EventOfInterest interest) throws Failure {
        // Debug.on();Debug.print("ObserverEqualValue:
        // "+interest);Debug.off();
        IntEvent event = (IntEvent) interest;
        if ((event.isValueEvent() && event.min() != _value)
            || (event.isMaxEvent() && event.max() < _value)
            || (event.isMinEvent() && event.min() > _value)) {
          exp.constrainer().fail("from ObserverEqualValue");
        }
        _exp.setValue(_value);
      }
    } // ~ ObserverEqualValue

    _exp.setValue(_value); // may fail
    _exp.attachObserver(new ObserverEqualValue());
    return null;
  }
  public ConstraintExpEqualsValue(IntExp exp, int value) {
    super(exp.constrainer());

    if (constrainer().showInternalNames()) {
      _name = "(" + exp.name() + "==" + value + ")";
    }

    _exp = exp;
    _value = value;
  }
  public void testEventPropagation() {
    class IntSetVarValueObserver extends Observer {
      int counter = 0;

      @Override
      public Object master() {
        return this;
      }

      @Override
      public int subscriberMask() {
        return IntSetEventConstants.VALUE;
      }

      @Override
      public void update(Subject var, EventOfInterest ev) throws Failure {
        // IntSetVar.IntSetEvent e = (IntSetVar.IntSetEvent)ev;

        System.out.println("value#" + counter + " obtained: " + ((IntSetVar) var).value());
        counter++;
      }
    }

    IntSetVar var1 = C.addIntSetVar(new int[] {1, 2, 3, 4, 5, 6, 7});
    var1.attachObserver(new IntSetVarValueObserver());
    IntSetVar var2 = C.addIntSetVar(new int[] {5, 6, 7, 8, 9, 10, 11});
    IntSetVar var3 = C.addIntSetVar(new int[] {28, 78, 23, 1, 4, 7});

    IntSetVarArray array = new IntSetVarArray(C, 1);
    array.set(var1, 0);

    Goal generate = new GoalIntSetGenerate(array);

    IntExp intersectionWithVar2 = var1.intersectionWith(var2).cardinality();
    IntExp intersectionWithVar3 = var1.intersectionWith(var3).cardinality();

    Goal alt1 = new GoalMinimize(generate, intersectionWithVar2.neg());
    Goal alt2 = new GoalMinimize(generate, intersectionWithVar3.neg());

    alt1 = new GoalAnd(alt1, new GoalFail(C));

    Goal main = new GoalOr(alt1, alt2);
    C.execute(main);
    System.out.println("Succeeded");
  }
  public Goal execute() throws Failure {
    class ObserverExpNotValue extends Observer {
      @Override
      public Object master() {
        return ConstraintExpNotValue.this;
      }

      @Override
      public int subscriberMask() {
        return EventOfInterest.VALUE;
      }

      @Override
      public String toString() {
        return "ObserverExpNotValue:" + _exp.name() + "!=" + _value;
      }

      @Override
      public void update(Subject exp, EventOfInterest interest) throws Failure {
        // Debug.print("ObserverExpNotValue("+_exp+") "+interest);
        int value = ((IntEvent) interest).min();
        if (value == _value) {
          // constrainer().fail("attempt to set a removed value
          // "+value+" for "+exp);
          constrainer().fail();
        }
      }
    } // ~ ObserverExpNotValue

    if (_value == _exp.max()) {
      _exp.setMax(_value - 1);
    } else if (_value == _exp.min()) {
      _exp.setMin(_value + 1);
    } else {
      // Debug.print("attach ObserverExpNotValue("+_exp+","+_value+")");
      _exp.attachObserver(new ObserverExpNotValue());
    }

    return null;
  }
  public Goal execute() throws Failure {
    _exp1.setMax(_exp2.max() - _offset); // may fail
    _exp1.attachObserver(new ObserverExp1Min());

    _exp2.setMin(_exp1.min() + _offset); // may fail
    _exp2.attachObserver(new ObserverExp2Max());
    return null;
  }
  /** exp1 <= exp2 + offset */
  public ConstraintExpLessExp(IntExp exp1, IntExp exp2, int offset) {
    super(exp1.constrainer());

    _exp1 = exp1;
    _exp2 = exp2;
    _offset = offset;
    _opposite = null;

    if (constrainer().showInternalNames()) {
      if (offset == 0) {
        _name = "(" + exp1.name() + "<=" + exp2.name() + ")";
      } else if (offset > 0) {
        _name = "(" + exp1.name() + "<=" + exp2.name() + "+" + offset + ")";
      } else {
        _name = "(" + exp1.name() + "<=" + exp2.name() + offset + ")";
      }
    }
  }
 @Override
 public boolean isLinear() {
   return _exp.isLinear();
 }
 @Override
 public IntBoolExp toIntBoolExp() {
   return _exp1.le(_exp2.add(_offset));
 }
 @Override
 public boolean isLinear() {
   return (_exp1.isLinear() && _exp2.isLinear());
 }
 @Override
 public IntBoolExp toIntBoolExp() {
   return _exp.ne(_value);
 }