diff --git a/api/server/src/main/java/org/softwareheritage/graph/algo/Visit.java b/api/server/src/main/java/org/softwareheritage/graph/algo/Visit.java index 9d74926..c1118ee 100644 --- a/api/server/src/main/java/org/softwareheritage/graph/algo/Visit.java +++ b/api/server/src/main/java/org/softwareheritage/graph/algo/Visit.java @@ -1,98 +1,104 @@ package org.softwareheritage.graph.algo; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.Stack; import it.unimi.dsi.big.webgraph.LazyLongIterator; +import it.unimi.dsi.bits.LongArrayBitVector; import org.softwareheritage.graph.AllowedEdges; import org.softwareheritage.graph.Graph; import org.softwareheritage.graph.Node; import org.softwareheritage.graph.SwhId; import org.softwareheritage.graph.SwhPath; public class Visit { public enum OutputFmt { ONLY_NODES, ONLY_PATHS, NODES_AND_PATHS } Graph graph; boolean useTransposed; AllowedEdges edges; // LinkedHashSet is necessary to preserve insertion order LinkedHashSet nodes; ArrayList paths; Stack currentPath; + LongArrayBitVector visited; public Visit(Graph graph, SwhId src, String edgesFmt, String direction, OutputFmt output) { if (!direction.matches("forward|backward")) { throw new IllegalArgumentException("Unknown traversal direction: " + direction); } this.graph = graph; this.useTransposed = (direction.equals("backward")); this.edges = new AllowedEdges(edgesFmt); this.nodes = new LinkedHashSet(); this.paths = new ArrayList(); this.currentPath = new Stack(); + this.visited = LongArrayBitVector.ofLength(graph.getNbNodes()); long nodeId = graph.getNodeId(src); if (output == OutputFmt.ONLY_NODES) { dfsOutputOnlyNodes(nodeId); } else { dfs(nodeId); } } public LinkedHashSet getNodes() { return nodes; } public ArrayList getPaths() { return paths; } private void dfs(long currentNodeId) { nodes.add(graph.getSwhId(currentNodeId)); currentPath.push(currentNodeId); long degree = graph.degree(currentNodeId, useTransposed); LazyLongIterator neighbors = graph.neighbors(currentNodeId, useTransposed); long visitedNeighbors = 0; while (degree-- > 0) { long neighborNodeId = neighbors.nextLong(); Node.Type currentNodeType = graph.getSwhId(currentNodeId).getType(); Node.Type neighborNodeType = graph.getSwhId(neighborNodeId).getType(); if (edges.isAllowed(currentNodeType, neighborNodeType)) { dfs(neighborNodeId); visitedNeighbors++; } } if (visitedNeighbors == 0) { SwhPath path = new SwhPath(); for (long nodeId : currentPath) { path.add(graph.getSwhId(nodeId)); } paths.add(path); } currentPath.pop(); } private void dfsOutputOnlyNodes(long currentNodeId) { nodes.add(graph.getSwhId(currentNodeId)); + visited.set(currentNodeId); + long degree = graph.degree(currentNodeId, useTransposed); LazyLongIterator neighbors = graph.neighbors(currentNodeId, useTransposed); while (degree-- > 0) { long neighborNodeId = neighbors.nextLong(); Node.Type currentNodeType = graph.getSwhId(currentNodeId).getType(); Node.Type neighborNodeType = graph.getSwhId(neighborNodeId).getType(); - if (edges.isAllowed(currentNodeType, neighborNodeType)) { + if (!visited.getBoolean(neighborNodeId) + && edges.isAllowed(currentNodeType, neighborNodeType)) { dfsOutputOnlyNodes(neighborNodeId); } } } }