Skip to main content

pathfinding_indexed/
indexed_graph.rs

1//! Indexed graph storage and algorithms.
2
3use num_traits::{Bounded, Signed, Zero};
4use rustc_hash::{FxHashMap, FxHashSet};
5use std::collections::VecDeque;
6use std::hash::Hash;
7
8/// A directed graph stored as dense `usize` indices with weighted adjacency lists.
9#[derive(Clone, Debug)]
10pub struct IndexedGraph<C> {
11    adjacency: Vec<Vec<(usize, C)>>,
12}
13
14impl<C> IndexedGraph<C> {
15    /// Build a directed indexed graph from an adjacency list.
16    #[must_use]
17    pub const fn from_adjacency(adjacency: Vec<Vec<(usize, C)>>) -> Self {
18        Self { adjacency }
19    }
20
21    /// Return the number of nodes in the graph.
22    #[must_use]
23    pub const fn node_count(&self) -> usize {
24        self.adjacency.len()
25    }
26
27    /// Return the number of nodes in the graph.
28    #[must_use]
29    pub const fn len(&self) -> usize {
30        self.adjacency.len()
31    }
32
33    /// Return true if the graph contains no nodes.
34    #[must_use]
35    pub const fn is_empty(&self) -> bool {
36        self.adjacency.is_empty()
37    }
38
39    /// Return the adjacency list for `node`.
40    #[must_use]
41    pub fn successors(&self, node: usize) -> &[(usize, C)] {
42        &self.adjacency[node]
43    }
44
45    /// Return all adjacency lists.
46    #[must_use]
47    pub fn adjacency(&self) -> &[Vec<(usize, C)>] {
48        &self.adjacency
49    }
50
51    /// Run A* from `start` to a node satisfying `success`.
52    pub fn astar<FH, FS>(
53        &self,
54        start: usize,
55        mut heuristic: FH,
56        mut success: FS,
57    ) -> Option<(Vec<usize>, C)>
58    where
59        C: Zero + Ord + Copy,
60        FH: FnMut(usize) -> C,
61        FS: FnMut(usize) -> bool,
62    {
63        crate::directed::astar::astar(
64            &start,
65            |node| self.successors(*node).iter().copied(),
66            |node| heuristic(*node),
67            |node| success(*node),
68        )
69    }
70
71    /// Run A* and return all shortest paths as an iterator.
72    pub fn astar_bag<FH, FS>(
73        &self,
74        start: usize,
75        mut heuristic: FH,
76        mut success: FS,
77    ) -> Option<(impl Iterator<Item = Vec<usize>>, C)>
78    where
79        C: Zero + Ord + Copy,
80        FH: FnMut(usize) -> C,
81        FS: FnMut(usize) -> bool,
82    {
83        crate::directed::astar::astar_bag(
84            &start,
85            |node| self.successors(*node).iter().copied(),
86            |node| heuristic(*node),
87            |node| success(*node),
88        )
89    }
90
91    /// Run A* and collect all shortest paths into a vector.
92    pub fn astar_bag_collect<FH, FS>(
93        &self,
94        start: usize,
95        mut heuristic: FH,
96        mut success: FS,
97    ) -> Option<(Vec<Vec<usize>>, C)>
98    where
99        C: Zero + Ord + Copy,
100        FH: FnMut(usize) -> C,
101        FS: FnMut(usize) -> bool,
102    {
103        crate::directed::astar::astar_bag_collect(
104            &start,
105            |node| self.successors(*node).iter().copied(),
106            |node| heuristic(*node),
107            |node| success(*node),
108        )
109    }
110
111    /// Run BFS from `start` to a node satisfying `success`.
112    pub fn bfs<FS>(&self, start: usize, mut success: FS) -> Option<Vec<usize>>
113    where
114        FS: FnMut(usize) -> bool,
115    {
116        crate::directed::bfs::bfs(
117            &start,
118            |node| self.successors(*node).iter().map(|edge| edge.0),
119            |node| success(*node),
120        )
121    }
122
123    /// Run BFS and return the shortest loop starting and ending at `start`.
124    #[must_use]
125    pub fn bfs_loop(&self, start: usize) -> Option<Vec<usize>> {
126        crate::directed::bfs::bfs_loop(&start, |node| {
127            self.successors(*node).iter().map(|edge| edge.0)
128        })
129    }
130
131    /// Run bidirectional BFS from `start` to `end`.
132    #[must_use]
133    pub fn bfs_bidirectional(&self, start: usize, end: usize) -> Option<Vec<usize>> {
134        let mut predecessors = vec![Vec::new(); self.node_count()];
135        for (from, edges) in self.adjacency.iter().enumerate() {
136            for &(to, _) in edges {
137                predecessors[to].push(from);
138            }
139        }
140
141        crate::directed::bfs::bfs_bidirectional(
142            &start,
143            &end,
144            |node| {
145                self.successors(*node)
146                    .iter()
147                    .map(|edge| edge.0)
148                    .collect::<Vec<_>>()
149            },
150            |node| predecessors[*node].clone(),
151        )
152    }
153
154    /// Iterate over nodes reachable from `start` using BFS order.
155    pub fn bfs_reach(&self, start: usize) -> impl Iterator<Item = usize> + '_ {
156        crate::directed::bfs::bfs_reach(start, |node| {
157            self.successors(*node).iter().map(|edge| edge.0)
158        })
159    }
160
161    /// Run DFS from `start` to a node satisfying `success`.
162    pub fn dfs<FS>(&self, start: usize, mut success: FS) -> Option<Vec<usize>>
163    where
164        FS: FnMut(usize) -> bool,
165    {
166        crate::directed::dfs::dfs(
167            start,
168            |node| self.successors(*node).iter().map(|edge| edge.0),
169            |node| success(*node),
170        )
171    }
172
173    /// Iterate over nodes reachable from `start` using DFS order.
174    pub fn dfs_reach(&self, start: usize) -> impl Iterator<Item = usize> + '_ {
175        crate::directed::dfs::dfs_reach(start, |node| {
176            self.successors(*node).iter().map(|edge| edge.0)
177        })
178    }
179
180    /// Run IDDFS from `start` to a node satisfying `success`.
181    pub fn iddfs<FS>(&self, start: usize, mut success: FS) -> Option<Vec<usize>>
182    where
183        FS: FnMut(usize) -> bool,
184    {
185        crate::directed::iddfs::iddfs(
186            start,
187            |node| self.successors(*node).iter().map(|edge| edge.0),
188            |node| success(*node),
189        )
190    }
191
192    /// Run IDA* from `start` to a node satisfying `success`.
193    pub fn idastar<FH, FS>(
194        &self,
195        start: usize,
196        mut heuristic: FH,
197        mut success: FS,
198    ) -> Option<(Vec<usize>, C)>
199    where
200        C: Zero + Ord + Copy,
201        FH: FnMut(usize) -> C,
202        FS: FnMut(usize) -> bool,
203    {
204        crate::directed::idastar::idastar(
205            &start,
206            |node| self.successors(*node).iter().copied(),
207            |node| heuristic(*node),
208            |node| success(*node),
209        )
210    }
211
212    /// Run Dijkstra from `start` to a node satisfying `success`.
213    pub fn dijkstra<FS>(&self, start: usize, mut success: FS) -> Option<(Vec<usize>, C)>
214    where
215        C: Zero + Ord + Copy,
216        FS: FnMut(usize) -> bool,
217    {
218        crate::directed::dijkstra::dijkstra(
219            &start,
220            |node| self.successors(*node).iter().copied(),
221            |node| success(*node),
222        )
223    }
224
225    /// Compute shortest paths to all reachable nodes from `start`.
226    pub fn dijkstra_all(&self, start: usize) -> Vec<Option<(usize, C)>>
227    where
228        C: Zero + Ord + Copy,
229    {
230        let parents = crate::directed::dijkstra::dijkstra_all(&start, |node| {
231            self.successors(*node).iter().copied()
232        });
233        let mut out = vec![None; self.node_count()];
234        for (node, (parent, cost)) in parents {
235            out[node] = Some((parent, cost));
236        }
237        out
238    }
239
240    /// Compute shortest paths until `stop` returns true.
241    pub fn dijkstra_partial<FS>(
242        &self,
243        start: usize,
244        mut stop: FS,
245    ) -> (Vec<Option<(usize, C)>>, Option<usize>)
246    where
247        C: Zero + Ord + Copy,
248        FS: FnMut(usize) -> bool,
249    {
250        let (parents, reached) = crate::directed::dijkstra::dijkstra_partial(
251            &start,
252            |node| self.successors(*node).iter().copied(),
253            |node| stop(*node),
254        );
255        let mut out = vec![None; self.node_count()];
256        for (node, (parent, cost)) in parents {
257            out[node] = Some((parent, cost));
258        }
259        (out, reached)
260    }
261
262    /// Iterate over nodes reached by Dijkstra, yielding the node, parent, and total cost.
263    pub fn dijkstra_reach(
264        &self,
265        start: usize,
266    ) -> impl Iterator<Item = (usize, Option<usize>, C)> + '_
267    where
268        C: Zero + Ord + Copy + Hash,
269    {
270        crate::directed::dijkstra::dijkstra_reach(&start, |node| {
271            self.successors(*node).iter().copied()
272        })
273        .map(|item| (item.node, item.parent, item.total_cost))
274    }
275
276    /// Run the BMSSP-based SSSP algorithm from `start`.
277    pub fn bmssp<FS>(&self, start: usize, success: FS) -> Option<(Vec<usize>, C)>
278    where
279        C: Zero + Ord + Copy,
280        FS: FnMut(usize) -> bool,
281    {
282        crate::directed::bmssp::bmssp_indexed(
283            start,
284            |node| self.successors(node).iter().copied(),
285            success,
286            self.node_count(),
287        )
288    }
289
290    /// Compute BMSSP parents and costs for all reachable nodes from `start`.
291    #[must_use]
292    pub fn bmssp_all(&self, start: usize) -> Vec<Option<(usize, C)>>
293    where
294        C: Zero + Ord + Copy,
295    {
296        crate::directed::bmssp::bmssp_all_indexed(
297            start,
298            |node| self.successors(node).iter().copied(),
299            self.node_count(),
300        )
301    }
302
303    /// Run Fringe search from `start` to a node satisfying `success`.
304    pub fn fringe<FH, FS>(
305        &self,
306        start: usize,
307        mut heuristic: FH,
308        mut success: FS,
309    ) -> Option<(Vec<usize>, C)>
310    where
311        C: Bounded + Zero + Ord + Copy,
312        FH: FnMut(usize) -> C,
313        FS: FnMut(usize) -> bool,
314    {
315        crate::directed::fringe::fringe(
316            &start,
317            |node| self.successors(*node).iter().copied(),
318            |node| heuristic(*node),
319            |node| success(*node),
320        )
321    }
322
323    /// Compute a topological ordering of all nodes.
324    ///
325    /// # Errors
326    ///
327    /// Returns `Err(node)` when a cycle is detected.
328    pub fn topological_sort(&self) -> Result<Vec<usize>, usize> {
329        let nodes = (0..self.node_count()).collect::<Vec<_>>();
330        crate::directed::topological_sort::topological_sort(&nodes, |node| {
331            self.successors(*node).iter().map(|edge| edge.0)
332        })
333    }
334
335    /// Compute strongly connected components for all nodes.
336    #[must_use]
337    pub fn strongly_connected_components(&self) -> Vec<Vec<usize>> {
338        let nodes = (0..self.node_count()).collect::<Vec<_>>();
339        crate::directed::strongly_connected_components::strongly_connected_components(
340            &nodes,
341            |node| self.successors(*node).iter().map(|edge| edge.0),
342        )
343    }
344
345    /// Compute strongly connected components reachable from `start`.
346    #[must_use]
347    pub fn strongly_connected_components_from(&self, start: usize) -> Vec<Vec<usize>> {
348        crate::directed::strongly_connected_components::strongly_connected_components_from(
349            &start,
350            |node| self.successors(*node).iter().map(|edge| edge.0),
351        )
352    }
353
354    /// Compute the strongly connected component containing `node`.
355    #[must_use]
356    pub fn strongly_connected_component(&self, node: usize) -> Vec<usize> {
357        crate::directed::strongly_connected_components::strongly_connected_component(&node, |n| {
358            self.successors(*n).iter().map(|edge| edge.0)
359        })
360    }
361
362    /// Count the number of paths from `start` to nodes satisfying `success`.
363    pub fn count_paths<FS>(&self, start: usize, mut success: FS) -> usize
364    where
365        FS: FnMut(usize) -> bool,
366    {
367        crate::directed::count_paths::count_paths(
368            start,
369            |node| self.successors(*node).iter().map(|edge| edge.0),
370            |node| success(*node),
371        )
372    }
373
374    /// Compute k-shortest paths using Yen's algorithm.
375    pub fn yen<FS>(&self, start: usize, mut success: FS, k: usize) -> Vec<(Vec<usize>, C)>
376    where
377        C: Zero + Ord + Copy,
378        FS: FnMut(usize) -> bool,
379    {
380        crate::directed::yen::yen(
381            &start,
382            |node| self.successors(*node).iter().copied(),
383            |node| success(*node),
384            k,
385        )
386    }
387
388    /// Compute the maximum flow and minimum cut using Edmonds-Karp.
389    ///
390    /// # Panics
391    ///
392    /// Panics if `source` or `sink` are out of bounds.
393    #[expect(clippy::too_many_lines)]
394    #[expect(clippy::type_complexity)]
395    #[must_use]
396    pub fn edmonds_karp(
397        &self,
398        source: usize,
399        sink: usize,
400    ) -> (Vec<((usize, usize), C)>, C, Vec<((usize, usize), C)>)
401    where
402        C: Copy + Zero + Signed + Ord + Bounded,
403    {
404        let node_count = self.node_count();
405        if node_count == 0 || source == sink {
406            return (Vec::new(), Zero::zero(), Vec::new());
407        }
408        assert!(source < node_count, "source out of bounds");
409        assert!(sink < node_count, "sink out of bounds");
410
411        let mut capacity = vec![vec![C::zero(); node_count]; node_count];
412        for (from, edges) in self.adjacency.iter().enumerate() {
413            for &(to, weight) in edges {
414                capacity[from][to] = capacity[from][to] + weight;
415            }
416        }
417
418        let mut predecessors = vec![Vec::new(); node_count];
419        for (from, edges) in self.adjacency.iter().enumerate() {
420            for &(to, _) in edges {
421                predecessors[to].push(from);
422            }
423        }
424
425        let mut flow = vec![vec![C::zero(); node_count]; node_count];
426        let mut total = C::zero();
427
428        loop {
429            let mut parent = vec![usize::MAX; node_count];
430            let mut direction = vec![true; node_count];
431            let mut path_capacity = vec![C::zero(); node_count];
432            let mut queue = VecDeque::new();
433
434            parent[source] = source;
435            path_capacity[source] = C::max_value();
436            queue.push_back(source);
437
438            while let Some(node) = queue.pop_front() {
439                let capacity_so_far = path_capacity[node];
440                for &(next, _) in &self.adjacency[node] {
441                    if parent[next] != usize::MAX || next == source {
442                        continue;
443                    }
444                    let residual = capacity[node][next] - flow[node][next];
445                    if residual <= Zero::zero() {
446                        continue;
447                    }
448                    parent[next] = node;
449                    direction[next] = true;
450                    path_capacity[next] = if capacity_so_far < residual {
451                        capacity_so_far
452                    } else {
453                        residual
454                    };
455                    if next == sink {
456                        break;
457                    }
458                    queue.push_back(next);
459                }
460                if parent[sink] != usize::MAX {
461                    break;
462                }
463                for &prev in &predecessors[node] {
464                    if parent[prev] != usize::MAX || prev == source {
465                        continue;
466                    }
467                    let residual = flow[prev][node];
468                    if residual <= Zero::zero() {
469                        continue;
470                    }
471                    parent[prev] = node;
472                    direction[prev] = false;
473                    path_capacity[prev] = if capacity_so_far < residual {
474                        capacity_so_far
475                    } else {
476                        residual
477                    };
478                    if prev == sink {
479                        break;
480                    }
481                    queue.push_back(prev);
482                }
483                if parent[sink] != usize::MAX {
484                    break;
485                }
486            }
487
488            if parent[sink] == usize::MAX {
489                break;
490            }
491
492            let augment = path_capacity[sink];
493            let mut node = sink;
494            while node != source {
495                let prev = parent[node];
496                if direction[node] {
497                    flow[prev][node] = flow[prev][node] + augment;
498                } else {
499                    flow[node][prev] = flow[node][prev] - augment;
500                }
501                node = prev;
502            }
503            total = total + augment;
504        }
505
506        let mut reachable = vec![false; node_count];
507        let mut queue = VecDeque::new();
508        reachable[source] = true;
509        queue.push_back(source);
510        while let Some(node) = queue.pop_front() {
511            for &(next, _) in &self.adjacency[node] {
512                if reachable[next] {
513                    continue;
514                }
515                let residual = capacity[node][next] - flow[node][next];
516                if residual > Zero::zero() {
517                    reachable[next] = true;
518                    queue.push_back(next);
519                }
520            }
521            for &prev in &predecessors[node] {
522                if reachable[prev] {
523                    continue;
524                }
525                if flow[prev][node] > Zero::zero() {
526                    reachable[prev] = true;
527                    queue.push_back(prev);
528                }
529            }
530        }
531
532        let mut flows = Vec::new();
533        for (from, edges) in self.adjacency.iter().enumerate() {
534            for &(to, _) in edges {
535                let value = flow[from][to];
536                if value > Zero::zero() {
537                    flows.push(((from, to), value));
538                }
539            }
540        }
541
542        let mut cuts = Vec::new();
543        for (from, edges) in self.adjacency.iter().enumerate() {
544            if !reachable[from] {
545                continue;
546            }
547            for &(to, _) in edges {
548                if reachable[to] {
549                    continue;
550                }
551                let cap = capacity[from][to];
552                if cap > Zero::zero() {
553                    cuts.push(((from, to), cap));
554                }
555            }
556        }
557
558        (flows, total, cuts)
559    }
560}
561
562/// An undirected graph stored as dense `usize` indices with weighted adjacency lists.
563#[derive(Clone, Debug)]
564pub struct IndexedUndirectedGraph<C> {
565    adjacency: Vec<Vec<(usize, C)>>,
566    edges: Vec<(usize, usize, C)>,
567}
568
569impl<C> IndexedUndirectedGraph<C> {
570    /// Build an undirected indexed graph from a list of edges.
571    #[must_use]
572    pub fn from_edges(node_count: usize, edges: Vec<(usize, usize, C)>) -> Self
573    where
574        C: Clone,
575    {
576        let mut adjacency = vec![Vec::new(); node_count];
577        for (u, v, w) in &edges {
578            debug_assert!(*u < node_count, "edge start out of bounds");
579            debug_assert!(*v < node_count, "edge end out of bounds");
580            adjacency[*u].push((*v, w.clone()));
581            adjacency[*v].push((*u, w.clone()));
582        }
583        Self { adjacency, edges }
584    }
585
586    /// Return the number of nodes in the graph.
587    #[must_use]
588    pub const fn node_count(&self) -> usize {
589        self.adjacency.len()
590    }
591
592    /// Return the adjacency list for `node`.
593    #[must_use]
594    pub fn successors(&self, node: usize) -> &[(usize, C)] {
595        &self.adjacency[node]
596    }
597
598    /// Return the canonical edge list (each edge appears once).
599    #[must_use]
600    pub fn edges(&self) -> &[(usize, usize, C)] {
601        &self.edges
602    }
603
604    /// Return all adjacency lists.
605    #[must_use]
606    pub fn adjacency(&self) -> &[Vec<(usize, C)>] {
607        &self.adjacency
608    }
609
610    /// Compute connected components of the graph.
611    #[must_use]
612    pub fn connected_components(&self) -> Vec<Vec<usize>> {
613        let nodes = (0..self.node_count()).collect::<Vec<_>>();
614        let components =
615            crate::undirected::connected_components::connected_components(&nodes, |n| {
616                self.successors(*n).iter().map(|edge| edge.0)
617            });
618        components
619            .into_iter()
620            .map(|set| set.into_iter().collect())
621            .collect()
622    }
623
624    /// Return the component index for each node.
625    #[must_use]
626    pub fn component_index(&self) -> Vec<usize> {
627        let components = self.connected_components();
628        let mut index = vec![usize::MAX; self.node_count()];
629        for (component_idx, component) in components.iter().enumerate() {
630            for &node in component {
631                index[node] = component_idx;
632            }
633        }
634        index
635    }
636
637    /// Return connected components (alias of [`Self::connected_components`]).
638    #[must_use]
639    pub fn components(&self) -> Vec<Vec<usize>> {
640        self.connected_components()
641    }
642
643    /// Separate connected components using neighbor groups derived from adjacency.
644    #[must_use]
645    pub fn separate_components(&self) -> (Vec<usize>, Vec<usize>) {
646        let groups = (0..self.node_count())
647            .map(|node| {
648                let mut group = Vec::with_capacity(self.adjacency[node].len() + 1);
649                group.push(node);
650                group.extend(self.adjacency[node].iter().map(|edge| edge.0));
651                group
652            })
653            .collect::<Vec<_>>();
654        let (mapping, group_indices) =
655            crate::undirected::connected_components::separate_components(&groups);
656        let mut node_components = vec![usize::MAX; self.node_count()];
657        for (node, component) in mapping {
658            node_components[*node] = component;
659        }
660        (node_components, group_indices)
661    }
662
663    /// Enumerate all maximal cliques and collect them into a vector.
664    #[must_use]
665    pub fn maximal_cliques_collect(&self) -> Vec<Vec<usize>> {
666        let adjacency_sets = self.adjacency_sets();
667        let mut connected = |a: &usize, b: &usize| adjacency_sets[*a].contains(b);
668        crate::undirected::cliques::maximal_cliques_collect(0..self.node_count(), &mut connected)
669            .into_iter()
670            .map(|set| set.into_iter().collect())
671            .collect()
672    }
673
674    /// Enumerate all maximal cliques and send them to `consumer`.
675    pub fn maximal_cliques<CO>(&self, mut consumer: CO)
676    where
677        CO: FnMut(&Vec<usize>),
678    {
679        let adjacency_sets = self.adjacency_sets();
680        let mut connected = |a: &usize, b: &usize| adjacency_sets[*a].contains(b);
681        let mut adapter = |clique: &std::collections::HashSet<usize>| {
682            let clique_vec = clique.iter().copied().collect::<Vec<_>>();
683            consumer(&clique_vec);
684        };
685        crate::undirected::cliques::maximal_cliques(
686            0..self.node_count(),
687            &mut connected,
688            &mut adapter,
689        );
690    }
691
692    /// Compute a minimum spanning tree using Kruskal's algorithm (collected).
693    #[must_use]
694    pub fn kruskal(&self) -> Vec<(usize, usize, C)>
695    where
696        C: Clone + Ord,
697    {
698        self.kruskal_indices().collect()
699    }
700
701    /// Compute a minimum spanning tree using Kruskal's algorithm (iterator).
702    pub fn kruskal_indices(&self) -> impl Iterator<Item = (usize, usize, C)>
703    where
704        C: Clone + Ord,
705    {
706        crate::undirected::kruskal::kruskal_indices(self.node_count(), self.edges())
707    }
708
709    /// Compute a minimum spanning tree using Prim's algorithm.
710    #[must_use]
711    pub fn prim(&self) -> Vec<(usize, usize, C)>
712    where
713        C: Clone + Ord,
714    {
715        crate::undirected::prim::prim(self.edges())
716            .into_iter()
717            .map(|(a, b, c)| (*a, *b, c))
718            .collect()
719    }
720
721    fn adjacency_sets(&self) -> Vec<FxHashSet<usize>> {
722        self.adjacency
723            .iter()
724            .map(|edges| edges.iter().map(|edge| edge.0).collect())
725            .collect()
726    }
727}
728
729/// Helper that maps external node values to dense indices and builds an indexed graph.
730#[derive(Clone, Debug)]
731pub struct IndexedGraphMap<N, C> {
732    graph: IndexedGraph<C>,
733    nodes: Vec<N>,
734    index: FxHashMap<N, usize>,
735}
736
737impl<N, C> IndexedGraphMap<N, C>
738where
739    N: Eq + Hash + Clone,
740{
741    /// Build a mapped graph from seed nodes and a successor function.
742    pub fn from_nodes_and_successors<I, FN, IN>(nodes: I, mut successors: FN) -> Self
743    where
744        I: IntoIterator<Item = N>,
745        FN: FnMut(&N) -> IN,
746        IN: IntoIterator<Item = (N, C)>,
747    {
748        let mut mapped = Self {
749            graph: IndexedGraph::from_adjacency(Vec::new()),
750            nodes: Vec::new(),
751            index: FxHashMap::default(),
752        };
753
754        for node in nodes {
755            mapped.ensure_index(node);
756        }
757
758        let mut cursor = 0usize;
759        while cursor < mapped.nodes.len() {
760            let node = mapped.nodes[cursor].clone();
761            let mut edges = Vec::new();
762            for (neighbor, cost) in successors(&node) {
763                let neighbor_idx = mapped.ensure_index(neighbor);
764                edges.push((neighbor_idx, cost));
765            }
766            if cursor >= mapped.graph.adjacency.len() {
767                mapped.graph.adjacency.push(edges);
768            } else {
769                mapped.graph.adjacency[cursor] = edges;
770            }
771            cursor += 1;
772        }
773
774        mapped
775    }
776
777    /// Return the indexed graph.
778    #[must_use]
779    pub const fn graph(&self) -> &IndexedGraph<C> {
780        &self.graph
781    }
782
783    /// Return the indexed graph, consuming the mapping helper.
784    #[must_use]
785    pub fn into_graph(self) -> IndexedGraph<C> {
786        self.graph
787    }
788
789    /// Return the number of nodes in the mapped graph.
790    #[must_use]
791    pub const fn node_count(&self) -> usize {
792        self.graph.node_count()
793    }
794
795    /// Return the index assigned to `node`.
796    #[must_use]
797    pub fn index_of(&self, node: &N) -> Option<usize> {
798        self.index.get(node).copied()
799    }
800
801    /// Return the external node value for a given index.
802    #[must_use]
803    pub fn node(&self, index: usize) -> Option<&N> {
804        self.nodes.get(index)
805    }
806
807    /// Return the mapped node values in index order.
808    #[must_use]
809    pub fn nodes(&self) -> &[N] {
810        &self.nodes
811    }
812
813    fn ensure_index(&mut self, node: N) -> usize {
814        if let Some(&idx) = self.index.get(&node) {
815            return idx;
816        }
817        let idx = self.nodes.len();
818        self.nodes.push(node.clone());
819        self.index.insert(node, idx);
820        self.graph.adjacency.push(Vec::new());
821        idx
822    }
823}