mirror of
https://github.com/AleoHQ/leo.git
synced 2024-11-11 04:49:15 +03:00
Get cycle if toposort fails
This commit is contained in:
parent
9fbcdfc9f3
commit
8b8c7b5c0a
@ -27,9 +27,9 @@ impl Node for Symbol {}
|
||||
|
||||
/// Errors in graph operations.
|
||||
#[derive(Debug)]
|
||||
pub enum GraphError {
|
||||
/// A cycle was detected in the graph.
|
||||
CycleDetected,
|
||||
pub enum GraphError<N: Node> {
|
||||
/// An error that is emitted when a cycle is detected in the graph. Contains the path of cycle.
|
||||
CycleDetected(Vec<N>),
|
||||
}
|
||||
|
||||
/// A directed graph.
|
||||
@ -68,21 +68,40 @@ impl<N: Node> DiGraph<N> {
|
||||
}
|
||||
|
||||
/// Detects if there is a cycle in the graph.
|
||||
pub fn topological_sort(&self) -> Result<IndexSet<N>, GraphError> {
|
||||
pub fn topological_sort(&self) -> Result<IndexSet<N>, GraphError<N>> {
|
||||
// The set of nodes that do not need to be visited again.
|
||||
let mut finished: IndexSet<N> = IndexSet::with_capacity(self.nodes.len());
|
||||
// The set of nodes that are on the path to the current node in the search.
|
||||
let mut discovered: IndexSet<N> = IndexSet::new();
|
||||
|
||||
// Perform a depth-first search of the graph, starting from `node`, for each node in the graph.
|
||||
for node in self.nodes.iter() {
|
||||
// If the node has not been explored, explore it.
|
||||
if !discovered.contains(node) //TODO: Can we remove this check? Any nodes added to discovered in `contains_cycle_from` are removed.
|
||||
&& !finished.contains(node)
|
||||
&& self.contains_cycle_from(*node, &mut discovered, &mut finished)
|
||||
{
|
||||
// A cycle was found.
|
||||
return Err(GraphError::CycleDetected);
|
||||
if !finished.contains(node) {
|
||||
// The set of nodes that are on the path to the current node in the search.
|
||||
let mut discovered: IndexSet<N> = IndexSet::new();
|
||||
// Check if there is a cycle in the graph starting from `node`.
|
||||
if self.contains_cycle_from(*node, &mut discovered, &mut finished) {
|
||||
let path = match discovered.pop() {
|
||||
// TODO: Should this error more silently?
|
||||
None => unreachable!("If `contains_cycle_from` returns `true`, `discovered` is not empty."),
|
||||
Some(node) => {
|
||||
let mut path = vec![node];
|
||||
// Backtrack through the discovered nodes to find the cycle.
|
||||
while let Some(next) = discovered.pop() {
|
||||
// Add the node to the path.
|
||||
path.push(next);
|
||||
// If the node is the same as the first node in the path, we have found the cycle.
|
||||
if next == node {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Reverse the path to get the cycle in the correct order.
|
||||
path.reverse();
|
||||
path
|
||||
}
|
||||
};
|
||||
// A cycle was detected. Return the path of the cycle.
|
||||
return Err(GraphError::CycleDetected(path));
|
||||
}
|
||||
}
|
||||
}
|
||||
// No cycle was found. Return the set of nodes in topological order.
|
||||
@ -100,6 +119,9 @@ impl<N: Node> DiGraph<N> {
|
||||
for child in children.iter() {
|
||||
// If the node already been discovered, there is a cycle.
|
||||
if discovered.contains(child) {
|
||||
// Insert the child node into the set of discovered nodes; this is used to reconstruct the cycle.
|
||||
// Note that this case is always hit when there is a cycle.
|
||||
discovered.insert(*child);
|
||||
return true;
|
||||
}
|
||||
// If the node has not been explored, explore it.
|
||||
@ -110,7 +132,7 @@ impl<N: Node> DiGraph<N> {
|
||||
}
|
||||
|
||||
// Remove the node from the set of discovered nodes.
|
||||
discovered.remove(&node);
|
||||
discovered.pop();
|
||||
// Add the node to the set of finished nodes.
|
||||
finished.insert(node);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user