/**
   * Verify that some non-looping paths from {@code a} to {@code b} pass through at least one node
   * where {@code nodePredicate} is true.
   */
  private boolean checkSomePathsWithoutBackEdges(DiGraphNode<N, E> a, DiGraphNode<N, E> b) {
    if (nodePredicate.apply(a.getValue()) && (inclusive || (a != start && a != end))) {
      return true;
    }
    if (a == b) {
      return false;
    }
    for (DiGraphEdge<N, E> e : a.getOutEdges()) {
      // Once we visited that edge once, we no longer need to
      // re-visit it again.
      if (e.getAnnotation() == VISITED_EDGE) {
        continue;
      }
      e.setAnnotation(VISITED_EDGE);

      if (ignoreEdge(e)) {
        continue;
      }
      if (e.getAnnotation() == BACK_EDGE) {
        continue;
      }

      DiGraphNode<N, E> next = e.getDestination();
      if (checkSomePathsWithoutBackEdges(next, b)) {
        return true;
      }
    }
    return false;
  }
 /**
  * Assert that there exists no control flow edge of the given type
  * from some node with the first token to the return node.
  */
 private static void assertNoReturnEdge(ControlFlowGraph<Node> cfg,
     int startToken) {
   List<DiGraphEdge<Node, Branch>> edges = getAllEdges(cfg);
   for (DiGraphEdge<Node, Branch> edge : edges) {
     Node source = edge.getSource().getValue();
     DiGraphNode<Node, Branch> dest = edge.getDestination();
     if (source.getType() == startToken) {
       assertFalse("Token " + startToken + " should not have an out going"
           + " edge to the implicit return", cfg.isImplicitReturn(dest));
       return;
     }
   }
 }
  /**
   * Assert that there exists a control flow edge of the given type
   * from some node with the first token to the return node.
   */
  private static void assertReturnEdge(ControlFlowGraph<Node> cfg,
      int startToken) {
    List<DiGraphEdge<Node, Branch>> edges = getAllEdges(cfg);
    for (DiGraphEdge<Node, Branch> edge : edges) {
      Node source = edge.getSource().getValue();
      DiGraphNode<Node, Branch> dest = edge.getDestination();
      if (source.getType() == startToken &&
          cfg.isImplicitReturn(dest)) {
        return;
      }
    }

    fail("No return edge found");
  }
 private void discoverBackEdges(DiGraphNode<N, E> u) {
   u.setAnnotation(GRAY);
   for (DiGraphEdge<N, E> e : u.getOutEdges()) {
     if (ignoreEdge(e)) {
       continue;
     }
     DiGraphNode<N, E> v = e.getDestination();
     if (v.getAnnotation() == WHITE) {
       discoverBackEdges(v);
     } else if (v.getAnnotation() == GRAY) {
       e.setAnnotation(BACK_EDGE);
     }
   }
   u.setAnnotation(BLACK);
 }
 /**
  * Gets all the control flow edges from some node with the first token to
  * some node with the second token.
  */
 private static List<DiGraphEdge<Node, Branch>> getAllEdges(
     ControlFlowGraph<Node> cfg, int startToken, int endToken) {
   List<DiGraphEdge<Node, Branch>> edges = getAllEdges(cfg);
   Iterator<DiGraphEdge<Node, Branch>> it = edges.iterator();
   while (it.hasNext()) {
     DiGraphEdge<Node, Branch> edge = it.next();
     Node startNode = edge.getSource().getValue();
     Node endNode = edge.getDestination().getValue();
     if (startNode == null || endNode == null ||
         startNode.getType() != startToken || endNode.getType() != endToken) {
       it.remove();
     }
   }
   return edges;
 }
  /**
   * Gets all the control flow edges of the given type from some node with
   * the first token to some node with the second token.
   * This edge must flow from a parent to one of its descendants.
   */
  private static List<DiGraphEdge<Node, Branch>> getAllDownEdges(
      ControlFlowGraph<Node> cfg, int startToken, int endToken, Branch type) {
    List<DiGraphEdge<Node, Branch>> edges =
        getAllEdges(cfg, startToken, endToken, type);
    Iterator<DiGraphEdge<Node, Branch>> it = edges.iterator();
    while (it.hasNext()) {
      DiGraphEdge<Node, Branch> edge = it.next();
      Node source = edge.getSource().getValue();
      Node dest = edge.getDestination().getValue();
      if (!isAncestor(source, dest)) {
        it.remove();
      }
    }

    return edges;
  }