Skip to main content

dugong_graphlib/graph/
core.rs

1//! Core graph container implementation.
2
3use rustc_hash::FxBuildHasher;
4use std::cell::RefCell;
5
6use super::adj_cache::{DirectedAdjCache, UndirectedAdjCache};
7use super::edge_key::{EdgeKey, EdgeKeyView};
8use super::entries::{EdgeEntry, NodeEntry};
9use super::options::GraphOptions;
10
11type HashMap<K, V> = hashbrown::HashMap<K, V, FxBuildHasher>;
12type HashSet<T> = hashbrown::HashSet<T, FxBuildHasher>;
13
14pub struct Graph<N, E, G>
15where
16    N: Default + 'static,
17    E: Default + 'static,
18    G: Default,
19{
20    options: GraphOptions,
21
22    graph_label: G,
23    default_node_label: Box<dyn Fn() -> N + Send + Sync>,
24    default_edge_label: Box<dyn Fn() -> E + Send + Sync>,
25
26    nodes: Vec<Option<NodeEntry<N>>>,
27    node_len: usize,
28    node_index: HashMap<String, usize>,
29
30    edges: Vec<Option<EdgeEntry<E>>>,
31    edge_len: usize,
32    edge_index: HashMap<EdgeKey, usize>,
33
34    // Compound graph parent/children relationships.
35    //
36    // Dagre-style algorithms touch `parent(...)` and `children_iter(...)` frequently; storing
37    // these relationships by node index avoids repeated string hashing and avoids allocating
38    // duplicate `String`s when setting parent links between already-present nodes.
39    parent_ix: Vec<Option<usize>>,
40    children_ix: Vec<Vec<usize>>,
41
42    // Many Dagre algorithms call `predecessors` / `successors` / `in_edges` / `out_edges`
43    // repeatedly. Scanning `self.edges` each time is O(E) per query and dominates runtime
44    // for large graphs. We keep a lazily rebuilt adjacency cache for directed graphs.
45    //
46    // Note: This uses interior mutability to keep query APIs on `&self`.
47    directed_adj_gen: u64,
48    directed_adj_cache: RefCell<Option<DirectedAdjCache>>,
49
50    // Some Dagre helpers (especially `network-simplex`) use undirected trees. Make adjacency
51    // queries for undirected graphs fast as well.
52    undirected_adj_gen: u64,
53    undirected_adj_cache: RefCell<Option<UndirectedAdjCache>>,
54}
55
56impl<N, E, G> Graph<N, E, G>
57where
58    N: Default + 'static,
59    E: Default + 'static,
60    G: Default,
61{
62    fn insert_node_entry(&mut self, id: String, label: N) -> usize {
63        self.invalidate_adj();
64        let idx = self.nodes.len();
65        self.nodes.push(Some(NodeEntry {
66            id: id.clone(),
67            label,
68        }));
69        self.node_len += 1;
70        self.node_index.insert(id, idx);
71        self.parent_ix.push(None);
72        self.children_ix.push(Vec::new());
73        idx
74    }
75
76    fn trim_trailing_node_tombstones(&mut self) {
77        while matches!(self.nodes.last(), Some(None)) {
78            self.nodes.pop();
79            self.parent_ix.pop();
80            self.children_ix.pop();
81        }
82    }
83
84    fn trim_trailing_edge_tombstones(&mut self) {
85        while matches!(self.edges.last(), Some(None)) {
86            self.edges.pop();
87        }
88    }
89
90    pub fn compact_if_sparse(&mut self, max_capacity_factor: f64) -> bool {
91        // Note: `nodes.len()` / `edges.len()` are slot capacities; `node_len` / `edge_len` are
92        // live counts. When a graph is built once and then repeatedly mutated (layout pipelines
93        // add/remove dummy nodes), tombstones can accumulate. Callers that reuse graphs can
94        // periodically compact to keep memory and cache rebuild costs bounded.
95        let nodes_sparse = if self.node_len == 0 {
96            !self.nodes.is_empty()
97        } else {
98            max_capacity_factor > 1.0
99                && (self.nodes.len() as f64) > (self.node_len as f64) * max_capacity_factor
100        };
101        let edges_sparse = if self.edge_len == 0 {
102            !self.edges.is_empty()
103        } else {
104            max_capacity_factor > 1.0
105                && (self.edges.len() as f64) > (self.edge_len as f64) * max_capacity_factor
106        };
107
108        if !(nodes_sparse || edges_sparse) {
109            return false;
110        }
111
112        self.compact();
113        true
114    }
115
116    fn compact(&mut self) {
117        self.invalidate_adj();
118
119        if self.node_len == 0 {
120            self.nodes.clear();
121            self.node_index.clear();
122            self.node_len = 0;
123
124            self.edges.clear();
125            self.edge_index.clear();
126            self.edge_len = 0;
127
128            self.parent_ix.clear();
129            self.children_ix.clear();
130            return;
131        }
132
133        let old_nodes = std::mem::take(&mut self.nodes);
134        let old_parent_ix = std::mem::take(&mut self.parent_ix);
135        let old_children_ix = std::mem::take(&mut self.children_ix);
136        let mut node_remap: Vec<Option<usize>> = vec![None; old_nodes.len()];
137
138        let mut new_nodes: Vec<Option<NodeEntry<N>>> = Vec::with_capacity(self.node_len);
139        let mut new_node_index: HashMap<String, usize> = HashMap::default();
140        for (old_ix, slot) in old_nodes.into_iter().enumerate() {
141            let Some(node) = slot else {
142                continue;
143            };
144            let new_ix = new_nodes.len();
145            new_node_index.insert(node.id.clone(), new_ix);
146            node_remap[old_ix] = Some(new_ix);
147            new_nodes.push(Some(node));
148        }
149
150        self.nodes = new_nodes;
151        self.node_index = new_node_index;
152        self.node_len = self.nodes.len();
153
154        self.parent_ix = vec![None; self.nodes.len()];
155        self.children_ix = vec![Vec::new(); self.nodes.len()];
156        if self.options.compound {
157            for (old_parent, old_children) in old_children_ix.into_iter().enumerate() {
158                let Some(new_parent) = node_remap.get(old_parent).copied().flatten() else {
159                    continue;
160                };
161                let new_children_vec = self
162                    .children_ix
163                    .get_mut(new_parent)
164                    .expect("children_ix resized to node slots");
165                for old_child in old_children {
166                    let Some(new_child) = node_remap.get(old_child).copied().flatten() else {
167                        continue;
168                    };
169                    new_children_vec.push(new_child);
170                    if let Some(slot) = self.parent_ix.get_mut(new_child) {
171                        *slot = Some(new_parent);
172                    }
173                }
174            }
175
176            // If the old representation had stray `parent_ix` links without a corresponding child
177            // entry, keep the best-effort behavior by remapping those as well.
178            for (old_child, old_parent) in old_parent_ix.into_iter().enumerate() {
179                let Some(old_parent) = old_parent else {
180                    continue;
181                };
182                let Some(new_child) = node_remap.get(old_child).copied().flatten() else {
183                    continue;
184                };
185                let Some(new_parent) = node_remap.get(old_parent).copied().flatten() else {
186                    continue;
187                };
188                if self.parent_ix.get(new_child).copied().flatten().is_some() {
189                    continue;
190                }
191                if let Some(slot) = self.parent_ix.get_mut(new_child) {
192                    *slot = Some(new_parent);
193                }
194                if let Some(ch) = self.children_ix.get_mut(new_parent) {
195                    if !ch.contains(&new_child) {
196                        ch.push(new_child);
197                    }
198                }
199            }
200        }
201
202        let old_edges = std::mem::take(&mut self.edges);
203        let mut new_edges: Vec<Option<EdgeEntry<E>>> = Vec::with_capacity(self.edge_len);
204        let mut new_edge_index: HashMap<EdgeKey, usize> = HashMap::default();
205        let mut new_edge_len: usize = 0;
206
207        for slot in old_edges.into_iter() {
208            let Some(mut edge) = slot else {
209                continue;
210            };
211            let Some(v_ix) = node_remap.get(edge.v_ix).copied().flatten() else {
212                continue;
213            };
214            let Some(w_ix) = node_remap.get(edge.w_ix).copied().flatten() else {
215                continue;
216            };
217            edge.v_ix = v_ix;
218            edge.w_ix = w_ix;
219
220            let new_ix = new_edges.len();
221            new_edge_index.insert(edge.key.clone(), new_ix);
222            new_edges.push(Some(edge));
223            new_edge_len += 1;
224        }
225
226        self.edges = new_edges;
227        self.edge_index = new_edge_index;
228        self.edge_len = new_edge_len;
229    }
230
231    fn invalidate_directed_adj(&mut self) {
232        if !self.options.directed {
233            return;
234        }
235        self.directed_adj_gen = self.directed_adj_gen.wrapping_add(1);
236        *self.directed_adj_cache.get_mut() = None;
237    }
238
239    fn invalidate_undirected_adj(&mut self) {
240        if self.options.directed {
241            return;
242        }
243        self.undirected_adj_gen = self.undirected_adj_gen.wrapping_add(1);
244        *self.undirected_adj_cache.get_mut() = None;
245    }
246
247    fn invalidate_adj(&mut self) {
248        self.invalidate_directed_adj();
249        self.invalidate_undirected_adj();
250    }
251
252    fn ensure_directed_adj<'a>(&'a self) -> std::cell::RefMut<'a, DirectedAdjCache> {
253        debug_assert!(self.options.directed);
254        let generation = self.directed_adj_gen;
255        let mut cache = self.directed_adj_cache.borrow_mut();
256        let stale = cache
257            .as_ref()
258            .map(|c| c.generation != generation)
259            .unwrap_or(true);
260        if stale {
261            // Build a CSR-like adjacency index to avoid allocating many small `Vec`s
262            // and to keep adjacency queries cache-friendly.
263            let node_slots = self.nodes.len();
264            let mut out_offsets: Vec<usize> = vec![0; node_slots + 1];
265            let mut in_offsets: Vec<usize> = vec![0; node_slots + 1];
266
267            for e in self.edges.iter().filter_map(|e| e.as_ref()) {
268                out_offsets[e.v_ix + 1] += 1;
269                in_offsets[e.w_ix + 1] += 1;
270            }
271
272            for i in 1..=node_slots {
273                out_offsets[i] += out_offsets[i - 1];
274                in_offsets[i] += in_offsets[i - 1];
275            }
276
277            let mut out_edges: Vec<usize> = vec![0; out_offsets[node_slots]];
278            let mut in_edges: Vec<usize> = vec![0; in_offsets[node_slots]];
279            let mut out_cursors = out_offsets.clone();
280            let mut in_cursors = in_offsets.clone();
281
282            for (edge_idx, e) in self.edges.iter().enumerate() {
283                let Some(e) = e.as_ref() else {
284                    continue;
285                };
286                let out_pos = out_cursors[e.v_ix];
287                out_edges[out_pos] = edge_idx;
288                out_cursors[e.v_ix] += 1;
289
290                let in_pos = in_cursors[e.w_ix];
291                in_edges[in_pos] = edge_idx;
292                in_cursors[e.w_ix] += 1;
293            }
294
295            *cache = Some(DirectedAdjCache {
296                generation,
297                out_offsets,
298                out_edges,
299                in_offsets,
300                in_edges,
301            });
302        }
303        std::cell::RefMut::map(cache, |c| {
304            c.as_mut()
305                .expect("directed adjacency cache should be present after ensure")
306        })
307    }
308
309    fn ensure_undirected_adj<'a>(&'a self) -> std::cell::RefMut<'a, UndirectedAdjCache> {
310        debug_assert!(!self.options.directed);
311        let generation = self.undirected_adj_gen;
312        let mut cache = self.undirected_adj_cache.borrow_mut();
313        let stale = cache
314            .as_ref()
315            .map(|c| c.generation != generation)
316            .unwrap_or(true);
317        if stale {
318            let node_slots = self.nodes.len();
319            let mut offsets: Vec<usize> = vec![0; node_slots + 1];
320
321            for e in self.edges.iter().filter_map(|e| e.as_ref()) {
322                offsets[e.v_ix + 1] += 1;
323                offsets[e.w_ix + 1] += 1;
324            }
325
326            for i in 1..=node_slots {
327                offsets[i] += offsets[i - 1];
328            }
329
330            let mut edges: Vec<usize> = vec![0; offsets[node_slots]];
331            let mut cursors = offsets.clone();
332            for (edge_idx, e) in self.edges.iter().enumerate() {
333                let Some(e) = e.as_ref() else {
334                    continue;
335                };
336                let v_pos = cursors[e.v_ix];
337                edges[v_pos] = edge_idx;
338                cursors[e.v_ix] += 1;
339
340                let w_pos = cursors[e.w_ix];
341                edges[w_pos] = edge_idx;
342                cursors[e.w_ix] += 1;
343            }
344
345            *cache = Some(UndirectedAdjCache {
346                generation,
347                offsets,
348                edges,
349            });
350        }
351
352        std::cell::RefMut::map(cache, |c| {
353            c.as_mut()
354                .expect("undirected adjacency cache should be present after ensure")
355        })
356    }
357
358    fn edge_key_view<'a>(&self, v: &'a str, w: &'a str, name: Option<&'a str>) -> EdgeKeyView<'a> {
359        let (v, w) = if self.options.directed || v <= w {
360            (v, w)
361        } else {
362            (w, v)
363        };
364        let name = if self.options.multigraph { name } else { None };
365        EdgeKeyView { v, w, name }
366    }
367
368    fn edge_key_view_from_key<'a>(&self, key: &'a EdgeKey) -> EdgeKeyView<'a> {
369        let mut v = key.v.as_str();
370        let mut w = key.w.as_str();
371        if !self.options.directed && v > w {
372            (v, w) = (w, v);
373        }
374        let name = if self.options.multigraph {
375            key.name.as_deref()
376        } else {
377            None
378        };
379        EdgeKeyView { v, w, name }
380    }
381
382    fn edge_index_of_view(&self, view: EdgeKeyView<'_>) -> Option<usize> {
383        self.edge_index.get(&view).copied()
384    }
385
386    fn canonicalize_endpoints(&self, v: String, w: String) -> (String, String) {
387        if self.options.directed || v <= w {
388            (v, w)
389        } else {
390            (w, v)
391        }
392    }
393
394    fn canonicalize_name(&self, name: Option<String>) -> Option<String> {
395        if self.options.multigraph { name } else { None }
396    }
397
398    fn canonicalize_key(&self, mut key: EdgeKey) -> EdgeKey {
399        if !self.options.directed && key.v > key.w {
400            (key.v, key.w) = (key.w, key.v);
401        }
402        key.name = self.canonicalize_name(key.name);
403        key
404    }
405
406    pub fn new(options: GraphOptions) -> Self {
407        Self {
408            options,
409            graph_label: G::default(),
410            default_node_label: Box::new(N::default),
411            default_edge_label: Box::new(E::default),
412            nodes: Vec::new(),
413            node_len: 0,
414            node_index: HashMap::default(),
415            edges: Vec::new(),
416            edge_len: 0,
417            edge_index: HashMap::default(),
418            parent_ix: Vec::new(),
419            children_ix: Vec::new(),
420            directed_adj_gen: 0,
421            directed_adj_cache: RefCell::new(None),
422            undirected_adj_gen: 0,
423            undirected_adj_cache: RefCell::new(None),
424        }
425    }
426
427    pub fn with_capacity(
428        options: GraphOptions,
429        node_capacity: usize,
430        edge_capacity: usize,
431    ) -> Self {
432        let mut g = Self::new(options);
433        g.nodes.reserve(node_capacity);
434        g.edges.reserve(edge_capacity);
435        g.node_index.reserve(node_capacity);
436        g.edge_index.reserve(edge_capacity);
437        g.parent_ix.reserve(node_capacity);
438        g.children_ix.reserve(node_capacity);
439        g
440    }
441
442    pub fn options(&self) -> GraphOptions {
443        self.options
444    }
445
446    pub fn is_multigraph(&self) -> bool {
447        self.options.multigraph
448    }
449
450    pub fn is_compound(&self) -> bool {
451        self.options.compound
452    }
453
454    pub fn is_directed(&self) -> bool {
455        self.options.directed
456    }
457
458    pub fn set_graph(&mut self, label: G) -> &mut Self {
459        self.graph_label = label;
460        self
461    }
462
463    pub fn graph(&self) -> &G {
464        &self.graph_label
465    }
466
467    pub fn graph_mut(&mut self) -> &mut G {
468        &mut self.graph_label
469    }
470
471    pub fn set_default_node_label<F>(&mut self, f: F) -> &mut Self
472    where
473        F: Fn() -> N + Send + Sync + 'static,
474    {
475        self.default_node_label = Box::new(f);
476        self
477    }
478
479    pub fn set_default_edge_label<F>(&mut self, f: F) -> &mut Self
480    where
481        F: Fn() -> E + Send + Sync + 'static,
482    {
483        self.default_edge_label = Box::new(f);
484        self
485    }
486
487    pub fn has_node(&self, id: &str) -> bool {
488        self.node_index.contains_key(id)
489    }
490
491    pub fn node_ix(&self, id: &str) -> Option<usize> {
492        self.node_index.get(id).copied()
493    }
494
495    pub fn node_id_by_ix(&self, ix: usize) -> Option<&str> {
496        self.nodes
497            .get(ix)
498            .and_then(|n| n.as_ref())
499            .map(|n| n.id.as_str())
500    }
501
502    pub fn node_label_by_ix(&self, ix: usize) -> Option<&N> {
503        self.nodes
504            .get(ix)
505            .and_then(|n| n.as_ref())
506            .map(|n| &n.label)
507    }
508
509    pub fn node_label_mut_by_ix(&mut self, ix: usize) -> Option<&mut N> {
510        self.nodes
511            .get_mut(ix)
512            .and_then(|n| n.as_mut())
513            .map(|n| &mut n.label)
514    }
515
516    pub fn has_edge_ix(&self, v_ix: usize, w_ix: usize) -> bool {
517        self.edge_by_endpoints_ix(v_ix, w_ix).is_some()
518    }
519
520    pub fn edge_by_endpoints_ix(&self, v_ix: usize, w_ix: usize) -> Option<&E> {
521        if self.options.directed {
522            let cache = self.ensure_directed_adj();
523            for &edge_idx in cache.out_edges(v_ix) {
524                let Some(e) = self.edges.get(edge_idx).and_then(|e| e.as_ref()) else {
525                    continue;
526                };
527                debug_assert_eq!(e.v_ix, v_ix);
528                if e.w_ix == w_ix {
529                    return Some(&e.label);
530                }
531            }
532            return None;
533        }
534
535        for e in self.edges.iter().filter_map(|e| e.as_ref()) {
536            if (e.v_ix == v_ix && e.w_ix == w_ix) || (e.v_ix == w_ix && e.w_ix == v_ix) {
537                return Some(&e.label);
538            }
539        }
540        None
541    }
542
543    pub fn set_node(&mut self, id: impl Into<String>, label: N) -> &mut Self {
544        let id = id.into();
545        if let Some(&idx) = self.node_index.get(&id) {
546            if let Some(node) = self.nodes.get_mut(idx).and_then(|n| n.as_mut()) {
547                node.label = label;
548            }
549            return self;
550        }
551        let _ = self.insert_node_entry(id, label);
552        self
553    }
554
555    pub fn ensure_node(&mut self, id: impl Into<String>) -> &mut Self {
556        let id = id.into();
557        if self.node_index.contains_key(&id) {
558            return self;
559        }
560        let label = (self.default_node_label)();
561        self.set_node(id, label)
562    }
563
564    pub fn ensure_node_ref(&mut self, id: &str) -> &mut Self {
565        if self.node_index.contains_key(id) {
566            return self;
567        }
568        let label = (self.default_node_label)();
569        self.set_node(id.to_string(), label)
570    }
571
572    pub fn node(&self, id: &str) -> Option<&N> {
573        let idx = *self.node_index.get(id)?;
574        self.nodes
575            .get(idx)
576            .and_then(|n| n.as_ref())
577            .map(|n| &n.label)
578    }
579
580    pub fn node_mut(&mut self, id: &str) -> Option<&mut N> {
581        let idx = *self.node_index.get(id)?;
582        self.nodes
583            .get_mut(idx)
584            .and_then(|n| n.as_mut())
585            .map(|n| &mut n.label)
586    }
587
588    pub fn node_count(&self) -> usize {
589        self.node_len
590    }
591
592    pub fn nodes(&self) -> impl Iterator<Item = &str> {
593        self.nodes
594            .iter()
595            .filter_map(|n| n.as_ref().map(|n| n.id.as_str()))
596    }
597
598    pub fn node_ids(&self) -> Vec<String> {
599        self.nodes
600            .iter()
601            .filter_map(|n| n.as_ref().map(|n| n.id.clone()))
602            .collect()
603    }
604
605    pub fn edge_count(&self) -> usize {
606        self.edge_len
607    }
608
609    pub fn edge_key_by_ix(&self, edge_ix: usize) -> Option<&EdgeKey> {
610        self.edges
611            .get(edge_ix)
612            .and_then(|e| e.as_ref())
613            .map(|e| &e.key)
614    }
615
616    pub fn edges(&self) -> impl Iterator<Item = &EdgeKey> {
617        self.edges.iter().filter_map(|e| e.as_ref().map(|e| &e.key))
618    }
619
620    pub fn for_each_edge<F>(&self, mut f: F)
621    where
622        F: FnMut(&EdgeKey, &E),
623    {
624        for e in &self.edges {
625            let Some(e) = e.as_ref() else {
626                continue;
627            };
628            f(&e.key, &e.label);
629        }
630    }
631
632    pub fn for_each_edge_ix<F>(&self, mut f: F)
633    where
634        F: FnMut(usize, usize, &EdgeKey, &E),
635    {
636        for e in &self.edges {
637            let Some(e) = e.as_ref() else {
638                continue;
639            };
640            f(e.v_ix, e.w_ix, &e.key, &e.label);
641        }
642    }
643
644    pub fn for_each_edge_entry_ix<F>(&self, mut f: F)
645    where
646        F: FnMut(usize, usize, usize, &EdgeKey, &E),
647    {
648        for (edge_ix, e) in self.edges.iter().enumerate() {
649            let Some(e) = e.as_ref() else {
650                continue;
651            };
652            f(edge_ix, e.v_ix, e.w_ix, &e.key, &e.label);
653        }
654    }
655
656    pub fn for_each_edge_mut<F>(&mut self, mut f: F)
657    where
658        F: FnMut(&EdgeKey, &mut E),
659    {
660        for e in &mut self.edges {
661            let Some(e) = e.as_mut() else {
662                continue;
663            };
664            f(&e.key, &mut e.label);
665        }
666    }
667
668    pub fn for_each_node<F>(&self, mut f: F)
669    where
670        F: FnMut(&str, &N),
671    {
672        for n in &self.nodes {
673            let Some(n) = n.as_ref() else {
674                continue;
675            };
676            f(&n.id, &n.label);
677        }
678    }
679
680    pub fn for_each_node_ix<F>(&self, mut f: F)
681    where
682        F: FnMut(usize, &str, &N),
683    {
684        for (idx, n) in self.nodes.iter().enumerate() {
685            let Some(n) = n.as_ref() else {
686                continue;
687            };
688            f(idx, &n.id, &n.label);
689        }
690    }
691
692    pub fn for_each_node_mut<F>(&mut self, mut f: F)
693    where
694        F: FnMut(&str, &mut N),
695    {
696        for n in &mut self.nodes {
697            let Some(n) = n.as_mut() else {
698                continue;
699            };
700            f(&n.id, &mut n.label);
701        }
702    }
703
704    pub fn edge_keys(&self) -> Vec<EdgeKey> {
705        self.edges
706            .iter()
707            .filter_map(|e| e.as_ref().map(|e| e.key.clone()))
708            .collect()
709    }
710
711    pub fn set_edge(&mut self, v: impl Into<String>, w: impl Into<String>) -> &mut Self {
712        self.set_edge_named(v, w, None::<String>, None)
713    }
714
715    pub fn set_edge_with_label(
716        &mut self,
717        v: impl Into<String>,
718        w: impl Into<String>,
719        label: E,
720    ) -> &mut Self {
721        self.set_edge_named(v, w, None::<String>, Some(label))
722    }
723
724    pub fn set_edge_named(
725        &mut self,
726        v: impl Into<String>,
727        w: impl Into<String>,
728        name: Option<impl Into<String>>,
729        label: Option<E>,
730    ) -> &mut Self {
731        let (v, w) = self.canonicalize_endpoints(v.into(), w.into());
732        self.ensure_node(v.clone());
733        self.ensure_node(w.clone());
734
735        let name = self.canonicalize_name(name.map(Into::into));
736        let key = EdgeKey { v, w, name };
737
738        if let Some(&idx) = self.edge_index.get(&key) {
739            if let Some(label) = label {
740                if let Some(edge) = self.edges.get_mut(idx).and_then(|e| e.as_mut()) {
741                    edge.label = label;
742                }
743            }
744            return self;
745        }
746
747        self.invalidate_adj();
748        let v_ix = *self
749            .node_index
750            .get(&key.v)
751            .expect("ensure_node should have inserted the endpoint node");
752        let w_ix = *self
753            .node_index
754            .get(&key.w)
755            .expect("ensure_node should have inserted the endpoint node");
756        let idx = self.edges.len();
757        self.edges.push(Some(EdgeEntry {
758            key: key.clone(),
759            v_ix,
760            w_ix,
761            label: label.unwrap_or_else(|| (self.default_edge_label)()),
762        }));
763        self.edge_len += 1;
764        self.edge_index.insert(key, idx);
765        self
766    }
767
768    pub fn set_path(&mut self, nodes: &[&str]) -> &mut Self {
769        if nodes.len() < 2 {
770            return self;
771        }
772        for pair in nodes.windows(2) {
773            let v = pair[0];
774            let w = pair[1];
775            self.set_edge(v, w);
776        }
777        self
778    }
779
780    pub fn has_edge(&self, v: &str, w: &str, name: Option<&str>) -> bool {
781        let view = self.edge_key_view(v, w, name);
782        self.edge_index_of_view(view).is_some()
783    }
784
785    pub fn edge(&self, v: &str, w: &str, name: Option<&str>) -> Option<&E> {
786        let view = self.edge_key_view(v, w, name);
787        let idx = self.edge_index_of_view(view)?;
788        self.edges
789            .get(idx)
790            .and_then(|e| e.as_ref())
791            .map(|e| &e.label)
792    }
793
794    pub fn edge_mut(&mut self, v: &str, w: &str, name: Option<&str>) -> Option<&mut E> {
795        let view = self.edge_key_view(v, w, name);
796        let idx = self.edge_index_of_view(view)?;
797        self.edges
798            .get_mut(idx)
799            .and_then(|e| e.as_mut())
800            .map(|e| &mut e.label)
801    }
802
803    pub fn edge_by_key(&self, key: &EdgeKey) -> Option<&E> {
804        let view = self.edge_key_view_from_key(key);
805        let idx = self.edge_index_of_view(view)?;
806        self.edges
807            .get(idx)
808            .and_then(|e| e.as_ref())
809            .map(|e| &e.label)
810    }
811
812    pub fn edge_mut_by_key(&mut self, key: &EdgeKey) -> Option<&mut E> {
813        let view = self.edge_key_view_from_key(key);
814        let idx = self.edge_index_of_view(view)?;
815        self.edges
816            .get_mut(idx)
817            .and_then(|e| e.as_mut())
818            .map(|e| &mut e.label)
819    }
820
821    fn remove_edge_at_index(&mut self, idx: usize) {
822        self.invalidate_adj();
823        let Some(edge) = self.edges.get(idx).and_then(|e| e.as_ref()) else {
824            return;
825        };
826        let _ = self.edge_index.remove_entry(&edge.key);
827        self.edges[idx] = None;
828        self.edge_len = self.edge_len.saturating_sub(1);
829        self.trim_trailing_edge_tombstones();
830    }
831
832    pub fn remove_edge_key(&mut self, key: &EdgeKey) -> bool {
833        let view = self.edge_key_view_from_key(key);
834        let Some(idx) = self.edge_index_of_view(view) else {
835            return false;
836        };
837        self.remove_edge_at_index(idx);
838        true
839    }
840
841    pub fn remove_edge(&mut self, v: &str, w: &str, name: Option<&str>) -> bool {
842        let view = self.edge_key_view(v, w, name);
843        let Some(idx) = self.edge_index_of_view(view) else {
844            return false;
845        };
846        self.remove_edge_at_index(idx);
847        true
848    }
849
850    pub fn remove_node(&mut self, id: &str) -> bool {
851        let Some(idx) = self.node_index.remove(id) else {
852            return false;
853        };
854
855        self.invalidate_adj();
856        if let Some(slot) = self.nodes.get_mut(idx) {
857            if slot.is_some() {
858                *slot = None;
859                self.node_len = self.node_len.saturating_sub(1);
860            }
861        }
862
863        // Remove incident edges.
864        for e in self.edges.iter_mut() {
865            let Some(edge) = e.as_ref() else {
866                continue;
867            };
868            if edge.v_ix == idx || edge.w_ix == idx {
869                let key = edge.key.clone();
870                let _ = self.edge_index.remove_entry(&key);
871                *e = None;
872                self.edge_len = self.edge_len.saturating_sub(1);
873            }
874        }
875
876        if self.options.compound {
877            // Remove parent link.
878            if let Some(prev_parent_ix) = self.parent_ix.get_mut(idx).and_then(|p| p.take()) {
879                if let Some(ch) = self.children_ix.get_mut(prev_parent_ix) {
880                    ch.retain(|&c| c != idx);
881                }
882            }
883
884            // Remove children links.
885            if let Some(ch) = self.children_ix.get_mut(idx) {
886                for &child_ix in ch.iter() {
887                    if let Some(slot) = self.parent_ix.get_mut(child_ix) {
888                        if *slot == Some(idx) {
889                            *slot = None;
890                        }
891                    }
892                }
893                ch.clear();
894            }
895        }
896
897        self.trim_trailing_edge_tombstones();
898        self.trim_trailing_node_tombstones();
899
900        true
901    }
902
903    pub fn successors(&self, v: &str) -> Vec<&str> {
904        if !self.options.directed {
905            return self.adjacent_nodes(v);
906        }
907        let Some(&v_idx) = self.node_index.get(v) else {
908            return Vec::new();
909        };
910        let cache = self.ensure_directed_adj();
911        let out_edges = cache.out_edges(v_idx);
912        let mut out: Vec<&str> = Vec::with_capacity(out_edges.len());
913        for &edge_idx in out_edges {
914            let Some(edge) = self.edges.get(edge_idx).and_then(|e| e.as_ref()) else {
915                continue;
916            };
917            out.push(edge.key.w.as_str());
918        }
919        out
920    }
921
922    pub fn predecessors(&self, v: &str) -> Vec<&str> {
923        if !self.options.directed {
924            return self.adjacent_nodes(v);
925        }
926        let Some(&v_idx) = self.node_index.get(v) else {
927            return Vec::new();
928        };
929        let cache = self.ensure_directed_adj();
930        let in_edges = cache.in_edges(v_idx);
931        let mut out: Vec<&str> = Vec::with_capacity(in_edges.len());
932        for &edge_idx in in_edges {
933            let Some(edge) = self.edges.get(edge_idx).and_then(|e| e.as_ref()) else {
934                continue;
935            };
936            out.push(edge.key.v.as_str());
937        }
938        out
939    }
940
941    pub fn first_successor<'a>(&'a self, v: &str) -> Option<&'a str> {
942        if !self.options.directed {
943            return self.adjacent_nodes(v).into_iter().next();
944        }
945        let &v_idx = self.node_index.get(v)?;
946        let w = {
947            let cache = self.ensure_directed_adj();
948            let edge_idx = *cache.out_edges(v_idx).first()?;
949            self.edges.get(edge_idx)?.as_ref()?.key.w.as_str()
950        };
951        Some(w)
952    }
953
954    pub fn first_predecessor<'a>(&'a self, v: &str) -> Option<&'a str> {
955        if !self.options.directed {
956            return self.adjacent_nodes(v).into_iter().next();
957        }
958        let &v_idx = self.node_index.get(v)?;
959        let u = {
960            let cache = self.ensure_directed_adj();
961            let edge_idx = *cache.in_edges(v_idx).first()?;
962            self.edges.get(edge_idx)?.as_ref()?.key.v.as_str()
963        };
964        Some(u)
965    }
966
967    pub fn extend_successors<'a>(&'a self, v: &str, out: &mut Vec<&'a str>) {
968        if !self.options.directed {
969            out.extend(self.adjacent_nodes(v));
970            return;
971        }
972        let Some(&v_idx) = self.node_index.get(v) else {
973            return;
974        };
975        let cache = self.ensure_directed_adj();
976        let out_edges = cache.out_edges(v_idx);
977        out.reserve(out_edges.len());
978        for &edge_idx in out_edges {
979            let Some(edge) = self.edges.get(edge_idx).and_then(|e| e.as_ref()) else {
980                continue;
981            };
982            out.push(edge.key.w.as_str());
983        }
984    }
985
986    pub fn extend_predecessors<'a>(&'a self, v: &str, out: &mut Vec<&'a str>) {
987        if !self.options.directed {
988            out.extend(self.adjacent_nodes(v));
989            return;
990        }
991        let Some(&v_idx) = self.node_index.get(v) else {
992            return;
993        };
994        let cache = self.ensure_directed_adj();
995        let in_edges = cache.in_edges(v_idx);
996        out.reserve(in_edges.len());
997        for &edge_idx in in_edges {
998            let Some(edge) = self.edges.get(edge_idx).and_then(|e| e.as_ref()) else {
999                continue;
1000            };
1001            out.push(edge.key.v.as_str());
1002        }
1003    }
1004
1005    pub fn for_each_successor<'a, F>(&'a self, v: &str, mut f: F)
1006    where
1007        F: FnMut(&'a str),
1008    {
1009        if !self.options.directed {
1010            for w in self.adjacent_nodes(v) {
1011                f(w);
1012            }
1013            return;
1014        }
1015        let Some(&v_idx) = self.node_index.get(v) else {
1016            return;
1017        };
1018        let cache = self.ensure_directed_adj();
1019        for &edge_idx in cache.out_edges(v_idx) {
1020            let Some(edge) = self.edges.get(edge_idx).and_then(|e| e.as_ref()) else {
1021                continue;
1022            };
1023            f(edge.key.w.as_str());
1024        }
1025    }
1026
1027    pub fn for_each_predecessor<'a, F>(&'a self, v: &str, mut f: F)
1028    where
1029        F: FnMut(&'a str),
1030    {
1031        if !self.options.directed {
1032            for u in self.adjacent_nodes(v) {
1033                f(u);
1034            }
1035            return;
1036        }
1037        let Some(&v_idx) = self.node_index.get(v) else {
1038            return;
1039        };
1040        let cache = self.ensure_directed_adj();
1041        for &edge_idx in cache.in_edges(v_idx) {
1042            let Some(edge) = self.edges.get(edge_idx).and_then(|e| e.as_ref()) else {
1043                continue;
1044            };
1045            f(edge.key.v.as_str());
1046        }
1047    }
1048
1049    pub fn neighbors(&self, v: &str) -> Vec<&str> {
1050        if !self.options.directed {
1051            return self.adjacent_nodes(v);
1052        }
1053        let mut out: Vec<&str> = Vec::new();
1054        for w in self.successors(v) {
1055            if !out.iter().any(|x| x == &w) {
1056                out.push(w);
1057            }
1058        }
1059        for u in self.predecessors(v) {
1060            if !out.iter().any(|x| x == &u) {
1061                out.push(u);
1062            }
1063        }
1064        out
1065    }
1066
1067    fn adjacent_nodes(&self, v: &str) -> Vec<&str> {
1068        debug_assert!(!self.options.directed);
1069        let Some(&v_ix) = self.node_index.get(v) else {
1070            return Vec::new();
1071        };
1072        let cache = self.ensure_undirected_adj();
1073        let mut seen: HashSet<usize> = HashSet::default();
1074        let mut out: Vec<&str> = Vec::new();
1075        for &edge_idx in cache.edges(v_ix) {
1076            let Some(e) = self.edges.get(edge_idx).and_then(|e| e.as_ref()) else {
1077                continue;
1078            };
1079            let other_ix = if e.v_ix == v_ix { e.w_ix } else { e.v_ix };
1080            if !seen.insert(other_ix) {
1081                continue;
1082            }
1083            let Some(other) = self.node_id_by_ix(other_ix) else {
1084                continue;
1085            };
1086            out.push(other);
1087        }
1088        out
1089    }
1090
1091    pub fn out_edges(&self, v: &str, w: Option<&str>) -> Vec<EdgeKey> {
1092        if self.options.directed {
1093            let Some(&v_idx) = self.node_index.get(v) else {
1094                return Vec::new();
1095            };
1096            let cache = self.ensure_directed_adj();
1097            let out_edges = cache.out_edges(v_idx);
1098            let mut out: Vec<EdgeKey> = Vec::with_capacity(out_edges.len());
1099            for &edge_idx in out_edges {
1100                let Some(e) = self.edges.get(edge_idx).and_then(|e| e.as_ref()) else {
1101                    continue;
1102                };
1103                if w.is_none_or(|w| e.key.w == w) {
1104                    out.push(e.key.clone());
1105                }
1106            }
1107            return out;
1108        }
1109
1110        let Some(&v_ix) = self.node_index.get(v) else {
1111            return Vec::new();
1112        };
1113        let cache = self.ensure_undirected_adj();
1114        let mut out: Vec<EdgeKey> = Vec::new();
1115        for &edge_idx in cache.edges(v_ix) {
1116            let Some(e) = self.edges.get(edge_idx).and_then(|e| e.as_ref()) else {
1117                continue;
1118            };
1119            if let Some(w) = w {
1120                let other_ix = if e.v_ix == v_ix { e.w_ix } else { e.v_ix };
1121                if self.node_id_by_ix(other_ix).is_some_and(|id| id == w) {
1122                    out.push(e.key.clone());
1123                }
1124            } else {
1125                out.push(e.key.clone());
1126            }
1127        }
1128        out
1129    }
1130
1131    pub fn in_edges(&self, v: &str, w: Option<&str>) -> Vec<EdgeKey> {
1132        if self.options.directed {
1133            let Some(&v_idx) = self.node_index.get(v) else {
1134                return Vec::new();
1135            };
1136            let cache = self.ensure_directed_adj();
1137            let in_edges = cache.in_edges(v_idx);
1138            let mut out: Vec<EdgeKey> = Vec::with_capacity(in_edges.len());
1139            for &edge_idx in in_edges {
1140                let Some(e) = self.edges.get(edge_idx).and_then(|e| e.as_ref()) else {
1141                    continue;
1142                };
1143                if w.is_none_or(|w| e.key.v == w) {
1144                    out.push(e.key.clone());
1145                }
1146            }
1147            return out;
1148        }
1149        self.out_edges(v, w)
1150    }
1151
1152    pub fn for_each_out_edge<F>(&self, v: &str, w: Option<&str>, mut f: F)
1153    where
1154        F: FnMut(&EdgeKey, &E),
1155    {
1156        if self.options.directed {
1157            let Some(&v_idx) = self.node_index.get(v) else {
1158                return;
1159            };
1160            let cache = self.ensure_directed_adj();
1161            for &edge_idx in cache.out_edges(v_idx) {
1162                let Some(e) = self.edges.get(edge_idx).and_then(|e| e.as_ref()) else {
1163                    continue;
1164                };
1165                if w.is_none_or(|w| e.key.w == w) {
1166                    f(&e.key, &e.label);
1167                }
1168            }
1169            return;
1170        }
1171
1172        let Some(&v_ix) = self.node_index.get(v) else {
1173            return;
1174        };
1175        let cache = self.ensure_undirected_adj();
1176        for &edge_idx in cache.edges(v_ix) {
1177            let Some(e) = self.edges.get(edge_idx).and_then(|e| e.as_ref()) else {
1178                continue;
1179            };
1180            if let Some(w) = w {
1181                let other_ix = if e.v_ix == v_ix { e.w_ix } else { e.v_ix };
1182                if self.node_id_by_ix(other_ix).is_some_and(|id| id == w) {
1183                    f(&e.key, &e.label);
1184                }
1185            } else {
1186                f(&e.key, &e.label);
1187            }
1188        }
1189    }
1190
1191    pub fn for_each_in_edge<F>(&self, v: &str, w: Option<&str>, mut f: F)
1192    where
1193        F: FnMut(&EdgeKey, &E),
1194    {
1195        if self.options.directed {
1196            let Some(&v_idx) = self.node_index.get(v) else {
1197                return;
1198            };
1199            let cache = self.ensure_directed_adj();
1200            for &edge_idx in cache.in_edges(v_idx) {
1201                let Some(e) = self.edges.get(edge_idx).and_then(|e| e.as_ref()) else {
1202                    continue;
1203                };
1204                if w.is_none_or(|w| e.key.v == w) {
1205                    f(&e.key, &e.label);
1206                }
1207            }
1208            return;
1209        }
1210
1211        self.for_each_out_edge(v, w, f);
1212    }
1213
1214    pub fn set_edge_key(&mut self, key: EdgeKey, label: E) -> &mut Self {
1215        let key = self.canonicalize_key(key);
1216        self.set_edge_named(key.v, key.w, key.name, Some(label))
1217    }
1218
1219    pub fn for_each_out_edge_ix<F>(&self, v_ix: usize, w_ix: Option<usize>, mut f: F)
1220    where
1221        F: FnMut(usize, usize, &EdgeKey, &E),
1222    {
1223        if !self.options.directed {
1224            return;
1225        }
1226        let cache = self.ensure_directed_adj();
1227        for &edge_idx in cache.out_edges(v_ix) {
1228            let Some(e) = self.edges.get(edge_idx).and_then(|e| e.as_ref()) else {
1229                continue;
1230            };
1231            debug_assert_eq!(e.v_ix, v_ix);
1232            if w_ix.is_none_or(|w_ix| e.w_ix == w_ix) {
1233                f(e.v_ix, e.w_ix, &e.key, &e.label);
1234            }
1235        }
1236    }
1237
1238    pub fn for_each_out_edge_entry_ix<F>(&self, v_ix: usize, w_ix: Option<usize>, mut f: F)
1239    where
1240        F: FnMut(usize, usize, usize, &EdgeKey, &E),
1241    {
1242        if !self.options.directed {
1243            return;
1244        }
1245        let cache = self.ensure_directed_adj();
1246        for &edge_ix in cache.out_edges(v_ix) {
1247            let Some(e) = self.edges.get(edge_ix).and_then(|e| e.as_ref()) else {
1248                continue;
1249            };
1250            debug_assert_eq!(e.v_ix, v_ix);
1251            if w_ix.is_none_or(|w_ix| e.w_ix == w_ix) {
1252                f(edge_ix, e.v_ix, e.w_ix, &e.key, &e.label);
1253            }
1254        }
1255    }
1256
1257    pub fn for_each_neighbor_ix<F>(&self, v_ix: usize, mut f: F)
1258    where
1259        F: FnMut(usize),
1260    {
1261        if self.options.directed {
1262            let cache = self.ensure_directed_adj();
1263            for &edge_idx in cache.out_edges(v_ix) {
1264                let Some(e) = self.edges.get(edge_idx).and_then(|e| e.as_ref()) else {
1265                    continue;
1266                };
1267                debug_assert_eq!(e.v_ix, v_ix);
1268                f(e.w_ix);
1269            }
1270            for &edge_idx in cache.in_edges(v_ix) {
1271                let Some(e) = self.edges.get(edge_idx).and_then(|e| e.as_ref()) else {
1272                    continue;
1273                };
1274                debug_assert_eq!(e.w_ix, v_ix);
1275                f(e.v_ix);
1276            }
1277            return;
1278        }
1279
1280        let cache = self.ensure_undirected_adj();
1281        for &edge_idx in cache.edges(v_ix) {
1282            let Some(e) = self.edges.get(edge_idx).and_then(|e| e.as_ref()) else {
1283                continue;
1284            };
1285            let other_ix = if e.v_ix == v_ix { e.w_ix } else { e.v_ix };
1286            f(other_ix);
1287        }
1288    }
1289
1290    pub fn for_each_in_edge_ix<F>(&self, v_ix: usize, w_ix: Option<usize>, mut f: F)
1291    where
1292        F: FnMut(usize, usize, &EdgeKey, &E),
1293    {
1294        if !self.options.directed {
1295            return;
1296        }
1297        let cache = self.ensure_directed_adj();
1298        for &edge_idx in cache.in_edges(v_ix) {
1299            let Some(e) = self.edges.get(edge_idx).and_then(|e| e.as_ref()) else {
1300                continue;
1301            };
1302            debug_assert_eq!(e.w_ix, v_ix);
1303            if w_ix.is_none_or(|w_ix| e.v_ix == w_ix) {
1304                f(e.v_ix, e.w_ix, &e.key, &e.label);
1305            }
1306        }
1307    }
1308
1309    pub fn for_each_in_edge_entry_ix<F>(&self, v_ix: usize, w_ix: Option<usize>, mut f: F)
1310    where
1311        F: FnMut(usize, usize, usize, &EdgeKey, &E),
1312    {
1313        if !self.options.directed {
1314            return;
1315        }
1316        let cache = self.ensure_directed_adj();
1317        for &edge_ix in cache.in_edges(v_ix) {
1318            let Some(e) = self.edges.get(edge_ix).and_then(|e| e.as_ref()) else {
1319                continue;
1320            };
1321            debug_assert_eq!(e.w_ix, v_ix);
1322            if w_ix.is_none_or(|w_ix| e.v_ix == w_ix) {
1323                f(edge_ix, e.v_ix, e.w_ix, &e.key, &e.label);
1324            }
1325        }
1326    }
1327
1328    pub fn set_parent(&mut self, child: impl Into<String>, parent: impl Into<String>) -> &mut Self {
1329        if !self.options.compound {
1330            return self;
1331        }
1332        let child = child.into();
1333        let parent = parent.into();
1334        self.ensure_node(child.clone());
1335        self.ensure_node(parent.clone());
1336        let Some(&child_ix) = self.node_index.get(&child) else {
1337            return self;
1338        };
1339        let Some(&parent_ix) = self.node_index.get(&parent) else {
1340            return self;
1341        };
1342        self.set_parent_ix(child_ix, parent_ix);
1343        self
1344    }
1345
1346    pub fn set_parent_ref(&mut self, child: &str, parent: &str) -> &mut Self {
1347        if !self.options.compound {
1348            return self;
1349        }
1350        self.ensure_node_ref(child);
1351        self.ensure_node_ref(parent);
1352        let Some(&child_ix) = self.node_index.get(child) else {
1353            return self;
1354        };
1355        let Some(&parent_ix) = self.node_index.get(parent) else {
1356            return self;
1357        };
1358        self.set_parent_ix(child_ix, parent_ix);
1359        self
1360    }
1361
1362    pub fn set_parent_ix(&mut self, child_ix: usize, parent_ix: usize) -> &mut Self {
1363        if !self.options.compound {
1364            return self;
1365        }
1366        if child_ix >= self.nodes.len() || parent_ix >= self.nodes.len() {
1367            return self;
1368        }
1369
1370        let prev = self.parent_ix.get(child_ix).copied().flatten();
1371        if prev == Some(parent_ix) {
1372            return self;
1373        }
1374
1375        if let Some(prev_parent_ix) = prev {
1376            if let Some(ch) = self.children_ix.get_mut(prev_parent_ix) {
1377                ch.retain(|&c| c != child_ix);
1378            }
1379        }
1380
1381        if let Some(slot) = self.parent_ix.get_mut(child_ix) {
1382            *slot = Some(parent_ix);
1383        }
1384        if let Some(ch) = self.children_ix.get_mut(parent_ix) {
1385            if !ch.contains(&child_ix) {
1386                ch.push(child_ix);
1387            }
1388        }
1389        self
1390    }
1391
1392    pub fn clear_parent(&mut self, child: &str) -> &mut Self {
1393        if !self.options.compound {
1394            return self;
1395        }
1396        let Some(&child_ix) = self.node_index.get(child) else {
1397            return self;
1398        };
1399        let Some(prev_parent_ix) = self.parent_ix.get(child_ix).copied().flatten() else {
1400            return self;
1401        };
1402        if let Some(slot) = self.parent_ix.get_mut(child_ix) {
1403            *slot = None;
1404        }
1405        if let Some(ch) = self.children_ix.get_mut(prev_parent_ix) {
1406            ch.retain(|&c| c != child_ix);
1407        }
1408        self
1409    }
1410
1411    pub fn parent(&self, child: &str) -> Option<&str> {
1412        if !self.options.compound {
1413            return None;
1414        }
1415        let &child_ix = self.node_index.get(child)?;
1416        let parent_ix = self.parent_ix.get(child_ix).copied().flatten()?;
1417        self.node_id_by_ix(parent_ix)
1418    }
1419
1420    pub fn children_iter<'a>(&'a self, parent: &str) -> impl Iterator<Item = &'a str> + 'a {
1421        let children = if self.options.compound {
1422            self.node_index
1423                .get(parent)
1424                .and_then(|&p_ix| self.children_ix.get(p_ix))
1425                .map(|v| v.as_slice())
1426                .unwrap_or(&[])
1427        } else {
1428            &[]
1429        };
1430        let mut i = 0usize;
1431        std::iter::from_fn(move || {
1432            while i < children.len() {
1433                let child_ix = children[i];
1434                i += 1;
1435                if let Some(id) = self.node_id_by_ix(child_ix) {
1436                    return Some(id);
1437                }
1438            }
1439            None
1440        })
1441    }
1442
1443    pub fn children(&self, parent: &str) -> Vec<&str> {
1444        self.children_iter(parent).collect()
1445    }
1446
1447    pub fn children_root(&self) -> Vec<&str> {
1448        if !self.options.compound {
1449            return self.nodes().collect();
1450        }
1451        let mut out: Vec<&str> = Vec::new();
1452        for (ix, n) in self.nodes.iter().enumerate() {
1453            let Some(n) = n.as_ref() else {
1454                continue;
1455            };
1456            if self.parent_ix.get(ix).copied().flatten().is_none() {
1457                out.push(n.id.as_str());
1458            }
1459        }
1460        out
1461    }
1462
1463    pub fn sources(&self) -> Vec<&str> {
1464        if !self.options.directed {
1465            return self.nodes().collect();
1466        }
1467        self.nodes
1468            .iter()
1469            .filter_map(|n| n.as_ref())
1470            .filter(|n| self.in_edges(&n.id, None).is_empty())
1471            .map(|n| n.id.as_str())
1472            .collect()
1473    }
1474
1475    pub fn node_edges(&self, v: &str) -> Vec<EdgeKey> {
1476        let mut out: Vec<EdgeKey> = Vec::new();
1477        let mut seen: HashSet<EdgeKey> = HashSet::default();
1478        for e in &self.edges {
1479            let Some(e) = e.as_ref() else {
1480                continue;
1481            };
1482            if (e.key.v == v || e.key.w == v) && seen.insert(e.key.clone()) {
1483                out.push(e.key.clone());
1484            }
1485        }
1486        out
1487    }
1488}