/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* 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 com.intellij.util.graph.impl;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.util.Chunk;
import com.intellij.util.graph.*;
import org.jetbrains.annotations.NotNull;
import java.util.*;
/**
* @author nik
*/
public class GraphAlgorithmsImpl extends GraphAlgorithms {
@Override
public <Node> List<Node> findShortestPath(@NotNull Graph<Node> graph, @NotNull Node start, @NotNull Node finish) {
return new ShortestPathFinder<>(graph).findPath(start, finish);
}
@NotNull
@Override
public <Node> List<List<Node>> findKShortestPaths(@NotNull Graph<Node> graph, @NotNull Node start, @NotNull Node finish, int k,
@NotNull ProgressIndicator progressIndicator) {
return new KShortestPathsFinder<>(graph, start, finish, progressIndicator).findShortestPaths(k);
}
@NotNull
@Override
public <Node> Set<List<Node>> findCycles(@NotNull Graph<Node> graph, @NotNull Node node) {
return new CycleFinder<>(graph).getNodeCycles(node);
}
@NotNull
@Override
public <Node> Graph<Node> invertEdgeDirections(@NotNull final Graph<Node> graph) {
return new Graph<Node>() {
public Collection<Node> getNodes() {
return graph.getNodes();
}
public Iterator<Node> getIn(final Node n) {
return graph.getOut(n);
}
public Iterator<Node> getOut(final Node n) {
return graph.getIn(n);
}
};
}
@NotNull
@Override
public <Node> Graph<Chunk<Node>> computeSCCGraph(@NotNull final Graph<Node> graph) {
final DFSTBuilder<Node> builder = new DFSTBuilder<>(graph);
final Collection<Collection<Node>> components = builder.getComponents();
final List<Chunk<Node>> chunks = new ArrayList<>(components.size());
final Map<Node, Chunk<Node>> nodeToChunkMap = new LinkedHashMap<>();
for (Collection<Node> component : components) {
final Set<Node> chunkNodes = new LinkedHashSet<>();
final Chunk<Node> chunk = new Chunk<>(chunkNodes);
chunks.add(chunk);
for (Node node : component) {
chunkNodes.add(node);
nodeToChunkMap.put(node, chunk);
}
}
return GraphGenerator.generate(CachingSemiGraph.cache(new InboundSemiGraph<Chunk<Node>>() {
@Override
public Collection<Chunk<Node>> getNodes() {
return chunks;
}
@Override
public Iterator<Chunk<Node>> getIn(Chunk<Node> chunk) {
final Set<Node> chunkNodes = chunk.getNodes();
final Set<Chunk<Node>> ins = new LinkedHashSet<>();
for (final Node node : chunkNodes) {
for (Iterator<Node> nodeIns = graph.getIn(node); nodeIns.hasNext(); ) {
final Node in = nodeIns.next();
if (!chunk.containsNode(in)) {
ins.add(nodeToChunkMap.get(in));
}
}
}
return ins.iterator();
}
}));
}
@Override
public <Node> void collectOutsRecursively(@NotNull Graph<Node> graph, Node start, Set<Node> set) {
if (!set.add(start)) {
return;
}
Iterator<Node> iterator = graph.getOut(start);
while (iterator.hasNext()) {
Node node = iterator.next();
collectOutsRecursively(graph, node, set);
}
}
@NotNull
@Override
public <Node> Collection<Chunk<Node>> computeStronglyConnectedComponents(@NotNull Graph<Node> graph) {
return computeSCCGraph(graph).getNodes();
}
@NotNull
@Override
public <Node> List<List<Node>> removePathsWithCycles(@NotNull List<List<Node>> paths) {
final List<List<Node>> result = new ArrayList<>();
for (List<Node> path : paths) {
if (!containsCycle(path)) {
result.add(path);
}
}
return result;
}
private static boolean containsCycle(List<?> path) {
return new HashSet<Object>(path).size() != path.size();
}
}