#![deny(unsafe_code)]
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
pub mod arc;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct NodeId(pub usize);
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct ReifiedGraph<T> {
pub nodes: Vec<(NodeId, T)>,
pub edges: Vec<(NodeId, NodeId)>,
pub root: NodeId,
}
pub fn node_id_of<T>(rc: &Rc<RefCell<T>>) -> NodeId {
NodeId(Rc::as_ptr(rc) as *const () as usize)
}
pub fn collect_nodes<T, F>(root: &Rc<RefCell<T>>, children: &F) -> HashMap<NodeId, Rc<RefCell<T>>>
where
F: Fn(&T) -> Vec<Rc<RefCell<T>>>,
{
let mut visited = HashMap::new();
collect_nodes_inner(root, children, &mut visited);
visited
}
fn collect_nodes_inner<T, F>(
node: &Rc<RefCell<T>>,
children: &F,
visited: &mut HashMap<NodeId, Rc<RefCell<T>>>,
) where
F: Fn(&T) -> Vec<Rc<RefCell<T>>>,
{
let id = node_id_of(node);
if visited.contains_key(&id) {
return;
}
visited.insert(id, node.clone());
let kids = children(&node.borrow());
for kid in &kids {
collect_nodes_inner(kid, children, visited);
}
}
pub fn reify_graph<T, F>(root: Rc<RefCell<T>>, children: F) -> ReifiedGraph<T>
where
F: Fn(&T) -> Vec<Rc<RefCell<T>>>,
T: Clone,
{
let all_nodes = collect_nodes(&root, &children);
let root_id = node_id_of(&root);
let mut nodes = Vec::with_capacity(all_nodes.len());
let mut edges = Vec::new();
for (id, rc) in &all_nodes {
let borrowed = rc.borrow();
nodes.push((*id, borrowed.clone()));
for kid in children(&borrowed) {
let kid_id = node_id_of(&kid);
edges.push((*id, kid_id));
}
}
ReifiedGraph {
nodes,
edges,
root: root_id,
}
}
pub fn reflect_graph<T, F>(graph: ReifiedGraph<T>, set_children: F) -> Rc<RefCell<T>>
where
F: Fn(&mut T, Vec<Rc<RefCell<T>>>),
T: Clone,
{
let mut rc_map: HashMap<NodeId, Rc<RefCell<T>>> = HashMap::new();
for (id, data) in &graph.nodes {
rc_map.insert(*id, Rc::new(RefCell::new(data.clone())));
}
let mut adj: HashMap<NodeId, Vec<NodeId>> = HashMap::new();
for (from, to) in &graph.edges {
adj.entry(*from).or_default().push(*to);
}
for (id, rc) in &rc_map {
if let Some(child_ids) = adj.get(id) {
let children: Vec<Rc<RefCell<T>>> =
child_ids.iter().map(|cid| rc_map[cid].clone()).collect();
set_children(&mut rc.borrow_mut(), children);
}
}
rc_map[&graph.root].clone()
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Clone, Debug, PartialEq)]
struct Node {
value: i32,
children: Vec<Rc<RefCell<Node>>>,
}
fn children_of(n: &Node) -> Vec<Rc<RefCell<Node>>> {
n.children.clone()
}
fn set_children_of(n: &mut Node, kids: Vec<Rc<RefCell<Node>>>) {
n.children = kids;
}
#[test]
fn node_id_identity() {
let a = Rc::new(RefCell::new(Node {
value: 1,
children: vec![],
}));
let b = a.clone();
assert_eq!(node_id_of(&a), node_id_of(&b));
let c = Rc::new(RefCell::new(Node {
value: 1,
children: vec![],
}));
assert_ne!(node_id_of(&a), node_id_of(&c));
}
#[test]
fn collect_nodes_simple_linked_list() {
let c = Rc::new(RefCell::new(Node {
value: 2,
children: vec![],
}));
let b = Rc::new(RefCell::new(Node {
value: 1,
children: vec![c.clone()],
}));
let a = Rc::new(RefCell::new(Node {
value: 0,
children: vec![b.clone()],
}));
let nodes = collect_nodes(&a, &children_of);
assert_eq!(nodes.len(), 3);
}
#[test]
fn collect_nodes_with_cycle() {
let a = Rc::new(RefCell::new(Node {
value: 0,
children: vec![],
}));
let b = Rc::new(RefCell::new(Node {
value: 1,
children: vec![a.clone()],
}));
a.borrow_mut().children.push(b.clone());
let nodes = collect_nodes(&a, &children_of);
assert_eq!(nodes.len(), 2);
}
#[test]
fn reify_dag_with_shared_children() {
let shared = Rc::new(RefCell::new(Node {
value: 99,
children: vec![],
}));
let a = Rc::new(RefCell::new(Node {
value: 1,
children: vec![shared.clone()],
}));
let b = Rc::new(RefCell::new(Node {
value: 2,
children: vec![shared.clone()],
}));
let root = Rc::new(RefCell::new(Node {
value: 0,
children: vec![a.clone(), b.clone(), shared.clone()],
}));
let graph = reify_graph(root, children_of);
assert_eq!(graph.nodes.len(), 4);
assert_eq!(graph.edges.len(), 5);
}
#[test]
fn reify_five_node_dag() {
let n4 = Rc::new(RefCell::new(Node {
value: 4,
children: vec![],
}));
let n3 = Rc::new(RefCell::new(Node {
value: 3,
children: vec![n4.clone()],
}));
let n2 = Rc::new(RefCell::new(Node {
value: 2,
children: vec![n3.clone()],
}));
let n1 = Rc::new(RefCell::new(Node {
value: 1,
children: vec![n3.clone(), n4.clone()],
}));
let n0 = Rc::new(RefCell::new(Node {
value: 0,
children: vec![n1.clone(), n2.clone()],
}));
let graph = reify_graph(n0, children_of);
assert_eq!(graph.nodes.len(), 5);
assert_eq!(graph.edges.len(), 6);
}
#[test]
fn round_trip_reify_reflect() {
let leaf1 = Rc::new(RefCell::new(Node {
value: 10,
children: vec![],
}));
let leaf2 = Rc::new(RefCell::new(Node {
value: 20,
children: vec![],
}));
let root = Rc::new(RefCell::new(Node {
value: 0,
children: vec![leaf1, leaf2],
}));
let graph = reify_graph(root.clone(), children_of);
let rebuilt = reflect_graph(graph, set_children_of);
assert_eq!(rebuilt.borrow().value, 0);
assert_eq!(rebuilt.borrow().children.len(), 2);
let mut child_values: Vec<i32> = rebuilt
.borrow()
.children
.iter()
.map(|c| c.borrow().value)
.collect();
child_values.sort();
assert_eq!(child_values, vec![10, 20]);
}
#[test]
fn round_trip_preserves_sharing() {
let shared = Rc::new(RefCell::new(Node {
value: 42,
children: vec![],
}));
let root = Rc::new(RefCell::new(Node {
value: 0,
children: vec![shared.clone(), shared.clone()],
}));
let graph = reify_graph(root, children_of);
let rebuilt = reflect_graph(graph, set_children_of);
let children = &rebuilt.borrow().children;
assert_eq!(children.len(), 2);
assert_eq!(
Rc::as_ptr(&children[0]) as usize,
Rc::as_ptr(&children[1]) as usize
);
}
}