Skip to main content

igraph/
graph.rs

1//! The [`Graph`] type and igraph's basic interface (`igraph_interface.h`).
2//!
3//! [`Graph`] is the C struct `igraph_t` itself, made Rusty: it owns its data
4//! (destroyed on [`Drop`]), it is [`Clone`] (via `igraph_copy`) and [`Send`],
5//! and its methods return [`Result`]s instead of error codes. Vertices and
6//! edges are identified by consecutive integer ids starting from zero, as in
7//! igraph; ids are `i64` (`igraph_int_t`) while counts are `usize`.
8//!
9//! This module contains the fundamental operations: construction, adding
10//! and removing vertices and edges, and queries about the structure.
11//! Algorithms live in the other modules of the crate, most of them as further
12//! methods of [`Graph`].
13//!
14//! ```
15//! use igraph::prelude::*;
16//!
17//! // A directed triangle plus a pendant vertex.
18//! let mut g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 4, true).unwrap();
19//! g.add_edge(2, 3).unwrap();
20//! assert_eq!((g.vcount(), g.ecount()), (4, 4));
21//! assert_eq!(g.neighbors(2, NeighborMode::Out).unwrap(), vec![0, 3]);
22//! assert_eq!(g.edge(3).unwrap(), (2, 3));
23//! assert_eq!(g.get_eid(1, 2, true).unwrap(), Some(1));
24//! assert_eq!(g.get_eid(2, 1, true).unwrap(), None);
25//!
26//! let h = g.clone();
27//! g.delete_vertices(3).unwrap();
28//! assert_eq!((g.vcount(), h.vcount()), (3, 4));
29//! ```
30//!
31//! The functions of `igraph_interface.h` map as follows:
32//!
33//! | C function | Rust |
34//! |------------|------|
35//! | `igraph_empty`, `igraph_create` | [`Graph::new`], [`Graph::empty`], [`Graph::from_edges`], [`Graph::from_flat_edges`] |
36//! | `igraph_empty_attrs` | [`Graph::empty`] followed by [`set_graph_attr`](Graph::set_graph_attr) (see [`attributes`](crate::attributes)) |
37//! | `igraph_copy`, `igraph_destroy` | [`Clone`], [`Graph::try_clone`], [`Drop`] |
38//! | `igraph_add_vertices`, `igraph_add_edge(s)` | [`add_vertices`](Graph::add_vertices), [`add_vertex`](Graph::add_vertex), [`add_edge`](Graph::add_edge), [`add_edge_id`](Graph::add_edge_id), [`add_edges`](Graph::add_edges), [`add_edges_from_slice`](Graph::add_edges_from_slice), [`add_edges_from_vector`](Graph::add_edges_from_vector) |
39//! | `igraph_delete_vertices(_map)`, `igraph_delete_edges` | [`delete_vertices`](Graph::delete_vertices), [`delete_vertices_map`](Graph::delete_vertices_map), [`delete_edges`](Graph::delete_edges) |
40//! | `igraph_vcount`, `igraph_ecount`, `igraph_is_directed` | [`vcount`](Graph::vcount), [`ecount`](Graph::ecount), [`num_vertices`](Graph::num_vertices), [`num_edges`](Graph::num_edges), [`vertices`](Graph::vertices), [`edge_ids`](Graph::edge_ids), [`is_directed`](Graph::is_directed) |
41//! | `igraph_neighbors`, `igraph_incident` | [`neighbors`](Graph::neighbors), [`neighbors_with`](Graph::neighbors_with), [`incident`](Graph::incident) |
42//! | `igraph_degree(_1)` | [`degree`](Graph::degree), [`degree_of`](Graph::degree_of) |
43//! | `igraph_edge(s)` | [`edge`](Graph::edge), [`edges`](Graph::edges), [`edges_flat`](Graph::edges_flat), [`edge_list`](Graph::edge_list), [`edges_iter`](Graph::edges_iter) |
44//! | `IGRAPH_FROM`, `IGRAPH_TO`, `IGRAPH_OTHER` | [`edge_from`](Graph::edge_from), [`edge_to`](Graph::edge_to), [`other_endpoint`](Graph::other_endpoint) |
45//! | `igraph_get_eid(s)`, `igraph_get_all_eids_between` | [`get_eid`](Graph::get_eid), [`has_edge`](Graph::has_edge), [`get_eids`](Graph::get_eids), [`get_eids_opt`](Graph::get_eids_opt), [`get_all_eids_between`](Graph::get_all_eids_between) |
46//! | `igraph_is_same_graph` | [`is_same_graph`](Graph::is_same_graph), [`PartialEq`] |
47//! | `igraph_invalidate_cache` | [`invalidate_cache`](Graph::invalidate_cache) |
48//! | `igraph_vs_*`, `igraph_es_*` (`igraph_iterators.h`) | [`select_vertices`](Graph::select_vertices), [`select_edges`](Graph::select_edges), [`vs_size`](Graph::vs_size), [`es_size`](Graph::es_size), [`adjacent_vertices`](Graph::adjacent_vertices), [`incident_edges`](Graph::incident_edges) |
49//!
50//! For raw FFI users, [`Graph::init_with`] turns any C function that
51//! initializes an `igraph_t` into a `Result<Graph>`, and [`Graph::setup`]
52//! initializes igraph for the calling thread (every safe wrapper does it
53//! automatically, see [`crate::error::ensure_init`]).
54//!
55//! `Graph` is [`Send`] (it can be moved to another thread) but not [`Sync`]:
56//! igraph lazily caches properties inside the graph even through `const`
57//! pointers.
58//!
59//! # Where to go next
60//!
61//! Most graphs are not built edge by edge:
62//!
63//! - deterministic graphs (rings, lattices, trees, the named graphs of
64//!   [`Graph::famous`], ...) are in [`constructors`](crate::constructors),
65//!   random graph models in [`games`](crate::games), and file readers and
66//!   writers in [`foreign`](crate::foreign);
67//! - conversions to and from matrices and edge lists are in
68//!   [`conversion`](crate::conversion) (e.g. [`Graph::get_adjacency`]) and
69//!   [`constructors`](crate::constructors) (e.g. [`Graph::adjacency`]);
70//! - structural queries beyond degrees (strength, simplicity, multi-edges,
71//!   density, ...) are in [`structural`](crate::structural), subgraphs and
72//!   simplification in [`operators`](crate::operators), and vertex, edge and
73//!   graph attributes in [`attributes`](crate::attributes);
74//! - for repeated neighborhood queries, [`adjlist`](crate::adjlist) builds
75//!   adjacency and incidence lists once.
76//!
77//! ```
78//! use igraph::prelude::*;
79//!
80//! // Zachary's karate club, one of igraph's named graphs.
81//! let club = Graph::famous("Zachary").unwrap();
82//! assert_eq!((club.vcount(), club.ecount()), (34, 78));
83//! let degree = club.degree(.., NeighborMode::All, Loops::Twice).unwrap();
84//! // The handshake lemma: every edge has two endpoints.
85//! assert_eq!(degree.iter().sum::<i64>(), 2 * 78);
86//! // The instructor (0) and the administrator (33) are the hubs.
87//! assert_eq!((degree[0], degree[33]), (16, 17));
88//! assert_eq!(club.maxdegree(.., NeighborMode::All, Loops::Twice).unwrap(), 17);
89//! ```
90
91use crate::{
92    constants::*,
93    error::{Error, Result, ensure_init},
94    ffi::*,
95    igraph_call,
96    selector::{EdgeSelector, VertexSelector},
97    vector::VectorInt,
98};
99use std::{fmt, mem::MaybeUninit, ops::Range};
100
101/// An igraph graph (`igraph_t`), see the [module docs](self).
102pub type Graph = igraph_t;
103
104/// Vertex identifier (`igraph_int_t`).
105pub type VertexId = igraph_int_t;
106/// Edge identifier (`igraph_int_t`).
107pub type EdgeId = igraph_int_t;
108
109impl Drop for igraph_t {
110    /// Destroys the graph with `igraph_destroy`.
111    fn drop(&mut self) {
112        // A zeroed (never initialized) graph has null storage: nothing to free.
113        if !self.from.stor_begin.is_null() {
114            unsafe { igraph_destroy(self) };
115        }
116    }
117}
118
119impl Clone for igraph_t {
120    /// Deep copy with `igraph_copy`.
121    fn clone(&self) -> Self {
122        Self::init_with(|g| unsafe { igraph_copy(g, self) }).expect("igraph failed to copy a graph")
123    }
124}
125
126// A graph owns its data; the (lazily filled) property cache is mutated even
127// through `&Graph`, hence `Send` but not `Sync`.
128unsafe impl Send for igraph_t {}
129
130impl fmt::Display for igraph_t {
131    /// A one-line summary, e.g. `Undirected graph with 3 vertices and 2 edges`.
132    ///
133    /// The alternate form (`{:#}`) appends the edge list, one edge per line,
134    /// as `from -> to` (directed) or `from -- to` (undirected):
135    ///
136    /// ```
137    /// use igraph::prelude::*;
138    /// let g = Graph::from_edges(&[(0, 1), (1, 2)], 3, false).unwrap();
139    /// assert_eq!(g.to_string(), "Undirected graph with 3 vertices and 2 edges");
140    /// assert_eq!(format!("{g:#}"), "Undirected graph with 3 vertices and 2 edges\n0 -- 1\n1 -- 2");
141    /// ```
142    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143        write!(
144            f,
145            "{} graph with {} vertices and {} edges",
146            if self.is_directed() {
147                "Directed"
148            } else {
149                "Undirected"
150            },
151            self.vcount(),
152            self.ecount()
153        )?;
154        if f.alternate() {
155            let arrow = if self.is_directed() { "->" } else { "--" };
156            for (from, to) in self.edge_list() {
157                write!(f, "\n{from} {arrow} {to}")?;
158            }
159        }
160        Ok(())
161    }
162}
163
164impl PartialEq for igraph_t {
165    /// Two graphs are equal when they are the same *labelled* graph: same
166    /// directedness, vertex count and multiset of edges written in terms of
167    /// vertex ids (the order of the edges, and of the endpoints of undirected
168    /// edges, does not matter), see [`is_same_graph`](Graph::is_same_graph).
169    /// Use the isomorphism functions to compare structure up to relabeling.
170    fn eq(&self, other: &Self) -> bool {
171        self.is_same_graph(other).unwrap_or(false)
172    }
173}
174
175impl igraph_t {
176    /// Initializes igraph for the calling thread (see [`crate::error::ensure_init`]).
177    ///
178    /// Calling it is optional: every safe wrapper does it automatically.
179    pub fn setup() {
180        ensure_init();
181    }
182
183    /// Creates a graph by running an igraph *constructor*, i.e. a C function
184    /// that initializes the `igraph_t` pointed to by its argument.
185    ///
186    /// This is the building block of all the wrappers returning new graphs,
187    /// and it is useful to call raw constructors not wrapped by this crate.
188    /// `f` must either fail or fully initialize the graph; if it reports
189    /// success without initializing it, [`ErrorKind::Internal`](crate::error::ErrorKind::Internal)
190    /// is returned:
191    ///
192    /// ```
193    /// use igraph::{ffi, prelude::*};
194    /// let ring = Graph::init_with(|g| unsafe { ffi::igraph_ring(g, 5, false, false, true) }).unwrap();
195    /// assert_eq!(ring.ecount(), 5);
196    /// ```
197    pub fn init_with(f: impl FnOnce(*mut igraph_t) -> igraph_error_t) -> Result<Self> {
198        // Like `igraph_call!`: initializes the thread, forgets any stale
199        // error record, and refuses to nest too deeply inside callbacks.
200        crate::error::prepare_call()?;
201        // Zeroed storage is safe to drop (see `Drop`), should `f` fail early.
202        let mut graph = MaybeUninit::<igraph_t>::zeroed();
203        crate::error::check(f(graph.as_mut_ptr()))?;
204        // On failure igraph has already released whatever it allocated. On
205        // success, make sure the closure really initialized the graph: a
206        // (safe) closure returning `IGRAPH_SUCCESS` without calling a
207        // constructor would otherwise hand out a graph with null storage.
208        let graph = unsafe { graph.assume_init() };
209        if graph.from.stor_begin.is_null() {
210            return Err(Error::new(
211                crate::error::ErrorKind::Internal,
212                "the constructor passed to Graph::init_with did not initialize the graph",
213            ));
214        }
215        Ok(graph)
216    }
217
218    /// Creates a graph with `num_vertices` isolated vertices ([`igraph_empty`](https://igraph.org/c/html/latest/igraph-Basic.html#igraph_empty)).
219    ///
220    /// # Panics
221    /// If igraph fails to allocate the graph, like [`Vec`] does, or if
222    /// `num_vertices` exceeds igraph's maximum vertex count (see
223    /// [`empty`](Self::empty) for the fallible version).
224    pub fn new(num_vertices: usize, directed: bool) -> Self {
225        Self::empty(num_vertices, directed).expect("igraph failed to create a graph")
226    }
227
228    /// Creates a graph with `num_vertices` isolated vertices ([`igraph_empty`](https://igraph.org/c/html/latest/igraph-Basic.html#igraph_empty)).
229    ///
230    /// # Errors
231    /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if
232    /// `num_vertices` does not fit in an `i64`;
233    /// [`ErrorKind::Range`](crate::error::ErrorKind::Range) if it exceeds
234    /// igraph's maximum vertex count, or
235    /// [`ErrorKind::OutOfMemory`](crate::error::ErrorKind::OutOfMemory).
236    pub fn empty(num_vertices: usize, directed: bool) -> Result<Self> {
237        let n = count_arg(num_vertices, "number of vertices")?;
238        Self::init_with(|g| unsafe { igraph_empty(g, n, directed) })
239    }
240
241    /// Creates a graph from a list of `(from, to)` edges
242    /// ([`igraph_create`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_create)).
243    ///
244    /// The graph has `max(num_vertices, largest id + 1)` vertices; in an
245    /// undirected graph `(a, b)` and `(b, a)` denote the same edge. Multi-edges
246    /// and loops are kept; edge `i` is the `i`-th pair. See
247    /// [`constructors`](crate::constructors) for ready-made graphs, and
248    /// [`Graph::read_graph_edgelist_from_str`] to parse an edge list.
249    ///
250    /// ```
251    /// use igraph::prelude::*;
252    /// let g = Graph::from_edges(&[(0, 1), (1, 2)], 5, true).unwrap();
253    /// assert_eq!((g.vcount(), g.ecount()), (5, 2));
254    /// // The vertex count grows to fit the largest id.
255    /// let h = Graph::from_edges(&[(0, 7)], 0, false).unwrap();
256    /// assert_eq!(h.vcount(), 8);
257    /// ```
258    ///
259    /// # Errors
260    /// [`ErrorKind::InvalidVertexId`](crate::error::ErrorKind::InvalidVertexId) for negative ids.
261    pub fn from_edges(
262        edges: &[(VertexId, VertexId)],
263        num_vertices: usize,
264        directed: bool,
265    ) -> Result<Self> {
266        let flat: VectorInt = edges.iter().flat_map(|&(a, b)| [a, b]).collect();
267        Self::from_flat_edges(&flat, num_vertices, directed)
268    }
269
270    /// Creates a graph from a flat edge list `[from0, to0, from1, to1, ...]`
271    /// ([`igraph_create`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_create)).
272    ///
273    /// # Errors
274    /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) for an odd length
275    /// or a `num_vertices` that does not fit in an `i64`,
276    /// [`ErrorKind::InvalidVertexId`](crate::error::ErrorKind::InvalidVertexId) for negative ids.
277    pub fn from_flat_edges(
278        edges: &[VertexId],
279        num_vertices: usize,
280        directed: bool,
281    ) -> Result<Self> {
282        if !edges.len().is_multiple_of(2) {
283            return Err(Error::invalid(
284                "the flat edge list must have an even length",
285            ));
286        }
287        let n = count_arg(num_vertices, "number of vertices")?;
288        let view = VectorInt::view(edges);
289        Self::init_with(|g| unsafe { igraph_create(g, view.as_ptr(), n, directed) })
290    }
291
292    /// Number of vertices ([`igraph_vcount`](https://igraph.org/c/html/latest/igraph-Basic.html#igraph_vcount)), O(1).
293    pub fn vcount(&self) -> usize {
294        unsafe { igraph_vcount(self) as usize }
295    }
296
297    /// Number of edges ([`igraph_ecount`](https://igraph.org/c/html/latest/igraph-Basic.html#igraph_ecount)), O(1).
298    pub fn ecount(&self) -> usize {
299        unsafe { igraph_ecount(self) as usize }
300    }
301
302    /// Number of vertices; alias of [`vcount`](Self::vcount).
303    pub fn num_vertices(&self) -> usize {
304        self.vcount()
305    }
306
307    /// Number of edges; alias of [`ecount`](Self::ecount).
308    pub fn num_edges(&self) -> usize {
309        self.ecount()
310    }
311
312    /// Whether the graph is directed ([`igraph_is_directed`](https://igraph.org/c/html/latest/igraph-Basic.html#igraph_is_directed)).
313    pub fn is_directed(&self) -> bool {
314        unsafe { igraph_is_directed(self) }
315    }
316
317    /// The range of all vertex ids, `0..vcount`.
318    pub fn vertices(&self) -> Range<VertexId> {
319        0..self.vcount() as VertexId
320    }
321
322    /// The range of all edge ids, `0..ecount`.
323    pub fn edge_ids(&self) -> Range<EdgeId> {
324        0..self.ecount() as EdgeId
325    }
326
327    /// Adds `n` isolated vertices, with ids `vcount..vcount + n`
328    /// ([`igraph_add_vertices`](https://igraph.org/c/html/latest/igraph-Basic.html#igraph_add_vertices)).
329    ///
330    /// # Errors
331    /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if
332    /// `n` does not fit in an `i64`;
333    /// [`ErrorKind::Overflow`](crate::error::ErrorKind::Overflow) or
334    /// [`ErrorKind::Range`](crate::error::ErrorKind::Range) if the new vertex
335    /// count would overflow or exceed igraph's maximum vertex count.
336    pub fn add_vertices(&mut self, n: usize) -> Result<()> {
337        let n = count_arg(n, "number of vertices to add")?;
338        igraph_call!(igraph_add_vertices(self, n, std::ptr::null()))
339    }
340
341    /// Adds a single edge ([`igraph_add_edge`](https://igraph.org/c/html/latest/igraph-Basic.html#igraph_add_edge)); for many edges
342    /// [`add_edges`](Self::add_edges) is much faster.
343    ///
344    /// # Errors
345    /// [`ErrorKind::InvalidVertexId`](crate::error::ErrorKind::InvalidVertexId) if an endpoint does not exist.
346    pub fn add_edge(&mut self, from: VertexId, to: VertexId) -> Result<()> {
347        igraph_call!(igraph_add_edge(self, from, to))
348    }
349
350    /// Adds the given `(from, to)` edges, with ids `ecount..` in order
351    /// ([`igraph_add_edges`](https://igraph.org/c/html/latest/igraph-Basic.html#igraph_add_edges)).
352    ///
353    /// # Errors
354    /// [`ErrorKind::InvalidVertexId`](crate::error::ErrorKind::InvalidVertexId) if an endpoint does not
355    /// exist (then no edge is added).
356    pub fn add_edges(&mut self, edges: &[(VertexId, VertexId)]) -> Result<()> {
357        let flat: VectorInt = edges.iter().flat_map(|&(a, b)| [a, b]).collect();
358        self.add_edges_from_vector(&flat)
359    }
360
361    /// Adds the given `(from, to)` edges; alias of [`add_edges`](Self::add_edges).
362    pub fn add_edges_from_slice(&mut self, edges: &[(VertexId, VertexId)]) -> Result<()> {
363        self.add_edges(edges)
364    }
365
366    /// Adds the edges of a flat list `[from0, to0, from1, to1, ...]` ([`igraph_add_edges`](https://igraph.org/c/html/latest/igraph-Basic.html#igraph_add_edges)).
367    ///
368    /// # Errors
369    /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) for an odd length,
370    /// [`ErrorKind::InvalidVertexId`](crate::error::ErrorKind::InvalidVertexId) for missing vertices.
371    pub fn add_edges_from_vector(&mut self, edges: &[VertexId]) -> Result<()> {
372        if !edges.len().is_multiple_of(2) {
373            return Err(Error::invalid(
374                "the flat edge list must have an even length",
375            ));
376        }
377        let view = VectorInt::view(edges);
378        igraph_call!(igraph_add_edges(self, view.as_ptr(), std::ptr::null()))
379    }
380
381    /// Removes the selected edges ([`igraph_delete_edges`](https://igraph.org/c/html/latest/igraph-Basic.html#igraph_delete_edges)); the
382    /// remaining edges keep their relative order and are renumbered to stay
383    /// consecutive.
384    ///
385    /// # Errors
386    /// [`ErrorKind::InvalidEdgeId`](crate::error::ErrorKind::InvalidEdgeId) for invalid edges.
387    pub fn delete_edges<'a>(&mut self, edges: impl Into<EdgeSelector<'a>>) -> Result<()> {
388        let es = edges.into().to_raw()?;
389        igraph_call!(igraph_delete_edges(self, es.get()))
390    }
391
392    /// Removes the selected vertices and their incident edges
393    /// ([`igraph_delete_vertices`](https://igraph.org/c/html/latest/igraph-Basic.html#igraph_delete_vertices)); the remaining vertices and edges are
394    /// renumbered to stay consecutive (see
395    /// [`delete_vertices_map`](Self::delete_vertices_map) for the mapping).
396    /// To keep the original graph, build the complementary
397    /// [`Graph::induced_subgraph`] instead.
398    ///
399    /// # Errors
400    /// [`ErrorKind::InvalidVertexId`](crate::error::ErrorKind::InvalidVertexId) for invalid vertices.
401    pub fn delete_vertices<'a>(&mut self, vertices: impl Into<VertexSelector<'a>>) -> Result<()> {
402        let vs = vertices.into().to_raw()?;
403        igraph_call!(igraph_delete_vertices(self, vs.get()))
404    }
405
406    /// Removes the selected vertices and their incident edges, returning
407    /// `(map, invmap)`
408    /// ([`igraph_delete_vertices_map`](https://igraph.org/c/html/latest/igraph-Basic.html#igraph_delete_vertices_map)):
409    /// `map[old]` is the new id of vertex `old`, or `-1` if it was deleted,
410    /// and `invmap[new]` is the old id of vertex `new`.
411    ///
412    /// ```
413    /// use igraph::prelude::*;
414    /// let mut g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
415    /// let (map, invmap) = g.delete_vertices_map(&[1]).unwrap();
416    /// assert_eq!(map, vec![0, -1, 1, 2]);
417    /// assert_eq!(invmap, vec![0, 2, 3]);
418    /// assert_eq!(g.edge_list(), vec![(1, 2)]);
419    /// ```
420    ///
421    /// # Errors
422    /// [`ErrorKind::InvalidVertexId`](crate::error::ErrorKind::InvalidVertexId) for invalid vertices.
423    pub fn delete_vertices_map<'a>(
424        &mut self,
425        vertices: impl Into<VertexSelector<'a>>,
426    ) -> Result<(Vec<VertexId>, Vec<VertexId>)> {
427        let vs = vertices.into().to_raw()?;
428        let mut map = VectorInt::new();
429        let mut invmap = VectorInt::new();
430        igraph_call!(igraph_delete_vertices_map(
431            self,
432            vs.get(),
433            &mut map,
434            &mut invmap
435        ))?;
436        Ok((map.into(), invmap.into()))
437    }
438
439    /// Neighbors of a vertex, sorted, counting loop edges twice and keeping
440    /// multi-edges, so that the result has as many entries as the degree
441    /// ([`igraph_neighbors`](https://igraph.org/c/html/latest/igraph-Basic.html#igraph_neighbors) with `IGRAPH_LOOPS_TWICE`
442    /// and `IGRAPH_MULTIPLE`); see [`neighbors_with`](Self::neighbors_with)
443    /// for other conventions.
444    ///
445    /// For many queries on the same graph, an
446    /// [`AdjList`](crate::adjlist::AdjList) is faster; for the vertices
447    /// within a given distance, see [`Graph::neighborhood`].
448    ///
449    /// ```
450    /// use igraph::prelude::*;
451    /// let star = Graph::star(4, StarMode::Undirected, 0).unwrap();
452    /// assert_eq!(star.neighbors(0, NeighborMode::All).unwrap(), vec![1, 2, 3]);
453    /// assert_eq!(star.neighbors(2, NeighborMode::All).unwrap(), vec![0]);
454    /// ```
455    ///
456    /// # Errors
457    /// [`ErrorKind::InvalidVertexId`](crate::error::ErrorKind::InvalidVertexId) for an invalid vertex.
458    pub fn neighbors(&self, vertex: VertexId, mode: NeighborMode) -> Result<Vec<VertexId>> {
459        self.neighbors_with(vertex, mode, Loops::Twice, true)
460    }
461
462    /// Neighbors of a vertex with full control over loops and multi-edges
463    /// ([`igraph_neighbors`](https://igraph.org/c/html/latest/igraph-Basic.html#igraph_neighbors)). The result is sorted.
464    ///
465    /// `mode` selects out-, in- or all neighbors in directed graphs (it is
466    /// ignored for undirected ones); `loops` says whether a loop edge makes
467    /// the vertex its own neighbor zero, one or two times; with
468    /// `multiple = false` each neighbor appears once however many parallel
469    /// edges lead to it.
470    ///
471    /// ```
472    /// use igraph::prelude::*;
473    /// let g = Graph::from_edges(&[(0, 0), (0, 1), (0, 1)], 2, false).unwrap();
474    /// assert_eq!(g.neighbors_with(0, NeighborMode::All, Loops::Twice, true).unwrap(), vec![0, 0, 1, 1]);
475    /// assert_eq!(g.neighbors_with(0, NeighborMode::All, Loops::None, false).unwrap(), vec![1]);
476    /// ```
477    ///
478    /// # Errors
479    /// [`ErrorKind::InvalidVertexId`](crate::error::ErrorKind::InvalidVertexId) for an invalid vertex.
480    pub fn neighbors_with(
481        &self,
482        vertex: VertexId,
483        mode: NeighborMode,
484        loops: Loops,
485        multiple: bool,
486    ) -> Result<Vec<VertexId>> {
487        let mut res = VectorInt::new();
488        igraph_call!(igraph_neighbors(
489            self,
490            &mut res,
491            vertex,
492            mode.into(),
493            loops.into(),
494            multiple
495        ))?;
496        Ok(res.into())
497    }
498
499    /// Ids of the edges incident to a vertex, ordered like the neighbors
500    /// of [`neighbors_with`](Self::neighbors_with) ([`igraph_incident`](https://igraph.org/c/html/latest/igraph-Basic.html#igraph_incident)).
501    ///
502    /// # Errors
503    /// [`ErrorKind::InvalidVertexId`](crate::error::ErrorKind::InvalidVertexId) for an invalid vertex.
504    pub fn incident(
505        &self,
506        vertex: VertexId,
507        mode: NeighborMode,
508        loops: Loops,
509    ) -> Result<Vec<EdgeId>> {
510        let mut res = VectorInt::new();
511        igraph_call!(igraph_incident(
512            self,
513            &mut res,
514            vertex,
515            mode.into(),
516            loops.into()
517        ))?;
518        Ok(res.into())
519    }
520
521    /// Degrees of the selected vertices, in selector order
522    /// ([`igraph_degree`](https://igraph.org/c/html/latest/igraph-Basic.html#igraph_degree)); `loops` says whether a loop edge counts zero,
523    /// one or two times (two is the convention of the handshake lemma).
524    /// `mode` selects out-, in- or total degrees in directed graphs.
525    ///
526    /// See also [`Graph::strength`] (weighted degrees), [`Graph::maxdegree`]
527    /// and [`Graph::mean_degree`].
528    ///
529    /// ```
530    /// use igraph::prelude::*;
531    /// // A loop at 0 and a double edge 0 - 1.
532    /// let g = Graph::from_edges(&[(0, 0), (0, 1), (0, 1)], 2, false).unwrap();
533    /// assert_eq!(g.degree(.., NeighborMode::All, Loops::Twice).unwrap(), vec![4, 2]);
534    /// assert_eq!(g.degree(.., NeighborMode::All, Loops::Once).unwrap(), vec![3, 2]);
535    /// assert_eq!(g.degree(&[0], NeighborMode::All, Loops::None).unwrap(), vec![2]);
536    /// ```
537    ///
538    /// # Errors
539    /// [`ErrorKind::InvalidVertexId`](crate::error::ErrorKind::InvalidVertexId) for invalid vertices.
540    pub fn degree<'a>(
541        &self,
542        vertices: impl Into<VertexSelector<'a>>,
543        mode: NeighborMode,
544        loops: Loops,
545    ) -> Result<Vec<igraph_int_t>> {
546        let vs = vertices.into().to_raw()?;
547        let mut res = VectorInt::new();
548        igraph_call!(igraph_degree(
549            self,
550            &mut res,
551            vs.get(),
552            mode.into(),
553            loops.into()
554        ))?;
555        Ok(res.into())
556    }
557
558    /// Degree of a single vertex, with the conventions of
559    /// [`degree`](Self::degree) but faster (O(1) when loops count twice,
560    /// O(degree) otherwise)
561    /// ([`igraph_degree_1`](https://igraph.org/c/html/latest/igraph-Basic.html#igraph_degree_1)).
562    ///
563    /// ```
564    /// use igraph::prelude::*;
565    /// let wheel = Graph::wheel(6, WheelMode::Undirected, 0).unwrap();
566    /// assert_eq!(wheel.degree_of(0, NeighborMode::All, Loops::Twice).unwrap(), 5);
567    /// assert_eq!(wheel.degree_of(3, NeighborMode::All, Loops::Twice).unwrap(), 3);
568    /// assert!(wheel.degree_of(6, NeighborMode::All, Loops::Twice).is_err());
569    /// ```
570    ///
571    /// # Errors
572    /// [`ErrorKind::InvalidVertexId`](crate::error::ErrorKind::InvalidVertexId) for an invalid vertex.
573    pub fn degree_of(
574        &self,
575        vertex: VertexId,
576        mode: NeighborMode,
577        loops: Loops,
578    ) -> Result<igraph_int_t> {
579        // igraph 1.0.0 and 1.0.1 do not validate `vid` in `igraph_degree_1`
580        // (it indexes the graph's index vectors directly): check it here.
581        if !self.vertices().contains(&vertex) {
582            return Err(Error::new(
583                crate::error::ErrorKind::InvalidVertexId,
584                format!(
585                    "vertex {vertex} is not in the graph (it has {} vertices)",
586                    self.vcount()
587                ),
588            ));
589        }
590        let mut deg = 0;
591        igraph_call!(igraph_degree_1(
592            self,
593            &mut deg,
594            vertex,
595            mode.into(),
596            loops.into()
597        ))?;
598        Ok(deg)
599    }
600
601    /// Endpoints `(from, to)` of an edge ([`igraph_edge`](https://igraph.org/c/html/latest/igraph-Basic.html#igraph_edge)); for
602    /// undirected graphs `from <= to`.
603    ///
604    /// # Errors
605    /// [`ErrorKind::InvalidEdgeId`](crate::error::ErrorKind::InvalidEdgeId) for an invalid edge.
606    pub fn edge(&self, edge: EdgeId) -> Result<(VertexId, VertexId)> {
607        let (mut from, mut to) = (0, 0);
608        igraph_call!(igraph_edge(self, edge, &mut from, &mut to))?;
609        Ok((from, to))
610    }
611
612    /// Endpoints of the selected edges, in selector order ([`igraph_edges`](https://igraph.org/c/html/latest/igraph-Basic.html#igraph_edges)).
613    ///
614    /// # Errors
615    /// [`ErrorKind::InvalidEdgeId`](crate::error::ErrorKind::InvalidEdgeId) for invalid edges.
616    pub fn edges<'a>(
617        &self,
618        edges: impl Into<EdgeSelector<'a>>,
619    ) -> Result<Vec<(VertexId, VertexId)>> {
620        let es = edges.into().to_raw()?;
621        let mut res = VectorInt::new();
622        igraph_call!(igraph_edges(self, es.get(), &mut res, false))?;
623        Ok(res
624            .as_chunks::<2>()
625            .0
626            .iter()
627            .map(|&[a, b]| (a, b))
628            .collect())
629    }
630
631    /// All the edges as `(from, to)` pairs, in edge id order (for
632    /// undirected graphs `from <= to`).
633    ///
634    /// See [`edges_flat`](Self::edges_flat) and [`Graph::get_edgelist`] for
635    /// a flat list, and [`Graph::write_graph_edgelist_to_string`] for the
636    /// text format.
637    pub fn edge_list(&self) -> Vec<(VertexId, VertexId)> {
638        self.edges(EdgeSelector::All)
639            .expect("listing all edges cannot fail")
640    }
641
642    /// Id of an edge between two vertices, or `None` if they are not
643    /// connected ([`igraph_get_eid`](https://igraph.org/c/html/latest/igraph-Basic.html#igraph_get_eid)). With `directed = false`, edge
644    /// directions are ignored in directed graphs. With multi-edges, any one
645    /// of them may be returned.
646    ///
647    /// # Errors
648    /// [`ErrorKind::InvalidVertexId`](crate::error::ErrorKind::InvalidVertexId) for invalid vertices.
649    pub fn get_eid(&self, from: VertexId, to: VertexId, directed: bool) -> Result<Option<EdgeId>> {
650        let mut eid = -1;
651        igraph_call!(igraph_get_eid(self, &mut eid, from, to, directed, false))?;
652        Ok((eid >= 0).then_some(eid))
653    }
654
655    /// Ids of the edges connecting the given vertex pairs ([`igraph_get_eids`](https://igraph.org/c/html/latest/igraph-Basic.html#igraph_get_eids));
656    /// see [`get_eids_opt`](Self::get_eids_opt) to tolerate missing edges.
657    ///
658    /// # Errors
659    /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if some pair is not
660    /// connected, [`ErrorKind::InvalidVertexId`](crate::error::ErrorKind::InvalidVertexId) for invalid vertices.
661    pub fn get_eids(&self, pairs: &[(VertexId, VertexId)], directed: bool) -> Result<Vec<EdgeId>> {
662        let flat: VectorInt = pairs.iter().flat_map(|&(a, b)| [a, b]).collect();
663        let mut res = VectorInt::new();
664        igraph_call!(igraph_get_eids(self, &mut res, &flat, directed, true))?;
665        Ok(res.into())
666    }
667
668    /// Ids of all the (multi-)edges between two vertices
669    /// ([`igraph_get_all_eids_between`](https://igraph.org/c/html/latest/igraph-Basic.html#igraph_get_all_eids_between)).
670    ///
671    /// # Errors
672    /// [`ErrorKind::InvalidVertexId`](crate::error::ErrorKind::InvalidVertexId) for invalid vertices.
673    pub fn get_all_eids_between(
674        &self,
675        from: VertexId,
676        to: VertexId,
677        directed: bool,
678    ) -> Result<Vec<EdgeId>> {
679        let mut res = VectorInt::new();
680        igraph_call!(igraph_get_all_eids_between(
681            self, &mut res, from, to, directed
682        ))?;
683        Ok(res.into())
684    }
685
686    /// Whether two graphs are the same labelled graph: same directedness,
687    /// same number of vertices and precisely the same edges, regardless of
688    /// their order ([`igraph_is_same_graph`](https://igraph.org/c/html/latest/igraph-Basic.html#igraph_is_same_graph)).
689    /// It is what `==` on graphs computes. To compare graphs up to a
690    /// relabeling of their vertices, use [`Graph::isomorphic`].
691    ///
692    /// ```
693    /// use igraph::prelude::*;
694    /// let a = Graph::from_edges(&[(0, 1), (2, 1)], 3, false).unwrap();
695    /// let b = Graph::from_edges(&[(1, 2), (0, 1)], 3, false).unwrap();
696    /// let c = Graph::from_edges(&[(0, 2), (1, 2)], 3, false).unwrap(); // isomorphic only
697    /// assert!(a.is_same_graph(&b).unwrap());
698    /// assert!(!a.is_same_graph(&c).unwrap());
699    /// ```
700    pub fn is_same_graph(&self, other: &Self) -> Result<bool> {
701        let mut res = false;
702        igraph_call!(igraph_is_same_graph(self, other, &mut res))?;
703        Ok(res)
704    }
705
706    /// Resolves a vertex selector into the list of vertex ids it denotes
707    /// (`igraph_vs_as_vector`, undocumented in `igraph_iterators.h`).
708    ///
709    /// # Errors
710    /// [`ErrorKind::InvalidVertexId`](crate::error::ErrorKind::InvalidVertexId) (or
711    /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) for ranges) if the selector names
712    /// missing vertices.
713    pub fn select_vertices<'a>(
714        &self,
715        vertices: impl Into<VertexSelector<'a>>,
716    ) -> Result<Vec<VertexId>> {
717        let vs = vertices.into().to_raw()?;
718        let mut res = VectorInt::new();
719        igraph_call!(igraph_vs_as_vector(self, vs.get(), &mut res))?;
720        Ok(res.into())
721    }
722
723    /// Resolves an edge selector into the list of edge ids it denotes
724    /// ([`igraph_es_as_vector`](https://igraph.org/c/html/latest/igraph-Iterators.html#igraph_es_as_vector)).
725    ///
726    /// # Errors
727    /// [`ErrorKind::InvalidEdgeId`](crate::error::ErrorKind::InvalidEdgeId) for missing edges, and
728    /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) for vertex pairs that are not connected.
729    pub fn select_edges<'a>(&self, edges: impl Into<EdgeSelector<'a>>) -> Result<Vec<EdgeId>> {
730        let es = edges.into().to_raw()?;
731        let mut res = VectorInt::new();
732        igraph_call!(igraph_es_as_vector(self, es.get(), &mut res))?;
733        Ok(res.into())
734    }
735
736    /// Invalidates igraph's internal cache of graph properties
737    /// ([`igraph_invalidate_cache`](https://igraph.org/c/html/latest/igraph-Basic.html#igraph_invalidate_cache)); only needed after unsafe raw mutation.
738    pub fn invalidate_cache(&self) {
739        unsafe { igraph_invalidate_cache(self) }
740    }
741
742    /// Runs `f` (typically one igraph call) so that igraph's property cache
743    /// cannot corrupt an adjacency list built in `IGRAPH_ALL` mode with
744    /// `IGRAPH_NO_MULTIPLE`.
745    ///
746    /// In igraph 1.0.0 and 1.0.1, `igraph_adjlist_init` and
747    /// `igraph_lazy_adjlist_init` skip the removal of duplicate neighbors when
748    /// the cache says the graph has no multi-edges. For a *directed* graph
749    /// with a mutual pair `u -> v`, `v -> u` that is wrong in `IGRAPH_ALL`
750    /// mode (each endpoint lists the other twice), and algorithms that rely
751    /// on distinct neighbors then fail assertions or corrupt memory;
752    /// `igraph_adjlist_init` may even cache a wrong `HAS_MULTI = true`.
753    /// Affected C functions include cliques, independent sets, coloring,
754    /// girth, chordality, triangles and transitivity, `igraph_ecc`,
755    /// eccentricity/radius/center/pseudo-diameter, simple paths, local scan,
756    /// Jaccard/Dice similarity, graph power and Reingold–Tilford.
757    ///
758    /// For directed graphs the cache is dropped before `f` and again after
759    /// it, even if `f` panics; undirected graphs are unaffected and `f` runs
760    /// directly. It only costs recomputing cached properties. `Graph` is
761    /// `Send` but not `Sync`, so no other thread observes the cache meanwhile.
762    pub(crate) fn with_fresh_multi_cache<T>(&self, f: impl FnOnce() -> T) -> T {
763        struct Invalidate<'a>(&'a igraph_t);
764        impl Drop for Invalidate<'_> {
765            fn drop(&mut self) {
766                self.0.invalidate_cache();
767            }
768        }
769        if !self.is_directed() {
770            return f();
771        }
772        self.invalidate_cache();
773        let _after = Invalidate(self);
774        f()
775    }
776
777    /// A deep copy of the graph, reporting allocation failures as an
778    /// error instead of panicking like [`Clone::clone`]
779    /// ([`igraph_copy`](https://igraph.org/c/html/latest/igraph-Basic.html#igraph_copy)).
780    pub fn try_clone(&self) -> Result<Self> {
781        Self::init_with(|g| unsafe { igraph_copy(g, self) })
782    }
783
784    /// Adds one isolated vertex and returns its id
785    /// ([`igraph_add_vertices`](https://igraph.org/c/html/latest/igraph-Basic.html#igraph_add_vertices)).
786    ///
787    /// ```
788    /// use igraph::prelude::*;
789    /// let mut g = Graph::new(2, false);
790    /// let v = g.add_vertex().unwrap();
791    /// g.add_edge(0, v).unwrap();
792    /// assert_eq!((v, g.vcount()), (2, 3));
793    /// ```
794    pub fn add_vertex(&mut self) -> Result<VertexId> {
795        let id = self.vcount() as VertexId;
796        self.add_vertices(1)?;
797        Ok(id)
798    }
799
800    /// Adds an edge and returns its id (edges are appended, so the new id
801    /// is the previous edge count)
802    /// ([`igraph_add_edge`](https://igraph.org/c/html/latest/igraph-Basic.html#igraph_add_edge)).
803    pub fn add_edge_id(&mut self, from: VertexId, to: VertexId) -> Result<EdgeId> {
804        let id = self.ecount() as EdgeId;
805        self.add_edge(from, to)?;
806        Ok(id)
807    }
808
809    /// Whether some edge connects `from` and `to` (in this direction if
810    /// `directed` is true and the graph is directed)
811    /// ([`igraph_get_eid`](https://igraph.org/c/html/latest/igraph-Basic.html#igraph_get_eid)).
812    ///
813    /// # Errors
814    /// [`ErrorKind::InvalidVertexId`](crate::error::ErrorKind::InvalidVertexId) for invalid vertices.
815    pub fn has_edge(&self, from: VertexId, to: VertexId, directed: bool) -> Result<bool> {
816        Ok(self.get_eid(from, to, directed)?.is_some())
817    }
818
819    /// Ids of the edges connecting the given vertex pairs, with `None` for
820    /// the pairs that are not connected
821    /// ([`igraph_get_eids`](https://igraph.org/c/html/latest/igraph-Basic.html#igraph_get_eids)
822    /// with `error = false`).
823    ///
824    /// ```
825    /// use igraph::prelude::*;
826    /// let g = Graph::from_edges(&[(0, 1), (1, 2)], 3, false).unwrap();
827    /// assert_eq!(g.get_eids_opt(&[(2, 1), (0, 2)], true).unwrap(), vec![Some(1), None]);
828    /// ```
829    ///
830    /// # Errors
831    /// [`ErrorKind::InvalidVertexId`](crate::error::ErrorKind::InvalidVertexId) for invalid vertices.
832    pub fn get_eids_opt(
833        &self,
834        pairs: &[(VertexId, VertexId)],
835        directed: bool,
836    ) -> Result<Vec<Option<EdgeId>>> {
837        let flat: VectorInt = pairs.iter().flat_map(|&(a, b)| [a, b]).collect();
838        let mut res = VectorInt::new();
839        igraph_call!(igraph_get_eids(self, &mut res, &flat, directed, false))?;
840        Ok(res.iter().map(|&e| (e >= 0).then_some(e)).collect())
841    }
842
843    /// Endpoints of the selected edges as a flat list; with `bycol = false`
844    /// it is `[from0, to0, from1, to1, ...]`, with `bycol = true` it is
845    /// `[from0, from1, ..., to0, to1, ...]`
846    /// ([`igraph_edges`](https://igraph.org/c/html/latest/igraph-Basic.html#igraph_edges)).
847    pub fn edges_flat<'a>(
848        &self,
849        edges: impl Into<EdgeSelector<'a>>,
850        bycol: bool,
851    ) -> Result<Vec<VertexId>> {
852        let es = edges.into().to_raw()?;
853        let mut res = VectorInt::new();
854        igraph_call!(igraph_edges(self, es.get(), &mut res, bycol))?;
855        Ok(res.into())
856    }
857
858    /// The source vertex of an edge (`IGRAPH_FROM`).
859    ///
860    /// # Errors
861    /// [`ErrorKind::InvalidEdgeId`](crate::error::ErrorKind::InvalidEdgeId) for an invalid edge.
862    pub fn edge_from(&self, edge: EdgeId) -> Result<VertexId> {
863        Ok(self.edge(edge)?.0)
864    }
865
866    /// The target vertex of an edge (`IGRAPH_TO`).
867    ///
868    /// # Errors
869    /// [`ErrorKind::InvalidEdgeId`](crate::error::ErrorKind::InvalidEdgeId) for an invalid edge.
870    pub fn edge_to(&self, edge: EdgeId) -> Result<VertexId> {
871        Ok(self.edge(edge)?.1)
872    }
873
874    /// The endpoint of `edge` opposite to `vertex` (`IGRAPH_OTHER`); for a
875    /// loop edge it is `vertex` itself.
876    ///
877    /// # Errors
878    /// [`ErrorKind::InvalidEdgeId`](crate::error::ErrorKind::InvalidEdgeId) for an invalid edge, and
879    /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) if `vertex` is not an
880    /// endpoint of `edge`.
881    pub fn other_endpoint(&self, edge: EdgeId, vertex: VertexId) -> Result<VertexId> {
882        let (from, to) = self.edge(edge)?;
883        if to == vertex {
884            Ok(from)
885        } else if from == vertex {
886            Ok(to)
887        } else {
888            Err(Error::invalid(format!(
889                "vertex {vertex} is not an endpoint of edge {edge}"
890            )))
891        }
892    }
893
894    /// Number of vertices in a vertex selector for this graph
895    /// ([`igraph_vs_size`](https://igraph.org/c/html/latest/igraph-Iterators.html#igraph_vs_size)).
896    ///
897    /// This only *counts*, it does not validate: lists and ranges are
898    /// counted by their length (duplicates included) even if they name
899    /// missing vertices, and a single missing vertex counts as `0`. Only
900    /// the adjacent and non-adjacent selectors query the graph, and fail for
901    /// a missing center.
902    /// Use [`select_vertices`](Self::select_vertices) to resolve and
903    /// validate a selector.
904    ///
905    /// ```
906    /// use igraph::prelude::*;
907    /// let g = Graph::ring(5, false, false, true).unwrap();
908    /// assert_eq!(g.vs_size(..).unwrap(), 5);
909    /// assert_eq!(g.vs_size(&[1, 1, 4]).unwrap(), 3);
910    /// assert_eq!(g.vs_size(VertexSelector::non_adjacent(0, NeighborMode::All)).unwrap(), 3);
911    /// assert_eq!(g.vs_size(9).unwrap(), 0); // not validated
912    /// assert!(g.select_vertices(9).is_err());
913    /// ```
914    ///
915    /// # Errors
916    /// [`ErrorKind::InvalidVertexId`](crate::error::ErrorKind::InvalidVertexId)
917    /// for an adjacent or non-adjacent selector around a missing vertex.
918    pub fn vs_size<'a>(&self, vertices: impl Into<VertexSelector<'a>>) -> Result<usize> {
919        let vs = vertices.into().to_raw()?;
920        let mut n = 0;
921        igraph_call!(igraph_vs_size(self, vs.as_ptr(), &mut n))?;
922        Ok(n as usize)
923    }
924
925    /// Number of edges in an edge selector for this graph
926    /// ([`igraph_es_size`](https://igraph.org/c/html/latest/igraph-Iterators.html#igraph_es_size)).
927    ///
928    /// Like [`vs_size`](Self::vs_size), lists and ranges are counted by
929    /// their length without validation (and a single missing edge counts as
930    /// `0`); incident, pair, path and all-between selectors query the graph.
931    /// Use [`select_edges`](Self::select_edges) to resolve and validate a
932    /// selector.
933    ///
934    /// # Errors
935    /// [`ErrorKind::InvalidVertexId`](crate::error::ErrorKind::InvalidVertexId)
936    /// for selectors around missing vertices,
937    /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) for
938    /// pairs that are not connected.
939    pub fn es_size<'a>(&self, edges: impl Into<EdgeSelector<'a>>) -> Result<usize> {
940        let es = edges.into().to_raw()?;
941        let mut n = 0;
942        igraph_call!(igraph_es_size(self, es.as_ptr(), &mut n))?;
943        Ok(n as usize)
944    }
945
946    /// Neighbors of the vertex through an adjacency *selector*
947    /// ([`igraph_vs_adj`](https://igraph.org/c/html/latest/igraph-Iterators.html#igraph_vs_adj) resolved with
948    /// `igraph_vs_as_vector`, which is undocumented in `igraph_iterators.h`), with full control of
949    /// loops and multi-edges; the result equals
950    /// [`neighbors_with`](Self::neighbors_with) with the same arguments.
951    /// Unlike [`VertexSelector::Adjacent`], which always ignores loops and
952    /// multi-edges, the conventions are configurable.
953    ///
954    /// # Errors
955    /// [`ErrorKind::InvalidVertexId`](crate::error::ErrorKind::InvalidVertexId) for an invalid vertex.
956    pub fn adjacent_vertices(
957        &self,
958        vertex: VertexId,
959        mode: NeighborMode,
960        loops: Loops,
961        multiple: bool,
962    ) -> Result<Vec<VertexId>> {
963        ensure_init();
964        let mut vs = MaybeUninit::<igraph_vs_t>::uninit();
965        igraph_call!(igraph_vs_adj(
966            vs.as_mut_ptr(),
967            vertex,
968            mode.into(),
969            loops.into(),
970            multiple
971        ))?;
972        // Adjacency selectors own no memory: no `igraph_vs_destroy` needed.
973        let vs = unsafe { vs.assume_init() };
974        let mut res = VectorInt::new();
975        igraph_call!(igraph_vs_as_vector(self, vs, &mut res))?;
976        Ok(res.into())
977    }
978
979    /// Incident edges of the vertex with the given loop handling, in
980    /// selector order ([`igraph_es_incident`](https://igraph.org/c/html/latest/igraph-Iterators.html#igraph_es_incident) resolved
981    /// with [`igraph_es_as_vector`](https://igraph.org/c/html/latest/igraph-Iterators.html#igraph_es_as_vector)); unlike
982    /// [`EdgeSelector::Incident`], which lists every loop once, the loop
983    /// counting mode is configurable. The result equals
984    /// [`incident`](Self::incident) with the same arguments.
985    ///
986    /// # Errors
987    /// [`ErrorKind::InvalidVertexId`](crate::error::ErrorKind::InvalidVertexId) for an invalid vertex.
988    pub fn incident_edges(
989        &self,
990        vertex: VertexId,
991        mode: NeighborMode,
992        loops: Loops,
993    ) -> Result<Vec<EdgeId>> {
994        ensure_init();
995        let mut es = MaybeUninit::<igraph_es_t>::uninit();
996        igraph_call!(igraph_es_incident(
997            es.as_mut_ptr(),
998            vertex,
999            mode.into(),
1000            loops.into()
1001        ))?;
1002        let mut es = unsafe { es.assume_init() };
1003        let mut res = VectorInt::new();
1004        let r = igraph_call!(igraph_es_as_vector(self, std::ptr::read(&es), &mut res));
1005        unsafe { igraph_es_destroy(&mut es) };
1006        r?;
1007        Ok(res.into())
1008    }
1009
1010    /// Iterates over the edges as `(id, from, to)` triples, in id order.
1011    ///
1012    /// ```
1013    /// use igraph::prelude::*;
1014    /// let g = Graph::from_edges(&[(0, 1), (1, 2)], 3, true).unwrap();
1015    /// let e: Vec<_> = g.edges_iter().collect();
1016    /// assert_eq!(e, vec![(0, 0, 1), (1, 1, 2)]);
1017    /// ```
1018    pub fn edges_iter(&self) -> impl Iterator<Item = (EdgeId, VertexId, VertexId)> + '_ {
1019        // `from`/`to` are igraph's edge arrays (see `IGRAPH_FROM`/`IGRAPH_TO`);
1020        // like `igraph_edge`, report undirected edges as `(smaller, larger)`,
1021        // igraph stores them the other way around.
1022        let directed = self.is_directed();
1023        self.from
1024            .iter()
1025            .zip(self.to.iter())
1026            .enumerate()
1027            .map(move |(e, (&a, &b))| {
1028                if directed {
1029                    (e as EdgeId, a, b)
1030                } else {
1031                    (e as EdgeId, b, a)
1032                }
1033            })
1034    }
1035}
1036
1037/// Converts a count given as `usize` into an `igraph_int_t`, reporting an
1038/// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue) error
1039/// (instead of wrapping around to a negative number) when it does not fit.
1040fn count_arg(n: usize, what: &str) -> Result<igraph_int_t> {
1041    igraph_int_t::try_from(n)
1042        .map_err(|_| Error::invalid(format!("{what} ({n}) does not fit in an igraph_int_t")))
1043}