All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dagger.internal.codegen.base.TarjanSCCs Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2021 The Dagger Authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package dagger.internal.codegen.base;

import static com.google.common.base.Preconditions.checkState;
import static java.lang.Math.min;

import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.graph.SuccessorsFunction;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * An implementation of Tarjan's algorithm for finding the SCC of a graph. This is based on the
 * psuedo code algorithm here:
 * http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
 */
public final class TarjanSCCs {

  /** Returns the set of strongly connected components in reverse topological order. */
  public static  ImmutableList> compute(
      ImmutableCollection nodes, SuccessorsFunction successorsFunction) {
    return new TarjanSCC<>(nodes, successorsFunction).compute();
  }

  private static class TarjanSCC {
    private final ImmutableCollection nodes;
    private final SuccessorsFunction successorsFunction;
    private final Deque stack;
    private final Set onStack;
    private final Map indexes;
    private final Map lowLinks;
    private final List> stronglyConnectedComponents = new ArrayList<>();

    TarjanSCC(ImmutableCollection nodes, SuccessorsFunction successorsFunction) {
      this.nodes = nodes;
      this.successorsFunction = successorsFunction;
      this.stack = new ArrayDeque<>(nodes.size());
      this.onStack = Sets.newHashSetWithExpectedSize(nodes.size());
      this.indexes = Maps.newHashMapWithExpectedSize(nodes.size());
      this.lowLinks = Maps.newHashMapWithExpectedSize(nodes.size());
    }

    private ImmutableList> compute() {
      checkState(indexes.isEmpty(), "TarjanSCC#compute() can only be called once per instance!");
      for (NodeT node : nodes) {
        if (!indexes.containsKey(node)) {
          stronglyConnect(node);
        }
      }
      return ImmutableList.copyOf(stronglyConnectedComponents);
    }

    private void stronglyConnect(NodeT node) {
      // Set the index and lowLink for node to the smallest unused index and add it to the stack
      lowLinks.put(node, indexes.size());
      indexes.put(node, indexes.size());
      stack.push(node);
      onStack.add(node);

      for (NodeT successor : successorsFunction.successors(node)) {
        if (!indexes.containsKey(successor)) {
          // Successor has not been processed.
          stronglyConnect(successor);
          lowLinks.put(node, min(lowLinks.get(node), lowLinks.get(successor)));
        } else if (onStack.contains(successor)) {
          // Successor is on the stack and hence in the current SCC.
          lowLinks.put(node, min(lowLinks.get(node), indexes.get(successor)));
        } else {
          // Successor is not on the stack and hence in an already processed SCC, so ignore.
        }
      }

      // If node is the root of the SCC, pop the stack until reaching the root to get all SCC nodes.
      if (lowLinks.get(node).equals(indexes.get(node))) {
        ImmutableSet.Builder scc = ImmutableSet.builder();
        NodeT currNode;
        do {
          currNode = stack.pop();
          onStack.remove(currNode);
          scc.add(currNode);
        } while (!node.equals(currNode));
        stronglyConnectedComponents.add(scc.build());
      }
    }
  }

  private TarjanSCCs() {}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy