private void buildSCC() {
   digraph.removeNode(n2);
   digraph.removeNode(n2 + 1);
   digraph.addNode(n2);
   digraph.addNode(n2 + 1);
   // TODO CHECK THIS PART
   for (int i = 0; i < n; i++) {
     if (free.get(i)) {
       digraph.addArc(n2, i);
     } else {
       digraph.addArc(i, n2);
     }
   }
   for (int i = n; i < n2; i++) {
     if (free.get(i)) {
       digraph.addArc(i, n2 + 1);
     } else {
       digraph.addArc(n2 + 1, i);
     }
   }
   SCCfinder.findAllSCC();
   nodeSCC = SCCfinder.getNodesSCC();
   digraph.removeNode(n2);
   digraph.removeNode(n2 + 1);
 }
 @Override
 public void propagate(int evtmask) throws ContradictionException {
   if (PropagatorEventType.isFullPropagation(evtmask)) {
     if (n2 < n + vars[n].getLB()) {
       fails(); // TODO: could be more precise, for explanation purpose
     }
     buildDigraph();
   }
   digraph.removeNode(n2);
   digraph.removeNode(n2 + 1);
   free.clear();
   for (int i = 0; i < n; i++) {
     if (digraph.getPredOf(i).size() == 0) {
       free.set(i);
     }
   }
   for (int i = n; i < n2; i++) {
     if (digraph.getSuccOf(i).size() == 0) {
       free.set(i);
     }
   }
   int card = repairMatching();
   vars[n].updateUpperBound(card, this);
   if (vars[n].getLB() == card) {
     filter();
   }
   for (int i = 0; i < idms.length; i++) {
     idms[i].unfreeze();
   }
 }
 private void tryToMatch(int i) throws ContradictionException {
   int mate = augmentPath_BFS(i);
   if (mate != -1) {
     free.clear(mate);
     free.clear(i);
     int tmp = mate;
     while (tmp != i) {
       digraph.removeArc(father[tmp], tmp);
       digraph.addArc(tmp, father[tmp]);
       tmp = father[tmp];
     }
   }
 }
 private void buildDigraph() {
   for (int i = 0; i < n2; i++) {
     digraph.getSuccOf(i).clear();
     digraph.getPredOf(i).clear();
   }
   free.set(0, n2);
   int j, k, ub;
   IntVar v;
   for (int i = 0; i < n2 + 2; i++) {
     digraph.removeNode(i);
   }
   for (int i = 0; i < n; i++) {
     v = vars[i];
     ub = v.getUB();
     for (k = v.getLB(); k <= ub; k = v.nextValue(k)) {
       j = map.get(k);
       digraph.addArc(i, j);
     }
   }
 }
 private int repairMatching() throws ContradictionException {
   for (int i = free.nextSetBit(0); i >= 0 && i < n; i = free.nextSetBit(i + 1)) {
     tryToMatch(i);
   }
   int card = 0;
   for (int i = 0; i < n; i++) {
     if (digraph.getPredOf(i).size() > 0) {
       card++;
     }
   }
   return card;
 }
 private int augmentPath_BFS(int root) {
   in.clear();
   int indexFirst = 0, indexLast = 0;
   fifo[indexLast++] = root;
   int x;
   ISetIterator succs;
   while (indexFirst != indexLast) {
     x = fifo[indexFirst++];
     succs = digraph.getSuccOf(x).iterator();
     while (succs.hasNext()) {
       int y = succs.nextInt();
       if (!in.get(y)) {
         father[y] = x;
         fifo[indexLast++] = y;
         in.set(y);
         if (free.get(y)) {
           return y;
         }
       }
     }
   }
   return -1;
 }
 private void filter() throws ContradictionException {
   buildSCC();
   int j, ub;
   IntVar v;
   for (int i = 0; i < n; i++) {
     v = vars[i];
     ub = v.getUB();
     for (int k = v.getLB(); k <= ub; k = v.nextValue(k)) {
       j = map.get(k);
       if (nodeSCC[i] != nodeSCC[j]) {
         if (digraph.getPredOf(i).contains(j)) {
           v.instantiateTo(k, this);
         } else {
           v.removeValue(k, this);
           digraph.removeArc(i, j);
         }
       }
     }
     if (!v.hasEnumeratedDomain()) {
       ub = v.getUB();
       for (int k = v.getLB(); k <= ub; k = v.nextValue(k)) {
         j = map.get(k);
         if (digraph.arcExists(i, j) || digraph.arcExists(j, i)) {
           break;
         } else {
           v.removeValue(k, this);
         }
       }
       int lb = v.getLB();
       for (int k = ub; k >= lb; k = v.previousValue(k)) {
         j = map.get(k);
         if (digraph.arcExists(i, j) || digraph.arcExists(j, i)) {
           break;
         } else {
           v.removeValue(k, this);
         }
       }
     }
   }
 }