/**
   * Recursive part of tarjans algorithm to find strongly connected components. Algorithm is e.g.
   * described in <a
   * href="http://en.wikipedia.org/wiki/Tarjan's_strongly_connected_components_algorithm">wikipedia</a>
   *
   * @param maxdfs The current value for maxdfs.
   * @param v The current Node to process.
   * @param workset Set of all unreached Nodes.
   * @param stack Temporary datastructure for algorithm.
   * @param nodeStrMap Because the sccs are returned as a list of variables (i.e. Strings), the
   *     mapping from Node to String must be given.
   * @param sccs All found strongly connected components are added to this list.
   * @return an updated value for maxdfs.
   */
  private static int tarjan(
      int maxdfs,
      DirectedGraph.Node v,
      Set<DirectedGraph.Node> workset,
      LinkedList<DirectedGraph.Node> stack,
      Map<DirectedGraph.Node, String> nodeStrMap,
      List<List<String>> sccs) {

    v.dfs = maxdfs;
    v.lowlink = maxdfs;
    maxdfs++;
    stack.push(v);
    workset.remove(v);

    for (DirectedGraph.Node succ : v.getSuccessors()) {
      if (workset.contains(succ)) {
        maxdfs = tarjan(maxdfs, succ, workset, stack, nodeStrMap, sccs);
        v.lowlink = Math.min(v.lowlink, succ.lowlink);
      } else if (succ.dfs > 0) { // <==> stack.contains(succ)
        v.lowlink = Math.min(v.lowlink, succ.dfs);
      }
    }

    if (v.lowlink == v.dfs) {

      DirectedGraph.Node succ;
      LinkedList<String> scc = new LinkedList<>();

      do {
        succ = stack.pop();
        succ.dfs = -succ.dfs;
        scc.add(nodeStrMap.get(succ));
      } while (!succ.equals(v));

      if (scc.size() > 1) {
        sccs.add(scc);
      }
    }

    return maxdfs;
  }