Skip to main content

igraph/
components.rs

1//! Connectivity: connected components, cut vertices and bridges, vertex
2//! separators, cohesive blocks, reachability and neighborhoods.
3//!
4//! This module binds five igraph headers:
5//!
6//! - `igraph_components.h`: (weakly or strongly) connected components,
7//!   decomposition into component graphs, articulation points, bridges,
8//!   biconnected components and percolation curves;
9//! - `igraph_separators.h`: vertex separators (sets of vertices whose removal
10//!   disconnects the graph);
11//! - `igraph_cohesive_blocks.h`: the Moody–White hierarchy of cohesive blocks;
12//! - `igraph_reachability.h`: which vertices can reach which others, and the
13//!   transitive closure;
14//! - `igraph_neighborhood.h`: the vertices within a given distance of some
15//!   vertices, as sets, counts or induced subgraphs.
16//!
17//! All the graph algorithms are methods of [`Graph`]; the only free function
18//! is [`edgelist_percolation`]. The randomized percolation curves draw from
19//! the calling thread's default random number generator, so they are
20//! reproducible after [`rng::seed`](crate::rng::seed).
21//!
22//! # Example
23//!
24//! ```
25//! use igraph::prelude::*;
26//!
27//! // Two triangles joined by the edge 2-3, plus an isolated vertex 6.
28//! let g = Graph::from_edges(
29//!     &[(0, 1), (1, 2), (2, 0), (2, 3), (3, 4), (4, 5), (5, 3)],
30//!     7,
31//!     false,
32//! )
33//! .unwrap();
34//!
35//! let cc = g.connected_components(Connectedness::Weak).unwrap();
36//! assert_eq!(cc.count, 2);
37//! assert_eq!(cc.sizes, vec![6, 1]);
38//! assert!(!g.is_connected(Connectedness::Weak).unwrap());
39//!
40//! // The edge 2-3 (id 3) is the only bridge; 2 and 3 are the cut vertices.
41//! assert_eq!(g.bridges().unwrap(), vec![3]);
42//! let mut cut = g.articulation_points().unwrap();
43//! cut.sort();
44//! assert_eq!(cut, vec![2, 3]);
45//!
46//! // Removing vertex 2 separates vertex 0 from vertex 5.
47//! assert!(g.is_separator(2).unwrap());
48//!
49//! // Vertices at distance at most 1 from vertex 3.
50//! assert_eq!(g.neighborhood(3, Some(1), NeighborMode::All, 0).unwrap(), vec![vec![3, 2, 4, 5]]);
51//!
52//! // Zachary's karate club: one component, a single cut vertex (the
53//! // instructor, 0) and a nested hierarchy of cohesive blocks.
54//! let karate = Graph::famous("Zachary").unwrap();
55//! assert!(karate.is_connected(Connectedness::Weak).unwrap());
56//! assert_eq!(karate.articulation_points().unwrap(), vec![0]);
57//! let blocks = karate.cohesive_blocks().unwrap();
58//! assert_eq!(blocks.cohesion, vec![1, 2, 2, 4, 3, 3, 4, 3]);
59//! ```
60//!
61//! # Provided functionality
62//!
63//! | Rust | C function | Purpose |
64//! |------|------------|---------|
65//! | [`Graph::connected_components`] | `igraph_connected_components` | membership, sizes and number of components |
66//! | [`Graph::is_connected`] | `igraph_is_connected` | weak / strong connectedness test |
67//! | [`Graph::decompose`] | `igraph_decompose` | one [`Graph`] per component |
68//! | [`Graph::articulation_points`] | `igraph_articulation_points` | cut vertices |
69//! | [`Graph::bridges`] | `igraph_bridges` | cut edges |
70//! | [`Graph::biconnected_components`] | `igraph_biconnected_components` | [`BiconnectedComponents`] |
71//! | [`Graph::is_biconnected`] | `igraph_is_biconnected` | 2-vertex-connectedness test |
72//! | [`Graph::bond_percolation`] | `igraph_bond_percolation` | giant component while adding edges |
73//! | [`Graph::site_percolation`] | `igraph_site_percolation` | giant component while adding vertices |
74//! | [`edgelist_percolation`] | `igraph_edgelist_percolation` | bond percolation of a bare edge list |
75//! | [`Graph::is_separator`] | `igraph_is_separator` | does removing a vertex set disconnect the graph? |
76//! | [`Graph::is_minimal_separator`] | `igraph_is_minimal_separator` | ... and no proper subset does? |
77//! | [`Graph::all_minimal_st_separators`] | `igraph_all_minimal_st_separators` | all minimal (s,t) separators |
78//! | [`Graph::minimum_size_separators`] | `igraph_minimum_size_separators` | all minimum-size vertex separators |
79//! | [`Graph::cohesive_blocks`] | `igraph_cohesive_blocks` | [`CohesiveBlocks`] hierarchy |
80//! | [`Graph::reachability`] | `igraph_reachability` | [`Reachability`] bitsets per strong component |
81//! | [`Graph::count_reachable`] | `igraph_count_reachable` | number of reachable vertices |
82//! | [`Graph::transitive_closure`] | `igraph_transitive_closure` | the transitive closure graph |
83//! | [`Graph::neighborhood_size`] | `igraph_neighborhood_size` | sizes of the k-neighborhoods |
84//! | [`Graph::neighborhood`] | `igraph_neighborhood` | vertices of the k-neighborhoods |
85//! | [`Graph::neighborhood_graphs`] | `igraph_neighborhood_graphs` | induced subgraphs of the k-neighborhoods |
86//!
87//! # See also
88//!
89//! - Single-source reachability: [`Graph::subcomponent`]; traversals:
90//!   [`Graph::bfs`], [`Graph::dfs`]; distances:
91//!   [`Graph::distances`].
92//! - Quantitative connectivity (minimum cuts, Menger paths):
93//!   [`Graph::vertex_connectivity`], [`Graph::edge_connectivity`],
94//!   [`Graph::st_vertex_connectivity`], [`Graph::all_st_mincuts`] and
95//!   [`Graph::dominator_tree`] in [`flow`](crate::flow).
96//! - Forests, DAGs and orderings: [`Graph::is_tree`], [`Graph::is_forest`],
97//!   [`Graph::is_dag`], [`Graph::topological_sorting`].
98//! - Subgraphs of components or neighborhoods: [`Graph::induced_subgraph`];
99//!   graphs whose edges are k-neighborhoods: [`Graph::connect_neighborhood`],
100//!   [`Graph::graph_power`].
101//! - Another nested "cohesion" decomposition: [`Graph::coreness`]
102//!   (k-cores).
103
104use crate::{
105    bitset::BitsetList,
106    constants::{Connectedness, NeighborMode},
107    error::{Error, ErrorKind, Result},
108    ffi::*,
109    graph::{EdgeId, Graph, VertexId},
110    igraph_call,
111    list::{GraphList, VectorIntList},
112    selector::VertexSelector,
113    vector::VectorInt,
114};
115
116fn to_usizes(v: VectorInt) -> Vec<usize> {
117    v.iter().map(|&x| x as usize).collect()
118}
119
120/// Converts a count to `igraph_int_t`, saturating instead of wrapping to a
121/// negative value (which igraph would interpret differently).
122fn int_sat(x: usize) -> igraph_int_t {
123    igraph_int_t::try_from(x).unwrap_or(igraph_int_t::MAX)
124}
125
126/// Converts an optional non-negative order / limit into igraph's convention
127/// where a negative value means "unlimited".
128fn order_raw(order: Option<usize>) -> igraph_int_t {
129    order.map_or(-1, int_sat)
130}
131
132// ---------------------------------------------------------------------------
133// Result structs
134// ---------------------------------------------------------------------------
135
136/// The connected components of a graph, see [`Graph::connected_components`].
137#[derive(Debug, Clone, PartialEq, Eq)]
138pub struct ConnectedComponents {
139    /// `membership[v]` is the id (in `0..count`) of the component of vertex `v`.
140    pub membership: Vec<VertexId>,
141    /// `sizes[c]` is the number of vertices in component `c`.
142    pub sizes: Vec<usize>,
143    /// The number of components.
144    pub count: usize,
145}
146
147impl ConnectedComponents {
148    /// The vertices of component `c`, in increasing id order (empty if `c`
149    /// is out of range).
150    pub fn members(&self, c: usize) -> Vec<VertexId> {
151        self.membership
152            .iter()
153            .enumerate()
154            .filter(|&(_, &m)| m as usize == c)
155            .map(|(v, _)| v as VertexId)
156            .collect()
157    }
158
159    /// All the components as vertex lists, indexed by component id.
160    pub fn groups(&self) -> Vec<Vec<VertexId>> {
161        let mut groups: Vec<Vec<VertexId>> =
162            self.sizes.iter().map(|&s| Vec::with_capacity(s)).collect();
163        for (v, &m) in self.membership.iter().enumerate() {
164            groups[m as usize].push(v as VertexId);
165        }
166        groups
167    }
168
169    /// Id of a largest ("giant") component, the first one on ties; `None`
170    /// for the null graph.
171    pub fn largest(&self) -> Option<usize> {
172        let max = *self.sizes.iter().max()?;
173        self.sizes.iter().position(|&s| s == max)
174    }
175
176    /// Whether vertices `u` and `v` lie in the same component (`false` for
177    /// out-of-range ids).
178    pub fn same_component(&self, u: VertexId, v: VertexId) -> bool {
179        let get = |x: VertexId| usize::try_from(x).ok().and_then(|i| self.membership.get(i));
180        matches!((get(u), get(v)), (Some(a), Some(b)) if a == b)
181    }
182}
183
184/// The biconnected components of a graph, see
185/// [`Graph::biconnected_components`].
186///
187/// All the lists are indexed by component id, in `0..count`.
188#[derive(Debug, Clone, PartialEq, Eq)]
189pub struct BiconnectedComponents {
190    /// The number of biconnected components.
191    pub count: usize,
192    /// For each component, the edges of one of its spanning trees.
193    pub tree_edges: Vec<Vec<EdgeId>>,
194    /// For each component, all of its edges. Every non-loop edge of the graph
195    /// belongs to exactly one component.
196    pub component_edges: Vec<Vec<EdgeId>>,
197    /// For each component, its vertices. A vertex may belong to several
198    /// components (exactly when it is an articulation point) and isolated
199    /// vertices belong to none.
200    pub components: Vec<Vec<VertexId>>,
201    /// The articulation points (cut vertices) of the graph.
202    pub articulation_points: Vec<VertexId>,
203}
204
205/// A bond (edge) percolation curve, see [`Graph::bond_percolation`] and
206/// [`edgelist_percolation`].
207#[derive(Debug, Clone, PartialEq, Eq)]
208pub struct BondPercolation {
209    /// `giant_size[i]` is the size of the largest component after the first
210    /// `i + 1` edges have been added.
211    pub giant_size: Vec<usize>,
212    /// `vertex_count[i]` is the number of vertices having at least one
213    /// incident edge after the first `i + 1` edges have been added.
214    pub vertex_count: Vec<usize>,
215}
216
217/// A site (vertex) percolation curve, see [`Graph::site_percolation`].
218#[derive(Debug, Clone, PartialEq, Eq)]
219pub struct SitePercolation {
220    /// `giant_size[i]` is the size of the largest component after the first
221    /// `i + 1` vertices have been added.
222    pub giant_size: Vec<usize>,
223    /// `edge_count[i]` is the number of edges among the first `i + 1` added
224    /// vertices.
225    pub edge_count: Vec<usize>,
226}
227
228/// The cohesive block hierarchy of a graph, see [`Graph::cohesive_blocks`].
229///
230/// Block `0` is always the whole graph; the lists are indexed by block id.
231#[derive(Debug, Clone, PartialEq)]
232pub struct CohesiveBlocks {
233    /// The vertex ids of each block.
234    pub blocks: Vec<Vec<VertexId>>,
235    /// The cohesion (vertex connectivity) of each block.
236    pub cohesion: Vec<usize>,
237    /// The parent block of each block in the hierarchy (`None` for the root).
238    pub parent: Vec<Option<usize>>,
239    /// The hierarchy as a (directed) tree graph: vertex `i` is block `i`, and
240    /// there is an edge from each block to each of its children.
241    pub block_tree: Graph,
242}
243
244impl CohesiveBlocks {
245    /// Number of blocks.
246    pub fn len(&self) -> usize {
247        self.blocks.len()
248    }
249
250    /// Whether there are no blocks. Results of [`Graph::cohesive_blocks`]
251    /// always contain at least the root block (even for the null graph,
252    /// whose root block is empty), so this is `false` for them.
253    pub fn is_empty(&self) -> bool {
254        self.blocks.is_empty()
255    }
256
257    /// Ids of the child blocks of block `b`.
258    pub fn children(&self, b: usize) -> Vec<usize> {
259        (0..self.parent.len())
260            .filter(|&i| self.parent[i] == Some(b))
261            .collect()
262    }
263
264    /// The largest cohesion of a block containing vertex `v` (its
265    /// "embeddedness"), or `None` if no block contains it.
266    pub fn max_cohesion_of(&self, v: VertexId) -> Option<usize> {
267        self.blocks
268            .iter()
269            .zip(&self.cohesion)
270            .filter(|(b, _)| b.contains(&v))
271            .map(|(_, &c)| c)
272            .max()
273    }
274}
275
276/// Reachability information, see [`Graph::reachability`].
277///
278/// Vertices in the same strongly connected component reach exactly the same
279/// vertices, so the reachable sets are stored once per component.
280#[derive(Debug, Clone, PartialEq, Eq)]
281pub struct Reachability {
282    /// `membership[v]` is the id of the (strongly, for directed graphs with
283    /// `Out`/`In` modes) connected component of `v`.
284    pub membership: Vec<VertexId>,
285    /// `sizes[c]` is the number of vertices of component `c`.
286    pub sizes: Vec<usize>,
287    /// The number of components.
288    pub count: usize,
289    /// `reach[c][v]` is `true` when vertex `v` is reachable from the vertices
290    /// of component `c` (every vertex reaches itself).
291    pub reach: Vec<Vec<bool>>,
292}
293
294impl Reachability {
295    /// Whether `to` is reachable from `from` (`false` for out-of-range ids).
296    pub fn is_reachable(&self, from: VertexId, to: VertexId) -> bool {
297        usize::try_from(from)
298            .ok()
299            .and_then(|f| self.membership.get(f))
300            .and_then(|&c| self.reach.get(c as usize))
301            .and_then(|row| usize::try_from(to).ok().and_then(|t| row.get(t).copied()))
302            .unwrap_or(false)
303    }
304
305    /// The vertices reachable from `from`, in increasing id order (empty for
306    /// an out-of-range id).
307    pub fn reachable_from(&self, from: VertexId) -> Vec<VertexId> {
308        let Some(&c) = usize::try_from(from)
309            .ok()
310            .and_then(|f| self.membership.get(f))
311        else {
312            return Vec::new();
313        };
314        self.reach[c as usize]
315            .iter()
316            .enumerate()
317            .filter(|&(_, &r)| r)
318            .map(|(v, _)| v as VertexId)
319            .collect()
320    }
321}
322
323/// Runs `f` with a raw `igraph_vs_t` built from `sel`, keeping any backing
324/// storage alive (and at a fixed address) for the whole call.
325///
326/// Vertex lists are passed as a zero-copy view of the Rust slice, whose
327/// `igraph_vector_int_t` header lives in this stack frame and never moves
328/// while `f` runs (`igraph_vss_vector` stores a pointer to that header).
329/// The other selectors go through [`VertexSelector::to_raw`].
330fn with_vs(sel: VertexSelector<'_>, f: impl FnOnce(igraph_vs_t) -> Result<()>) -> Result<()> {
331    if let VertexSelector::List(list) = &sel {
332        let view = VectorInt::view(list);
333        // SAFETY: `view` outlives the call of `f` and is not moved meanwhile.
334        let vs = unsafe { igraph_vss_vector(view.as_ptr()) };
335        return f(vs);
336    }
337    let raw = sel.to_raw()?;
338    f(raw.get())
339}
340
341// ---------------------------------------------------------------------------
342// Free functions
343// ---------------------------------------------------------------------------
344
345/// Bond percolation curve of a bare list of vertex pairs.
346///
347/// Edges `(u, v)` are added one after the other, and after each addition the
348/// size of the largest connected component and the number of non-isolated
349/// vertices are recorded. It differs from [`Graph::bond_percolation`] in that
350/// no graph is needed: vertices are identified by the ids appearing in
351/// `edges`, which must be non-negative (the largest id determines how much
352/// memory is allocated). Self-loops are allowed.
353///
354/// This function is marked *experimental* in igraph.
355///
356/// Time complexity: O(|E| α(|E|)), α being the inverse Ackermann function.
357///
358/// Binds [`igraph_edgelist_percolation`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_edgelist_percolation).
359///
360/// # Errors
361/// [`ErrorKind::InvalidVertexId`] for negative vertex ids and for the id
362/// `i64::MAX` (rejected on the Rust side: igraph 1.0.1 computes the vertex
363/// count as the largest id plus one, which would overflow and abort the
364/// process). [`ErrorKind::OutOfMemory`] when the largest id is too large
365/// to allocate per-vertex storage for.
366///
367/// # Examples
368/// ```
369/// use igraph::components::edgelist_percolation;
370///
371/// // A triangle with an extra self-loop on vertex 0.
372/// let p = edgelist_percolation(&[(0, 0), (0, 1), (1, 2), (2, 0)]).unwrap();
373/// assert_eq!(p.giant_size, vec![1, 2, 3, 3]);
374/// assert_eq!(p.vertex_count, vec![1, 2, 3, 3]);
375/// ```
376pub fn edgelist_percolation(edges: &[(VertexId, VertexId)]) -> Result<BondPercolation> {
377    if edges
378        .iter()
379        .any(|&(a, b)| a == igraph_int_t::MAX || b == igraph_int_t::MAX)
380    {
381        return Err(Error::new(
382            ErrorKind::InvalidVertexId,
383            format!("vertex id {} is too large", igraph_int_t::MAX),
384        ));
385    }
386    let flat: VectorInt = edges.iter().flat_map(|&(a, b)| [a, b]).collect();
387    let mut giant = VectorInt::new();
388    let mut count = VectorInt::new();
389    igraph_call!(igraph_edgelist_percolation(&flat, &mut giant, &mut count))?;
390    Ok(BondPercolation {
391        giant_size: to_usizes(giant),
392        vertex_count: to_usizes(count),
393    })
394}
395
396// ---------------------------------------------------------------------------
397// Graph methods
398// ---------------------------------------------------------------------------
399
400impl igraph_t {
401    // ----- components -------------------------------------------------------
402
403    /// The (weakly or strongly) connected components of the graph.
404    ///
405    /// With [`Connectedness::Weak`] edge directions are ignored; with
406    /// [`Connectedness::Strong`] two vertices are in the same component when
407    /// each is reachable from the other by a directed path. The mode is
408    /// ignored for undirected graphs.
409    ///
410    /// Strongly connected components are numbered in *topological order*:
411    /// vertex `v` is reachable from `u` only if
412    /// `membership[u] <= membership[v]`. Weak components are numbered in
413    /// order of their smallest vertex id.
414    ///
415    /// Time complexity: O(|V| + |E|).
416    ///
417    /// See also [`is_connected`](Self::is_connected) for a (cached) yes/no
418    /// answer, [`decompose`](Self::decompose) to get the components as
419    /// graphs, and [`Graph::subcomponent`] for the component of a single
420    /// vertex.
421    ///
422    /// Binds [`igraph_connected_components`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_connected_components).
423    ///
424    /// # Examples
425    /// ```
426    /// use igraph::prelude::*;
427    ///
428    /// // 0 -> 1 -> 2 -> 0 is a directed cycle, 3 only has an in-edge.
429    /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3)], 4, true).unwrap();
430    /// let weak = g.connected_components(Connectedness::Weak).unwrap();
431    /// assert_eq!(weak.count, 1);
432    /// let strong = g.connected_components(Connectedness::Strong).unwrap();
433    /// assert_eq!(strong.count, 2);
434    /// assert!(strong.same_component(0, 2));
435    /// assert!(!strong.same_component(0, 3));
436    /// ```
437    pub fn connected_components(&self, mode: Connectedness) -> Result<ConnectedComponents> {
438        let mut membership = VectorInt::new();
439        let mut csize = VectorInt::new();
440        let mut no: igraph_int_t = 0;
441        igraph_call!(igraph_connected_components(
442            self,
443            &mut membership,
444            &mut csize,
445            &mut no,
446            mode.into()
447        ))?;
448        Ok(ConnectedComponents {
449            membership: membership.into(),
450            sizes: to_usizes(csize),
451            count: no as usize,
452        })
453    }
454
455    /// Whether the graph is (weakly or strongly) connected.
456    ///
457    /// A graph is connected when every vertex is reachable from every other;
458    /// for directed graphs, [`Connectedness::Strong`] follows edge directions
459    /// while [`Connectedness::Weak`] ignores them. The mode is ignored for
460    /// undirected graphs. By definition the null graph (no vertices) is
461    /// **not** connected, while the singleton graph is.
462    ///
463    /// The result is cached inside the graph, so repeated calls without
464    /// modifications are O(1); otherwise the time complexity is O(|V| + |E|).
465    ///
466    /// See also [`is_biconnected`](Self::is_biconnected) for
467    /// 2-vertex-connectedness, and [`Graph::vertex_connectivity`] /
468    /// [`Graph::edge_connectivity`] for *how* connected a graph is.
469    ///
470    /// Binds [`igraph_is_connected`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_is_connected).
471    ///
472    /// # Examples
473    /// ```
474    /// use igraph::prelude::*;
475    /// let g = Graph::from_edges(&[(0, 1), (1, 2)], 3, true).unwrap();
476    /// assert!(g.is_connected(Connectedness::Weak).unwrap());
477    /// assert!(!g.is_connected(Connectedness::Strong).unwrap());
478    /// assert!(!Graph::new(0, false).is_connected(Connectedness::Weak).unwrap());
479    /// ```
480    pub fn is_connected(&self, mode: Connectedness) -> Result<bool> {
481        let mut res = false;
482        igraph_call!(igraph_is_connected(self, &mut res, mode.into()))?;
483        Ok(res)
484    }
485
486    /// Splits the graph into one separate [`Graph`] per connected component.
487    ///
488    /// `max_components` limits the number of returned graphs (`None` for no
489    /// limit): the first ones found are kept (for weak components, in order
490    /// of their smallest vertex id). Components
491    /// with fewer than `min_vertices` vertices are skipped (e.g. `2` drops the
492    /// isolated vertices). Vertex ids are renumbered in each component graph,
493    /// preserving their relative order; directedness is preserved.
494    ///
495    /// Time complexity: O(|V| + |E|).
496    ///
497    /// Each graph is the subgraph induced by one component: use
498    /// [`connected_components`](Self::connected_components) with
499    /// [`Graph::induced_subgraph`] to extract only some of them, or to keep
500    /// the mapping to the original vertex ids.
501    ///
502    /// Binds [`igraph_decompose`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_decompose).
503    ///
504    /// # Examples
505    /// ```
506    /// use igraph::prelude::*;
507    /// // A triangle, a single edge and an isolated vertex.
508    /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (3, 4)], 6, false).unwrap();
509    /// let parts = g.decompose(Connectedness::Weak, None, 2).unwrap();
510    /// let sizes: Vec<_> = parts.iter().map(|p| (p.vcount(), p.ecount())).collect();
511    /// assert_eq!(sizes, vec![(3, 3), (2, 1)]);
512    /// ```
513    pub fn decompose(
514        &self,
515        mode: Connectedness,
516        max_components: Option<usize>,
517        min_vertices: usize,
518    ) -> Result<Vec<Graph>> {
519        let mut list = GraphList::new();
520        igraph_call!(igraph_decompose(
521            self,
522            &mut list,
523            mode.into(),
524            order_raw(max_components),
525            int_sat(min_vertices)
526        ))?;
527        Ok(list.into_vec())
528    }
529
530    /// The articulation points (cut vertices) of the graph.
531    ///
532    /// A vertex is an articulation point if its removal increases the number
533    /// of (weakly) connected components. Edge directions are ignored. The
534    /// vertices are returned in no particular order.
535    ///
536    /// A graph without articulation points is not necessarily biconnected:
537    /// the null graph, the singleton graph and edgeless graphs have none
538    /// either; use [`is_biconnected`](Self::is_biconnected) for that.
539    /// Conversely `K2`, which igraph considers biconnected, has no
540    /// articulation points. (The C documentation of 1.0.0 and 1.0.1 lists
541    /// `K2` among the counterexamples, but [`is_biconnected`](Self::is_biconnected)
542    /// returns `true` for it.)
543    ///
544    /// Time complexity: O(|V| + |E|).
545    ///
546    /// See also [`biconnected_components`](Self::biconnected_components),
547    /// which also returns the articulation points, and
548    /// [`bridges`](Self::bridges) for the edge analogue.
549    ///
550    /// Binds [`igraph_articulation_points`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_articulation_points).
551    ///
552    /// # Examples
553    /// ```
554    /// use igraph::prelude::*;
555    /// // A path 0-1-2-3: the inner vertices are cut vertices.
556    /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
557    /// let mut ap = g.articulation_points().unwrap();
558    /// ap.sort();
559    /// assert_eq!(ap, vec![1, 2]);
560    /// ```
561    pub fn articulation_points(&self) -> Result<Vec<VertexId>> {
562        let mut res = VectorInt::new();
563        igraph_call!(igraph_articulation_points(self, &mut res))?;
564        Ok(res.into())
565    }
566
567    /// The bridges (cut edges) of the graph, as edge ids.
568    ///
569    /// An edge is a bridge if its removal increases the number of (weakly)
570    /// connected components. Edge directions are ignored. The edges are
571    /// returned in no particular order. A multi-edge is never a bridge (its
572    /// parallel copies keep the endpoints connected), and neither is a
573    /// self-loop.
574    ///
575    /// Time complexity: O(|V| + |E|).
576    ///
577    /// A connected graph has a bridge exactly when its
578    /// [`edge_connectivity`](Graph::edge_connectivity) is 1 (and it has more
579    /// than one vertex).
580    ///
581    /// Binds [`igraph_bridges`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_bridges).
582    ///
583    /// # Examples
584    /// ```
585    /// use igraph::prelude::*;
586    /// // A square 0-1-2-3 with a pendant edge 3-4 (edge id 4).
587    /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0), (3, 4)], 5, false).unwrap();
588    /// assert_eq!(g.bridges().unwrap(), vec![4]);
589    /// ```
590    pub fn bridges(&self) -> Result<Vec<EdgeId>> {
591        let mut res = VectorInt::new();
592        igraph_call!(igraph_bridges(self, &mut res))?;
593        Ok(res.into())
594    }
595
596    /// The biconnected components of the graph (edge directions are ignored).
597    ///
598    /// A graph is biconnected if removing any single vertex leaves it
599    /// connected; a biconnected component is a maximal biconnected subgraph.
600    /// The components partition the (non-loop) *edges* of the graph, while a
601    /// vertex can belong to several of them (the articulation points) or to
602    /// none (isolated vertices). igraph considers the two-vertex complete
603    /// graph `K2` biconnected, but not single vertices, so a single
604    /// biconnected component does not imply that the graph is biconnected
605    /// (there may be isolated vertices): use
606    /// [`is_biconnected`](Self::is_biconnected) for that. Self-loops belong to
607    /// no component.
608    ///
609    /// All outputs are computed: the number of components, a spanning tree of
610    /// each, their edges and vertices, and the articulation points. Time
611    /// complexity: O(|V| + |E|) for the trees alone, but igraph documents
612    /// computing the vertex sets as quadratic and the edge sets as cubic in
613    /// |V| in the worst case.
614    ///
615    /// Binds [`igraph_biconnected_components`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_biconnected_components).
616    ///
617    /// # Examples
618    /// ```
619    /// use igraph::prelude::*;
620    /// // Two triangles sharing vertex 2 ("bowtie").
621    /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3), (3, 4), (4, 2)], 5, false)
622    ///     .unwrap();
623    /// let bc = g.biconnected_components().unwrap();
624    /// assert_eq!(bc.count, 2);
625    /// assert_eq!(bc.articulation_points, vec![2]);
626    /// assert!(bc.components.iter().all(|c| c.len() == 3 && c.contains(&2)));
627    /// ```
628    pub fn biconnected_components(&self) -> Result<BiconnectedComponents> {
629        let mut no: igraph_int_t = 0;
630        let mut tree = VectorIntList::new();
631        let mut edges = VectorIntList::new();
632        let mut comps = VectorIntList::new();
633        let mut ap = VectorInt::new();
634        igraph_call!(igraph_biconnected_components(
635            self, &mut no, &mut tree, &mut edges, &mut comps, &mut ap
636        ))?;
637        Ok(BiconnectedComponents {
638            count: no as usize,
639            tree_edges: tree.to_vecs(),
640            component_edges: edges.to_vecs(),
641            components: comps.to_vecs(),
642            articulation_points: ap.into(),
643        })
644    }
645
646    /// Whether the graph is biconnected (edge directions are ignored).
647    ///
648    /// A graph is biconnected if removing any single vertex (and its
649    /// incident edges) does not disconnect it. igraph does not consider the
650    /// null and singleton graphs biconnected, but does consider `K2`
651    /// biconnected. For graphs with at least three vertices this is the same
652    /// as having [`vertex_connectivity`](Graph::vertex_connectivity) at
653    /// least 2, but much cheaper to compute.
654    ///
655    /// Time complexity: O(|V| + |E|).
656    ///
657    /// Binds [`igraph_is_biconnected`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_is_biconnected).
658    ///
659    /// # Examples
660    /// ```
661    /// use igraph::prelude::*;
662    /// let square = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], 4, false).unwrap();
663    /// assert!(square.is_biconnected().unwrap());
664    /// let path = Graph::from_edges(&[(0, 1), (1, 2)], 3, false).unwrap();
665    /// assert!(!path.is_biconnected().unwrap());
666    /// ```
667    pub fn is_biconnected(&self) -> Result<bool> {
668        let mut res = false;
669        igraph_call!(igraph_is_biconnected(self, &mut res))?;
670        Ok(res)
671    }
672
673    /// Bond (edge) percolation curve: the size of the giant component as the
674    /// edges of the graph are added one by one to its vertices.
675    ///
676    /// `edge_order` gives the order in which the edge ids are added and must
677    /// not contain duplicates; it may list only a subset of the edges. With
678    /// `None` a uniformly random order is used, drawn from the calling
679    /// thread's default random number generator (reproducible after
680    /// [`rng::seed`](crate::rng::seed)). Reversing both the order and the
681    /// resulting `giant_size` gives the curve for edge *removal*. Edge
682    /// directions are ignored; the vertices are only counted once they get
683    /// an incident edge, so isolated vertices never contribute.
684    ///
685    /// This function is marked *experimental* in igraph.
686    ///
687    /// Time complexity: O(|V| + |E| α(|E|)), α being the inverse Ackermann
688    /// function.
689    ///
690    /// See also [`site_percolation`](Self::site_percolation) for the
691    /// vertex version, [`edgelist_percolation`] to percolate arbitrary vertex
692    /// pairs, and [`connected_components`](Self::connected_components) for
693    /// the component sizes of a fixed graph.
694    ///
695    /// Binds [`igraph_bond_percolation`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_bond_percolation).
696    ///
697    /// # Errors
698    /// [`ErrorKind::InvalidEdgeId`] for an edge id out of range and
699    /// [`ErrorKind::InvalidValue`] for duplicates in `edge_order`. Both are
700    /// checked on the Rust side: igraph 1.0.0 and 1.0.1 size their
701    /// duplicate-detection bitset by the *length* of `edge_order` rather than
702    /// by the number of edges, so an unchecked partial order or out-of-range
703    /// id would make them access memory out of bounds.
704    ///
705    /// # Examples
706    /// ```
707    /// use igraph::prelude::*;
708    /// // The 4-cycle 0-1-2-3-0, adding edges 0-1, 2-3, 1-2, 3-0.
709    /// let c4 = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], 4, false).unwrap();
710    /// let p = c4.bond_percolation(Some(&[0, 2, 1, 3])).unwrap();
711    /// assert_eq!(p.giant_size, vec![2, 2, 4, 4]);
712    /// assert_eq!(p.vertex_count, vec![2, 4, 4, 4]);
713    ///
714    /// // A random order is reproducible after seeding; the final point is
715    /// // always the whole (connected) graph.
716    /// let grid = Graph::square_lattice(&[5, 5], 1, false, false, None).unwrap();
717    /// rng::seed(7).unwrap();
718    /// let a = grid.bond_percolation(None).unwrap();
719    /// rng::seed(7).unwrap();
720    /// let b = grid.bond_percolation(None).unwrap();
721    /// assert_eq!(a, b);
722    /// assert_eq!(a.giant_size.last(), Some(&25));
723    /// ```
724    pub fn bond_percolation(&self, edge_order: Option<&[EdgeId]>) -> Result<BondPercolation> {
725        if let Some(order) = edge_order {
726            let m = self.ecount();
727            let mut seen = vec![false; m];
728            for &e in order {
729                let i = usize::try_from(e).ok().filter(|&i| i < m).ok_or_else(|| {
730                    Error::new(
731                        ErrorKind::InvalidEdgeId,
732                        format!("invalid edge id {e} in edge order (graph has {m} edges)"),
733                    )
734                })?;
735                if std::mem::replace(&mut seen[i], true) {
736                    return Err(Error::invalid(format!(
737                        "duplicate edge {e} in edge order vector"
738                    )));
739                }
740            }
741        }
742        let order = edge_order.map(VectorInt::view);
743        let op = order.as_ref().map_or(std::ptr::null(), |v| v.as_ptr());
744        let mut giant = VectorInt::new();
745        let mut count = VectorInt::new();
746        igraph_call!(igraph_bond_percolation(self, &mut giant, &mut count, op))?;
747        Ok(BondPercolation {
748            giant_size: to_usizes(giant),
749            vertex_count: to_usizes(count),
750        })
751    }
752
753    /// Site (vertex) percolation curve: the size of the giant component as
754    /// the vertices of the graph are added one by one, together with the
755    /// edges among the vertices added so far.
756    ///
757    /// `vertex_order` gives the order in which vertices are added and must
758    /// not contain duplicates; it may list only a subset of the vertices.
759    /// With `None` a uniformly random order is used, drawn from the calling
760    /// thread's default random number generator exactly as igraph would
761    /// (reproducible after [`rng::seed`](crate::rng::seed)). Reversing both
762    /// the order and the resulting `giant_size` gives the curve for vertex
763    /// *removal* (an attack/failure scenario). Edge directions are ignored;
764    /// multi-edges count with their multiplicity in `edge_count`.
765    ///
766    /// This function is marked *experimental* in igraph.
767    ///
768    /// Time complexity: O(|V| + |E| α(|E|)).
769    ///
770    /// See also [`bond_percolation`](Self::bond_percolation) for the edge
771    /// version and [`Graph::coreness`] or
772    /// [`Graph::vertex_connectivity`] for other robustness measures.
773    ///
774    /// Binds [`igraph_site_percolation`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_site_percolation).
775    ///
776    /// # Self-loops
777    /// igraph 1.0.0 and 1.0.1 count every self-loop *twice* in `edge_count`
778    /// (it lists loops twice among a vertex's neighbors). This wrapper
779    /// corrects the count, so a self-loop counts as one edge. To be able to
780    /// do so with `vertex_order = None`, the random order is generated on
781    /// the Rust side with the same random draws igraph makes internally.
782    ///
783    /// # Errors
784    /// [`ErrorKind::InvalidVertexId`] for invalid vertex ids and
785    /// [`ErrorKind::InvalidValue`] for duplicates in `vertex_order`.
786    ///
787    /// # Examples
788    /// ```
789    /// use igraph::prelude::*;
790    /// let c4 = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], 4, false).unwrap();
791    /// let p = c4.site_percolation(Some(&[0, 2, 1, 3])).unwrap();
792    /// assert_eq!(p.giant_size, vec![1, 1, 3, 4]);
793    /// assert_eq!(p.edge_count, vec![0, 0, 2, 4]);
794    ///
795    /// // Removing the hub of a star shatters it at once.
796    /// let star = Graph::star(6, StarMode::Undirected, 0).unwrap();
797    /// let attack = [0, 1, 2, 3, 4, 5]; // hub first
798    /// let build: Vec<i64> = attack.iter().rev().copied().collect();
799    /// let mut after = star.site_percolation(Some(&build)).unwrap().giant_size;
800    /// after.reverse(); // after[k]: largest component with k vertices removed
801    /// assert_eq!(after, vec![6, 1, 1, 1, 1, 1]);
802    /// ```
803    pub fn site_percolation(&self, vertex_order: Option<&[VertexId]>) -> Result<SitePercolation> {
804        let shuffled: VectorInt;
805        let order: &[VertexId] = match vertex_order {
806            Some(order) => order,
807            None => {
808                // Same as igraph: `igraph_vector_int_init_range` + `_shuffle`.
809                let mut all: VectorInt = (0..self.vcount() as VertexId).collect();
810                all.shuffle();
811                shuffled = all;
812                &shuffled
813            }
814        };
815        let view = VectorInt::view(order);
816        let mut giant = VectorInt::new();
817        let mut count = VectorInt::new();
818        igraph_call!(igraph_site_percolation(
819            self,
820            &mut giant,
821            &mut count,
822            view.as_ptr()
823        ))?;
824        let mut edge_count = to_usizes(count);
825        if self.has_loop()? {
826            // igraph succeeded, so every id in `order` is valid.
827            let mut loops = vec![0usize; self.vcount()];
828            for (from, to) in self.edge_list() {
829                if from == to {
830                    loops[from as usize] += 1;
831                }
832            }
833            let mut seen = 0;
834            for (count, &v) in edge_count.iter_mut().zip(order) {
835                seen += loops[v as usize];
836                *count -= seen;
837            }
838        }
839        Ok(SitePercolation {
840            giant_size: to_usizes(giant),
841            edge_count,
842        })
843    }
844
845    // ----- separators -------------------------------------------------------
846
847    /// Whether removing the `candidate` vertices disconnects the graph.
848    ///
849    /// A vertex set `S` is a separator if there are vertices `u` and `v`
850    /// outside `S`, connected in the graph, such that every path between
851    /// them passes through `S`.
852    /// Edge directions are ignored and duplicate ids in `candidate` are
853    /// ignored. Removing *all* vertices, or all but one, is never a
854    /// separation, and the empty set is never a separator either, not even
855    /// in a disconnected graph: what matters is whether *removing* the set
856    /// separates some vertices that were connected before.
857    ///
858    /// Time complexity: O(|V| + |E|).
859    ///
860    /// See also [`minimum_size_separators`](Self::minimum_size_separators)
861    /// to *find* the smallest separators, and
862    /// [`Graph::st_vertex_connectivity`] for the size of the smallest set
863    /// separating two given vertices.
864    ///
865    /// Binds [`igraph_is_separator`](https://igraph.org/c/html/latest/igraph-Separators.html#igraph_is_separator).
866    ///
867    /// # Examples
868    /// ```
869    /// use igraph::prelude::*;
870    /// // A star with center 0.
871    /// let star = Graph::from_edges(&[(0, 1), (0, 2), (0, 3)], 4, false).unwrap();
872    /// assert!(star.is_separator(0).unwrap());
873    /// assert!(!star.is_separator(1).unwrap());
874    /// assert!(!star.is_separator(1..4).unwrap());
875    /// ```
876    pub fn is_separator<'a>(&self, candidate: impl Into<VertexSelector<'a>>) -> Result<bool> {
877        let mut res = false;
878        with_vs(candidate.into(), |vs| {
879            igraph_call!(igraph_is_separator(self, vs, &mut res))
880        })?;
881        Ok(res)
882    }
883
884    /// Whether `candidate` is a *minimal* separator: a separator (see
885    /// [`is_separator`](Self::is_separator)) none of whose proper subsets is
886    /// a separator. Edge directions are ignored.
887    ///
888    /// Time complexity: O(|V| + |E|).
889    ///
890    /// Binds [`igraph_is_minimal_separator`](https://igraph.org/c/html/latest/igraph-Separators.html#igraph_is_minimal_separator).
891    ///
892    /// # Examples
893    /// ```
894    /// use igraph::prelude::*;
895    /// // The path 0-1-2-3.
896    /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
897    /// assert!(g.is_minimal_separator(1).unwrap());
898    /// assert!(g.is_separator(&[1, 2]).unwrap());
899    /// assert!(!g.is_minimal_separator(&[1, 2]).unwrap());
900    /// ```
901    pub fn is_minimal_separator<'a>(
902        &self,
903        candidate: impl Into<VertexSelector<'a>>,
904    ) -> Result<bool> {
905        let mut res = false;
906        with_vs(candidate.into(), |vs| {
907            igraph_call!(igraph_is_minimal_separator(self, vs, &mut res))
908        })?;
909        Ok(res)
910    }
911
912    /// All the vertex sets that are minimal (s, t) separators for some pair
913    /// of vertices s, t.
914    ///
915    /// Some returned sets are not minimal with respect to disconnecting the
916    /// graph: in the graph `0-1-2-3-4-1` the sets `{1}`, `{2, 4}` and
917    /// `{1, 3}` are returned, and `{1, 3}` is minimal for separating 2 from 4
918    /// although `{1}` alone disconnects the graph. Edge directions are
919    /// ignored. Unlike [`minimum_size_separators`](Self::minimum_size_separators),
920    /// a disconnected graph is handled component by component (its
921    /// separators are those of its components). Uses the algorithm of Berry,
922    /// Bordat and Cogis (1999).
923    ///
924    /// Time complexity: O(n |V|³), n being the number of separators.
925    ///
926    /// Binds [`igraph_all_minimal_st_separators`](https://igraph.org/c/html/latest/igraph-Separators.html#igraph_all_minimal_st_separators).
927    ///
928    /// # Examples
929    /// ```
930    /// use igraph::prelude::*;
931    /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 1)], 5, false).unwrap();
932    /// let mut seps: Vec<Vec<i64>> = g
933    ///     .all_minimal_st_separators()
934    ///     .unwrap()
935    ///     .into_iter()
936    ///     .map(|mut s| { s.sort(); s })
937    ///     .collect();
938    /// seps.sort();
939    /// assert_eq!(seps, vec![vec![1], vec![1, 3], vec![2, 4]]);
940    /// ```
941    pub fn all_minimal_st_separators(&self) -> Result<Vec<Vec<VertexId>>> {
942        let mut res = VectorIntList::new();
943        igraph_call!(igraph_all_minimal_st_separators(self, &mut res))?;
944        Ok(res.to_vecs())
945    }
946
947    /// All the vertex separators of minimum size.
948    ///
949    /// A vertex set is a separator if its removal disconnects the graph. The
950    /// graph must be undirected. A graph that is already disconnected has no
951    /// separators (an empty list is returned), and neither do complete
952    /// graphs. Each separator has exactly
953    /// [`vertex_connectivity`](Graph::vertex_connectivity) vertices. The
954    /// separators are returned in arbitrary order. Uses the algorithm of
955    /// Kanevsky (1993).
956    ///
957    /// See also [`Graph::all_st_mincuts`] for the edge analogue between two
958    /// given vertices.
959    ///
960    /// Binds [`igraph_minimum_size_separators`](https://igraph.org/c/html/latest/igraph-Separators.html#igraph_minimum_size_separators).
961    ///
962    /// # Errors
963    /// [`ErrorKind::InvalidValue`] for
964    /// directed graphs.
965    ///
966    /// # Examples
967    /// ```
968    /// use igraph::prelude::*;
969    /// // The 5-cycle: removing any two non-adjacent vertices disconnects it.
970    /// let c5 = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)], 5, false).unwrap();
971    /// let seps = c5.minimum_size_separators().unwrap();
972    /// assert_eq!(seps.len(), 5);
973    /// assert!(seps.iter().all(|s| s.len() == 2));
974    /// ```
975    pub fn minimum_size_separators(&self) -> Result<Vec<Vec<VertexId>>> {
976        let mut res = VectorIntList::new();
977        igraph_call!(igraph_minimum_size_separators(self, &mut res))?;
978        Ok(res.to_vecs())
979    }
980
981    // ----- cohesive blocks --------------------------------------------------
982
983    /// The hierarchical cohesive block structure of the graph (Moody and
984    /// White, 2003).
985    ///
986    /// A vertex set is *k-cohesive* when the subgraph it induces has vertex
987    /// connectivity at least k; it is *maximally* k-cohesive when no superset
988    /// is. Cohesive blocking starts from the whole graph and recursively
989    /// identifies the maximally l-cohesive subsets, l > k, of each k-cohesive
990    /// block, yielding a tree of nested blocks rooted at the whole graph
991    /// (block `0`, with `parent` `None`).
992    ///
993    /// The cohesion of each block is the
994    /// [`vertex_connectivity`](Graph::vertex_connectivity) of the subgraph it
995    /// induces. The root block has cohesion 0 when the graph is
996    /// disconnected; the null graph yields a single, empty root block.
997    ///
998    /// The graph must be undirected and simple (see [`Graph::simplify`]).
999    ///
1000    /// See also [`Graph::coreness`] for the (cheaper, degree-based) k-core
1001    /// hierarchy, and [`Graph::cohesion`] for the cohesion of the whole
1002    /// graph.
1003    ///
1004    /// Binds [`igraph_cohesive_blocks`](https://igraph.org/c/html/latest/igraph-Flows.html#igraph_cohesive_blocks).
1005    ///
1006    /// # Errors
1007    /// [`ErrorKind::InvalidValue`] for
1008    /// directed or non-simple graphs.
1009    ///
1010    /// # Examples
1011    /// ```
1012    /// use igraph::prelude::*;
1013    /// // Two 4-cliques (0..4 and 3..7) sharing vertex 3.
1014    /// let mut edges = vec![];
1015    /// for block in [[0, 1, 2, 3], [3, 4, 5, 6]] {
1016    ///     for i in 0..4 {
1017    ///         for j in i + 1..4 {
1018    ///             edges.push((block[i], block[j]));
1019    ///         }
1020    ///     }
1021    /// }
1022    /// let g = Graph::from_edges(&edges, 7, false).unwrap();
1023    /// let cb = g.cohesive_blocks().unwrap();
1024    /// assert_eq!(cb.len(), 3);
1025    /// assert_eq!(cb.cohesion, vec![1, 3, 3]);
1026    /// assert_eq!(cb.parent, vec![None, Some(0), Some(0)]);
1027    /// assert_eq!(cb.children(0), vec![1, 2]);
1028    /// ```
1029    pub fn cohesive_blocks(&self) -> Result<CohesiveBlocks> {
1030        let mut blocks = VectorIntList::new();
1031        let mut cohesion = VectorInt::new();
1032        let mut parent = VectorInt::new();
1033        let block_tree = Graph::init_with(|tree| unsafe {
1034            igraph_cohesive_blocks(self, &mut blocks, &mut cohesion, &mut parent, tree)
1035        })?;
1036        Ok(CohesiveBlocks {
1037            blocks: blocks.to_vecs(),
1038            cohesion: to_usizes(cohesion),
1039            parent: parent.iter().map(|&p| usize::try_from(p).ok()).collect(),
1040            block_tree,
1041        })
1042    }
1043
1044    // ----- reachability -----------------------------------------------------
1045
1046    /// Which vertices are reachable from each vertex.
1047    ///
1048    /// The result groups vertices by the component they belong to (strongly
1049    /// connected components for directed graphs with
1050    /// [`NeighborMode::Out`]/[`NeighborMode::In`], plain components
1051    /// otherwise), since vertices of one component reach the same set, and
1052    /// stores for each component the set of reachable vertices as booleans.
1053    /// With `Out` edges are followed along their direction, with `In`
1054    /// against it, with `All` directions are ignored; `mode` is ignored for
1055    /// undirected graphs. A vertex always reaches itself.
1056    ///
1057    /// Time complexity: O(|C||V|/w + |V| + |E|), |C| being the number of
1058    /// components and w the machine word size.
1059    ///
1060    /// See also [`count_reachable`](Self::count_reachable) for the sizes
1061    /// only, [`transitive_closure`](Self::transitive_closure) for the same
1062    /// information as a graph, and [`Graph::subcomponent`] for the vertices
1063    /// reachable from a single vertex.
1064    ///
1065    /// Binds [`igraph_reachability`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_reachability).
1066    ///
1067    /// # Examples
1068    /// ```
1069    /// use igraph::prelude::*;
1070    /// let g = Graph::from_edges(&[(0, 1), (1, 2), (3, 2)], 4, true).unwrap();
1071    /// let r = g.reachability(NeighborMode::Out).unwrap();
1072    /// assert!(r.is_reachable(0, 2));
1073    /// assert!(!r.is_reachable(2, 0));
1074    /// assert_eq!(r.reachable_from(3), vec![2, 3]);
1075    /// ```
1076    pub fn reachability(&self, mode: NeighborMode) -> Result<Reachability> {
1077        let mut membership = VectorInt::new();
1078        let mut csize = VectorInt::new();
1079        let mut no: igraph_int_t = 0;
1080        let mut reach = BitsetList::new();
1081        igraph_call!(igraph_reachability(
1082            self,
1083            &mut membership,
1084            &mut csize,
1085            &mut no,
1086            &mut reach,
1087            mode.into()
1088        ))?;
1089        Ok(Reachability {
1090            membership: membership.into(),
1091            sizes: to_usizes(csize),
1092            count: no as usize,
1093            reach: reach.iter().map(|bits| bits.to_vec()).collect(),
1094        })
1095    }
1096
1097    /// The number of vertices reachable from each vertex, itself included.
1098    ///
1099    /// `mode` has the same meaning as in [`reachability`](Self::reachability):
1100    /// with [`NeighborMode::In`] it counts the vertices that can reach each
1101    /// vertex.
1102    ///
1103    /// Time complexity: O(|C||V|/w + |V| + |E|).
1104    ///
1105    /// Equivalently, it is [`neighborhood_size`](Self::neighborhood_size)
1106    /// with unlimited order and `mindist = 0`, but faster: the work is shared
1107    /// by all the vertices of a strongly connected component instead of
1108    /// running one breadth-first search per vertex.
1109    ///
1110    /// Binds [`igraph_count_reachable`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_count_reachable).
1111    ///
1112    /// # Examples
1113    /// ```
1114    /// use igraph::prelude::*;
1115    /// // The directed path 0 -> 1 -> 2.
1116    /// let g = Graph::from_edges(&[(0, 1), (1, 2)], 3, true).unwrap();
1117    /// assert_eq!(g.count_reachable(NeighborMode::Out).unwrap(), vec![3, 2, 1]);
1118    /// assert_eq!(g.count_reachable(NeighborMode::In).unwrap(), vec![1, 2, 3]);
1119    /// ```
1120    pub fn count_reachable(&self, mode: NeighborMode) -> Result<Vec<usize>> {
1121        let mut res = VectorInt::new();
1122        igraph_call!(igraph_count_reachable(self, &mut res, mode.into()))?;
1123        Ok(to_usizes(res))
1124    }
1125
1126    /// The transitive closure of the graph.
1127    ///
1128    /// The result has the same vertices and directedness, and an edge
1129    /// `i -> j` (`i != j`) exactly when `j` is reachable from `i` in the
1130    /// original graph; it is simple (no loops nor multi-edges). For undirected
1131    /// graphs every component becomes a clique.
1132    ///
1133    /// Time complexity: O(|V|² + |E|).
1134    ///
1135    /// For a bounded number of steps use [`Graph::graph_power`] (a new graph)
1136    /// or [`Graph::connect_neighborhood`] (in place).
1137    ///
1138    /// Binds [`igraph_transitive_closure`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_transitive_closure).
1139    ///
1140    /// # Examples
1141    /// ```
1142    /// use igraph::prelude::*;
1143    /// let g = Graph::from_edges(&[(0, 1), (1, 2)], 3, true).unwrap();
1144    /// let tc = g.transitive_closure().unwrap();
1145    /// assert_eq!(tc.ecount(), 3);
1146    /// assert!(tc.get_eid(0, 2, true).unwrap().is_some());
1147    /// ```
1148    pub fn transitive_closure(&self) -> Result<Graph> {
1149        Graph::init_with(|closure| unsafe { igraph_transitive_closure(self, closure) })
1150    }
1151
1152    // ----- neighborhoods ----------------------------------------------------
1153
1154    /// The sizes of the neighborhoods of the selected vertices.
1155    ///
1156    /// The neighborhood of order `k` of a vertex contains the vertices at
1157    /// distance at most `k` from it: order 0 is the vertex itself, order 1
1158    /// adds its neighbors, and so on; `order = None` means no limit (the
1159    /// whole reachable set). Vertices closer than `mindist` are not counted:
1160    /// `mindist = 1` excludes the vertex itself, `2` its neighbors too, etc.
1161    /// With [`NeighborMode::Out`] paths follow edge directions, with
1162    /// [`NeighborMode::In`] they go against them, [`NeighborMode::All`]
1163    /// ignores them.
1164    ///
1165    /// Time complexity: O(n d o), n being the number of selected vertices,
1166    /// d the average degree and o the order.
1167    ///
1168    /// See also [`Graph::distances`] for the distances themselves and
1169    /// [`Graph::bfs`] for a full breadth-first traversal.
1170    ///
1171    /// Binds [`igraph_neighborhood_size`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_neighborhood_size).
1172    ///
1173    /// # Errors
1174    /// Invalid vertex ids, or `mindist > order`.
1175    ///
1176    /// # Examples
1177    /// ```
1178    /// use igraph::prelude::*;
1179    /// // The path 0-1-2-3-4.
1180    /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4)], 5, false).unwrap();
1181    /// assert_eq!(g.neighborhood_size(.., Some(1), NeighborMode::All, 0).unwrap(), vec![2, 3, 3, 3, 2]);
1182    /// // Vertices at distance exactly 2.
1183    /// assert_eq!(g.neighborhood_size(.., Some(2), NeighborMode::All, 2).unwrap(), vec![1, 1, 2, 1, 1]);
1184    /// ```
1185    pub fn neighborhood_size<'a>(
1186        &self,
1187        vids: impl Into<VertexSelector<'a>>,
1188        order: Option<usize>,
1189        mode: NeighborMode,
1190        mindist: usize,
1191    ) -> Result<Vec<usize>> {
1192        let mut res = VectorInt::new();
1193        with_vs(vids.into(), |vs| {
1194            igraph_call!(igraph_neighborhood_size(
1195                self,
1196                &mut res,
1197                vs,
1198                order_raw(order),
1199                mode.into(),
1200                int_sat(mindist)
1201            ))
1202        })?;
1203        Ok(to_usizes(res))
1204    }
1205
1206    /// The neighborhoods of the selected vertices, as vertex lists.
1207    ///
1208    /// See [`neighborhood_size`](Self::neighborhood_size) for the meaning of
1209    /// `order` (`None` = unlimited), `mode` and `mindist`. Each list is in
1210    /// breadth-first order: the vertex itself first (unless excluded by
1211    /// `mindist`), then vertices at distance 1, 2, ...
1212    ///
1213    /// Time complexity: O(n d o).
1214    ///
1215    /// See also [`Graph::connect_neighborhood`], which adds an edge from each
1216    /// vertex to every member of its neighborhood.
1217    ///
1218    /// Binds [`igraph_neighborhood`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_neighborhood).
1219    ///
1220    /// # Errors
1221    /// Invalid vertex ids, or `mindist > order`.
1222    ///
1223    /// # Examples
1224    /// ```
1225    /// use igraph::prelude::*;
1226    /// // The directed path 0 -> 1 -> 2 -> 3.
1227    /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, true).unwrap();
1228    /// assert_eq!(g.neighborhood(1, None, NeighborMode::Out, 0).unwrap(), vec![vec![1, 2, 3]]);
1229    /// assert_eq!(g.neighborhood(&[1, 3], Some(1), NeighborMode::In, 1).unwrap(), vec![vec![0], vec![2]]);
1230    /// ```
1231    pub fn neighborhood<'a>(
1232        &self,
1233        vids: impl Into<VertexSelector<'a>>,
1234        order: Option<usize>,
1235        mode: NeighborMode,
1236        mindist: usize,
1237    ) -> Result<Vec<Vec<VertexId>>> {
1238        let mut res = VectorIntList::new();
1239        with_vs(vids.into(), |vs| {
1240            igraph_call!(igraph_neighborhood(
1241                self,
1242                &mut res,
1243                vs,
1244                order_raw(order),
1245                mode.into(),
1246                int_sat(mindist)
1247            ))
1248        })?;
1249        Ok(res.to_vecs())
1250    }
1251
1252    /// The subgraphs induced by the neighborhoods of the selected vertices.
1253    ///
1254    /// See [`neighborhood_size`](Self::neighborhood_size) for the meaning of
1255    /// `order` (`None` = unlimited), `mode` and `mindist`. In each graph the
1256    /// vertices are renumbered consecutively *preserving their relative
1257    /// order* in the original graph (as in an induced subgraph), so the
1258    /// new id of an original vertex `v` is the number of neighborhood
1259    /// members with a smaller id than `v`. Each graph is thus the same as
1260    /// [`Graph::induced_subgraph`] of the corresponding
1261    /// [`neighborhood`](Self::neighborhood) (an "ego network").
1262    ///
1263    /// Time complexity: O(n (|V| + |E|)).
1264    ///
1265    /// Binds [`igraph_neighborhood_graphs`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_neighborhood_graphs).
1266    ///
1267    /// # Errors
1268    /// Invalid vertex ids, or `mindist > order`.
1269    ///
1270    /// # Examples
1271    /// ```
1272    /// use igraph::prelude::*;
1273    /// // A star with center 0 and leaves 1..=4: its 1-neighborhoods.
1274    /// let g = Graph::from_edges(&[(0, 1), (0, 2), (0, 3), (0, 4)], 5, false).unwrap();
1275    /// let egos = g.neighborhood_graphs(&[0, 1], Some(1), NeighborMode::All, 0).unwrap();
1276    /// assert_eq!((egos[0].vcount(), egos[0].ecount()), (5, 4));
1277    /// assert_eq!((egos[1].vcount(), egos[1].ecount()), (2, 1));
1278    /// ```
1279    pub fn neighborhood_graphs<'a>(
1280        &self,
1281        vids: impl Into<VertexSelector<'a>>,
1282        order: Option<usize>,
1283        mode: NeighborMode,
1284        mindist: usize,
1285    ) -> Result<Vec<Graph>> {
1286        let mut res = GraphList::new();
1287        with_vs(vids.into(), |vs| {
1288            igraph_call!(igraph_neighborhood_graphs(
1289                self,
1290                &mut res,
1291                vs,
1292                order_raw(order),
1293                mode.into(),
1294                int_sat(mindist)
1295            ))
1296        })?;
1297        Ok(res.into_vec())
1298    }
1299}