private static <T> int strongConnect(
        InferenceGraphNode<T> currentNode,
        int index,
        Stack<InferenceGraphNode<T>> currentStack,
        ArrayList<List<InferenceGraphNode<T>>> result) {
      currentNode.index = index;
      currentNode.lowlink = index;
      index++;

      currentStack.push(currentNode);

      for (InferenceGraphNode<T> dependantNode : currentNode.getDependencies()) {
        if (dependantNode.index == -1) {
          strongConnect(dependantNode, index, currentStack, result);
          currentNode.lowlink = Math.min(currentNode.lowlink, dependantNode.lowlink);
        } else if (currentStack.contains(dependantNode)) {
          currentNode.lowlink = Math.min(currentNode.lowlink, dependantNode.index);
        }
      }

      if (currentNode.lowlink == currentNode.index) {
        final ArrayList<InferenceGraphNode<T>> arrayList = new ArrayList<InferenceGraphNode<T>>();
        InferenceGraphNode<T> cyclicNode;
        do {
          cyclicNode = currentStack.pop();
          arrayList.add(cyclicNode);
        } while (cyclicNode != currentNode);
        result.add(arrayList);
      }
      return index;
    }
  public static List<List<InferenceVariable>> resolveOrder(
      Collection<InferenceVariable> vars, InferenceSession session) {
    Map<InferenceVariable, InferenceGraphNode<InferenceVariable>> nodes =
        new HashMap<InferenceVariable, InferenceGraphNode<InferenceVariable>>();
    for (InferenceVariable var : vars) {
      nodes.put(var, new InferenceGraphNode<InferenceVariable>(var));
    }

    for (InferenceVariable var : vars) {
      if (var.getInstantiation() != PsiType.NULL) continue;
      final InferenceGraphNode<InferenceVariable> node = nodes.get(var);
      final Set<InferenceVariable> dependencies = var.getDependencies(session);
      for (InferenceVariable dependentVariable : dependencies) {
        final InferenceGraphNode<InferenceVariable> dependency = nodes.get(dependentVariable);
        if (dependency != null) {
          node.addDependency(dependency);
        }
      }
    }
    final ArrayList<InferenceGraphNode<InferenceVariable>> acyclicNodes = initNodes(nodes.values());
    return ContainerUtil.map(
        acyclicNodes,
        new Function<InferenceGraphNode<InferenceVariable>, List<InferenceVariable>>() {
          @Override
          public List<InferenceVariable> fun(InferenceGraphNode<InferenceVariable> node) {
            return node.getValue();
          }
        });
  }
 public static <T> ArrayList<InferenceGraphNode<T>> initNodes(
     Collection<InferenceGraphNode<T>> allNodes) {
   final List<List<InferenceGraphNode<T>>> nodes = tarjan(allNodes);
   final ArrayList<InferenceGraphNode<T>> acyclicNodes = new ArrayList<InferenceGraphNode<T>>();
   for (List<InferenceGraphNode<T>> cycle : nodes) {
     acyclicNodes.add(InferenceGraphNode.merge(cycle, allNodes));
   }
   return acyclicNodes;
 }
    private static <T> InferenceGraphNode<T> merge(
        final List<InferenceGraphNode<T>> cycle, final Collection<InferenceGraphNode<T>> allNodes) {
      assert !cycle.isEmpty();
      final InferenceGraphNode<T> root = cycle.get(0);
      if (cycle.size() > 1) {
        for (int i = 1; i < cycle.size(); i++) {
          final InferenceGraphNode<T> cycleNode = cycle.get(i);

          root.copyFrom(cycleNode);
          root.filterInterCycleDependencies();

          for (InferenceGraphNode<T> node : allNodes) {
            if (node.myDependencies.remove(cycleNode)) {
              node.myDependencies.add(root);
            }
          }
        }
      }
      return root;
    }
 public static <T> List<List<InferenceGraphNode<T>>> tarjan(
     Collection<InferenceGraphNode<T>> nodes) {
   final ArrayList<List<InferenceGraphNode<T>>> result =
       new ArrayList<List<InferenceGraphNode<T>>>();
   final Stack<InferenceGraphNode<T>> currentStack = new Stack<InferenceGraphNode<T>>();
   int index = 0;
   for (InferenceGraphNode<T> node : nodes) {
     if (node.index == -1) {
       index += InferenceGraphNode.strongConnect(node, index, currentStack, result);
     }
   }
   return result;
 }