Skip to main content

dugong_graphlib/graph/
core.rs

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