89 lines
2.3 KiB
Rust
89 lines
2.3 KiB
Rust
use std::collections::{HashSet, VecDeque};
|
|
|
|
use crate::error::LayoutError;
|
|
use crate::model::Graph;
|
|
|
|
pub fn compute_hierarchy_levels(graph: &Graph) -> Result<Vec<usize>, LayoutError> {
|
|
validate_simple_dag(graph)?;
|
|
|
|
let node_count = graph.nodes.len();
|
|
if node_count == 0 {
|
|
return Ok(Vec::new());
|
|
}
|
|
|
|
let mut indegree = vec![0usize; node_count];
|
|
let mut outgoing = vec![Vec::new(); node_count];
|
|
|
|
for edge in &graph.edges {
|
|
indegree[edge.target] += 1;
|
|
outgoing[edge.source].push(edge.target);
|
|
}
|
|
|
|
let mut queue = VecDeque::new();
|
|
for (node_index, degree) in indegree.iter().enumerate() {
|
|
if *degree == 0 {
|
|
queue.push_back(node_index);
|
|
}
|
|
}
|
|
|
|
let mut levels = vec![0usize; node_count];
|
|
let mut visited = 0usize;
|
|
|
|
while let Some(node) = queue.pop_front() {
|
|
visited += 1;
|
|
let next_level = levels[node] + 1;
|
|
for &child in &outgoing[node] {
|
|
if levels[child] < next_level {
|
|
levels[child] = next_level;
|
|
}
|
|
indegree[child] -= 1;
|
|
if indegree[child] == 0 {
|
|
queue.push_back(child);
|
|
}
|
|
}
|
|
}
|
|
|
|
if visited != node_count {
|
|
return Err(LayoutError::CycleDetected);
|
|
}
|
|
|
|
Ok(levels)
|
|
}
|
|
|
|
pub(crate) fn validate_simple_dag(graph: &Graph) -> Result<(), LayoutError> {
|
|
let node_count = graph.nodes.len();
|
|
let mut seen_edges = HashSet::new();
|
|
|
|
for (edge_index, edge) in graph.edges.iter().enumerate() {
|
|
if edge.source >= node_count {
|
|
return Err(LayoutError::InvalidNodeIndex {
|
|
edge_index,
|
|
node_index: edge.source,
|
|
node_count,
|
|
});
|
|
}
|
|
if edge.target >= node_count {
|
|
return Err(LayoutError::InvalidNodeIndex {
|
|
edge_index,
|
|
node_index: edge.target,
|
|
node_count,
|
|
});
|
|
}
|
|
if edge.source == edge.target {
|
|
return Err(LayoutError::SelfLoop {
|
|
edge_index,
|
|
node: edge.source,
|
|
});
|
|
}
|
|
if !seen_edges.insert((edge.source, edge.target)) {
|
|
return Err(LayoutError::DuplicateEdge {
|
|
edge_index,
|
|
source: edge.source,
|
|
target: edge.target,
|
|
});
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|