|
17 | 17 | #include "nix/util/error.hh" |
18 | 18 |
|
19 | 19 | #include <boost/graph/graph_traits.hpp> |
| 20 | +#include <boost/graph/depth_first_search.hpp> |
20 | 21 | #include <boost/graph/reverse_graph.hpp> |
21 | 22 | #include <boost/graph/properties.hpp> |
22 | 23 |
|
@@ -222,6 +223,61 @@ DependencyGraph<NodeId, EdgeProperty>::getEdgeProperty(const NodeId & from, cons |
222 | 223 | return graph[edge]; |
223 | 224 | } |
224 | 225 |
|
| 226 | +template<GraphNodeId NodeId, typename EdgeProperty> |
| 227 | +std::vector<std::vector<NodeId>> DependencyGraph<NodeId, EdgeProperty>::findCycles() const |
| 228 | +{ |
| 229 | + using vertex_descriptor = typename boost::graph_traits<Graph>::vertex_descriptor; |
| 230 | + using edge_descriptor = typename boost::graph_traits<Graph>::edge_descriptor; |
| 231 | + |
| 232 | + std::vector<std::vector<vertex_descriptor>> cycleDescriptors; |
| 233 | + std::vector<vertex_descriptor> dfsPath; |
| 234 | + |
| 235 | + // Custom DFS visitor to detect back edges and extract cycles |
| 236 | + class CycleFinder : public boost::default_dfs_visitor |
| 237 | + { |
| 238 | + public: |
| 239 | + std::vector<std::vector<vertex_descriptor>> & cycles; |
| 240 | + std::vector<vertex_descriptor> & dfsPath; |
| 241 | + |
| 242 | + CycleFinder(std::vector<std::vector<vertex_descriptor>> & cycles, std::vector<vertex_descriptor> & dfsPath) |
| 243 | + : cycles(cycles) |
| 244 | + , dfsPath(dfsPath) |
| 245 | + { |
| 246 | + } |
| 247 | + |
| 248 | + void discover_vertex(vertex_descriptor v, const Graph & g) |
| 249 | + { |
| 250 | + dfsPath.push_back(v); |
| 251 | + } |
| 252 | + |
| 253 | + void finish_vertex(vertex_descriptor v, const Graph & g) |
| 254 | + { |
| 255 | + if (!dfsPath.empty() && dfsPath.back() == v) { |
| 256 | + dfsPath.pop_back(); |
| 257 | + } |
| 258 | + } |
| 259 | + |
| 260 | + void back_edge(edge_descriptor e, const Graph & g) |
| 261 | + { |
| 262 | + auto target = boost::target(e, g); |
| 263 | + auto cycleStart = std::ranges::find(dfsPath, target); |
| 264 | + std::vector<vertex_descriptor> cycle(cycleStart, dfsPath.end()); |
| 265 | + cycle.push_back(target); |
| 266 | + cycles.push_back(std::move(cycle)); |
| 267 | + } |
| 268 | + }; |
| 269 | + |
| 270 | + CycleFinder visitor(cycleDescriptors, dfsPath); |
| 271 | + boost::depth_first_search(graph, boost::visitor(visitor)); |
| 272 | + |
| 273 | + // Convert vertex_descriptors to NodeIds using ranges |
| 274 | + return cycleDescriptors | std::views::transform([&](const auto & cycleVerts) { |
| 275 | + return cycleVerts | std::views::transform([&](auto v) { return getNodeId(v); }) |
| 276 | + | std::ranges::to<std::vector<NodeId>>(); |
| 277 | + }) |
| 278 | + | std::ranges::to<std::vector>(); |
| 279 | +} |
| 280 | + |
225 | 281 | template<GraphNodeId NodeId, typename EdgeProperty> |
226 | 282 | std::vector<NodeId> DependencyGraph<NodeId, EdgeProperty>::getAllNodes() const |
227 | 283 | { |
|
0 commit comments