use crate::node::NodeId;
use std::collections::{HashMap, HashSet, VecDeque, hash_map::Entry};
pub(crate) struct AbstractGraph {
pub in_degree: HashMap<NodeId, usize>,
pub edges: HashMap<NodeId, HashSet<NodeId>>,
pub folded_nodes: HashMap<NodeId, NodeId>,
pub unfold_abstract_nodes: HashMap<NodeId, Vec<NodeId>>,
}
impl AbstractGraph {
pub fn new() -> Self {
Self {
in_degree: HashMap::new(),
edges: HashMap::new(),
folded_nodes: HashMap::new(),
unfold_abstract_nodes: HashMap::new(),
}
}
pub fn add_node(&mut self, node_id: NodeId) {
if let Entry::Vacant(entry) = self.in_degree.entry(node_id) {
entry.insert(0);
self.edges.insert(node_id, HashSet::new());
}
}
pub fn add_edge(&mut self, from: NodeId, to: NodeId) {
let mut abstract_flag_from = false;
let mut abstract_flag_to = false;
let from = if let Some(abstract_id) = self.get_abstract_node_id(&from) {
abstract_flag_from = true;
*abstract_id
} else {
from
};
let to = if let Some(abstract_id) = self.get_abstract_node_id(&to) {
abstract_flag_to = true;
*abstract_id
} else {
to
};
if abstract_flag_from && abstract_flag_to && from == to {
return;
}
log::debug!("Adding edge from {:?} to {:?}", from, to);
self.edges.get_mut(&from).unwrap().insert(to);
*self.in_degree.get_mut(&to).unwrap() += 1;
}
pub fn add_folded_node(&mut self, abstract_node_id: NodeId, concrete_node_id: Vec<NodeId>) {
self.add_node(abstract_node_id);
for concrete_id in &concrete_node_id {
self.folded_nodes.insert(*concrete_id, abstract_node_id);
}
self.unfold_abstract_nodes
.insert(abstract_node_id, concrete_node_id);
}
pub fn unfold_node(&self, abstract_node_id: NodeId) -> Option<&Vec<NodeId>> {
self.unfold_abstract_nodes.get(&abstract_node_id)
}
pub fn get_abstract_node_id(&self, node_id: &NodeId) -> Option<&NodeId> {
self.folded_nodes.get(node_id)
}
pub fn size(&self) -> usize {
self.in_degree.len()
}
#[allow(dead_code)]
pub fn check_loop(&self) -> bool {
let mut in_degree = self.in_degree.clone();
let mut visited_count = 0;
let mut queue: VecDeque<NodeId> = in_degree
.iter()
.filter_map(|(&node, °ree)| if degree == 0 { Some(node) } else { None })
.collect();
while let Some(node) = queue.pop_front() {
log::debug!("Visiting node: {:?}", node);
visited_count += 1;
if let Some(nexts) = self.edges.get(&node) {
for next in nexts {
let degree = in_degree.get_mut(next).unwrap();
*degree -= 1;
if *degree == 0 {
queue.push_back(*next);
}
}
}
}
log::debug!("Visited count: {}, Size: {}", visited_count, self.size());
visited_count != self.size()
}
pub fn get_topological_sort(&self) -> Option<Vec<NodeId>> {
let mut in_degree = self.in_degree.clone();
let mut queue: VecDeque<NodeId> = in_degree
.iter()
.filter_map(|(&node, °ree)| if degree == 0 { Some(node) } else { None })
.collect();
let mut sorted = Vec::new();
while let Some(node) = queue.pop_front() {
sorted.push(node);
if let Some(nexts) = self.edges.get(&node) {
for next in nexts {
let degree = in_degree.get_mut(next).unwrap();
*degree -= 1;
if *degree == 0 {
queue.push_back(*next);
}
}
}
}
if sorted.len() == self.size() {
Some(sorted)
} else {
None
}
}
}
#[cfg(test)]
mod abstract_graph_test {
use super::*;
#[test]
fn test_check_loop() {
let mut graph = AbstractGraph::new();
graph.add_node(NodeId(1));
graph.add_node(NodeId(2));
graph.add_edge(NodeId(1), NodeId(2));
graph.add_edge(NodeId(2), NodeId(1));
assert!(graph.check_loop());
}
#[test]
fn test_check_no_loop() {
let mut graph = AbstractGraph::new();
graph.add_node(NodeId(1));
graph.add_node(NodeId(2));
graph.add_edge(NodeId(1), NodeId(2));
assert!(!graph.check_loop());
}
}