use indexmap::IndexMap;
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq)]
pub struct Point {
pub x: f64,
pub y: f64,
}
#[derive(Debug, Clone, Default)]
pub struct NodeLabel {
pub width: f64,
pub height: f64,
pub x: Option<f64>,
pub y: Option<f64>,
pub rank: Option<i32>,
pub order: Option<i32>,
pub dummy: Option<String>,
pub border_type: Option<String>,
pub border_top: Option<String>,
pub border_bottom: Option<String>,
pub border_left: Option<Vec<Option<String>>>,
pub border_right: Option<Vec<Option<String>>>,
pub min_rank: Option<i32>,
pub max_rank: Option<i32>,
pub low: Option<i32>,
pub lim: Option<i32>,
pub parent_node: Option<String>,
pub self_edges: Option<Vec<SelfEdge>>,
pub edge_obj: Option<Edge>,
pub edge_label: Option<Box<EdgeLabel>>,
pub label: Option<String>,
pub labelpos: Option<String>,
pub e: Option<Edge>,
pub intersect_type: Option<&'static str>,
}
#[derive(Debug, Clone)]
pub struct SelfEdge {
pub e: Edge,
pub label: EdgeLabel,
}
#[derive(Debug, Clone, Default)]
pub struct EdgeLabel {
pub points: Option<Vec<Point>>,
pub width: Option<f64>,
pub height: Option<f64>,
pub minlen: Option<i32>,
pub weight: Option<f64>,
pub labelpos: Option<String>,
pub labeloffset: Option<f64>,
pub label_rank: Option<i32>,
pub x: Option<f64>,
pub y: Option<f64>,
pub reversed: Option<bool>,
pub forward_name: Option<String>,
pub self_edge: Option<bool>,
pub nesting_edge: Option<bool>,
pub cutvalue: Option<f64>,
pub lim: Option<i32>,
pub low: Option<i32>,
pub parent: Option<String>,
pub edge_label: Option<Box<EdgeLabel>>,
pub edge_obj: Option<Edge>,
}
#[derive(Debug, Clone, Default)]
pub struct GraphLabel {
pub width: Option<f64>,
pub height: Option<f64>,
pub compound: Option<bool>,
pub rankdir: Option<String>,
pub align: Option<String>,
pub nodesep: Option<f64>,
pub edgesep: Option<f64>,
pub ranksep: Option<f64>,
pub marginx: Option<f64>,
pub marginy: Option<f64>,
pub acyclicer: Option<String>,
pub ranker: Option<String>,
pub rankalign: Option<String>,
pub nesting_root: Option<String>,
pub node_rank_factor: Option<i32>,
pub dummy_chains: Option<Vec<String>>,
pub max_rank: Option<i32>,
pub root: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Edge {
pub v: String,
pub w: String,
pub name: Option<String>,
}
impl Edge {
pub fn new(v: &str, w: &str) -> Self {
Edge {
v: v.to_string(),
w: w.to_string(),
name: None,
}
}
pub fn named(v: &str, w: &str, name: &str) -> Self {
Edge {
v: v.to_string(),
w: w.to_string(),
name: Some(name.to_string()),
}
}
}
#[derive(Debug, Clone)]
struct EdgeEntry {
v: String,
w: String,
name: Option<String>,
label: EdgeLabel,
}
fn edge_key(v: &str, w: &str, name: Option<&str>) -> String {
match name {
Some(n) => format!("{}\x00{}\x00{}", v, w, n),
None => format!("{}\x00{}\x00\x01", v, w),
}
}
fn edge_key_undirected(v: &str, w: &str, name: Option<&str>) -> String {
if v <= w {
edge_key(v, w, name)
} else {
edge_key(w, v, name)
}
}
pub struct Graph {
pub is_directed: bool,
pub is_multigraph: bool,
pub is_compound: bool,
label: GraphLabel,
nodes: IndexMap<String, NodeLabel>,
default_node_label: Option<String>,
edges: IndexMap<String, EdgeEntry>,
in_edges: HashMap<String, IndexMap<String, Vec<String>>>, out_edges: HashMap<String, IndexMap<String, Vec<String>>>,
parent: HashMap<String, Option<String>>, children: HashMap<String, Vec<String>>, }
impl Graph {
pub fn new() -> Self {
Graph::with_options(false, false, false)
}
pub fn directed() -> Self {
Graph::with_options(true, false, false)
}
pub fn multigraph_compound() -> Self {
Graph::with_options(true, true, true)
}
pub fn undirected() -> Self {
Graph::with_options(false, false, false)
}
pub fn with_options(directed: bool, multigraph: bool, compound: bool) -> Self {
let mut g = Graph {
is_directed: directed,
is_multigraph: multigraph,
is_compound: compound,
label: GraphLabel::default(),
nodes: IndexMap::new(),
default_node_label: None,
edges: IndexMap::new(),
in_edges: HashMap::new(),
out_edges: HashMap::new(),
parent: HashMap::new(),
children: HashMap::new(),
};
g.children.insert("\x00".to_string(), Vec::new());
g
}
pub fn graph(&self) -> &GraphLabel {
&self.label
}
pub fn graph_mut(&mut self) -> &mut GraphLabel {
&mut self.label
}
pub fn set_graph(&mut self, label: GraphLabel) -> &mut Self {
self.label = label;
self
}
pub fn set_node(&mut self, v: &str, label: NodeLabel) {
if !self.nodes.contains_key(v) {
self.nodes.insert(v.to_string(), label);
self.in_edges.insert(v.to_string(), IndexMap::new());
self.out_edges.insert(v.to_string(), IndexMap::new());
if self.is_compound {
self.parent.insert(v.to_string(), None);
let root_children = self.children.entry("\x00".to_string()).or_default();
root_children.push(v.to_string());
self.children.insert(v.to_string(), Vec::new());
}
} else {
*self.nodes.get_mut(v).unwrap() = label;
}
}
pub fn set_node_default(&mut self, v: &str) {
self.set_node(v, NodeLabel::default());
}
pub fn node(&self, v: &str) -> &NodeLabel {
self.nodes
.get(v)
.unwrap_or_else(|| panic!("Node '{}' not found", v))
}
pub fn node_mut(&mut self, v: &str) -> &mut NodeLabel {
self.nodes
.get_mut(v)
.unwrap_or_else(|| panic!("Node '{}' not found", v))
}
pub fn node_opt(&self, v: &str) -> Option<&NodeLabel> {
self.nodes.get(v)
}
pub fn node_opt_mut(&mut self, v: &str) -> Option<&mut NodeLabel> {
self.nodes.get_mut(v)
}
pub fn has_node(&self, v: &str) -> bool {
self.nodes.contains_key(v)
}
pub fn remove_node(&mut self, v: &str) {
if !self.nodes.contains_key(v) {
return;
}
let edges_to_remove: Vec<Edge> =
self.node_edges(v).unwrap_or_default().into_iter().collect();
for e in edges_to_remove {
self.remove_edge_obj(&e);
}
if self.is_compound {
let par = self.parent.remove(v).flatten();
let par_key = par.unwrap_or_else(|| "\x00".to_string());
if let Some(ch) = self.children.get_mut(&par_key) {
ch.retain(|c| c != v);
}
if let Some(ch) = self.children.remove(v) {
for child in ch {
let child_par = self.parent.get_mut(&child);
if let Some(cp) = child_par {
*cp = if par_key == "\x00" {
None
} else {
Some(par_key.clone())
};
}
let new_par_children = self.children.entry(par_key.clone()).or_default();
new_par_children.push(child);
}
}
}
self.nodes.swap_remove(v);
self.in_edges.remove(v);
self.out_edges.remove(v);
}
pub fn nodes(&self) -> Vec<String> {
self.nodes.keys().cloned().collect()
}
pub fn node_count(&self) -> usize {
self.nodes.len()
}
pub fn sources(&self) -> Vec<String> {
self.nodes
.keys()
.filter(|v| self.in_edges.get(*v).is_none_or(|m| m.is_empty()))
.cloned()
.collect()
}
pub fn sinks(&self) -> Vec<String> {
self.nodes
.keys()
.filter(|v| self.out_edges.get(*v).is_none_or(|m| m.is_empty()))
.cloned()
.collect()
}
pub fn set_edge(&mut self, v: &str, w: &str, label: EdgeLabel, name: Option<&str>) {
let e = Edge {
v: v.to_string(),
w: w.to_string(),
name: name.map(|s| s.to_string()),
};
self.set_edge_obj(&e, label);
}
fn ekey(&self, v: &str, w: &str, name: Option<&str>) -> String {
if self.is_directed {
edge_key(v, w, name)
} else {
edge_key_undirected(v, w, name)
}
}
pub fn set_edge_obj(&mut self, e: &Edge, label: EdgeLabel) {
let key = self.ekey(&e.v, &e.w, e.name.as_deref());
if !self.has_node(&e.v) {
self.set_node_default(&e.v);
}
if !self.has_node(&e.w) {
self.set_node_default(&e.w);
}
if self.edges.contains_key(&key) {
self.edges.get_mut(&key).unwrap().label = label;
return;
}
if !self.is_multigraph && self.has_edge(&e.v, &e.w) {
let existing_key = self.ekey(&e.v, &e.w, None);
if let Some(entry) = self.edges.get_mut(&existing_key) {
entry.label = label;
}
return;
}
let entry = EdgeEntry {
v: e.v.clone(),
w: e.w.clone(),
name: e.name.clone(),
label,
};
self.edges.insert(key.clone(), entry);
self.out_edges
.entry(e.v.clone())
.or_default()
.entry(e.w.clone())
.or_default()
.push(key.clone());
if !self.is_directed {
self.out_edges
.entry(e.w.clone())
.or_default()
.entry(e.v.clone())
.or_default()
.push(key.clone());
}
self.in_edges
.entry(e.w.clone())
.or_default()
.entry(e.v.clone())
.or_default()
.push(key.clone());
if !self.is_directed {
self.in_edges
.entry(e.v.clone())
.or_default()
.entry(e.w.clone())
.or_default()
.push(key.clone());
}
}
pub fn edge_label(&self, v: &str, w: &str) -> Option<&EdgeLabel> {
let key = self.ekey(v, w, None);
self.edges.get(&key).map(|e| &e.label)
}
pub fn edge_label_named(&self, v: &str, w: &str, name: &str) -> Option<&EdgeLabel> {
let key = self.ekey(v, w, Some(name));
self.edges.get(&key).map(|e| &e.label)
}
pub fn edge(&self, e: &Edge) -> Option<&EdgeLabel> {
let key = self.ekey(&e.v, &e.w, e.name.as_deref());
self.edges.get(&key).map(|en| &en.label)
}
pub fn edge_mut(&mut self, e: &Edge) -> Option<&mut EdgeLabel> {
let key = self.ekey(&e.v, &e.w, e.name.as_deref());
self.edges.get_mut(&key).map(|en| &mut en.label)
}
pub fn edge_vw(&self, v: &str, w: &str) -> Option<&EdgeLabel> {
let key = self.ekey(v, w, None);
self.edges.get(&key).map(|e| &e.label)
}
pub fn edge_vw_mut(&mut self, v: &str, w: &str) -> Option<&mut EdgeLabel> {
let key = self.ekey(v, w, None);
self.edges.get_mut(&key).map(|e| &mut e.label)
}
pub fn has_edge(&self, v: &str, w: &str) -> bool {
self.out_edges.get(v).is_some_and(|m| m.contains_key(w))
}
pub fn has_edge_obj(&self, e: &Edge) -> bool {
let key = self.ekey(&e.v, &e.w, e.name.as_deref());
self.edges.contains_key(&key)
}
pub fn remove_edge(&mut self, v: &str, w: &str) {
let e = Edge::new(v, w);
self.remove_edge_obj(&e);
}
pub fn remove_edge_named(&mut self, v: &str, w: &str, name: &str) {
let e = Edge::named(v, w, name);
self.remove_edge_obj(&e);
}
pub fn remove_edge_obj(&mut self, e: &Edge) {
let key = self.ekey(&e.v, &e.w, e.name.as_deref());
if !self.edges.contains_key(&key) {
return;
}
self.edges.swap_remove(&key);
if let Some(m) = self.out_edges.get_mut(&e.v) {
if let Some(keys) = m.get_mut(&e.w) {
keys.retain(|k| k != &key);
if keys.is_empty() {
m.shift_remove(&e.w);
}
}
}
if !self.is_directed {
if let Some(m) = self.out_edges.get_mut(&e.w) {
if let Some(keys) = m.get_mut(&e.v) {
keys.retain(|k| k != &key);
if keys.is_empty() {
m.shift_remove(&e.v);
}
}
}
}
if let Some(m) = self.in_edges.get_mut(&e.w) {
if let Some(keys) = m.get_mut(&e.v) {
keys.retain(|k| k != &key);
if keys.is_empty() {
m.shift_remove(&e.v);
}
}
}
if !self.is_directed {
if let Some(m) = self.in_edges.get_mut(&e.v) {
if let Some(keys) = m.get_mut(&e.w) {
keys.retain(|k| k != &key);
if keys.is_empty() {
m.shift_remove(&e.w);
}
}
}
}
}
pub fn edges(&self) -> Vec<Edge> {
self.edges
.values()
.map(|e| Edge {
v: e.v.clone(),
w: e.w.clone(),
name: e.name.clone(),
})
.collect()
}
pub fn edge_count(&self) -> usize {
self.edges.len()
}
fn collect_edges_from_keys(&self, keys: Vec<String>) -> Vec<Edge> {
keys.into_iter()
.filter_map(|k| {
self.edges.get(&k).map(|e| Edge {
v: e.v.clone(),
w: e.w.clone(),
name: e.name.clone(),
})
})
.collect()
}
pub fn in_edges(&self, v: &str) -> Option<Vec<Edge>> {
let m = self.in_edges.get(v)?;
let mut result = Vec::new();
for keys in m.values() {
for k in keys {
if let Some(e) = self.edges.get(k) {
result.push(Edge {
v: e.v.clone(),
w: e.w.clone(),
name: e.name.clone(),
});
}
}
}
Some(result)
}
pub fn out_edges(&self, v: &str) -> Option<Vec<Edge>> {
let m = self.out_edges.get(v)?;
let mut result = Vec::new();
for keys in m.values() {
for k in keys {
if let Some(e) = self.edges.get(k) {
result.push(Edge {
v: e.v.clone(),
w: e.w.clone(),
name: e.name.clone(),
});
}
}
}
Some(result)
}
pub fn out_edges_to(&self, v: &str, w: &str) -> Option<Vec<Edge>> {
let keys = self.out_edges.get(v)?.get(w)?;
Some(self.collect_edges_from_keys(keys.clone()))
}
pub fn node_edges(&self, v: &str) -> Option<Vec<Edge>> {
if !self.has_node(v) {
return None;
}
let mut result = self.in_edges(v).unwrap_or_default();
let out = self.out_edges(v).unwrap_or_default();
for e in out {
if !(e.v == v
&& e.w == v
&& result
.iter()
.any(|r| r.v == e.v && r.w == e.w && r.name == e.name))
{
result.push(e);
}
}
Some(result)
}
pub fn predecessors(&self, v: &str) -> Option<Vec<String>> {
let m = self.in_edges.get(v)?;
Some(m.keys().cloned().collect())
}
pub fn successors(&self, v: &str) -> Option<Vec<String>> {
let m = self.out_edges.get(v)?;
Some(m.keys().cloned().collect())
}
pub fn neighbors(&self, v: &str) -> Option<Vec<String>> {
let mut n: Vec<String> = self.predecessors(v).unwrap_or_default();
let succ = self.successors(v).unwrap_or_default();
for s in succ {
if !n.contains(&s) {
n.push(s);
}
}
Some(n)
}
pub fn parent(&self, v: &str) -> Option<&str> {
self.parent.get(v).and_then(|p| p.as_deref())
}
pub fn set_parent(&mut self, v: &str, parent: Option<&str>) {
if !self.is_compound {
panic!("Not a compound graph");
}
if !self.has_node(v) {
self.set_node_default(v);
}
let old_par = self.parent.get(v).cloned().flatten();
let old_par_key = old_par.unwrap_or_else(|| "\x00".to_string());
if let Some(ch) = self.children.get_mut(&old_par_key) {
ch.retain(|c| c != v);
}
if let Some(new_par) = parent {
if !self.has_node(new_par) {
self.set_node_default(new_par);
}
self.parent.insert(v.to_string(), Some(new_par.to_string()));
self.children
.entry(new_par.to_string())
.or_default()
.push(v.to_string());
} else {
self.parent.insert(v.to_string(), None);
self.children
.entry("\x00".to_string())
.or_default()
.push(v.to_string());
}
}
pub fn children(&self, v: &str) -> Vec<String> {
if self.is_compound {
self.children.get(v).cloned().unwrap_or_default()
} else {
Vec::new()
}
}
pub fn is_multigraph(&self) -> bool {
self.is_multigraph
}
pub fn is_compound(&self) -> bool {
self.is_compound
}
pub fn is_directed(&self) -> bool {
self.is_directed
}
pub fn filter_nodes<F>(&self, pred: F) -> Graph
where
F: Fn(&str) -> bool,
{
let mut g = Graph::with_options(self.is_directed, self.is_multigraph, self.is_compound);
g.label = self.label.clone();
for v in self.nodes.keys() {
if pred(v) {
g.set_node(v, self.node(v).clone());
}
}
for e in self.edges.values() {
if g.has_node(&e.v) && g.has_node(&e.w) {
g.set_edge(&e.v, &e.w, e.label.clone(), e.name.as_deref());
}
}
if self.is_compound {
for v in g.nodes.keys().cloned().collect::<Vec<_>>() {
if let Some(p) = self.parent(&v) {
if g.has_node(p) {
g.set_parent(&v, Some(p));
}
}
}
}
g
}
}
impl Default for Graph {
fn default() -> Self {
Graph::with_options(true, false, false)
}
}
impl Clone for Graph {
fn clone(&self) -> Self {
Graph {
is_directed: self.is_directed,
is_multigraph: self.is_multigraph,
is_compound: self.is_compound,
label: self.label.clone(),
nodes: self.nodes.clone(),
default_node_label: self.default_node_label.clone(),
edges: self.edges.clone(),
in_edges: self.in_edges.clone(),
out_edges: self.out_edges.clone(),
parent: self.parent.clone(),
children: self.children.clone(),
}
}
}
pub fn postorder(graph: &Graph, roots: Vec<String>) -> Vec<String> {
let mut result = Vec::new();
let mut visited = std::collections::HashSet::new();
fn dfs(
graph: &Graph,
v: &str,
visited: &mut std::collections::HashSet<String>,
result: &mut Vec<String>,
) {
if visited.contains(v) {
return;
}
visited.insert(v.to_string());
if let Some(neighbors) = graph.neighbors(v) {
for w in neighbors {
dfs(graph, &w, visited, result);
}
}
result.push(v.to_string());
}
for r in roots {
dfs(graph, &r, &mut visited, &mut result);
}
result
}
pub fn preorder(graph: &Graph, roots: Vec<String>) -> Vec<String> {
let mut result = Vec::new();
let mut visited = std::collections::HashSet::new();
fn dfs(
graph: &Graph,
v: &str,
visited: &mut std::collections::HashSet<String>,
result: &mut Vec<String>,
) {
if visited.contains(v) {
return;
}
visited.insert(v.to_string());
result.push(v.to_string());
if let Some(neighbors) = graph.neighbors(v) {
for w in neighbors {
dfs(graph, &w, visited, result);
}
}
}
for r in roots {
dfs(graph, &r, &mut visited, &mut result);
}
result
}