Skip to main content

igraph/
paths.rs

1//! Shortest paths, distances, eccentricity, efficiency, random walks and
2//! related path algorithms (`igraph_paths.h`).
3//!
4//! Everything here is a method of [`Graph`] (or a free function when no graph
5//! is involved). Distances are returned as a [`Matrix`] (rows are sources,
6//! columns are targets, unreachable pairs hold `f64::INFINITY`), paths as
7//! `Vec<VertexId>` / `Vec<EdgeId>`, and multi-output functions return small
8//! named structs such as [`ShortestPaths`] or [`Diameter`].
9//!
10//! Edge weights are always passed as `Option<&[f64]>` indexed by edge id:
11//! `None` means an unweighted graph (every edge has length one). Most
12//! functions ignore edges with a positive infinite weight.
13//!
14//! # Example
15//!
16//! ```
17//! use igraph::prelude::*;
18//!
19//! // A weighted directed "diamond" with a shortcut: 0 → 1 → 3 and 0 → 2 → 3.
20//! let g = Graph::from_edges(&[(0, 1), (1, 3), (0, 2), (2, 3), (0, 3)], 4, true).unwrap();
21//! let w = [1.0, 1.0, 2.0, 2.0, 5.0];
22//!
23//! // Unweighted: the direct edge wins.
24//! assert_eq!(g.get_shortest_path(0, 3, None, NeighborMode::Out).unwrap().vertices, vec![0, 3]);
25//! // Weighted: go through vertex 1 (length 2 instead of 5).
26//! let p = g.get_shortest_path(0, 3, Some(&w), NeighborMode::Out).unwrap();
27//! assert_eq!(p.vertices, vec![0, 1, 3]);
28//! assert_eq!(p.edges, vec![0, 1]);
29//!
30//! let d = g.distances(0, .., Some(&w), NeighborMode::Out).unwrap();
31//! assert_eq!(d.row(0), vec![0.0, 1.0, 2.0, 2.0]);
32//!
33//! // The three shortest routes from 0 to 3, by increasing length.
34//! let k = g.get_k_shortest_paths(0, 3, 3, Some(&w), NeighborMode::Out).unwrap();
35//! let routes: Vec<_> = k.into_iter().map(|p| p.vertices).collect();
36//! assert_eq!(routes, vec![vec![0, 1, 3], vec![0, 2, 3], vec![0, 3]]);
37//!
38//! // Directed diameter (the longest shortest path) of the unweighted graph.
39//! assert_eq!(g.diameter().unwrap(), 1.0);
40//! ```
41//!
42//! # Provided functionality
43//!
44//! | Topic | Methods |
45//! |-------|---------|
46//! | Distance matrices | [`distances`](Graph::distances), [`distances_cutoff`](Graph::distances_cutoff), [`distances_dijkstra`](Graph::distances_dijkstra), [`distances_dijkstra_cutoff`](Graph::distances_dijkstra_cutoff), [`distances_bellman_ford`](Graph::distances_bellman_ford), [`distances_johnson`](Graph::distances_johnson), [`distances_floyd_warshall`](Graph::distances_floyd_warshall) |
47//! | One shortest path per target | [`get_shortest_paths`](Graph::get_shortest_paths), [`get_shortest_paths_dijkstra`](Graph::get_shortest_paths_dijkstra), [`get_shortest_paths_bellman_ford`](Graph::get_shortest_paths_bellman_ford) |
48//! | A single shortest path | [`get_shortest_path`](Graph::get_shortest_path), [`get_shortest_path_dijkstra`](Graph::get_shortest_path_dijkstra), [`get_shortest_path_bellman_ford`](Graph::get_shortest_path_bellman_ford), [`get_shortest_path_astar`](Graph::get_shortest_path_astar) |
49//! | All shortest paths | [`get_all_shortest_paths`](Graph::get_all_shortest_paths), [`get_all_shortest_paths_dijkstra`](Graph::get_all_shortest_paths_dijkstra) |
50//! | Other path enumerations | [`get_k_shortest_paths`](Graph::get_k_shortest_paths), [`get_all_simple_paths`](Graph::get_all_simple_paths) |
51//! | Widest (bottleneck) paths | [`get_widest_paths`](Graph::get_widest_paths), [`get_widest_path`](Graph::get_widest_path), [`widest_path_widths_dijkstra`](Graph::widest_path_widths_dijkstra), [`widest_path_widths_floyd_warshall`](Graph::widest_path_widths_floyd_warshall) |
52//! | Global path statistics | [`diameter`](Graph::diameter), [`diameter_with_path`](Graph::diameter_with_path), [`pseudo_diameter`](Graph::pseudo_diameter), [`radius`](Graph::radius), [`average_path_length`](Graph::average_path_length), [`average_path_length_details`](Graph::average_path_length_details), [`path_length_hist`](Graph::path_length_hist) |
53//! | Vertex path statistics | [`eccentricity`](Graph::eccentricity), [`graph_center`](Graph::graph_center) |
54//! | Efficiency | [`global_efficiency`](Graph::global_efficiency), [`local_efficiency`](Graph::local_efficiency), [`average_local_efficiency`](Graph::average_local_efficiency) |
55//! | Miscellaneous | [`random_walk`](Graph::random_walk), [`spanner`](Graph::spanner), [`voronoi`](Graph::voronoi), [`vertex_path_from_edge_path`](Graph::vertex_path_from_edge_path), [`expand_path_to_pairs`] |
56//!
57//! Global statistics on a classic network, built with
58//! [`Graph::famous`] from the [`constructors`](crate::constructors) module:
59//!
60//! ```
61//! use igraph::prelude::*;
62//!
63//! let karate = Graph::famous("Zachary").unwrap();
64//! assert_eq!(karate.diameter().unwrap(), 5.0);
65//! assert_eq!(karate.radius(None, NeighborMode::All).unwrap(), 3.0);
66//! let apl = karate.average_path_length(None, false, true).unwrap();
67//! assert!((apl - 2.408199643).abs() < 1e-9);
68//! // Histogram of the 561 vertex pairs by distance (the 78 edges first).
69//! let h = karate.path_length_hist(false).unwrap();
70//! assert_eq!(h.counts[0], 78.0);
71//! assert_eq!(h.counts.iter().sum::<f64>(), 561.0);
72//! ```
73//!
74//! # See also
75//!
76//! - [`centrality`](crate::centrality): distance based centralities such as
77//!   [`closeness`](Graph::closeness),
78//!   [`harmonic_centrality`](Graph::harmonic_centrality) and
79//!   [`betweenness`](Graph::betweenness) (which counts shortest paths).
80//! - [`visitor`](crate::visitor): breadth-first and depth-first traversals
81//!   ([`bfs`](Graph::bfs), [`dfs`](Graph::dfs)), the building blocks of
82//!   unweighted shortest paths.
83//! - [`components`](crate::components): reachability questions
84//!   ([`is_connected`](Graph::is_connected),
85//!   [`reachability`](Graph::reachability)) and bounded-distance
86//!   neighborhoods ([`neighborhood`](Graph::neighborhood)).
87//! - [`structural`](crate::structural): [`girth`](Graph::girth) (the
88//!   shortest cycle) and [`minimum_spanning_tree`](Graph::minimum_spanning_tree).
89//! - [`cycles`](crate::cycles): closed walks and special walks such as
90//!   [`find_cycle`](Graph::find_cycle) and
91//!   [`eulerian_path`](Graph::eulerian_path) (whose edge sequences
92//!   [`vertex_path_from_edge_path`](Graph::vertex_path_from_edge_path)
93//!   converts to vertices).
94//! - [`flow`](crate::flow): [`maxflow`](Graph::maxflow), the "capacity"
95//!   counterpart of the widest paths offered here.
96//! - [`community`](crate::community):
97//!   [`community_voronoi`](Graph::community_voronoi), a community detection
98//!   method built on [`voronoi`](Graph::voronoi).
99
100use crate::{
101    constants::{NeighborMode, RandomWalkStuck, VoronoiTiebreaker},
102    error::{Error, ErrorKind, Result, catch_panic},
103    ffi::*,
104    ffi_enum,
105    graph::{EdgeId, VertexId},
106    igraph_call,
107    list::VectorIntList,
108    matrix::Matrix,
109    selector::{EdgeSelector, VertexSelector},
110    vector::{Vector, VectorInt, View},
111};
112use std::{ffi::c_void, ptr};
113
114#[cfg(doc)]
115use crate::graph::Graph;
116
117ffi_enum! {
118    /// Variant of the Floyd–Warshall algorithm used by
119    /// [`Graph::distances_floyd_warshall`] (`igraph_floyd_warshall_algorithm_t`).
120    ///
121    /// The default is [`Automatic`](Self::Automatic).
122    #[derive(Default)]
123    pub enum FloydWarshallAlgorithm: igraph_floyd_warshall_algorithm_t {
124        /// Let igraph choose the best performing variant (currently always `Tree`).
125        #[default]
126        Automatic = igraph_floyd_warshall_algorithm_t_IGRAPH_FLOYD_WARSHALL_AUTOMATIC,
127        /// The textbook O(|V|³) Floyd–Warshall algorithm.
128        Original = igraph_floyd_warshall_algorithm_t_IGRAPH_FLOYD_WARSHALL_ORIGINAL,
129        /// The "Tree" speed-up of Brodnik, Grgurovič and Požar, faster in most cases.
130        Tree = igraph_floyd_warshall_algorithm_t_IGRAPH_FLOYD_WARSHALL_TREE,
131    }
132}
133
134/// A single path, described both by the vertices it visits and by the edges it
135/// traverses.
136///
137/// For a path of `k` edges, `vertices` has `k + 1` entries (source and target
138/// included) and `edges` has `k` entries. An empty `vertices` list means that
139/// there is no path.
140#[derive(Debug, Clone, PartialEq, Eq, Default)]
141pub struct GraphPath {
142    /// The vertex ids along the path, including both endpoints.
143    pub vertices: Vec<VertexId>,
144    /// The edge ids along the path.
145    pub edges: Vec<EdgeId>,
146}
147
148impl GraphPath {
149    /// Number of edges of the path (its unweighted length).
150    pub fn len(&self) -> usize {
151        self.edges.len()
152    }
153
154    /// Whether the path has no edges (no path at all, or a path from a vertex
155    /// to itself).
156    pub fn is_empty(&self) -> bool {
157        self.edges.is_empty()
158    }
159
160    /// Whether a path was found, i.e. `vertices` is non-empty.
161    pub fn exists(&self) -> bool {
162        !self.vertices.is_empty()
163    }
164
165    /// Sum of the weights of the traversed edges (`weights` is indexed by edge id).
166    ///
167    /// # Panics
168    /// If some edge id of the path is out of the bounds of `weights`.
169    pub fn weight(&self, weights: &[f64]) -> f64 {
170        self.edges.iter().map(|&e| weights[e as usize]).sum()
171    }
172}
173
174/// Single-source shortest (or widest) paths towards a set of targets, as
175/// returned by [`Graph::get_shortest_paths`] and friends.
176#[derive(Debug, Clone, PartialEq, Eq, Default)]
177pub struct ShortestPaths {
178    /// `vertices[i]` lists the vertices on the path to the `i`-th target
179    /// (empty if the target is unreachable).
180    pub vertices: Vec<Vec<VertexId>>,
181    /// `edges[i]` lists the edges on the path to the `i`-th target.
182    pub edges: Vec<Vec<EdgeId>>,
183    /// The shortest path tree, indexed by vertex id: the vertex from which each
184    /// vertex was reached. The source has `-1`, and vertices not reached
185    /// during the search have `-2` (the search stops as soon as all the
186    /// targets are reached).
187    pub parents: Vec<VertexId>,
188    /// The shortest path tree, indexed by vertex id: the edge through which
189    /// each vertex was reached; `-1` for the source and unreached vertices.
190    pub inbound_edges: Vec<EdgeId>,
191}
192
193impl ShortestPaths {
194    /// The `i`-th path (towards the `i`-th target) as a [`GraphPath`].
195    pub fn path(&self, i: usize) -> Option<GraphPath> {
196        Some(GraphPath {
197            vertices: self.vertices.get(i)?.clone(),
198            edges: self.edges.get(i)?.clone(),
199        })
200    }
201}
202
203/// All the shortest paths from one source, as returned by
204/// [`Graph::get_all_shortest_paths`].
205#[derive(Debug, Clone, PartialEq, Eq, Default)]
206pub struct AllShortestPaths {
207    /// Vertex lists of all the shortest paths, grouped by target in increasing
208    /// target id order. Unreachable targets contribute no path.
209    pub vertices: Vec<Vec<VertexId>>,
210    /// Edge lists of the same paths, in the same order.
211    pub edges: Vec<Vec<EdgeId>>,
212    /// Number of shortest paths from the source to each vertex (indexed by
213    /// vertex id). Only accurate for the requested targets.
214    pub nrgeo: Vec<i64>,
215}
216
217/// The diameter of a graph together with one longest geodesic, see
218/// [`Graph::diameter_with_path`].
219#[derive(Debug, Clone, PartialEq, Default)]
220pub struct Diameter {
221    /// The diameter: the (weighted) length of the longest shortest path.
222    /// `NaN` for the null graph; `INFINITY` for disconnected graphs when
223    /// `unconn` is `false`.
224    pub length: f64,
225    /// Source of a longest geodesic (`None` if there is no such path).
226    pub from: Option<VertexId>,
227    /// Target of a longest geodesic (`None` if there is no such path).
228    pub to: Option<VertexId>,
229    /// The longest geodesic, by vertices and edges.
230    pub path: GraphPath,
231}
232
233/// A pseudo-diameter and its endpoints, see [`Graph::pseudo_diameter`].
234#[derive(Debug, Clone, Copy, PartialEq, Default)]
235pub struct PseudoDiameter {
236    /// The eccentricity of the pseudo-peripheral vertex found: a lower bound
237    /// of the diameter.
238    pub length: f64,
239    /// Source of the corresponding path (`None` if there is no such path).
240    pub from: Option<VertexId>,
241    /// Target of the corresponding path (`None` if there is no such path).
242    pub to: Option<VertexId>,
243}
244
245/// Average shortest path length and number of disconnected pairs, see
246/// [`Graph::average_path_length_details`].
247#[derive(Debug, Clone, Copy, PartialEq, Default)]
248pub struct AveragePathLength {
249    /// The average length of the shortest paths.
250    pub average: f64,
251    /// The number of ordered vertex pairs `(u, v)` such that `v` is not
252    /// reachable from `u`.
253    pub unconnected_pairs: f64,
254}
255
256/// Histogram of shortest path lengths, see [`Graph::path_length_hist`].
257#[derive(Debug, Clone, PartialEq, Default)]
258pub struct PathLengthHistogram {
259    /// `counts[i]` is the number of vertex pairs at distance `i + 1`.
260    pub counts: Vec<f64>,
261    /// The number of vertex pairs whose second vertex is unreachable from the first.
262    pub unconnected: f64,
263}
264
265/// A Voronoi partitioning, see [`Graph::voronoi`].
266///
267/// Not to be confused with [`community::Voronoi`](crate::community::Voronoi),
268/// the result of the Voronoi *community detection*
269/// [`Graph::community_voronoi`].
270#[derive(Debug, Clone, PartialEq, Default)]
271pub struct VoronoiPartition {
272    /// For each vertex, the *index* (into the `generators` slice) of the
273    /// generator it belongs to, or `-1` when no generator reaches it.
274    pub membership: Vec<i64>,
275    /// For each vertex, the distance to/from its generator (`INFINITY` when unreachable).
276    pub distances: Vec<f64>,
277}
278
279/// Length limits for [`Graph::get_all_simple_paths`].
280///
281/// `None` means "no limit"; the default has no limits at all.
282///
283/// ```
284/// use igraph::paths::SimplePathsOptions;
285/// let opts = SimplePathsOptions::default().with_max_len(3).with_max_results(10);
286/// assert_eq!(opts.min_len, None);
287/// assert_eq!(opts.max_len, Some(3));
288/// ```
289#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
290pub struct SimplePathsOptions {
291    /// Minimum length (number of edges) of the returned paths.
292    pub min_len: Option<usize>,
293    /// Maximum length (number of edges) of the returned paths.
294    pub max_len: Option<usize>,
295    /// Stop after this many paths have been found.
296    pub max_results: Option<usize>,
297}
298
299impl SimplePathsOptions {
300    /// Sets the minimum path length.
301    pub fn with_min_len(mut self, min_len: usize) -> Self {
302        self.min_len = Some(min_len);
303        self
304    }
305
306    /// Sets the maximum path length.
307    pub fn with_max_len(mut self, max_len: usize) -> Self {
308        self.max_len = Some(max_len);
309        self
310    }
311
312    /// Sets the maximum number of returned paths.
313    pub fn with_max_results(mut self, max_results: usize) -> Self {
314        self.max_results = Some(max_results);
315        self
316    }
317}
318
319// ---------------------------------------------------------------------------
320// Private helpers.
321
322fn weights_view(weights: Option<&[f64]>) -> Option<View<'_, Vector>> {
323    weights.map(Vector::view)
324}
325
326fn weights_ptr(view: &Option<View<'_, Vector>>) -> *const igraph_vector_t {
327    view.as_ref().map_or(ptr::null(), |v| v.as_ptr())
328}
329
330fn opt_id(id: igraph_int_t) -> Option<VertexId> {
331    (id >= 0).then_some(id)
332}
333
334/// Converts a count to `igraph_int_t`, saturating at `igraph_int_t::MAX`
335/// (a plain `as` cast would turn huge counts into negative values, which
336/// igraph interprets as "unlimited" or rejects).
337fn saturating_int(value: usize) -> igraph_int_t {
338    igraph_int_t::try_from(value).unwrap_or(igraph_int_t::MAX)
339}
340
341fn limit(value: Option<usize>) -> igraph_int_t {
342    value.map_or(IGRAPH_UNLIMITED as igraph_int_t, saturating_int)
343}
344
345/// Maps an optional vertex to igraph's "negative means automatic"
346/// convention, rejecting explicit ids outside `0..vcount` (igraph would
347/// silently treat negative ones as `None`, and some functions report
348/// `IGRAPH_EINVAL` rather than `IGRAPH_EINVVID` for too large ones).
349fn opt_vertex_arg(vertex: Option<VertexId>, vcount: usize) -> Result<igraph_int_t> {
350    match vertex {
351        Some(v) if !(0..vcount as VertexId).contains(&v) => Err(Error::new(
352            ErrorKind::InvalidVertexId,
353            format!("invalid vertex id {v}"),
354        )),
355        Some(v) => Ok(v),
356        None => Ok(-1),
357    }
358}
359
360/// igraph treats negative cutoffs as "no cutoff"; a NaN cutoff has no
361/// meaning and is rejected.
362fn cutoff_value(cutoff: Option<f64>) -> Result<igraph_real_t> {
363    match cutoff {
364        Some(c) if c.is_nan() => Err(Error::invalid("the cutoff must not be NaN")),
365        Some(c) => Ok(c),
366        None => Ok(-1.0),
367    }
368}
369
370impl igraph_t {
371    /// Checks that the weight vector, if given, has one entry per edge.
372    fn paths_check_weights(&self, weights: Option<&[f64]>) -> Result<()> {
373        match weights {
374            Some(w) if w.len() != self.ecount() => Err(Error::invalid(format!(
375                "the weight vector has length {} but the graph has {} edges",
376                w.len(),
377                self.ecount()
378            ))),
379            _ => Ok(()),
380        }
381    }
382
383    /// Shared plumbing of the `igraph_distances*`-like functions.
384    fn distance_matrix<'a>(
385        &self,
386        from: impl Into<VertexSelector<'a>>,
387        to: impl Into<VertexSelector<'a>>,
388        weights: Option<&[f64]>,
389        call: impl FnOnce(
390            *mut igraph_matrix_t,
391            igraph_vs_t,
392            igraph_vs_t,
393            *const igraph_vector_t,
394        ) -> igraph_error_t,
395    ) -> Result<Matrix> {
396        self.paths_check_weights(weights)?;
397        let from = from.into().to_raw()?;
398        let to = to.into().to_raw()?;
399        let w = weights_view(weights);
400        let mut res = Matrix::new();
401        igraph_call!(call(&mut res, from.get(), to.get(), weights_ptr(&w)))?;
402        Ok(res)
403    }
404
405    /// Shared plumbing of the `igraph_get_shortest_paths*`-like functions.
406    fn shortest_paths_with<'a>(
407        &self,
408        to: impl Into<VertexSelector<'a>>,
409        weights: Option<&[f64]>,
410        call: impl FnOnce(
411            *mut igraph_vector_int_list_t,
412            *mut igraph_vector_int_list_t,
413            igraph_vs_t,
414            *const igraph_vector_t,
415            *mut igraph_vector_int_t,
416            *mut igraph_vector_int_t,
417        ) -> igraph_error_t,
418    ) -> Result<ShortestPaths> {
419        self.paths_check_weights(weights)?;
420        let to = to.into().to_raw()?;
421        let w = weights_view(weights);
422        let mut vertices = VectorIntList::new();
423        let mut edges = VectorIntList::new();
424        let mut parents = VectorInt::new();
425        let mut inbound = VectorInt::new();
426        igraph_call!(call(
427            &mut vertices,
428            &mut edges,
429            to.get(),
430            weights_ptr(&w),
431            &mut parents,
432            &mut inbound
433        ))?;
434        Ok(ShortestPaths {
435            vertices: vertices.to_vecs(),
436            edges: edges.to_vecs(),
437            parents: parents.into(),
438            inbound_edges: inbound.into(),
439        })
440    }
441
442    /// Shared plumbing of the `igraph_get_shortest_path*`-like functions.
443    fn single_path_with(
444        &self,
445        weights: Option<&[f64]>,
446        call: impl FnOnce(
447            *mut igraph_vector_int_t,
448            *mut igraph_vector_int_t,
449            *const igraph_vector_t,
450        ) -> igraph_error_t,
451    ) -> Result<GraphPath> {
452        self.paths_check_weights(weights)?;
453        let w = weights_view(weights);
454        let mut vertices = VectorInt::new();
455        let mut edges = VectorInt::new();
456        igraph_call!(call(&mut vertices, &mut edges, weights_ptr(&w)))?;
457        Ok(GraphPath {
458            vertices: vertices.into(),
459            edges: edges.into(),
460        })
461    }
462
463    /// Shared plumbing of the `igraph_get_all_shortest_paths*` functions.
464    fn all_shortest_paths_with<'a>(
465        &self,
466        to: impl Into<VertexSelector<'a>>,
467        weights: Option<&[f64]>,
468        call: impl FnOnce(
469            *mut igraph_vector_int_list_t,
470            *mut igraph_vector_int_list_t,
471            *mut igraph_vector_int_t,
472            igraph_vs_t,
473            *const igraph_vector_t,
474        ) -> igraph_error_t,
475    ) -> Result<AllShortestPaths> {
476        self.paths_check_weights(weights)?;
477        let to = to.into().to_raw()?;
478        let w = weights_view(weights);
479        let mut vertices = VectorIntList::new();
480        let mut edges = VectorIntList::new();
481        let mut nrgeo = VectorInt::new();
482        igraph_call!(call(
483            &mut vertices,
484            &mut edges,
485            &mut nrgeo,
486            to.get(),
487            weights_ptr(&w)
488        ))?;
489        Ok(AllShortestPaths {
490            vertices: vertices.to_vecs(),
491            edges: edges.to_vecs(),
492            nrgeo: nrgeo.into(),
493        })
494    }
495}
496
497// ---------------------------------------------------------------------------
498// Diameter, radius, eccentricity and averages.
499
500impl igraph_t {
501    /// The diameter of the graph: the length of its longest shortest path.
502    ///
503    /// This is the backwards-compatible shorthand of
504    /// [`diameter_with_path`](Self::diameter_with_path) for the most common
505    /// case: unweighted, following edge directions in directed graphs
506    /// (`directed = self.is_directed()`), and, for disconnected graphs,
507    /// returning the longest geodesic *within* a component (`unconn = true`).
508    /// The diameter of the null graph is `NaN`.
509    ///
510    /// See also [`girth`](Self::girth), the length of the *shortest* cycle,
511    /// and [`radius`](Self::radius), the smallest eccentricity.
512    ///
513    /// Binds [`igraph_diameter`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_diameter).
514    /// Time complexity: O(|V| |E|).
515    ///
516    /// # Examples
517    ///
518    /// ```
519    /// use igraph::prelude::*;
520    /// // A path on 5 vertices has diameter 4 ...
521    /// let path = Graph::path_graph(5, false, false).unwrap();
522    /// assert_eq!(path.diameter().unwrap(), 4.0);
523    /// // ... and closing it into a cycle halves it.
524    /// let mut cycle = path.clone();
525    /// cycle.add_edge(4, 0).unwrap();
526    /// assert_eq!(cycle.diameter().unwrap(), 2.0);
527    /// assert!(Graph::new(0, false).diameter().unwrap().is_nan());
528    /// ```
529    pub fn diameter(&self) -> Result<f64> {
530        let mut res = 0.0;
531        igraph_call!(igraph_diameter(
532            self,
533            ptr::null(),
534            &mut res,
535            ptr::null_mut(),
536            ptr::null_mut(),
537            ptr::null_mut(),
538            ptr::null_mut(),
539            self.is_directed(),
540            true
541        ))?;
542        Ok(res)
543    }
544
545    /// The (weighted) diameter of the graph together with its endpoints and
546    /// one longest geodesic.
547    ///
548    /// The diameter is the maximum eccentricity of the vertices, i.e. the
549    /// length of the longest shortest path.
550    ///
551    /// - `weights`: optional edge lengths (Dijkstra's algorithm is used when
552    ///   given); edges with positive infinite weight are ignored.
553    /// - `directed`: whether to follow edge directions (ignored for
554    ///   undirected graphs).
555    /// - `unconn`: for disconnected graphs, `true` returns the longest
556    ///   geodesic within a component, `false` returns `INFINITY`.
557    ///
558    /// The null graph has diameter `NaN`; `from`/`to` are `None` and the path
559    /// is empty when there is no diameter path.
560    ///
561    /// Binds [`igraph_diameter`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_diameter).
562    /// Time complexity: O(|V| |E|) unweighted, O(|V| |E| log |E|) weighted.
563    ///
564    /// # Examples
565    ///
566    /// ```
567    /// use igraph::prelude::*;
568    /// // The directed path 0 → 1 → ... → 9 (a non-circular directed ring, as
569    /// // in igraph's own example).
570    /// let ring = Graph::ring(10, true, false, false).unwrap();
571    /// let d = ring.diameter_with_path(None, true, true).unwrap();
572    /// assert_eq!(d.length, 9.0);
573    /// assert_eq!((d.from, d.to), (Some(0), Some(9)));
574    /// assert_eq!(d.path.vertices, (0..10).collect::<Vec<_>>());
575    /// assert_eq!(d.path.edges, (0..9).collect::<Vec<_>>());
576    /// ```
577    pub fn diameter_with_path(
578        &self,
579        weights: Option<&[f64]>,
580        directed: bool,
581        unconn: bool,
582    ) -> Result<Diameter> {
583        self.paths_check_weights(weights)?;
584        let w = weights_view(weights);
585        let mut length = 0.0;
586        let (mut from, mut to) = (-1, -1);
587        let mut vpath = VectorInt::new();
588        let mut epath = VectorInt::new();
589        igraph_call!(igraph_diameter(
590            self,
591            weights_ptr(&w),
592            &mut length,
593            &mut from,
594            &mut to,
595            &mut vpath,
596            &mut epath,
597            directed,
598            unconn
599        ))?;
600        Ok(Diameter {
601            length,
602            from: opt_id(from),
603            to: opt_id(to),
604            path: GraphPath {
605                vertices: vpath.into(),
606                edges: epath.into(),
607            },
608        })
609    }
610
611    /// An approximation (and lower bound) of the diameter, computed from a
612    /// pseudo-peripheral vertex.
613    ///
614    /// A pseudo-peripheral vertex `v` is such that for every vertex `u` as far
615    /// away from `v` as possible, `v` is also as far away from `u` as
616    /// possible. The search starts at `start` (a random vertex when `None`);
617    /// in disconnected graphs the result refers to the component of the start
618    /// vertex. `directed` and `unconn` have the same meaning as in
619    /// [`diameter_with_path`](Self::diameter_with_path): with `unconn =
620    /// false` a disconnected graph gives `INFINITY` and no endpoints. Returns
621    /// `NaN` for the null graph. With `start = None` the start vertex is drawn
622    /// from the calling thread's default random number generator (seed it
623    /// with [`rng::seed`](crate::rng::seed) for reproducible results).
624    ///
625    /// # Errors
626    /// [`ErrorKind::InvalidVertexId`] if
627    /// `start` is not a vertex of the graph (negative ids included).
628    ///
629    /// Binds [`igraph_pseudo_diameter`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_pseudo_diameter).
630    /// Time complexity: O(|V| |E| log |E|).
631    ///
632    /// # Examples
633    ///
634    /// ```
635    /// use igraph::prelude::*;
636    /// let path = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
637    /// let pd = path.pseudo_diameter(None, Some(1), false, true).unwrap();
638    /// // On trees the pseudo-diameter is exact.
639    /// assert_eq!(pd.length, 3.0);
640    /// ```
641    pub fn pseudo_diameter(
642        &self,
643        weights: Option<&[f64]>,
644        start: Option<VertexId>,
645        directed: bool,
646        unconn: bool,
647    ) -> Result<PseudoDiameter> {
648        self.paths_check_weights(weights)?;
649        let start = opt_vertex_arg(start, self.vcount())?;
650        let w = weights_view(weights);
651        let mut length = 0.0;
652        let (mut from, mut to) = (-1, -1);
653        self.with_fresh_multi_cache(|| {
654            igraph_call!(igraph_pseudo_diameter(
655                self,
656                weights_ptr(&w),
657                &mut length,
658                start,
659                &mut from,
660                &mut to,
661                directed,
662                unconn
663            ))
664        })?;
665        Ok(PseudoDiameter {
666            length,
667            from: opt_id(from),
668            to: opt_id(to),
669        })
670    }
671
672    /// Eccentricity of the selected vertices: the largest distance from (or
673    /// to, depending on `mode`) each vertex to any vertex reachable from it.
674    ///
675    /// Vertex pairs in different components are ignored, so isolated vertices
676    /// have eccentricity zero. `weights` must be non-negative and not NaN;
677    /// edges with infinite weight are ignored. The maximum eccentricity is
678    /// the [diameter](Self::diameter_with_path), the minimum the
679    /// [radius](Self::radius). See also [`closeness`](Self::closeness), which
680    /// averages the distances instead of taking their maximum.
681    ///
682    /// Binds [`igraph_eccentricity`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_eccentricity).
683    /// Time complexity: O(|V| |E| log |V| + |V|).
684    ///
685    /// # Examples
686    ///
687    /// ```
688    /// use igraph::prelude::*;
689    /// // A star: the center has eccentricity 1, the leaves 2.
690    /// let star = Graph::star(4, StarMode::Undirected, 0).unwrap();
691    /// assert_eq!(star.eccentricity(.., None, NeighborMode::All).unwrap(), vec![1.0, 2.0, 2.0, 2.0]);
692    /// // Only some vertices, in the requested order.
693    /// assert_eq!(star.eccentricity(vec![3, 0], None, NeighborMode::All).unwrap(), vec![2.0, 1.0]);
694    /// ```
695    pub fn eccentricity<'a>(
696        &self,
697        vids: impl Into<VertexSelector<'a>>,
698        weights: Option<&[f64]>,
699        mode: NeighborMode,
700    ) -> Result<Vec<f64>> {
701        self.paths_check_weights(weights)?;
702        let vs = vids.into().to_raw()?;
703        let w = weights_view(weights);
704        let mut res = Vector::new();
705        self.with_fresh_multi_cache(|| {
706            igraph_call!(igraph_eccentricity(
707                self,
708                weights_ptr(&w),
709                &mut res,
710                vs.get(),
711                mode.into()
712            ))
713        })?;
714        Ok(res.into())
715    }
716
717    /// The radius of the graph: the smallest eccentricity of its vertices
718    /// (`NaN` for the null graph).
719    ///
720    /// Binds [`igraph_radius`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_radius).
721    /// Time complexity: O(|V| |E| log |V| + |V|).
722    ///
723    /// # Examples
724    ///
725    /// ```
726    /// use igraph::prelude::*;
727    /// let path = Graph::path_graph(5, false, false).unwrap();
728    /// assert_eq!(path.radius(None, NeighborMode::All).unwrap(), 2.0);
729    /// // Weighted: stretching the first edge moves the center towards it.
730    /// let w = [10.0, 1.0, 1.0, 1.0];
731    /// assert_eq!(path.radius(Some(&w), NeighborMode::All).unwrap(), 10.0);
732    /// ```
733    pub fn radius(&self, weights: Option<&[f64]>, mode: NeighborMode) -> Result<f64> {
734        self.paths_check_weights(weights)?;
735        let w = weights_view(weights);
736        let mut res = 0.0;
737        self.with_fresh_multi_cache(|| {
738            igraph_call!(igraph_radius(self, weights_ptr(&w), &mut res, mode.into()))
739        })?;
740        Ok(res)
741    }
742
743    /// The center of the graph: the vertices of minimum eccentricity.
744    ///
745    /// In disconnected graphs the minimum is taken across all components.
746    /// This function is marked *experimental* in igraph.
747    ///
748    /// Binds [`igraph_graph_center`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_graph_center).
749    /// Time complexity: O(|V| |E| log |V| + |V|).
750    ///
751    /// # Examples
752    ///
753    /// ```
754    /// use igraph::prelude::*;
755    /// // The center of a path with an even number of vertices is its middle edge.
756    /// let path = Graph::path_graph(4, false, false).unwrap();
757    /// assert_eq!(path.graph_center(None, NeighborMode::All).unwrap(), vec![1, 2]);
758    /// ```
759    pub fn graph_center(
760        &self,
761        weights: Option<&[f64]>,
762        mode: NeighborMode,
763    ) -> Result<Vec<VertexId>> {
764        self.paths_check_weights(weights)?;
765        let w = weights_view(weights);
766        let mut res = VectorInt::new();
767        self.with_fresh_multi_cache(|| {
768            igraph_call!(igraph_graph_center(
769                self,
770                weights_ptr(&w),
771                &mut res,
772                mode.into()
773            ))
774        })?;
775        Ok(res.into())
776    }
777
778    /// The average shortest path length over all ordered pairs of distinct
779    /// vertices.
780    ///
781    /// - `weights`: optional non-negative edge lengths.
782    /// - `directed`: whether to follow edge directions (ignored for
783    ///   undirected graphs).
784    /// - `unconn`: if `true`, only pairs connected by a path are averaged;
785    ///   if `false`, disconnected graphs give `INFINITY`.
786    ///
787    /// Returns `NaN` when no pair can be included (e.g. fewer than two
788    /// vertices). See [`average_path_length_details`](Self::average_path_length_details)
789    /// to also obtain the number of disconnected pairs, and
790    /// [`closeness`](Self::closeness) for the per-vertex (inverse) averages.
791    ///
792    /// Binds [`igraph_average_path_length`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_average_path_length).
793    /// Time complexity: O(|V| |E| log |E| + |V|).
794    ///
795    /// # Examples
796    ///
797    /// ```
798    /// use igraph::prelude::*;
799    /// // In a triangle every pair is adjacent.
800    /// let k3 = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 3, false).unwrap();
801    /// assert_eq!(k3.average_path_length(None, false, true).unwrap(), 1.0);
802    /// // Path 0-1-2: distances 1, 1, 2 → average 4/3.
803    /// let p3 = Graph::from_edges(&[(0, 1), (1, 2)], 3, false).unwrap();
804    /// assert!((p3.average_path_length(None, false, true).unwrap() - 4.0 / 3.0).abs() < 1e-12);
805    /// ```
806    pub fn average_path_length(
807        &self,
808        weights: Option<&[f64]>,
809        directed: bool,
810        unconn: bool,
811    ) -> Result<f64> {
812        Ok(self
813            .average_path_length_details(weights, directed, unconn)?
814            .average)
815    }
816
817    /// Like [`average_path_length`](Self::average_path_length), also
818    /// returning the number of ordered vertex pairs `(u, v)` with `v`
819    /// unreachable from `u`.
820    ///
821    /// Binds [`igraph_average_path_length`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_average_path_length).
822    ///
823    /// # Examples
824    ///
825    /// ```
826    /// use igraph::prelude::*;
827    /// // Two disjoint edges: 4 ordered connected pairs, 8 unconnected ones.
828    /// let g = Graph::from_edges(&[(0, 1), (2, 3)], 4, false).unwrap();
829    /// let apl = g.average_path_length_details(None, false, true).unwrap();
830    /// assert_eq!(apl.average, 1.0);
831    /// assert_eq!(apl.unconnected_pairs, 8.0);
832    /// ```
833    pub fn average_path_length_details(
834        &self,
835        weights: Option<&[f64]>,
836        directed: bool,
837        unconn: bool,
838    ) -> Result<AveragePathLength> {
839        self.paths_check_weights(weights)?;
840        let w = weights_view(weights);
841        let (mut average, mut unconnected_pairs) = (0.0, 0.0);
842        igraph_call!(igraph_average_path_length(
843            self,
844            weights_ptr(&w),
845            &mut average,
846            &mut unconnected_pairs,
847            directed,
848            unconn
849        ))?;
850        Ok(AveragePathLength {
851            average,
852            unconnected_pairs,
853        })
854    }
855
856    /// Histogram of the (unweighted) shortest path lengths between all vertex
857    /// pairs.
858    ///
859    /// `counts[0]` is the number of pairs at distance 1, `counts[1]` at
860    /// distance 2, and so on. In undirected graphs (or with `directed =
861    /// false`) each unordered pair is counted once; in directed graphs with
862    /// `directed = true` both directions are counted.
863    ///
864    /// Binds [`igraph_path_length_hist`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_path_length_hist).
865    /// Time complexity: O(|V| |E|).
866    ///
867    /// # Examples
868    ///
869    /// ```
870    /// use igraph::prelude::*;
871    /// // A path on 4 vertices: 3 pairs at distance 1, 2 at distance 2, 1 at distance 3.
872    /// let p4 = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
873    /// let h = p4.path_length_hist(false).unwrap();
874    /// assert_eq!(h.counts, vec![3.0, 2.0, 1.0]);
875    /// assert_eq!(h.unconnected, 0.0);
876    /// ```
877    pub fn path_length_hist(&self, directed: bool) -> Result<PathLengthHistogram> {
878        let mut counts = Vector::new();
879        let mut unconnected = 0.0;
880        igraph_call!(igraph_path_length_hist(
881            self,
882            &mut counts,
883            &mut unconnected,
884            directed
885        ))?;
886        Ok(PathLengthHistogram {
887            counts: counts.into(),
888            unconnected,
889        })
890    }
891
892    /// The global efficiency of the network: the average of the inverse
893    /// distances between all ordered pairs of distinct vertices,
894    /// `E = 1/(N(N-1)) Σ_{i≠j} 1/d_ij` (Latora & Marchiori, 2001).
895    ///
896    /// Unreachable pairs contribute zero, so, unlike the
897    /// [average path length](Self::average_path_length), it is well defined
898    /// for disconnected graphs; graphs with fewer than two vertices give
899    /// `NaN`. It equals the mean of the normalized
900    /// [`harmonic_centrality`](Self::harmonic_centrality) of the vertices.
901    ///
902    /// Binds [`igraph_global_efficiency`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_global_efficiency).
903    /// Time complexity: O(|V| |E|) unweighted, O(|V| |E| log |E| + |V|) weighted.
904    ///
905    /// # Examples
906    ///
907    /// ```
908    /// use igraph::prelude::*;
909    /// let k4 = Graph::full(4, false, false).unwrap();
910    /// assert_eq!(k4.global_efficiency(None, false).unwrap(), 1.0);
911    /// // Path 0-1-2: inverse distances 1, 1, 1/2 (each counted twice) → 5/6.
912    /// let p3 = Graph::path_graph(3, false, false).unwrap();
913    /// assert!((p3.global_efficiency(None, false).unwrap() - 5.0 / 6.0).abs() < 1e-12);
914    /// ```
915    pub fn global_efficiency(&self, weights: Option<&[f64]>, directed: bool) -> Result<f64> {
916        self.paths_check_weights(weights)?;
917        let w = weights_view(weights);
918        let mut res = 0.0;
919        igraph_call!(igraph_global_efficiency(
920            self,
921            weights_ptr(&w),
922            &mut res,
923            directed
924        ))?;
925        Ok(res)
926    }
927
928    /// The local efficiency around each selected vertex.
929    ///
930    /// The vertex is removed and the average inverse distance between its
931    /// neighbors (through the rest of the network) is computed. Unreachable
932    /// pairs contribute zero; vertices with fewer than two neighbors have
933    /// local efficiency zero. `mode` selects which neighbors form the local
934    /// neighborhood in directed graphs (`NeighborMode::All` is a sensible
935    /// default), `directed` whether distances follow edge directions.
936    /// It is a distance based analogue of the local clustering coefficient
937    /// ([`transitivity_local_undirected`](Self::transitivity_local_undirected)).
938    ///
939    /// Binds [`igraph_local_efficiency`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_local_efficiency).
940    /// Time complexity: O(|E|² log |E|) weighted, O(|E|²) unweighted.
941    ///
942    /// # Examples
943    ///
944    /// ```
945    /// use igraph::prelude::*;
946    /// // In a 4-cycle the two neighbors of a vertex are at distance 2 once it is removed.
947    /// let c4 = Graph::cycle_graph(4, false, false).unwrap();
948    /// assert_eq!(c4.local_efficiency(.., None, false, NeighborMode::All).unwrap(), vec![0.5; 4]);
949    /// ```
950    pub fn local_efficiency<'a>(
951        &self,
952        vids: impl Into<VertexSelector<'a>>,
953        weights: Option<&[f64]>,
954        directed: bool,
955        mode: NeighborMode,
956    ) -> Result<Vec<f64>> {
957        self.paths_check_weights(weights)?;
958        let vs = vids.into().to_raw()?;
959        let w = weights_view(weights);
960        let mut res = Vector::new();
961        igraph_call!(igraph_local_efficiency(
962            self,
963            weights_ptr(&w),
964            &mut res,
965            vs.get(),
966            directed,
967            mode.into()
968        ))?;
969        Ok(res.into())
970    }
971
972    /// The average of the [local efficiencies](Self::local_efficiency) of all
973    /// vertices (zero for the null graph).
974    ///
975    /// Binds [`igraph_average_local_efficiency`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_average_local_efficiency).
976    /// Time complexity: O(|E|² log |E|) weighted, O(|E|²) unweighted.
977    ///
978    /// # Examples
979    ///
980    /// ```
981    /// use igraph::prelude::*;
982    /// let c4 = Graph::cycle_graph(4, false, false).unwrap();
983    /// assert_eq!(c4.average_local_efficiency(None, false, NeighborMode::All).unwrap(), 0.5);
984    /// ```
985    pub fn average_local_efficiency(
986        &self,
987        weights: Option<&[f64]>,
988        directed: bool,
989        mode: NeighborMode,
990    ) -> Result<f64> {
991        self.paths_check_weights(weights)?;
992        let w = weights_view(weights);
993        let mut res = 0.0;
994        igraph_call!(igraph_average_local_efficiency(
995            self,
996            weights_ptr(&w),
997            &mut res,
998            directed,
999            mode.into()
1000        ))?;
1001        Ok(res)
1002    }
1003}
1004
1005// ---------------------------------------------------------------------------
1006// Distance matrices.
1007
1008impl igraph_t {
1009    /// Shortest path lengths between the `from` and `to` vertices.
1010    ///
1011    /// Row `i` of the result holds the distances from the `i`-th source to
1012    /// every target (`INFINITY` if unreachable). `to` must not contain
1013    /// duplicates. `mode` chooses outgoing (`Out`), incoming (`In`) or
1014    /// undirected (`All`) paths in directed graphs.
1015    ///
1016    /// With `weights = None` a BFS is used; with weights, igraph picks the
1017    /// most suitable algorithm: Floyd–Warshall for dense all-pairs problems,
1018    /// Dijkstra for non-negative weights, and Bellman–Ford or Johnson when
1019    /// negative weights are present.
1020    ///
1021    /// See also [`bfs`](Self::bfs) (which also reports BFS distances),
1022    /// [`neighborhood`](Self::neighborhood) (the vertices within a given
1023    /// distance) and [`closeness`](Self::closeness) /
1024    /// [`harmonic_centrality`](Self::harmonic_centrality), which summarize
1025    /// the rows of this matrix.
1026    ///
1027    /// Binds [`igraph_distances`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_distances).
1028    /// Time complexity (unweighted): O(n(|V| + |E|)) for n sources.
1029    ///
1030    /// # Errors
1031    /// [`ErrorKind::InvalidVertexId`] for
1032    /// invalid vertices, [`ErrorKind::NegativeCycle`]
1033    /// if negative weights form a negative cycle.
1034    ///
1035    /// # Examples
1036    ///
1037    /// ```
1038    /// use igraph::prelude::*;
1039    /// let p = Graph::from_edges(&[(0, 1), (1, 2)], 3, false).unwrap();
1040    /// let d = p.distances(.., .., None, NeighborMode::All).unwrap();
1041    /// assert_eq!(d.to_rows(), vec![vec![0.0, 1.0, 2.0], vec![1.0, 0.0, 1.0], vec![2.0, 1.0, 0.0]]);
1042    /// // Only from vertex 0 to vertices 1 and 2:
1043    /// let d = p.distances(0, vec![1, 2], None, NeighborMode::All).unwrap();
1044    /// assert_eq!(d.to_rows(), vec![vec![1.0, 2.0]]);
1045    /// ```
1046    pub fn distances<'a>(
1047        &self,
1048        from: impl Into<VertexSelector<'a>>,
1049        to: impl Into<VertexSelector<'a>>,
1050        weights: Option<&[f64]>,
1051        mode: NeighborMode,
1052    ) -> Result<Matrix> {
1053        self.distance_matrix(from, to, weights, |res, from, to, w| unsafe {
1054            igraph_distances(self, w, res, from, to, mode.into())
1055        })
1056    }
1057
1058    /// Like [`distances`](Self::distances), but paths longer than `cutoff`
1059    /// are ignored (their length is reported as `INFINITY`).
1060    ///
1061    /// `cutoff = None` (or a negative value) means no cutoff. The search from
1062    /// each source stops at the cutoff, which may save much time. With
1063    /// `weights` this is the same as
1064    /// [`distances_dijkstra_cutoff`](Self::distances_dijkstra_cutoff).
1065    ///
1066    /// # Errors
1067    /// [`ErrorKind::InvalidValue`] for a NaN
1068    /// cutoff or invalid weights,
1069    /// [`ErrorKind::InvalidVertexId`] for
1070    /// invalid vertices.
1071    ///
1072    /// Binds [`igraph_distances_cutoff`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_distances_cutoff).
1073    /// Time complexity: O(s |E| + |V|) for s sources (unweighted).
1074    ///
1075    /// # Examples
1076    ///
1077    /// ```
1078    /// use igraph::prelude::*;
1079    /// let p = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
1080    /// let d = p.distances_cutoff(0, .., None, NeighborMode::All, Some(2.0)).unwrap();
1081    /// assert_eq!(d.row(0), vec![0.0, 1.0, 2.0, f64::INFINITY]);
1082    /// ```
1083    pub fn distances_cutoff<'a>(
1084        &self,
1085        from: impl Into<VertexSelector<'a>>,
1086        to: impl Into<VertexSelector<'a>>,
1087        weights: Option<&[f64]>,
1088        mode: NeighborMode,
1089        cutoff: Option<f64>,
1090    ) -> Result<Matrix> {
1091        let cutoff = cutoff_value(cutoff)?;
1092        self.distance_matrix(from, to, weights, |res, from, to, w| unsafe {
1093            igraph_distances_cutoff(self, w, res, from, to, mode.into(), cutoff)
1094        })
1095    }
1096
1097    /// Weighted shortest path lengths with Dijkstra's algorithm (binary heap),
1098    /// run independently from each source.
1099    ///
1100    /// Weights must be non-negative and not NaN; `None` falls back to the
1101    /// unweighted [`distances`](Self::distances).
1102    ///
1103    /// Binds [`igraph_distances_dijkstra`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_distances_dijkstra).
1104    /// Time complexity: O(s |E| log |V| + |V|) for s sources.
1105    ///
1106    /// # Errors
1107    /// [`ErrorKind::InvalidValue`] for
1108    /// negative or NaN weights, or a weight vector of the wrong length.
1109    ///
1110    /// # Examples
1111    ///
1112    /// ```
1113    /// use igraph::prelude::*;
1114    /// let tri = Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], 3, false).unwrap();
1115    /// let d = tri.distances_dijkstra(0, 2, Some(&[1.0, 1.0, 5.0]), NeighborMode::All).unwrap();
1116    /// assert_eq!(d[(0, 0)], 2.0);
1117    /// ```
1118    pub fn distances_dijkstra<'a>(
1119        &self,
1120        from: impl Into<VertexSelector<'a>>,
1121        to: impl Into<VertexSelector<'a>>,
1122        weights: Option<&[f64]>,
1123        mode: NeighborMode,
1124    ) -> Result<Matrix> {
1125        self.distance_matrix(from, to, weights, |res, from, to, w| unsafe {
1126            igraph_distances_dijkstra(self, res, from, to, w, mode.into())
1127        })
1128    }
1129
1130    /// Like [`distances_dijkstra`](Self::distances_dijkstra), ignoring paths
1131    /// longer than `cutoff` (`None` or negative: no cutoff).
1132    ///
1133    /// Binds [`igraph_distances_dijkstra_cutoff`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_distances_dijkstra_cutoff).
1134    /// Time complexity: at most O(s |E| log |V| + |V|); the cutoff limits the
1135    /// explored region.
1136    ///
1137    /// # Examples
1138    ///
1139    /// ```
1140    /// use igraph::prelude::*;
1141    /// // Path 0 -2- 1 -2- 2 -2- 3: with cutoff 4 vertex 3 (at distance 6) is out of reach.
1142    /// let p = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
1143    /// let d = p
1144    ///     .distances_dijkstra_cutoff(0, .., Some(&[2.0; 3]), NeighborMode::All, Some(4.0))
1145    ///     .unwrap();
1146    /// assert_eq!(d.row(0), vec![0.0, 2.0, 4.0, f64::INFINITY]);
1147    /// ```
1148    pub fn distances_dijkstra_cutoff<'a>(
1149        &self,
1150        from: impl Into<VertexSelector<'a>>,
1151        to: impl Into<VertexSelector<'a>>,
1152        weights: Option<&[f64]>,
1153        mode: NeighborMode,
1154        cutoff: Option<f64>,
1155    ) -> Result<Matrix> {
1156        let cutoff = cutoff_value(cutoff)?;
1157        self.distance_matrix(from, to, weights, |res, from, to, w| unsafe {
1158            igraph_distances_dijkstra_cutoff(self, res, from, to, w, mode.into(), cutoff)
1159        })
1160    }
1161
1162    /// Weighted shortest path lengths with the Bellman–Ford algorithm, which
1163    /// allows negative weights (but no negative cycles).
1164    ///
1165    /// If there are no negative weights,
1166    /// [`distances_dijkstra`](Self::distances_dijkstra) is faster.
1167    ///
1168    /// Binds [`igraph_distances_bellman_ford`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_distances_bellman_ford).
1169    /// Time complexity: O(s |E| |V|) for s sources.
1170    ///
1171    /// # Errors
1172    /// [`ErrorKind::NegativeCycle`] when a
1173    /// negative cycle is reachable.
1174    ///
1175    /// # Examples
1176    ///
1177    /// ```
1178    /// use igraph::prelude::*;
1179    /// let g = Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], 3, true).unwrap();
1180    /// let d = g.distances_bellman_ford(0, .., Some(&[2.0, -3.0, 1.0]), NeighborMode::Out).unwrap();
1181    /// assert_eq!(d.row(0), vec![0.0, 2.0, -1.0]);
1182    /// ```
1183    pub fn distances_bellman_ford<'a>(
1184        &self,
1185        from: impl Into<VertexSelector<'a>>,
1186        to: impl Into<VertexSelector<'a>>,
1187        weights: Option<&[f64]>,
1188        mode: NeighborMode,
1189    ) -> Result<Matrix> {
1190        self.distance_matrix(from, to, weights, |res, from, to, w| unsafe {
1191            igraph_distances_bellman_ford(self, res, from, to, w, mode.into())
1192        })
1193    }
1194
1195    /// Weighted shortest path lengths with Johnson's algorithm: negative
1196    /// weights are allowed (directed graphs only, no negative cycles).
1197    ///
1198    /// A single Bellman–Ford run reweights the edges to non-negative values,
1199    /// then Dijkstra is run from each source; this beats Bellman–Ford when
1200    /// there are many sources. Without negative weights Dijkstra is used
1201    /// directly; with `None` the unweighted algorithm is used. Undirected
1202    /// graphs with any negative weight are rejected, even when no negative
1203    /// edge is reachable from the sources, and so is `NeighborMode::All`
1204    /// combined with negative weights: igraph treats an undirected negative
1205    /// edge as a negative cycle.
1206    ///
1207    /// Binds [`igraph_distances_johnson`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_distances_johnson).
1208    /// Time complexity: O(s |V| log |V| + |V| |E|).
1209    ///
1210    /// # Errors
1211    /// [`ErrorKind::NegativeCycle`] when a negative cycle is reachable, and
1212    /// also for negative weights in an undirected graph or together with
1213    /// `NeighborMode::All`; [`ErrorKind::InvalidValue`] for NaN weights or a
1214    /// weight vector of the wrong length.
1215    ///
1216    /// # Examples
1217    ///
1218    /// ```
1219    /// use igraph::prelude::*;
1220    /// // 0 → 1 → 2 with a negative "discount" edge 1 → 2.
1221    /// let g = Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], 3, true).unwrap();
1222    /// let w = [2.0, -3.0, 1.0];
1223    /// let d = g.distances_johnson(.., .., Some(&w), NeighborMode::Out).unwrap();
1224    /// assert_eq!(d.row(0), vec![0.0, 2.0, -1.0]);
1225    /// // Following the edges backwards gives the transposed matrix.
1226    /// let d_in = g.distances_johnson(.., .., Some(&w), NeighborMode::In).unwrap();
1227    /// assert_eq!(d_in, d.transposed());
1228    /// ```
1229    pub fn distances_johnson<'a>(
1230        &self,
1231        from: impl Into<VertexSelector<'a>>,
1232        to: impl Into<VertexSelector<'a>>,
1233        weights: Option<&[f64]>,
1234        mode: NeighborMode,
1235    ) -> Result<Matrix> {
1236        self.distance_matrix(from, to, weights, |res, from, to, w| unsafe {
1237            igraph_distances_johnson(self, res, from, to, w, mode.into())
1238        })
1239    }
1240
1241    /// All-pairs weighted shortest path lengths with the Floyd–Warshall
1242    /// algorithm (or one of its faster variants, see
1243    /// [`FloydWarshallAlgorithm`]).
1244    ///
1245    /// Negative weights are allowed but negative cycles are not. The full
1246    /// all-pairs matrix is always computed internally, `from` and `to` only
1247    /// subset it. Useful for very dense graphs.
1248    ///
1249    /// Binds [`igraph_distances_floyd_warshall`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_distances_floyd_warshall).
1250    /// Time complexity: O(|V|³ + |E|) for the original variant, expected
1251    /// O(|V|² log² |V|) for the tree variant.
1252    ///
1253    /// # Examples
1254    ///
1255    /// ```
1256    /// use igraph::{paths::FloydWarshallAlgorithm, prelude::*};
1257    /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 3, true).unwrap();
1258    /// let d = g
1259    ///     .distances_floyd_warshall(.., .., None, NeighborMode::Out, FloydWarshallAlgorithm::Original)
1260    ///     .unwrap();
1261    /// assert_eq!(d.row(0), vec![0.0, 1.0, 2.0]);
1262    /// ```
1263    pub fn distances_floyd_warshall<'a>(
1264        &self,
1265        from: impl Into<VertexSelector<'a>>,
1266        to: impl Into<VertexSelector<'a>>,
1267        weights: Option<&[f64]>,
1268        mode: NeighborMode,
1269        method: FloydWarshallAlgorithm,
1270    ) -> Result<Matrix> {
1271        self.distance_matrix(from, to, weights, |res, from, to, w| unsafe {
1272            igraph_distances_floyd_warshall(self, res, from, to, w, mode.into(), method.into())
1273        })
1274    }
1275}
1276
1277// ---------------------------------------------------------------------------
1278// Shortest paths themselves.
1279
1280impl igraph_t {
1281    /// One shortest path from `from` to each of the `to` vertices.
1282    ///
1283    /// When several geodesics exist only one is returned (see
1284    /// [`get_all_shortest_paths`](Self::get_all_shortest_paths)). `to` may
1285    /// contain duplicates. With `weights` the weighted algorithms are used
1286    /// (Dijkstra, or Bellman–Ford for negative weights). The result also
1287    /// contains the shortest path tree (`parents` / `inbound_edges`).
1288    ///
1289    /// Binds [`igraph_get_shortest_paths`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_get_shortest_paths).
1290    /// Time complexity: O(|V| + |E|) unweighted.
1291    ///
1292    /// # Examples
1293    ///
1294    /// ```
1295    /// use igraph::prelude::*;
1296    /// let p = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
1297    /// let sp = p.get_shortest_paths(0, vec![3, 1], None, NeighborMode::All).unwrap();
1298    /// assert_eq!(sp.vertices, vec![vec![0, 1, 2, 3], vec![0, 1]]);
1299    /// assert_eq!(sp.edges, vec![vec![0, 1, 2], vec![0]]);
1300    /// assert_eq!(sp.parents, vec![-1, 0, 1, 2]);
1301    /// ```
1302    pub fn get_shortest_paths<'a>(
1303        &self,
1304        from: VertexId,
1305        to: impl Into<VertexSelector<'a>>,
1306        weights: Option<&[f64]>,
1307        mode: NeighborMode,
1308    ) -> Result<ShortestPaths> {
1309        self.shortest_paths_with(to, weights, |v, e, to, w, p, i| unsafe {
1310            igraph_get_shortest_paths(self, w, v, e, from, to, mode.into(), p, i)
1311        })
1312    }
1313
1314    /// Weighted shortest paths from one vertex with Dijkstra's algorithm
1315    /// (non-negative weights); `None` falls back to BFS.
1316    ///
1317    /// The result has the same shape as for
1318    /// [`get_shortest_paths`](Self::get_shortest_paths); the search stops as
1319    /// soon as all the targets are reached, so `parents` may contain `-2`
1320    /// for vertices that were never reached.
1321    ///
1322    /// Binds [`igraph_get_shortest_paths_dijkstra`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_get_shortest_paths_dijkstra).
1323    /// Time complexity: O(|E| log |V| + |V|).
1324    ///
1325    /// # Errors
1326    /// [`ErrorKind::InvalidValue`] for negative or NaN weights,
1327    /// [`ErrorKind::InvalidVertexId`] for invalid vertices.
1328    ///
1329    /// # Examples
1330    ///
1331    /// ```
1332    /// use igraph::prelude::*;
1333    /// // A square 0-1-2-3-0 whose edge (3, 0) is slow.
1334    /// let c4 = Graph::cycle_graph(4, false, false).unwrap();
1335    /// let w = [1.0, 1.0, 1.0, 5.0];
1336    /// let sp = c4.get_shortest_paths_dijkstra(0, .., Some(&w), NeighborMode::All).unwrap();
1337    /// assert_eq!(sp.vertices[3], vec![0, 1, 2, 3]);
1338    /// assert_eq!(sp.parents, vec![-1, 0, 1, 2]);
1339    /// assert_eq!(sp.inbound_edges, vec![-1, 0, 1, 2]);
1340    /// ```
1341    pub fn get_shortest_paths_dijkstra<'a>(
1342        &self,
1343        from: VertexId,
1344        to: impl Into<VertexSelector<'a>>,
1345        weights: Option<&[f64]>,
1346        mode: NeighborMode,
1347    ) -> Result<ShortestPaths> {
1348        self.shortest_paths_with(to, weights, |v, e, to, w, p, i| unsafe {
1349            igraph_get_shortest_paths_dijkstra(self, v, e, from, to, w, mode.into(), p, i)
1350        })
1351    }
1352
1353    /// Weighted shortest paths from one vertex with the Bellman–Ford
1354    /// algorithm, allowing negative weights (but no negative cycles).
1355    ///
1356    /// Binds [`igraph_get_shortest_paths_bellman_ford`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_get_shortest_paths_bellman_ford).
1357    /// Time complexity: O(|E| |V|).
1358    ///
1359    /// # Errors
1360    /// [`ErrorKind::NegativeCycle`] when a
1361    /// negative cycle is found.
1362    ///
1363    /// # Examples
1364    ///
1365    /// ```
1366    /// use igraph::prelude::*;
1367    /// // The negative edge 1 → 2 makes the detour through 1 the shortest route to 2.
1368    /// let g = Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], 3, true).unwrap();
1369    /// let sp = g
1370    ///     .get_shortest_paths_bellman_ford(0, .., Some(&[2.0, -3.0, 1.0]), NeighborMode::Out)
1371    ///     .unwrap();
1372    /// assert_eq!(sp.vertices, vec![vec![0], vec![0, 1], vec![0, 1, 2]]);
1373    /// ```
1374    pub fn get_shortest_paths_bellman_ford<'a>(
1375        &self,
1376        from: VertexId,
1377        to: impl Into<VertexSelector<'a>>,
1378        weights: Option<&[f64]>,
1379        mode: NeighborMode,
1380    ) -> Result<ShortestPaths> {
1381        self.shortest_paths_with(to, weights, |v, e, to, w, p, i| unsafe {
1382            igraph_get_shortest_paths_bellman_ford(self, v, e, from, to, w, mode.into(), p, i)
1383        })
1384    }
1385
1386    /// A single shortest path between two vertices (an arbitrary one if there
1387    /// are several). An empty [`GraphPath`] means `to` is unreachable (the
1388    /// BFS and Dijkstra searches then also emit an igraph warning).
1389    ///
1390    /// Binds [`igraph_get_shortest_path`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_get_shortest_path).
1391    /// Time complexity: O(|V| + |E|) unweighted.
1392    ///
1393    /// # Examples
1394    ///
1395    /// ```
1396    /// use igraph::prelude::*;
1397    /// let c = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)], 5, false).unwrap();
1398    /// let p = c.get_shortest_path(0, 3, None, NeighborMode::All).unwrap();
1399    /// assert_eq!(p.vertices, vec![0, 4, 3]);
1400    /// assert_eq!(p.len(), 2);
1401    /// ```
1402    pub fn get_shortest_path(
1403        &self,
1404        from: VertexId,
1405        to: VertexId,
1406        weights: Option<&[f64]>,
1407        mode: NeighborMode,
1408    ) -> Result<GraphPath> {
1409        self.single_path_with(weights, |v, e, w| unsafe {
1410            igraph_get_shortest_path(self, w, v, e, from, to, mode.into())
1411        })
1412    }
1413
1414    /// A single weighted shortest path with Dijkstra's algorithm
1415    /// (non-negative weights; `None` falls back to BFS). An empty
1416    /// [`GraphPath`] means that `to` is unreachable.
1417    ///
1418    /// Binds [`igraph_get_shortest_path_dijkstra`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_get_shortest_path_dijkstra).
1419    /// Time complexity: O(|E| log |V| + |V|).
1420    ///
1421    /// # Examples
1422    ///
1423    /// ```
1424    /// use igraph::prelude::*;
1425    /// let c4 = Graph::cycle_graph(4, false, false).unwrap();
1426    /// let w = [1.0, 1.0, 1.0, 5.0];
1427    /// let p = c4.get_shortest_path_dijkstra(0, 3, Some(&w), NeighborMode::All).unwrap();
1428    /// assert_eq!(p.vertices, vec![0, 1, 2, 3]);
1429    /// assert_eq!(p.weight(&w), 3.0);
1430    /// ```
1431    pub fn get_shortest_path_dijkstra(
1432        &self,
1433        from: VertexId,
1434        to: VertexId,
1435        weights: Option<&[f64]>,
1436        mode: NeighborMode,
1437    ) -> Result<GraphPath> {
1438        self.single_path_with(weights, |v, e, w| unsafe {
1439            igraph_get_shortest_path_dijkstra(self, v, e, from, to, w, mode.into())
1440        })
1441    }
1442
1443    /// A single weighted shortest path with the Bellman–Ford algorithm
1444    /// (negative weights allowed, negative cycles are not).
1445    ///
1446    /// Binds [`igraph_get_shortest_path_bellman_ford`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_get_shortest_path_bellman_ford).
1447    /// Time complexity: O(|E| |V|).
1448    ///
1449    /// # Errors
1450    /// [`ErrorKind::NegativeCycle`] when a negative cycle is found.
1451    ///
1452    /// # Examples
1453    ///
1454    /// ```
1455    /// use igraph::prelude::*;
1456    /// let g = Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], 3, true).unwrap();
1457    /// let w = [2.0, -3.0, 1.0];
1458    /// let p = g.get_shortest_path_bellman_ford(0, 2, Some(&w), NeighborMode::Out).unwrap();
1459    /// assert_eq!(p.edges, vec![0, 1]);
1460    /// assert_eq!(p.weight(&w), -1.0);
1461    /// // Closing a negative cycle makes shortest paths meaningless.
1462    /// let mut cyclic = g.clone();
1463    /// cyclic.add_edge(2, 0).unwrap();
1464    /// let err = cyclic
1465    ///     .get_shortest_path_bellman_ford(0, 2, Some(&[2.0, -3.0, 1.0, 0.5]), NeighborMode::Out)
1466    ///     .unwrap_err();
1467    /// assert_eq!(err.kind(), ErrorKind::NegativeCycle);
1468    /// ```
1469    pub fn get_shortest_path_bellman_ford(
1470        &self,
1471        from: VertexId,
1472        to: VertexId,
1473        weights: Option<&[f64]>,
1474        mode: NeighborMode,
1475    ) -> Result<GraphPath> {
1476        self.single_path_with(weights, |v, e, w| unsafe {
1477            igraph_get_shortest_path_bellman_ford(self, v, e, from, to, w, mode.into())
1478        })
1479    }
1480
1481    /// A single shortest path with the A* algorithm, guided by a heuristic.
1482    ///
1483    /// `heuristic(v, to)` must estimate the distance from the candidate
1484    /// vertex `v` to the target `to`; smaller values make `v` a better
1485    /// candidate. The result is a true shortest path if the heuristic is
1486    /// *admissible*, i.e. never overestimates the distance. A heuristic
1487    /// returning always `0.0` turns A* into Dijkstra's algorithm. Weights must
1488    /// be non-negative. The heuristic should return finite, non-negative
1489    /// values (NaN estimates break the priority queue ordering and give
1490    /// meaningless paths). A panic in the heuristic aborts the search and is
1491    /// propagated to the caller. An empty [`GraphPath`] means that `to` is
1492    /// unreachable.
1493    ///
1494    /// # Errors
1495    /// [`ErrorKind::InvalidVertexId`] for
1496    /// invalid `from`/`to`,
1497    /// [`ErrorKind::InvalidValue`] for
1498    /// negative or NaN weights.
1499    ///
1500    /// Binds [`igraph_get_shortest_path_astar`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_get_shortest_path_astar).
1501    /// Time complexity: worst case O(|E| log |V| + |V|); better heuristics
1502    /// mean faster searches.
1503    ///
1504    /// # Examples
1505    ///
1506    /// ```
1507    /// use igraph::prelude::*;
1508    /// // A 10 x 10 grid; vertex id = x + 10 y. Manhattan distance is admissible.
1509    /// let n = 10;
1510    /// let grid = Graph::square_lattice(&[10, 10], 1, false, false, None).unwrap();
1511    /// let manhattan = |a: i64, b: i64| ((a % n - b % n).abs() + (a / n - b / n).abs()) as f64;
1512    /// let p = grid.get_shortest_path_astar(0, 99, None, NeighborMode::All, manhattan).unwrap();
1513    /// assert_eq!(p.len(), 18);
1514    /// ```
1515    pub fn get_shortest_path_astar<F>(
1516        &self,
1517        from: VertexId,
1518        to: VertexId,
1519        weights: Option<&[f64]>,
1520        mode: NeighborMode,
1521        mut heuristic: F,
1522    ) -> Result<GraphPath>
1523    where
1524        F: FnMut(VertexId, VertexId) -> f64,
1525    {
1526        unsafe extern "C" fn trampoline<F: FnMut(VertexId, VertexId) -> f64>(
1527            result: *mut igraph_real_t,
1528            from: igraph_int_t,
1529            to: igraph_int_t,
1530            extra: *mut c_void,
1531        ) -> igraph_error_t {
1532            // The heuristic runs in a fresh level of igraph's "finally"
1533            // stack: otherwise a failing igraph call made by the closure
1534            // would free the temporaries of the running A* search (a
1535            // use-after-free in C).
1536            // SAFETY: bookkeeping on igraph's thread-local finally stack; the
1537            // matching EXIT runs below, as `catch_panic` never unwinds.
1538            unsafe { IGRAPH_FINALLY_ENTER() };
1539            let code = catch_panic(|| {
1540                let f = unsafe { &mut *(extra as *mut F) };
1541                let value = f(from, to);
1542                unsafe { *result = value };
1543                igraph_error_type_t_IGRAPH_SUCCESS
1544            });
1545            // SAFETY: closes the level opened above; any failed nested call
1546            // has already freed its own objects of that level.
1547            unsafe { IGRAPH_FINALLY_EXIT() };
1548            code
1549        }
1550        let extra = &mut heuristic as *mut F as *mut c_void;
1551        self.single_path_with(weights, |v, e, w| unsafe {
1552            igraph_get_shortest_path_astar(
1553                self,
1554                v,
1555                e,
1556                from,
1557                to,
1558                w,
1559                mode.into(),
1560                Some(trampoline::<F>),
1561                extra,
1562            )
1563        })
1564    }
1565
1566    /// *All* the shortest paths (geodesics) from `from` to the `to` vertices.
1567    ///
1568    /// Paths are grouped by target in increasing vertex id order; unreachable
1569    /// targets contribute nothing. Multi-edges are considered separately, so
1570    /// multigraphs may yield very many paths. `nrgeo[v]` counts the
1571    /// geodesics from `from` to `v`. With `weights`, Dijkstra's algorithm is
1572    /// used (see [`get_all_shortest_paths_dijkstra`](Self::get_all_shortest_paths_dijkstra)).
1573    /// Counting geodesics through each vertex is what
1574    /// [`betweenness`](Self::betweenness) does, for all sources at once.
1575    ///
1576    /// Binds [`igraph_get_all_shortest_paths`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_get_all_shortest_paths).
1577    /// Time complexity: O(|V| + |E|) for most graphs, O(|V|²) worst case.
1578    ///
1579    /// # Examples
1580    ///
1581    /// ```
1582    /// use igraph::prelude::*;
1583    /// // A 4-cycle has two geodesics between opposite corners.
1584    /// let c4 = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], 4, false).unwrap();
1585    /// let all = c4.get_all_shortest_paths(0, 2, None, NeighborMode::All).unwrap();
1586    /// let mut paths = all.vertices.clone();
1587    /// paths.sort();
1588    /// assert_eq!(paths, vec![vec![0, 1, 2], vec![0, 3, 2]]);
1589    /// assert_eq!(all.nrgeo[2], 2);
1590    /// ```
1591    pub fn get_all_shortest_paths<'a>(
1592        &self,
1593        from: VertexId,
1594        to: impl Into<VertexSelector<'a>>,
1595        weights: Option<&[f64]>,
1596        mode: NeighborMode,
1597    ) -> Result<AllShortestPaths> {
1598        self.all_shortest_paths_with(to, weights, |v, e, n, to, w| unsafe {
1599            igraph_get_all_shortest_paths(self, w, v, e, n, from, to, mode.into())
1600        })
1601    }
1602
1603    /// All the weighted shortest paths from one vertex, with Dijkstra's
1604    /// algorithm (non-negative weights; `None` falls back to the unweighted
1605    /// [`get_all_shortest_paths`](Self::get_all_shortest_paths)).
1606    ///
1607    /// Binds [`igraph_get_all_shortest_paths_dijkstra`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_get_all_shortest_paths_dijkstra).
1608    /// Time complexity: O(|E| log |V| + |V|).
1609    ///
1610    /// # Examples
1611    ///
1612    /// ```
1613    /// use igraph::prelude::*;
1614    /// let c4 = Graph::cycle_graph(4, false, false).unwrap();
1615    /// // Equal weights: two geodesics between opposite corners ...
1616    /// let all = c4.get_all_shortest_paths_dijkstra(0, 2, Some(&[1.0; 4]), NeighborMode::All).unwrap();
1617    /// assert_eq!(all.nrgeo[2], 2);
1618    /// // ... a slow edge (3, 0) leaves only one.
1619    /// let w = [1.0, 1.0, 1.0, 5.0];
1620    /// let all = c4.get_all_shortest_paths_dijkstra(0, 2, Some(&w), NeighborMode::All).unwrap();
1621    /// assert_eq!(all.vertices, vec![vec![0, 1, 2]]);
1622    /// assert_eq!(all.edges, vec![vec![0, 1]]);
1623    /// ```
1624    pub fn get_all_shortest_paths_dijkstra<'a>(
1625        &self,
1626        from: VertexId,
1627        to: impl Into<VertexSelector<'a>>,
1628        weights: Option<&[f64]>,
1629        mode: NeighborMode,
1630    ) -> Result<AllShortestPaths> {
1631        self.all_shortest_paths_with(to, weights, |v, e, n, to, w| unsafe {
1632            igraph_get_all_shortest_paths_dijkstra(self, v, e, n, from, to, w, mode.into())
1633        })
1634    }
1635
1636    /// The `k` shortest paths between two vertices, in order of increasing
1637    /// length (Yen's algorithm). Fewer than `k` paths are returned when fewer
1638    /// exist. Infinite weights are treated as missing edges.
1639    ///
1640    /// Binds [`igraph_get_k_shortest_paths`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_get_k_shortest_paths).
1641    /// Time complexity: O(k |V| (|V| log |V| + |E|)).
1642    ///
1643    /// # Examples
1644    ///
1645    /// ```
1646    /// use igraph::prelude::*;
1647    /// // In a 5-cycle there are exactly two paths between any two vertices.
1648    /// let c5 = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)], 5, false).unwrap();
1649    /// let paths = c5.get_k_shortest_paths(0, 2, 10, None, NeighborMode::All).unwrap();
1650    /// assert_eq!(paths.len(), 2);
1651    /// assert_eq!(paths[0].vertices, vec![0, 1, 2]);
1652    /// assert_eq!(paths[1].vertices, vec![0, 4, 3, 2]);
1653    /// ```
1654    pub fn get_k_shortest_paths(
1655        &self,
1656        from: VertexId,
1657        to: VertexId,
1658        k: usize,
1659        weights: Option<&[f64]>,
1660        mode: NeighborMode,
1661    ) -> Result<Vec<GraphPath>> {
1662        self.paths_check_weights(weights)?;
1663        let w = weights_view(weights);
1664        let mut vpaths = VectorIntList::new();
1665        let mut epaths = VectorIntList::new();
1666        igraph_call!(igraph_get_k_shortest_paths(
1667            self,
1668            weights_ptr(&w),
1669            &mut vpaths,
1670            &mut epaths,
1671            saturating_int(k),
1672            from,
1673            to,
1674            mode.into()
1675        ))?;
1676        Ok(vpaths
1677            .to_vecs()
1678            .into_iter()
1679            .zip(epaths.to_vecs())
1680            .map(|(vertices, edges)| GraphPath { vertices, edges })
1681            .collect())
1682    }
1683
1684    /// All the *simple* paths (no repeated vertex) starting at `from` and
1685    /// ending at one of the `to` vertices, as vertex lists.
1686    ///
1687    /// Multi-edges are ignored. There may be exponentially many simple paths:
1688    /// use `options` to bound their length or number. Paths are listed in
1689    /// the order they are found.
1690    ///
1691    /// Binds [`igraph_get_all_simple_paths`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_get_all_simple_paths).
1692    /// Time complexity: O(n!) in the worst case.
1693    ///
1694    /// # Examples
1695    ///
1696    /// ```
1697    /// use igraph::{paths::SimplePathsOptions, prelude::*};
1698    /// let k4 = Graph::from_edges(&[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)], 4, false).unwrap();
1699    /// // Paths from 0 to 3: the edge, two of length 2, two of length 3.
1700    /// let all = k4.get_all_simple_paths(0, 3, NeighborMode::All, SimplePathsOptions::default()).unwrap();
1701    /// assert_eq!(all.len(), 5);
1702    /// let short = SimplePathsOptions::default().with_max_len(2);
1703    /// assert_eq!(k4.get_all_simple_paths(0, 3, NeighborMode::All, short).unwrap().len(), 3);
1704    /// ```
1705    pub fn get_all_simple_paths<'a>(
1706        &self,
1707        from: VertexId,
1708        to: impl Into<VertexSelector<'a>>,
1709        mode: NeighborMode,
1710        options: SimplePathsOptions,
1711    ) -> Result<Vec<Vec<VertexId>>> {
1712        let to = to.into().to_raw()?;
1713        let mut res = VectorIntList::new();
1714        self.with_fresh_multi_cache(|| {
1715            igraph_call!(igraph_get_all_simple_paths(
1716                self,
1717                &mut res,
1718                from,
1719                to.get(),
1720                mode.into(),
1721                limit(options.min_len),
1722                limit(options.max_len),
1723                limit(options.max_results)
1724            ))
1725        })?;
1726        Ok(res.to_vecs())
1727    }
1728}
1729
1730// ---------------------------------------------------------------------------
1731// Widest paths.
1732
1733impl igraph_t {
1734    /// Widest (maximum bottleneck) paths from one vertex to the `to` vertices.
1735    ///
1736    /// The width of a path is the smallest weight among its edges, and a
1737    /// widest path maximizes it. `weights` are *widths* and are mandatory;
1738    /// they may be negative but not NaN, edges of width `-INFINITY` are
1739    /// ignored. The result has the same shape as for
1740    /// [`get_shortest_paths`](Self::get_shortest_paths). A widest path
1741    /// tells how much can be pushed along a *single* route; see
1742    /// [`maxflow`](Self::maxflow) for the total capacity over all routes.
1743    ///
1744    /// Binds [`igraph_get_widest_paths`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_get_widest_paths).
1745    /// Time complexity: O(|E| log |E| + |V|).
1746    ///
1747    /// # Examples
1748    ///
1749    /// ```
1750    /// use igraph::prelude::*;
1751    /// // Two routes from 0 to 3: a thin direct pipe and a wide detour.
1752    /// let g = Graph::from_edges(&[(0, 3), (0, 1), (1, 2), (2, 3)], 4, false).unwrap();
1753    /// let widths = [1.0, 10.0, 8.0, 9.0];
1754    /// let wp = g.get_widest_paths(0, .., &widths, NeighborMode::All).unwrap();
1755    /// assert_eq!(wp.vertices, vec![vec![0], vec![0, 1], vec![0, 1, 2], vec![0, 1, 2, 3]]);
1756    /// assert_eq!(wp.parents, vec![-1, 0, 1, 2]);
1757    /// ```
1758    pub fn get_widest_paths<'a>(
1759        &self,
1760        from: VertexId,
1761        to: impl Into<VertexSelector<'a>>,
1762        weights: &[f64],
1763        mode: NeighborMode,
1764    ) -> Result<ShortestPaths> {
1765        self.shortest_paths_with(to, Some(weights), |v, e, to, w, p, i| unsafe {
1766            igraph_get_widest_paths(self, v, e, from, to, w, mode.into(), p, i)
1767        })
1768    }
1769
1770    /// A single widest (maximum bottleneck) path between two vertices, see
1771    /// [`get_widest_paths`](Self::get_widest_paths).
1772    ///
1773    /// Binds [`igraph_get_widest_path`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_get_widest_path).
1774    /// Time complexity: O(|E| log |E| + |V|).
1775    ///
1776    /// # Examples
1777    ///
1778    /// ```
1779    /// use igraph::prelude::*;
1780    /// // Two routes from 0 to 3: a thin direct pipe and a wide detour.
1781    /// let g = Graph::from_edges(&[(0, 3), (0, 1), (1, 2), (2, 3)], 4, false).unwrap();
1782    /// let widths = [1.0, 10.0, 8.0, 9.0];
1783    /// let p = g.get_widest_path(0, 3, &widths, NeighborMode::All).unwrap();
1784    /// assert_eq!(p.vertices, vec![0, 1, 2, 3]);
1785    /// ```
1786    pub fn get_widest_path(
1787        &self,
1788        from: VertexId,
1789        to: VertexId,
1790        weights: &[f64],
1791        mode: NeighborMode,
1792    ) -> Result<GraphPath> {
1793        self.single_path_with(Some(weights), |v, e, w| unsafe {
1794            igraph_get_widest_path(self, v, e, from, to, w, mode.into())
1795        })
1796    }
1797
1798    /// Widths of the widest paths between vertices, with a modified
1799    /// Floyd–Warshall algorithm (good for dense graphs).
1800    ///
1801    /// Unreachable pairs have width `-INFINITY`, a vertex has width
1802    /// `INFINITY` to itself. The full matrix is always computed internally.
1803    /// The result equals that of
1804    /// [`widest_path_widths_dijkstra`](Self::widest_path_widths_dijkstra).
1805    ///
1806    /// Binds [`igraph_widest_path_widths_floyd_warshall`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_widest_path_widths_floyd_warshall).
1807    /// Time complexity: O(|V|³).
1808    ///
1809    /// # Examples
1810    ///
1811    /// ```
1812    /// use igraph::prelude::*;
1813    /// // Directed: 0 → 1 → 2 is wide, the direct edge 0 → 2 is thin, 2 reaches nothing.
1814    /// let g = Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], 3, true).unwrap();
1815    /// let w = g
1816    ///     .widest_path_widths_floyd_warshall(.., .., &[5.0, 4.0, 1.0], NeighborMode::Out)
1817    ///     .unwrap();
1818    /// let inf = f64::INFINITY;
1819    /// assert_eq!(w.to_rows(), vec![vec![inf, 5.0, 4.0], vec![-inf, inf, 4.0], vec![-inf, -inf, inf]]);
1820    /// ```
1821    pub fn widest_path_widths_floyd_warshall<'a>(
1822        &self,
1823        from: impl Into<VertexSelector<'a>>,
1824        to: impl Into<VertexSelector<'a>>,
1825        weights: &[f64],
1826        mode: NeighborMode,
1827    ) -> Result<Matrix> {
1828        self.distance_matrix(from, to, Some(weights), |res, from, to, w| unsafe {
1829            igraph_widest_path_widths_floyd_warshall(self, res, from, to, w, mode.into())
1830        })
1831    }
1832
1833    /// Widths of the widest paths between vertices, with a modified Dijkstra
1834    /// algorithm run from each source (good for sparse graphs). `to` must not
1835    /// contain duplicates.
1836    ///
1837    /// Unreachable pairs have width `-INFINITY`, a vertex has width
1838    /// `INFINITY` to itself.
1839    ///
1840    /// Binds [`igraph_widest_path_widths_dijkstra`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_widest_path_widths_dijkstra).
1841    /// Time complexity: O(s (|E| log |E| + |V|)) for s sources.
1842    ///
1843    /// # Examples
1844    ///
1845    /// ```
1846    /// use igraph::prelude::*;
1847    /// let g = Graph::from_edges(&[(0, 3), (0, 1), (1, 2), (2, 3)], 4, false).unwrap();
1848    /// let w = g.widest_path_widths_dijkstra(0, .., &[1.0, 10.0, 8.0, 9.0], NeighborMode::All).unwrap();
1849    /// assert_eq!(w.row(0), vec![f64::INFINITY, 10.0, 8.0, 8.0]);
1850    /// ```
1851    pub fn widest_path_widths_dijkstra<'a>(
1852        &self,
1853        from: impl Into<VertexSelector<'a>>,
1854        to: impl Into<VertexSelector<'a>>,
1855        weights: &[f64],
1856        mode: NeighborMode,
1857    ) -> Result<Matrix> {
1858        self.distance_matrix(from, to, Some(weights), |res, from, to, w| unsafe {
1859            igraph_widest_path_widths_dijkstra(self, res, from, to, w, mode.into())
1860        })
1861    }
1862}
1863
1864// ---------------------------------------------------------------------------
1865// Random walks, spanners, Voronoi partitions and path conversions.
1866
1867impl igraph_t {
1868    /// A random walk of (at most) `steps` steps starting at `start`.
1869    ///
1870    /// The walk follows edges according to `mode` (in directed graphs);
1871    /// multi-edges and loops are respected. With `weights` the next edge is
1872    /// chosen with probability proportional to its weight (non-negative, at
1873    /// least one positive weight among the out-edges of each vertex). When
1874    /// the walk reaches a vertex without outgoing edges, `stuck` decides
1875    /// whether to return the shorter walk ([`RandomWalkStuck::Return`]) or to
1876    /// fail ([`RandomWalkStuck::Error`]). The returned path has `steps + 1`
1877    /// vertices and `steps` edges unless it got stuck. Draws from the calling
1878    /// thread's default random number generator: seed it with
1879    /// [`rng::seed`](crate::rng::seed) for reproducible walks, or run the
1880    /// walk with a private generator through
1881    /// [`Rng::scoped`](crate::rng::Rng::scoped).
1882    ///
1883    /// Binds [`igraph_random_walk`](https://igraph.org/c/html/latest/igraph-Visitors.html#igraph_random_walk).
1884    /// Time complexity: O(l + d) unweighted, O(l log k + d) weighted, where l
1885    /// is the walk length, d the total degree of the visited vertices and k
1886    /// the average degree.
1887    ///
1888    /// # Errors
1889    /// - [`ErrorKind::RandomWalkStuck`] if
1890    ///   the walk gets stuck and `stuck` is [`RandomWalkStuck::Error`] (the
1891    ///   partial walk is discarded; use [`RandomWalkStuck::Return`] to keep it).
1892    /// - [`ErrorKind::InvalidValue`] for an
1893    ///   invalid `start` vertex (igraph 1.0.0 and 1.0.1 report
1894    ///   `IGRAPH_EINVAL` here, not `IGRAPH_EINVVID`), invalid weights
1895    ///   (negative, NaN or wrong length), or `steps >= i64::MAX`. The Rust side
1896    ///   also rejects infinite weights, and weights whose total over the edges
1897    ///   a walk can leave some vertex through exceeds `f64::MAX / 2` (loops
1898    ///   count twice in `All` mode): with such weights igraph 1.0.1 never
1899    ///   terminates. The check covers every vertex, not only the visited ones.
1900    ///
1901    /// # Examples
1902    ///
1903    /// ```
1904    /// use igraph::prelude::*;
1905    /// let c = Graph::cycle_graph(4, false, false).unwrap();
1906    /// // Seeding the (per-thread) default generator makes the walk reproducible.
1907    /// rng::seed(42).unwrap();
1908    /// let walk = c.random_walk(0, 20, None, NeighborMode::All, RandomWalkStuck::Error).unwrap();
1909    /// rng::seed(42).unwrap();
1910    /// let again = c.random_walk(0, 20, None, NeighborMode::All, RandomWalkStuck::Error).unwrap();
1911    /// assert_eq!(walk, again);
1912    /// assert_eq!(walk.vertices.len(), 21);
1913    /// assert_eq!(walk.edges.len(), 20);
1914    /// // Consecutive vertices are adjacent.
1915    /// for pair in walk.vertices.windows(2) {
1916    ///     assert!(c.get_eid(pair[0], pair[1], false).unwrap().is_some());
1917    /// }
1918    /// // A private generator leaves the default one untouched.
1919    /// let mut private = Rng::new(RngType::Pcg64, 7).unwrap();
1920    /// let w = private
1921    ///     .scoped(|| c.random_walk(0, 5, None, NeighborMode::All, RandomWalkStuck::Error))
1922    ///     .unwrap();
1923    /// assert_eq!(w.len(), 5);
1924    /// ```
1925    pub fn random_walk(
1926        &self,
1927        start: VertexId,
1928        steps: usize,
1929        weights: Option<&[f64]>,
1930        mode: NeighborMode,
1931        stuck: RandomWalkStuck,
1932    ) -> Result<GraphPath> {
1933        // igraph allocates `steps + 1` vertices: `steps` must stay below
1934        // `igraph_int_t::MAX` to avoid a signed overflow in C.
1935        let steps = match igraph_int_t::try_from(steps) {
1936            Ok(s) if s < igraph_int_t::MAX => s,
1937            _ => {
1938                return Err(Error::invalid(format!(
1939                    "too many random walk steps: {steps}"
1940                )));
1941            }
1942        };
1943        // igraph 1.0.1 rejects only negative and NaN weights. An infinite
1944        // weight, or finite weights whose sum at a vertex overflows, make the
1945        // cumulative distribution of that vertex infinite, and sampling from
1946        // [0, inf) never terminates in C.
1947        if let Some(ws) = weights {
1948            self.random_walk_check_weights(ws, mode)?;
1949        }
1950        self.single_path_with(weights, |v, e, w| unsafe {
1951            igraph_random_walk(self, w, v, e, start, mode.into(), steps, stuck.into())
1952        })
1953    }
1954
1955    /// Rust-side weight check of [`random_walk`](Self::random_walk): every
1956    /// weight must be finite, and the total weight of the edges a walk can
1957    /// leave each vertex through (loops count twice in `All` mode, as in
1958    /// igraph's incidence lists) must be at most `f64::MAX / 2`. The factor
1959    /// 2 leaves room for the rounding of igraph's own summation order.
1960    /// Weights of the wrong length are left to igraph, which reports them.
1961    fn random_walk_check_weights(&self, ws: &[f64], mode: NeighborMode) -> Result<()> {
1962        if ws.iter().any(|x| !x.is_finite()) {
1963            return Err(Error::invalid("random walk weights must be finite"));
1964        }
1965        if ws.len() != self.ecount() {
1966            return Ok(());
1967        }
1968        let (out, inn) = match mode {
1969            _ if !self.is_directed() => (true, true),
1970            NeighborMode::Out => (true, false),
1971            NeighborMode::In => (false, true),
1972            _ => (true, true),
1973        };
1974        let mut total = vec![0.0_f64; self.vcount()];
1975        for (&(from, to), &w) in self.edges(EdgeSelector::All)?.iter().zip(ws) {
1976            if out {
1977                total[from as usize] += w;
1978            }
1979            if inn {
1980                total[to as usize] += w;
1981            }
1982        }
1983        if total.iter().any(|&t| t > f64::MAX / 2.0) {
1984            return Err(Error::invalid(
1985                "the total random walk weight at some vertex overflows",
1986            ));
1987        }
1988        Ok(())
1989    }
1990
1991    /// The edge ids of a *spanner* of the graph with stretch factor `stretch`.
1992    ///
1993    /// A t-spanner is a subgraph `H` with the same vertices, in which the
1994    /// distance between any two vertices is at most `t` times their distance
1995    /// in the original graph. The randomized Baswana–Sen algorithm is used
1996    /// (drawing from the thread's default random number generator); edge
1997    /// directions are ignored. Use
1998    /// [`subgraph_from_edges`](Self::subgraph_from_edges) (with
1999    /// `delete_vertices = false`) to extract the spanner as a graph. See also
2000    /// [`minimum_spanning_tree`](Self::minimum_spanning_tree), the sparsest
2001    /// connected subgraph, which offers no stretch guarantee.
2002    ///
2003    /// Binds [`igraph_spanner`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_spanner).
2004    /// Expected time complexity: O(k |E|), with k = (t + 1) / 2.
2005    ///
2006    /// # Errors
2007    /// [`ErrorKind::InvalidValue`] if `stretch` is below one or not finite
2008    /// (checked on the Rust side for NaN and infinity: igraph 1.0.1 accepts a
2009    /// NaN stretch and never terminates for an infinite one), or if the
2010    /// weights are negative, NaN or of the wrong length.
2011    ///
2012    /// # Examples
2013    ///
2014    /// ```
2015    /// use igraph::prelude::*;
2016    /// let k8 = Graph::full(8, false, false).unwrap();
2017    /// rng::seed(1).unwrap();
2018    /// let kept = k8.spanner(3.0, None).unwrap();
2019    /// assert!(kept.len() <= k8.ecount());
2020    /// // Distances in the spanner are at most 3 times the original ones (all 1 here).
2021    /// let h = k8.subgraph_from_edges(kept, false).unwrap();
2022    /// assert_eq!(h.vcount(), 8);
2023    /// let d = h.distances(.., .., None, NeighborMode::All).unwrap();
2024    /// assert!(d.as_slice().iter().all(|&x| x <= 3.0));
2025    /// ```
2026    pub fn spanner(&self, stretch: f64, weights: Option<&[f64]>) -> Result<Vec<EdgeId>> {
2027        // igraph runs `(stretch + 1) / 2 - 1` clustering rounds: an infinite
2028        // stretch never terminates and a NaN one is silently accepted.
2029        if !stretch.is_finite() {
2030            return Err(Error::invalid(format!(
2031                "the stretch factor must be finite, got {stretch}"
2032            )));
2033        }
2034        self.paths_check_weights(weights)?;
2035        let w = weights_view(weights);
2036        let mut res = VectorInt::new();
2037        igraph_call!(igraph_spanner(self, &mut res, stretch, weights_ptr(&w)))?;
2038        Ok(res.into())
2039    }
2040
2041    /// Voronoi partitioning: assigns each vertex to its closest generator.
2042    ///
2043    /// Distances are computed from the generators (`mode = Out`), towards
2044    /// them (`In`) or ignoring directions (`All`); BFS is used for unweighted
2045    /// graphs, Dijkstra for (non-negative) weights. `tiebreaker` decides
2046    /// which generator wins at equal distance; note that
2047    /// [`VoronoiTiebreaker::Random`] may produce non-contiguous cells
2048    /// (and draws from the thread's default random number generator). See
2049    /// also [`community_voronoi`](Self::community_voronoi), which picks the
2050    /// generators automatically to detect communities.
2051    ///
2052    /// Binds [`igraph_voronoi`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_voronoi).
2053    /// Time complexity: O(log |S| |E| log |V| + |V|) weighted, O(log |S| |E| +
2054    /// |V|) unweighted, for |S| generators.
2055    ///
2056    /// # Examples
2057    ///
2058    /// ```
2059    /// use igraph::prelude::*;
2060    /// // Path 0-1-2-3-4-5 with generators at both ends.
2061    /// let p = Graph::path_graph(6, false, false).unwrap();
2062    /// let v = p.voronoi(&[0, 5], None, NeighborMode::All, VoronoiTiebreaker::First).unwrap();
2063    /// assert_eq!(v.membership, vec![0, 0, 0, 1, 1, 1]);
2064    /// assert_eq!(v.distances, vec![0.0, 1.0, 2.0, 2.0, 1.0, 0.0]);
2065    /// ```
2066    pub fn voronoi(
2067        &self,
2068        generators: &[VertexId],
2069        weights: Option<&[f64]>,
2070        mode: NeighborMode,
2071        tiebreaker: VoronoiTiebreaker,
2072    ) -> Result<VoronoiPartition> {
2073        self.paths_check_weights(weights)?;
2074        let gens = VectorInt::view(generators);
2075        let w = weights_view(weights);
2076        let mut membership = VectorInt::new();
2077        let mut distances = Vector::new();
2078        igraph_call!(igraph_voronoi(
2079            self,
2080            &mut membership,
2081            &mut distances,
2082            gens.as_ptr(),
2083            weights_ptr(&w),
2084            mode.into(),
2085            tiebreaker.into()
2086        ))?;
2087        Ok(VoronoiPartition {
2088            membership: membership.into(),
2089            distances: distances.into(),
2090        })
2091    }
2092
2093    /// Converts a walk given by its edge ids into the sequence of vertices it
2094    /// visits (one more than the number of edges).
2095    ///
2096    /// `start` is the first vertex; with `None` it is inferred from the walk
2097    /// (which then must have at least one edge). Vertices may repeat, so any
2098    /// walk, cycle or path is accepted, e.g. the edge sequences returned by
2099    /// [`find_cycle`](Self::find_cycle) or [`eulerian_path`](Self::eulerian_path).
2100    /// Edge ids are validated on the Rust side (igraph 1.0.0 and 1.0.1 do not
2101    /// check them).
2102    ///
2103    /// Binds [`igraph_vertex_path_from_edge_path`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_vertex_path_from_edge_path).
2104    /// Time complexity: O(n) for a walk of length n.
2105    ///
2106    /// # Errors
2107    /// [`ErrorKind::InvalidValue`] if the
2108    /// edges do not form a continuous walk from `start`, or if `start` is
2109    /// `None` and the walk is empty;
2110    /// [`ErrorKind::InvalidEdgeId`] for an
2111    /// edge id out of range;
2112    /// [`ErrorKind::InvalidVertexId`] for
2113    /// an invalid `start` vertex.
2114    ///
2115    /// # Examples
2116    ///
2117    /// ```
2118    /// use igraph::prelude::*;
2119    /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, true).unwrap();
2120    /// let v = g.vertex_path_from_edge_path(Some(0), &[0, 1, 2], NeighborMode::Out).unwrap();
2121    /// assert_eq!(v, vec![0, 1, 2, 3]);
2122    /// // Walking backwards along the directed edges.
2123    /// let v = g.vertex_path_from_edge_path(Some(3), &[2, 1], NeighborMode::In).unwrap();
2124    /// assert_eq!(v, vec![3, 2, 1]);
2125    /// ```
2126    pub fn vertex_path_from_edge_path(
2127        &self,
2128        start: Option<VertexId>,
2129        edge_path: &[EdgeId],
2130        mode: NeighborMode,
2131    ) -> Result<Vec<VertexId>> {
2132        // igraph 1.0.0 and 1.0.1 do not validate the edge ids here and would
2133        // read out of bounds: check them on the Rust side.
2134        let ecount = self.ecount() as EdgeId;
2135        if let Some(&e) = edge_path.iter().find(|&&e| !(0..ecount).contains(&e)) {
2136            return Err(Error::new(
2137                ErrorKind::InvalidEdgeId,
2138                format!("invalid edge id {e} in edge path"),
2139            ));
2140        }
2141        let start = opt_vertex_arg(start, self.vcount())?;
2142        let edges = VectorInt::view(edge_path);
2143        let mut res = VectorInt::new();
2144        igraph_call!(igraph_vertex_path_from_edge_path(
2145            self,
2146            start,
2147            edges.as_ptr(),
2148            &mut res,
2149            mode.into()
2150        ))?;
2151        Ok(res.into())
2152    }
2153}
2154
2155/// Turns a path given as a vertex sequence into the list of its consecutive
2156/// vertex pairs, e.g. `[a, b, c]` into `[(a, b), (b, c)]`.
2157///
2158/// The result can be passed to [`Graph::get_eids`] to find the edges along
2159/// the path. Paths with fewer than two vertices give an empty list.
2160///
2161/// Binds [`igraph_expand_path_to_pairs`](https://igraph.org/c/html/latest/igraph-Basic.html#igraph_expand_path_to_pairs).
2162///
2163/// # Examples
2164///
2165/// ```
2166/// use igraph::{paths::expand_path_to_pairs, prelude::*};
2167/// assert_eq!(expand_path_to_pairs(&[0, 4, 2]).unwrap(), vec![(0, 4), (4, 2)]);
2168/// assert!(expand_path_to_pairs(&[7]).unwrap().is_empty());
2169///
2170/// let g = Graph::from_edges(&[(0, 4), (4, 2)], 5, false).unwrap();
2171/// let pairs = expand_path_to_pairs(&[0, 4, 2]).unwrap();
2172/// // Graph::get_eids (core) turns the pairs into edge ids.
2173/// assert_eq!(g.get_eids(&pairs, false).unwrap(), vec![0, 1]);
2174/// ```
2175pub fn expand_path_to_pairs(path: &[VertexId]) -> Result<Vec<(VertexId, VertexId)>> {
2176    let mut v = VectorInt::from_slice(path);
2177    igraph_call!(igraph_expand_path_to_pairs(&mut v))?;
2178    Ok(v.as_chunks::<2>().0.iter().map(|&[a, b]| (a, b)).collect())
2179}