use std::collections::{HashSet, VecDeque}; use crate::error::LayoutError; use crate::model::Graph; pub fn compute_hierarchy_levels(graph: &Graph) -> Result, 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(()) }