private int visit(int node, int last) throws ContradictionException {
   if (node == -1) {
     contradiction(G, "G_R disconnected");
   }
   if (node == last) {
     return 1;
   }
   int next = -1;
   INeighbors succs = G_R.getSuccessorsOf(node);
   for (int x = succs.getFirstElement(); x >= 0; x = succs.getNextElement()) {
     if (G_R.getPredecessorsOf(x).neighborhoodSize() == 1) {
       if (next != -1) {
         return 0;
       }
       next = x;
     } else {
       G_R.removeArc(node, x);
     }
   }
   succs = mates[node];
   int from, to;
   for (int e = succs.getFirstElement(); e >= 0; e = succs.getNextElement()) {
     to = e % n;
     if (sccOf[to].get() != next) {
       from = e / n - 1;
       G.removeArc(from, to, this);
       mates[node].remove(e);
     }
   }
   return visit(next, last) + 1;
 }
 @Override
 public ESat isEntailed() {
   if (G.instantiated()) {
     int nr = 0;
     for (int i = 0; i < n_R.get(); i++) {
       nr += G_R.getSuccessorsOf(i).neighborhoodSize();
     }
     if (nr == n_R.get() - 1) {
       return ESat.TRUE;
     }
     return ESat.FALSE;
   }
   return ESat.UNDEFINED;
 }
 /**
  * Maintain incrementally the reduced graph and strongly connected components of a directed graph
  * variable Ensures that the reduced graph is a Hamiltonian path BEWARE REQUIRES A UNIQUE SOURCE
  * AND A UNIQUE SINK
  *
  * @param graph
  * @param constraint
  * @param solver
  */
 public PropReducedGraphHamPath(V graph, Constraint<V, Propagator<V>> constraint, Solver solver) {
   super((V[]) new DirectedGraphVar[] {graph}, solver, constraint, PropagatorPriority.LINEAR);
   G = graph;
   n = G.getEnvelopGraph().getNbNodes();
   n_R = environment.makeInt(0);
   G_R = new StoredDirectedGraph(environment, n, GraphType.DOUBLE_LINKED_LIST);
   sccOf = new IStateInt[n];
   sccFirst = new IStateInt[n];
   sccNext = new IStateInt[n];
   mates = new INeighbors[n];
   for (int i = 0; i < n; i++) {
     sccOf[i] = environment.makeInt(0);
     sccFirst[i] = environment.makeInt(-1);
     sccNext[i] = environment.makeInt(-1);
     G_R.getActiveNodes().desactivate(i);
     mates[i] = new StoredDoubleIntLinkedList(environment);
   }
   arcRemoved = new RemArc(this);
   sccComputed = new BitSet(n);
 }
 @Override
 public void propagate(int evtmask) throws ContradictionException {
   for (int i = 0; i < n; i++) {
     sccFirst[i].set(-1);
     sccNext[i].set(-1);
     mates[i].clear();
     G_R.getActiveNodes().desactivate(i);
   }
   ArrayList<TIntArrayList> allSCC = StrongConnectivityFinder.findAllSCCOf(G.getEnvelopGraph());
   int s = allSCC.size();
   n_R.set(s);
   int elem;
   TIntArrayList list;
   for (int i = 0; i < s; i++) {
     list = allSCC.get(i);
     G_R.getActiveNodes().activate(i);
     for (int j = list.size() - 1; j >= 0; j--) {
       elem = list.get(j);
       sccOf[elem].set(i);
       addNode(i, elem);
     }
   }
   INeighbors succs;
   int x;
   for (int i = 0; i < n; i++) {
     x = sccOf[i].get();
     succs = G.getEnvelopGraph().getSuccessorsOf(i);
     for (int j = succs.getFirstElement(); j >= 0; j = succs.getNextElement()) {
       if (x != sccOf[j].get()) {
         G_R.addArc(x, sccOf[j].get());
         mates[x].add((i + 1) * n + j);
       }
     }
   }
   int first = -1;
   int last = -1;
   for (int i = 0; i < s; i++) {
     if (G_R.getPredecessorsOf(i).isEmpty()) {
       first = i;
     }
     if (G_R.getSuccessorsOf(i).isEmpty()) {
       last = i;
     }
   }
   if (first == -1 || last == -1 || first == last) {
     contradiction(G, "");
   }
   if (visit(first, last) != n_R.get()) {
     contradiction(G, "");
   }
   int to, arc;
   for (int i = 0; i < n; i++) {
     to = G.getKernelGraph().getSuccessorsOf(i).getFirstElement();
     x = sccOf[i].get();
     if (to != -1 && sccOf[to].get() != x && mates[x].neighborhoodSize() > 1) {
       arc = (i + 1) * n + to;
       for (int a = mates[x].getFirstElement(); a >= 0; a = mates[x].getNextElement()) {
         if (a != arc) {
           G.removeArc(a / n - 1, a % n, this);
         }
       }
       mates[x].clear();
       mates[x].add(arc);
     }
   }
 }