igraph/adjlist.rs
1//! Adjacency and incidence lists (`igraph_adjlist.h`).
2//!
3//! Many algorithms visit the neighbors of every vertex over and over again
4//! (shortest paths from all sources, closeness, triangle counting, rewiring,
5//! ...). For them it pays off to extract the graph *once* into a list of
6//! vectors, one per vertex, and work on that. igraph offers four such
7//! structures, all wrapped here as owned Rust types that free their memory on
8//! [`Drop`]:
9//!
10//! | Rust type | C type | contents of list `v` | built |
11//! |--------------------|-------------------------|---------------------------------|------------------------|
12//! | [`AdjList`] | `igraph_adjlist_t` | neighbor vertex ids of `v` | eagerly, all vertices |
13//! | [`IncList`] | `igraph_inclist_t` | incident edge ids of `v` | eagerly, all vertices |
14//! | [`LazyAdjList`] | `igraph_lazy_adjlist_t` | neighbor vertex ids of `v` | on first access of `v` |
15//! | [`LazyIncList`] | `igraph_lazy_inclist_t` | incident edge ids of `v` | on first access of `v` |
16//!
17//! [`AdjList`] and [`IncList`] are *independent* of the graph after creation:
18//! the graph may be modified or dropped, and the lists may be freely edited
19//! (each entry is a [`VectorInt`], so it can grow and shrink) without
20//! affecting the graph. This makes them the ideal scratch representation for
21//! heavy structural edits: extract with [`Graph::adjlist_init`], edit the
22//! lists in O(1) or O(d) per operation, then rebuild a graph with
23//! [`AdjList::to_graph`] (`igraph_adjlist`), paying O(|V|+|E|) only once.
24//!
25//! The *lazy* variants instead borrow the graph (the borrow checker enforces
26//! that the graph outlives them and is not mutated meanwhile) and query the
27//! neighbors of a vertex only the first time it is asked for, caching the
28//! result. They are handy for algorithms that may visit only a small part of
29//! a large graph.
30//!
31//! # Rusty access
32//!
33//! - `al[v]` gives the neighbors of vertex `v` as a `&[i64]` slice
34//! ([`Index`]), `al[v][i] = x` edits in place ([`IndexMut`]);
35//! - [`AdjList::get_mut`] gives the underlying [`VectorInt`] to push, pop,
36//! sort or resize a list;
37//! - [`AdjList::iter`] / `for list in &al` iterate over the lists;
38//! - [`AdjList::to_vecs`] and `Vec::<Vec<i64>>::from(al)` convert to nested
39//! vectors, and `AdjList::from(vec![...])` / `.collect()` build one from
40//! Rust data;
41//! - [`Display`](std::fmt::Display) prints one line per vertex, exactly like
42//! igraph's `igraph_adjlist_print`.
43//!
44//! # Collapsing multi-edges
45//!
46//! With `multiple = false`, [`Graph::adjlist_init`] and
47//! [`Graph::lazy_adjlist_init`] list each neighbor once. In igraph 1.0.0 and
48//! 1.0.1 this is buggy when neighbors are gathered in both directions (mode
49//! `All`, or undirected graphs): mutual pairs `u -> w`, `w -> u` and single
50//! self-loops are mistaken for multi-edges, which corrupts the graph's cached
51//! [`Graph::has_multiple`] answer, and the lists themselves depend on that
52//! cache. These wrappers collapse such lists on the Rust side instead, so the
53//! result always matches [`Graph::neighbors_with`] and the cache stays
54//! correct.
55//!
56//! # Example
57//!
58//! ```
59//! use igraph::prelude::*;
60//! use igraph::adjlist::AdjList;
61//!
62//! // A directed triangle 0 -> 1 -> 2 -> 0 with an extra edge 0 -> 2.
63//! let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (0, 2)], 3, true).unwrap();
64//!
65//! let out = g.adjlist_init(NeighborMode::Out, Loops::Twice, true).unwrap();
66//! assert_eq!(out.len(), 3);
67//! assert_eq!(&out[0], &[1, 2]);
68//! assert_eq!(out.to_vecs(), vec![vec![1, 2], vec![2], vec![0]]);
69//!
70//! // Incoming neighbors, then the edge ids incident to each vertex.
71//! let inc = g.adjlist_init(NeighborMode::In, Loops::Twice, true).unwrap();
72//! assert_eq!(&inc[2], &[0, 1]);
73//! let il = g.inclist_init(NeighborMode::Out, Loops::Twice).unwrap();
74//! assert_eq!(&il[0], &[0, 3]);
75//!
76//! // Edit the adjacency list and turn it back into a graph: drop 2 -> 0.
77//! let mut al: AdjList = out.clone();
78//! al.get_mut(2).unwrap().clear();
79//! al.get_mut(0).unwrap().push(0); // and add a self-loop on 0
80//! let h = al.to_graph(NeighborMode::Out, false).unwrap();
81//! assert_eq!(h.ecount(), 4);
82//! assert_eq!(h.neighbors(0, NeighborMode::Out).unwrap(), vec![0, 1, 2]);
83//!
84//! // Lazy lists query the graph only when asked to.
85//! let mut lazy = g.lazy_adjlist_init(NeighborMode::All, Loops::Twice, false).unwrap();
86//! assert!(!lazy.has(0));
87//! assert_eq!(lazy.get(0).unwrap(), &[1, 2]);
88//! assert!(lazy.has(0));
89//!
90//! // Zachary's karate club: the handshake lemma, and the two hubs.
91//! let karate = Graph::famous("Zachary").unwrap();
92//! let al = karate.adjlist_init(NeighborMode::All, Loops::Twice, true).unwrap();
93//! assert_eq!(al.total_len(), 2 * karate.ecount());
94//! assert_eq!((al[0].len(), al[33].len()), (16, 17));
95//! ```
96//!
97//! # Functions of `igraph_adjlist.h`
98//!
99//! | C function | Rust |
100//! |--------------------------------------|------------------------------------------------|
101//! | `igraph_adjlist_init` | [`Graph::adjlist_init`], [`AdjList::new`] |
102//! | `igraph_adjlist_init_empty` | [`AdjList::init_empty`] |
103//! | `igraph_adjlist_init_complementer` | [`Graph::adjlist_init_complementer`], [`AdjList::complementer`] |
104//! | `igraph_adjlist_init_from_inclist` | [`Graph::adjlist_init_from_inclist`], [`AdjList::from_inclist`] |
105//! | `igraph_adjlist_destroy` | [`Drop`] |
106//! | `igraph_adjlist_size` | [`AdjList::len`] |
107//! | `igraph_adjlist_clear` | [`AdjList::clear`] |
108//! | `igraph_adjlist_sort` | [`AdjList::sort`] |
109//! | `igraph_adjlist_simplify` | [`AdjList::simplify`] |
110//! | `igraph_adjlist_print` / `_fprint` | [`AdjList::print`], [`AdjList::fprint`], [`Display`](std::fmt::Display) |
111//! | `igraph_adjlist_has_edge` | [`AdjList::has_edge`] |
112//! | `igraph_adjlist_replace_edge` | [`AdjList::replace_edge`] |
113//! | `igraph_adjlist_get` (macro) | [`Index`], [`AdjList::get`], [`AdjList::get_mut`] |
114//! | `igraph_adjlist` | [`Graph::adjlist`], [`AdjList::to_graph`] |
115//! | `igraph_inclist_init` | [`Graph::inclist_init`], [`IncList::new`] |
116//! | `igraph_inclist_init_empty` | [`IncList::init_empty`] |
117//! | `igraph_inclist_destroy` | [`Drop`] |
118//! | `igraph_inclist_size` | [`IncList::len`] |
119//! | `igraph_inclist_clear` | [`IncList::clear`] |
120//! | `igraph_inclist_print` / `_fprint` | [`IncList::print`], [`IncList::fprint`], [`Display`](std::fmt::Display) |
121//! | `igraph_inclist_get` (macro) | [`Index`], [`IncList::get`], [`IncList::get_mut`] |
122//! | `igraph_lazy_adjlist_init` | [`Graph::lazy_adjlist_init`], [`LazyAdjList::new`] |
123//! | `igraph_lazy_adjlist_destroy` | [`Drop`] |
124//! | `igraph_lazy_adjlist_clear` | [`LazyAdjList::clear`] |
125//! | `igraph_lazy_adjlist_size` | [`LazyAdjList::len`] |
126//! | `igraph_lazy_adjlist_has` (macro) | [`LazyAdjList::has`] |
127//! | `igraph_lazy_adjlist_get` (macro) | [`LazyAdjList::get`], [`LazyAdjList::get_mut`] |
128//! | `igraph_lazy_inclist_init` | [`Graph::lazy_inclist_init`], [`LazyIncList::new`] |
129//! | `igraph_lazy_inclist_destroy` | [`Drop`] |
130//! | `igraph_lazy_inclist_clear` | [`LazyIncList::clear`] |
131//! | `igraph_lazy_inclist_size` | [`LazyIncList::len`] |
132//! | `igraph_lazy_inclist_has` (macro) | [`LazyIncList::has`] |
133//! | `igraph_lazy_inclist_get` (macro) | [`LazyIncList::get`], [`LazyIncList::get_mut`] |
134//!
135//! # See also
136//!
137//! - [`Graph::neighbors_with`] and [`Graph::incident`] query a single vertex
138//! directly on the graph, without building a whole list;
139//! - [`Graph::get_adjacency`] gives the dense adjacency
140//! matrix and [`Graph::adjacency`] builds a graph from
141//! one, the matrix counterparts of [`Graph::adjlist_init`] and
142//! [`Graph::adjlist`];
143//! - [`Graph::complementer`] and [`Graph::simplify`]
144//! ([`operators`](crate::operators)) are the whole-graph counterparts of
145//! [`Graph::adjlist_init_complementer`] and [`AdjList::simplify`];
146//! - [`Graph::rewire`] implements degree-preserving rewiring on top of
147//! [`AdjList::has_edge`] / [`AdjList::replace_edge`];
148//! - [`Graph::bfs_simple`] and the other traversals of
149//! [`visitor`](crate::visitor) walk the graph for you.
150//!
151//! The igraph C documentation of
152//! [adjacency lists](https://igraph.org/c/html/latest/igraph-Data-structures.html)
153//! describes the underlying structures.
154
155use crate::{
156 constants::{Loops, NeighborMode},
157 error::{Error, ErrorKind, Result, check, ensure_init},
158 ffi::*,
159 graph::{Graph, VertexId},
160 igraph_call,
161 vector::VectorInt,
162};
163use std::{
164 ffi::c_char,
165 fmt,
166 iter::FusedIterator,
167 marker::PhantomData,
168 mem::MaybeUninit,
169 ops::{Index, IndexMut},
170};
171
172/// Owned adjacency list (`igraph_adjlist_t`): for each vertex, a vector of
173/// its neighbor vertex ids. See the [module docs](self).
174pub type AdjList = igraph_adjlist_t;
175
176/// Owned incidence list (`igraph_inclist_t`): for each vertex, a vector of
177/// the ids of its incident edges. See the [module docs](self).
178pub type IncList = igraph_inclist_t;
179
180/// Iterator over the per-vertex lists of an [`AdjList`] or [`IncList`],
181/// yielding each list as a slice (returned by [`AdjList::iter`] and
182/// [`IncList::iter`]).
183#[derive(Debug, Clone)]
184pub struct Iter<'a> {
185 inner: std::slice::Iter<'a, VectorInt>,
186}
187
188impl<'a> Iterator for Iter<'a> {
189 type Item = &'a [igraph_int_t];
190 fn next(&mut self) -> Option<Self::Item> {
191 self.inner.next().map(VectorInt::as_slice)
192 }
193 fn size_hint(&self) -> (usize, Option<usize>) {
194 self.inner.size_hint()
195 }
196 fn nth(&mut self, n: usize) -> Option<Self::Item> {
197 self.inner.nth(n).map(VectorInt::as_slice)
198 }
199}
200
201impl DoubleEndedIterator for Iter<'_> {
202 fn next_back(&mut self) -> Option<Self::Item> {
203 self.inner.next_back().map(VectorInt::as_slice)
204 }
205}
206
207impl ExactSizeIterator for Iter<'_> {}
208impl FusedIterator for Iter<'_> {}
209
210/// Runs `f` on a C `FILE*` backed by memory and returns what was written.
211///
212/// Used to expose igraph's `*_fprint` functions through [`std::io::Write`].
213fn capture_c_output(f: impl FnOnce(*mut FILE) -> igraph_error_t) -> Result<Vec<u8>> {
214 ensure_init();
215 let mut buf: *mut c_char = std::ptr::null_mut();
216 let mut size: usize = 0;
217 let file = unsafe { open_memstream(&mut buf, &mut size) };
218 if file.is_null() {
219 return Err(Error::new(ErrorKind::File, "cannot open a memory stream"));
220 }
221 let code = f(file);
222 // `fclose` finalizes `buf` and `size`.
223 let closed = unsafe { fclose(file) };
224 let bytes = if buf.is_null() {
225 Vec::new()
226 } else {
227 let bytes = unsafe { std::slice::from_raw_parts(buf as *const u8, size) }.to_vec();
228 unsafe { free(buf.cast()) };
229 bytes
230 };
231 check(code)?;
232 if closed != 0 {
233 return Err(Error::new(
234 ErrorKind::File,
235 "cannot close the memory stream",
236 ));
237 }
238 Ok(bytes)
239}
240
241/// Writes `bytes` to `out`, mapping I/O errors to [`ErrorKind::File`].
242fn write_all(out: &mut impl std::io::Write, bytes: &[u8]) -> Result<()> {
243 out.write_all(bytes)
244 .map_err(|e| Error::new(ErrorKind::File, format!("write failed: {e}")))
245}
246
247/// The error returned by the lazy lists when igraph could not compute the
248/// requested list (the only possible cause, for a valid vertex, is lack of
249/// memory).
250fn lazy_failure() -> Error {
251 match check(igraph_error_type_t_IGRAPH_ENOMEM) {
252 Err(e) => e,
253 Ok(()) => Error::new(ErrorKind::OutOfMemory, "cannot query the lazy list"),
254 }
255}
256
257/// How many copies of a self-loop survive in the list of its vertex when
258/// multi-edges are collapsed, or `None` when igraph's own collapsing can be
259/// used.
260///
261/// Workaround for a bug of igraph 1.0.0 and 1.0.1 (`src/graph/adjlist.c`,
262/// unchanged in 1.0.1): when `multiple = false` and the neighbors are
263/// gathered in both directions (mode `All`, or any undirected graph), igraph
264/// mistakes the two entries of a mutual pair `u -> w`, `w -> u` or of a
265/// single self-loop for a multi-edge. `igraph_adjlist_init` then caches
266/// "this graph has multi-edges" on the graph, so that later
267/// [`Graph::has_multiple`] / [`Graph::is_simple`] calls give wrong answers;
268/// and once the cache says "no multi-edges" (e.g. after [`Graph::simplify`]),
269/// both `igraph_adjlist_init` and `igraph_lazy_adjlist_init` skip collapsing,
270/// so mutual pairs of a directed graph are listed twice or once depending on
271/// the cache state. In these cases the lists are therefore requested *with*
272/// multi-edges and collapsed on the Rust side, with exactly igraph's
273/// semantics: each neighbor is listed once, and a self-loop (if kept) once,
274/// or twice with [`Loops::Twice`].
275fn collapse_rule(graph: &Graph, mode: NeighborMode, loops: Loops, multiple: bool) -> Option<usize> {
276 let both_ways = mode == NeighborMode::All || !graph.is_directed();
277 if multiple || !both_ways {
278 return None;
279 }
280 Some(if loops == Loops::Twice { 2 } else { 1 })
281}
282
283/// Collapses repeated entries of the *sorted* neighbor list of vertex `v`:
284/// every neighbor is kept once, `v` itself at most `max_loops` times.
285fn collapse_sorted(list: &mut VectorInt, v: VertexId, max_loops: usize) {
286 let slice = list.as_mut_slice();
287 let (mut read, mut write) = (0, 0);
288 while read < slice.len() {
289 let x = slice[read];
290 let run = slice[read..].iter().take_while(|&&y| y == x).count();
291 let keep = if x == v { run.min(max_loops) } else { 1 };
292 for _ in 0..keep {
293 slice[write] = x;
294 write += 1;
295 }
296 read += run;
297 }
298 list.truncate(write);
299}
300
301fn check_vertex(v: VertexId, n: usize) -> Result<usize> {
302 if v < 0 || v as u64 >= n as u64 {
303 Err(Error::new(
304 ErrorKind::InvalidVertexId,
305 format!("vertex id {v} out of range for a list of {n} vertices"),
306 ))
307 } else {
308 Ok(v as usize)
309 }
310}
311
312// Shared behaviour of the two eager lists, whose C layouts are identical
313// (a length and a C array of `igraph_vector_int_t`).
314macro_rules! impl_eager_list {
315 (
316 $ty:ident, $field:ident, $what:literal, $item:literal, c = $c:literal,
317 init_empty = $init_empty:ident, destroy = $destroy:ident,
318 clear = $clear:ident, print = $print:ident, fprint = $fprint:ident
319 ) => {
320 impl $ty {
321 #[doc = concat!("Creates ", $what, " for `n` vertices, all with empty lists (`", stringify!($init_empty), "`).")]
322 ///
323 /// Useful to *build* a structure vertex by vertex, e.g. before
324 /// turning it into a graph. Time complexity: O(n).
325 ///
326 #[doc = concat!("Binds [`", stringify!($init_empty), "`](", "https://igraph.org/c/html/latest/igraph-Data-structures.html#", stringify!($init_empty), ").")]
327 ///
328 /// # Errors
329 /// [`ErrorKind::InvalidValue`] if `n` does not fit in an
330 /// `igraph_int_t`, or an out-of-memory error if it is too large
331 /// to be allocated.
332 pub fn init_empty(n: usize) -> Result<Self> {
333 // A negative length would make igraph allocate a single slot
334 // and record a negative size.
335 let n = igraph_int_t::try_from(n)
336 .map_err(|_| Error::invalid(format!("{n} vertices is too many")))?;
337 let mut raw = MaybeUninit::<Self>::zeroed();
338 igraph_call!($init_empty(raw.as_mut_ptr(), n))?;
339 Ok(unsafe { raw.assume_init() })
340 }
341
342 #[doc = concat!("Number of vertices, i.e. of lists, in the ", $what, " (`", $c, "_size`).")]
343 ///
344 /// Time complexity: O(1).
345 ///
346 #[doc = concat!("Binds [`", $c, "_size`](", "https://igraph.org/c/html/latest/igraph-Data-structures.html#", $c, "_size).")]
347 pub fn len(&self) -> usize {
348 if self.$field.is_null() { 0 } else { self.length.max(0) as usize }
349 }
350
351 /// Whether there are no vertices at all.
352 pub fn is_empty(&self) -> bool {
353 self.len() == 0
354 }
355
356 #[doc = concat!("Total number of entries (", $item, ") stored in all the lists.")]
357 pub fn total_len(&self) -> usize {
358 self.as_raw_slice().iter().map(|v| v.len()).sum()
359 }
360
361 #[doc = concat!("Removes all ", $item, " from every list, keeping the number of vertices (`", stringify!($clear), "`).")]
362 ///
363 /// Time complexity: O(n), n being the number of vertices.
364 ///
365 #[doc = concat!("Binds [`", stringify!($clear), "`](", "https://igraph.org/c/html/latest/igraph-Data-structures.html#", stringify!($clear), ").")]
366 pub fn clear(&mut self) {
367 if !self.$field.is_null() {
368 unsafe { $clear(self) };
369 }
370 }
371
372 /// The per-vertex lists as a slice of owned igraph vectors.
373 pub fn as_raw_slice(&self) -> &[VectorInt] {
374 if self.$field.is_null() || self.length <= 0 {
375 &[]
376 } else {
377 unsafe { std::slice::from_raw_parts(self.$field, self.length as usize) }
378 }
379 }
380
381 /// The per-vertex lists as a mutable slice of owned igraph
382 /// vectors: every list can be resized, pushed to, sorted, or even
383 /// replaced by another [`VectorInt`].
384 pub fn as_raw_mut_slice(&mut self) -> &mut [VectorInt] {
385 if self.$field.is_null() || self.length <= 0 {
386 &mut []
387 } else {
388 unsafe { std::slice::from_raw_parts_mut(self.$field, self.length as usize) }
389 }
390 }
391
392 #[doc = concat!("The list of vertex `v`, or `None` if `v` is out of range (the [`", $c, "_get`](", "https://igraph.org/c/html/latest/igraph-Data-structures.html#", $c, "_get) macro).")]
393 ///
394 /// The panicking counterpart is indexing: `list[v]`.
395 pub fn get(&self, v: VertexId) -> Option<&[igraph_int_t]> {
396 usize::try_from(v).ok().and_then(|i| self.as_raw_slice().get(i)).map(|x| x.as_slice())
397 }
398
399 #[doc = concat!("The list of vertex `v` as a mutable [`VectorInt`], or `None` if `v` is out of range.")]
400 ///
401 /// Unlike `IndexMut`, this allows changing the length of the list
402 /// (`push`, `pop`, `resize`, `clear`, ...).
403 pub fn get_mut(&mut self, v: VertexId) -> Option<&mut VectorInt> {
404 usize::try_from(v).ok().and_then(move |i| self.as_raw_mut_slice().get_mut(i))
405 }
406
407 /// Iterates over the lists of the vertices `0, 1, ...`, as slices.
408 pub fn iter(&self) -> Iter<'_> {
409 Iter { inner: self.as_raw_slice().iter() }
410 }
411
412 /// Iterates mutably over the lists of the vertices `0, 1, ...`.
413 pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, VectorInt> {
414 self.as_raw_mut_slice().iter_mut()
415 }
416
417 /// Copies the lists into nested Rust vectors.
418 pub fn to_vecs(&self) -> Vec<Vec<igraph_int_t>> {
419 self.iter().map(<[igraph_int_t]>::to_vec).collect()
420 }
421
422 #[doc = concat!("Prints the lists to the standard output, one vertex per line, entries separated by spaces (`", stringify!($print), "`).")]
423 ///
424 /// The same text is produced by the `Display` implementation and
425 #[doc = concat!("by [`", stringify!($ty), "::fprint`].")]
426 ///
427 /// Note that the text goes to the C `stdout` stream, bypassing
428 /// Rust's output capturing (e.g. in `cargo test`): prefer
429 /// `println!("{list}")` in Rust code.
430 pub fn print(&self) -> Result<()> {
431 igraph_call!($print(self))
432 }
433
434 #[doc = concat!("Writes the lists to `out` in igraph's textual format, one vertex per line (`", stringify!($fprint), "`).")]
435 ///
436 /// The output is produced by the C library itself (through a
437 /// memory stream), and it matches the `Display` implementation.
438 ///
439 /// # Errors
440 /// [`ErrorKind::File`] if writing fails.
441 pub fn fprint(&self, out: &mut impl std::io::Write) -> Result<()> {
442 let bytes = capture_c_output(|f| unsafe { $fprint(self, f) })?;
443 write_all(out, &bytes)
444 }
445 }
446
447 impl Drop for $ty {
448 #[doc = concat!("Frees the lists with `", stringify!($destroy), "`.")]
449 fn drop(&mut self) {
450 if !self.$field.is_null() {
451 unsafe { $destroy(self) };
452 self.$field = std::ptr::null_mut();
453 self.length = 0;
454 }
455 }
456 }
457
458 impl Clone for $ty {
459 fn clone(&self) -> Self {
460 let mut copy = Self::init_empty(self.len()).expect("igraph failed to allocate a list");
461 for (dst, src) in copy.as_raw_mut_slice().iter_mut().zip(self.as_raw_slice()) {
462 *dst = src.clone();
463 }
464 copy
465 }
466 }
467
468 impl Default for $ty {
469 /// An empty structure with no vertices.
470 fn default() -> Self {
471 Self::init_empty(0).expect("igraph failed to allocate a list")
472 }
473 }
474
475 impl PartialEq for $ty {
476 /// Equal when both have the same number of vertices and the same
477 /// lists, in the same order.
478 fn eq(&self, other: &Self) -> bool {
479 self.as_raw_slice() == other.as_raw_slice()
480 }
481 }
482
483 impl Index<usize> for $ty {
484 type Output = [igraph_int_t];
485 /// The list of the given vertex as a slice.
486 ///
487 /// # Panics
488 /// If the vertex is out of range.
489 fn index(&self, v: usize) -> &[igraph_int_t] {
490 self.as_raw_slice()[v].as_slice()
491 }
492 }
493
494 impl IndexMut<usize> for $ty {
495 /// The list of the given vertex as a mutable slice (its length
496 #[doc = concat!("cannot change: use [`", stringify!($ty), "::get_mut`] for that).")]
497 ///
498 /// # Panics
499 /// If the vertex is out of range.
500 fn index_mut(&mut self, v: usize) -> &mut [igraph_int_t] {
501 self.as_raw_mut_slice()[v].as_mut_slice()
502 }
503 }
504
505 impl<'a> IntoIterator for &'a $ty {
506 type Item = &'a [igraph_int_t];
507 type IntoIter = Iter<'a>;
508 fn into_iter(self) -> Iter<'a> {
509 self.iter()
510 }
511 }
512
513 impl fmt::Display for $ty {
514 /// One line per vertex with the entries separated by spaces,
515 #[doc = concat!("exactly like `", stringify!($print), "`.")]
516 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
517 for list in self {
518 let mut first = true;
519 for x in list {
520 if !first {
521 f.write_str(" ")?;
522 }
523 first = false;
524 write!(f, "{x}")?;
525 }
526 writeln!(f)?;
527 }
528 Ok(())
529 }
530 }
531
532 impl From<&[Vec<igraph_int_t>]> for $ty {
533 /// Builds the structure from nested vectors, one per vertex.
534 fn from(lists: &[Vec<igraph_int_t>]) -> Self {
535 let mut res = Self::init_empty(lists.len()).expect("igraph failed to allocate a list");
536 for (dst, src) in res.as_raw_mut_slice().iter_mut().zip(lists) {
537 *dst = VectorInt::from_slice(src);
538 }
539 res
540 }
541 }
542
543 impl From<Vec<Vec<igraph_int_t>>> for $ty {
544 /// Builds the structure from nested vectors, one per vertex.
545 fn from(lists: Vec<Vec<igraph_int_t>>) -> Self {
546 Self::from(lists.as_slice())
547 }
548 }
549
550 impl FromIterator<Vec<igraph_int_t>> for $ty {
551 fn from_iter<I: IntoIterator<Item = Vec<igraph_int_t>>>(iter: I) -> Self {
552 let lists: Vec<_> = iter.into_iter().collect();
553 Self::from(lists.as_slice())
554 }
555 }
556
557 impl From<$ty> for Vec<Vec<igraph_int_t>> {
558 fn from(list: $ty) -> Self {
559 list.to_vecs()
560 }
561 }
562
563 impl From<&$ty> for Vec<Vec<igraph_int_t>> {
564 fn from(list: &$ty) -> Self {
565 list.to_vecs()
566 }
567 }
568
569 // The structure owns plain memory, with no interior mutability and no
570 // pointer to a graph.
571 unsafe impl Send for $ty {}
572 unsafe impl Sync for $ty {}
573 };
574}
575
576impl_eager_list!(
577 igraph_adjlist_t,
578 adjs,
579 "an adjacency list",
580 "neighbor ids",
581 c = "igraph_adjlist",
582 init_empty = igraph_adjlist_init_empty,
583 destroy = igraph_adjlist_destroy,
584 clear = igraph_adjlist_clear,
585 print = igraph_adjlist_print,
586 fprint = igraph_adjlist_fprint
587);
588impl_eager_list!(
589 igraph_inclist_t,
590 incs,
591 "an incidence list",
592 "edge ids",
593 c = "igraph_inclist",
594 init_empty = igraph_inclist_init_empty,
595 destroy = igraph_inclist_destroy,
596 clear = igraph_inclist_clear,
597 print = igraph_inclist_print,
598 fprint = igraph_inclist_fprint
599);
600
601impl igraph_adjlist_t {
602 /// Adjacency list of `graph`: same as [`Graph::adjlist_init`].
603 ///
604 /// ```
605 /// use igraph::prelude::*;
606 /// use igraph::adjlist::AdjList;
607 /// let g = Graph::from_edges(&[(0, 1), (1, 2)], 3, false).unwrap();
608 /// let al = AdjList::new(&g, NeighborMode::All, Loops::Twice, true).unwrap();
609 /// assert_eq!(&al[1], &[0, 2]);
610 /// ```
611 pub fn new(graph: &Graph, mode: NeighborMode, loops: Loops, multiple: bool) -> Result<Self> {
612 graph.adjlist_init(mode, loops, multiple)
613 }
614
615 /// Adjacency list of the complementer of `graph`: same as
616 /// [`Graph::adjlist_init_complementer`].
617 pub fn complementer(graph: &Graph, mode: NeighborMode, loops: Loops) -> Result<Self> {
618 graph.adjlist_init_complementer(mode, loops)
619 }
620
621 /// Adjacency list consistent with an incidence list of `graph`: same as
622 /// [`Graph::adjlist_init_from_inclist`].
623 pub fn from_inclist(graph: &Graph, inclist: &IncList) -> Result<Self> {
624 graph.adjlist_init_from_inclist(inclist)
625 }
626
627 /// Checks that every stored neighbor id is a valid index of the list.
628 fn check_ids(&self) -> Result<()> {
629 let n = self.len();
630 for (v, list) in self.iter().enumerate() {
631 if let Some(&bad) = list.iter().find(|&&u| u < 0 || u as u64 >= n as u64) {
632 return Err(Error::new(
633 ErrorKind::InvalidVertexId,
634 format!("the list of vertex {v} contains the invalid vertex id {bad}"),
635 ));
636 }
637 }
638 Ok(())
639 }
640
641 /// Checks that every edge between distinct vertices is listed from both
642 /// endpoints the same number of times, and that every self-loop is
643 /// listed an even number of times (the `duplicate = true` format).
644 fn check_duplicated(&self) -> Result<()> {
645 let mut forward = Vec::with_capacity(self.total_len());
646 let mut backward = Vec::with_capacity(self.total_len());
647 for (u, list) in self.iter().enumerate() {
648 let u = u as igraph_int_t;
649 let loops = list.iter().filter(|&&w| w == u).count();
650 if loops % 2 != 0 {
651 return Err(Error::invalid(format!(
652 "vertex {u} lists itself {loops} times: self-loops must be listed twice \
653 when duplicate = true"
654 )));
655 }
656 for &w in list.iter().filter(|&&w| w != u) {
657 forward.push((u, w));
658 backward.push((w, u));
659 }
660 }
661 forward.sort_unstable();
662 backward.sort_unstable();
663 if forward != backward {
664 let bad = forward
665 .iter()
666 .find(|e| backward.binary_search(e).is_err())
667 .or_else(|| backward.iter().find(|e| forward.binary_search(e).is_err()))
668 .copied()
669 .unwrap_or((0, 0));
670 return Err(Error::invalid(format!(
671 "the undirected edge {{{}, {}}} is not listed equally often by both of its \
672 endpoints, as required when duplicate = true",
673 bad.0.min(bad.1),
674 bad.0.max(bad.1)
675 )));
676 }
677 Ok(())
678 }
679
680 /// Sorts every neighbor list in increasing order (`igraph_adjlist_sort`).
681 ///
682 /// Lists created by [`Graph::adjlist_init`] are already sorted; this is
683 /// useful after edits, or after [`simplify`](Self::simplify), which does
684 /// not preserve the order. Sorted lists are required by
685 /// [`has_edge`](Self::has_edge) and [`replace_edge`](Self::replace_edge).
686 ///
687 /// Time complexity: O(m log m), m being the total number of entries.
688 ///
689 /// Binds [`igraph_adjlist_sort`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_adjlist_sort).
690 pub fn sort(&mut self) {
691 if !self.adjs.is_null() {
692 unsafe { igraph_adjlist_sort(self) };
693 }
694 }
695
696 /// Removes self-loops and repeated neighbors from every list
697 /// (`igraph_adjlist_simplify`).
698 ///
699 /// After the call, vertex `v` appears in no list of its own and each
700 /// neighbor appears at most once per list. The order of the remaining
701 /// entries is **not** preserved (removed entries are replaced with the
702 /// last one): call [`sort`](Self::sort) afterwards if needed. When the
703 /// list comes from a graph, prefer passing `Loops::None` and
704 /// `multiple = false` to [`Graph::adjlist_init`] instead; to simplify
705 /// the graph itself use [`Graph::simplify`].
706 ///
707 /// Time complexity: O(|V|+|E|).
708 ///
709 /// # Errors
710 /// [`ErrorKind::InvalidVertexId`] if some list contains an id that is not
711 /// a vertex of the list (checked on the Rust side, as the C code would
712 /// read out of bounds).
713 ///
714 /// # Examples
715 /// ```
716 /// use igraph::adjlist::AdjList;
717 /// let mut al = AdjList::from(vec![vec![0, 1, 1, 2], vec![0, 0], vec![0, 2]]);
718 /// al.simplify().unwrap();
719 /// al.sort();
720 /// assert_eq!(al.to_vecs(), vec![vec![1, 2], vec![0], vec![0]]);
721 /// ```
722 ///
723 /// Binds [`igraph_adjlist_simplify`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_adjlist_simplify).
724 pub fn simplify(&mut self) -> Result<()> {
725 self.check_ids()?;
726 if self.adjs.is_null() {
727 return Ok(());
728 }
729 igraph_call!(igraph_adjlist_simplify(self))
730 }
731
732 /// Whether the adjacency list contains the edge `from -> to`
733 /// (`igraph_adjlist_has_edge`), by binary search.
734 ///
735 /// The lists **must be sorted** (see [`sort`](Self::sort)), otherwise the
736 /// answer is unspecified. When `directed` is `true`, `to` is searched in
737 /// the list of `from`. When it is `false`, the edge is looked up in the
738 /// list of the *larger* endpoint only, i.e. `min(from, to)` is searched in
739 /// the list of `max(from, to)`: this matches both a full undirected
740 /// adjacency list and a "half" one where each vertex only stores its
741 /// neighbors with smaller or equal ids (the representation that
742 /// [`replace_edge`](Self::replace_edge) keeps consistent).
743 ///
744 /// Time complexity: O(log d), d being the length of the searched list.
745 ///
746 /// See also [`Graph::get_eid`], which answers the same question on the
747 /// graph itself.
748 ///
749 /// # Errors
750 /// [`ErrorKind::InvalidVertexId`] if `from` or `to` is out of range.
751 ///
752 /// # Examples
753 /// ```
754 /// use igraph::prelude::*;
755 /// let g = Graph::from_edges(&[(0, 1), (1, 2)], 3, true).unwrap();
756 /// let al = g.adjlist_init(NeighborMode::Out, Loops::Once, true).unwrap();
757 /// assert!(al.has_edge(0, 1, true).unwrap());
758 /// assert!(!al.has_edge(1, 0, true).unwrap());
759 /// assert_eq!(al.has_edge(0, 9, true).unwrap_err().kind(), ErrorKind::InvalidVertexId);
760 /// ```
761 ///
762 /// Binds `igraph_adjlist_has_edge` (declared in `igraph_adjlist.h` but not part of the
763 /// C reference manual).
764 pub fn has_edge(&self, from: VertexId, to: VertexId, directed: bool) -> Result<bool> {
765 let n = self.len();
766 check_vertex(from, n)?;
767 check_vertex(to, n)?;
768 // The C function takes a mutable pointer but does not modify the list.
769 let ptr = self as *const Self as *mut Self;
770 ensure_init();
771 Ok(unsafe { igraph_adjlist_has_edge(ptr, from, to, directed) })
772 }
773
774 /// Replaces the edge `from -> oldto` with `from -> newto`, keeping the
775 /// lists sorted (`igraph_adjlist_replace_edge`).
776 ///
777 /// The lists **must be sorted**. With `directed = true` the change is
778 /// made in the list of `from`. With `directed = false` the edges are
779 /// canonicalized as in [`has_edge`](Self::has_edge): `{from, oldto}` is
780 /// removed from the list of `max(from, oldto)` and `{from, newto}` is
781 /// inserted in the list of `max(from, newto)`; the lists of the smaller
782 /// endpoints are *not* touched, so this is meant for "half" undirected
783 /// adjacency lists where each vertex only stores its neighbors with
784 /// smaller or equal ids. This is the primitive of igraph's fast
785 /// degree-preserving rewiring, available ready-made as
786 /// [`Graph::rewire`].
787 ///
788 /// Time complexity: O(d), d being the length of the lists involved.
789 ///
790 /// # Errors
791 /// - [`ErrorKind::InvalidVertexId`] if a vertex is out of range;
792 /// - [`ErrorKind::InvalidValue`] if the edge to replace does not exist or
793 /// the new edge already exists.
794 ///
795 /// # Examples
796 /// ```
797 /// use igraph::adjlist::AdjList;
798 /// // Directed: 0 -> 1, 0 -> 2.
799 /// let mut al = AdjList::from(vec![vec![1, 2], vec![], vec![], vec![]]);
800 /// al.replace_edge(0, 1, 3, true).unwrap();
801 /// assert_eq!(&al[0], &[2, 3]);
802 /// assert!(al.replace_edge(0, 1, 3, true).is_err()); // 0 -> 1 is gone
803 /// ```
804 ///
805 /// Binds `igraph_adjlist_replace_edge` (declared in `igraph_adjlist.h` but not part of the
806 /// C reference manual).
807 pub fn replace_edge(
808 &mut self,
809 from: VertexId,
810 oldto: VertexId,
811 newto: VertexId,
812 directed: bool,
813 ) -> Result<()> {
814 let n = self.len();
815 check_vertex(from, n)?;
816 check_vertex(oldto, n)?;
817 check_vertex(newto, n)?;
818 igraph_call!(igraph_adjlist_replace_edge(
819 self, from, oldto, newto, directed
820 ))
821 }
822
823 /// Builds a graph from this adjacency list: same as [`Graph::adjlist`].
824 ///
825 /// ```
826 /// use igraph::prelude::*;
827 /// use igraph::adjlist::AdjList;
828 /// let al = AdjList::from(vec![vec![1, 2], vec![2], vec![]]);
829 /// let g = al.to_graph(NeighborMode::Out, false).unwrap();
830 /// assert!(g.is_directed());
831 /// assert_eq!(g.edge_list(), vec![(0, 1), (0, 2), (1, 2)]);
832 /// ```
833 pub fn to_graph(&self, mode: NeighborMode, duplicate: bool) -> Result<Graph> {
834 Graph::adjlist(self, mode, duplicate)
835 }
836}
837
838impl igraph_inclist_t {
839 /// Incidence list of `graph`: same as [`Graph::inclist_init`].
840 pub fn new(graph: &Graph, mode: NeighborMode, loops: Loops) -> Result<Self> {
841 graph.inclist_init(mode, loops)
842 }
843}
844
845impl igraph_t {
846 /// Adjacency list of the graph: for each vertex, the sorted ids of its
847 /// neighbors (`igraph_adjlist_init`).
848 ///
849 /// The list is independent of the graph after creation: it reflects the
850 /// graph at the time of the call and can be edited freely.
851 ///
852 /// - `mode`: in directed graphs, whether to list successors
853 /// ([`NeighborMode::Out`]), predecessors ([`NeighborMode::In`]) or both
854 /// ([`NeighborMode::All`]); ignored for undirected graphs.
855 /// - `loops`: [`Loops::None`] drops self-loops; [`Loops::Once`] lists each
856 /// loop edge once in the list of its vertex; [`Loops::Twice`] lists it
857 /// twice, but only if the graph is undirected or `mode` is
858 /// [`NeighborMode::All`] (otherwise it behaves as `Once`).
859 /// - `multiple`: `true` keeps parallel edges, so a neighbor appears as many
860 /// times as there are edges to it; `false` lists each neighbor once
861 /// (with mode `All` this also merges a mutual pair `u -> w`, `w -> u` of
862 /// a directed graph) and each kept self-loop once, or twice with
863 /// `Loops::Twice` in an undirected graph or with mode `All`.
864 ///
865 /// igraph 1.0.0 and 1.0.1 get `multiple = false` wrong when neighbors are
866 /// collected in both directions (mode `All`, or an undirected graph):
867 /// they mistake mutual pairs and single self-loops for multi-edges and
868 /// record that in the graph's property cache (so that
869 /// [`Graph::has_multiple`] later answers `true` for a graph without
870 /// multi-edges), and, if the cache already says "no multi-edges", they
871 /// list mutual pairs twice. This wrapper avoids both problems by
872 /// collapsing such lists itself, with the semantics described above.
873 ///
874 /// `Loops::Twice` with `multiple = true` is the fastest combination.
875 /// The lists are currently sorted, but igraph does not guarantee this for
876 /// the future: call [`AdjList::sort`] if you rely on it. The list of
877 /// vertex `v` equals [`Graph::neighbors_with`]`(v, mode, loops, multiple)`.
878 ///
879 /// Time complexity: O(|V|+|E|).
880 ///
881 /// See also [`Graph::lazy_adjlist_init`] (on-demand variant),
882 /// [`Graph::inclist_init`] (edge ids instead of vertex ids),
883 /// [`Graph::neighbors_with`] (one vertex) and
884 /// [`Graph::get_adjacency`] (dense matrix).
885 ///
886 /// # Examples
887 /// ```
888 /// use igraph::prelude::*;
889 /// // An undirected graph with a double edge 0-1 and a loop on 2.
890 /// let g = Graph::from_edges(&[(0, 1), (0, 1), (1, 2), (2, 2)], 3, false).unwrap();
891 /// let full = g.adjlist_init(NeighborMode::All, Loops::Twice, true).unwrap();
892 /// assert_eq!(full.to_vecs(), vec![vec![1, 1], vec![0, 0, 2], vec![1, 2, 2]]);
893 /// let simple = g.adjlist_init(NeighborMode::All, Loops::None, false).unwrap();
894 /// assert_eq!(simple.to_vecs(), vec![vec![1], vec![0, 2], vec![1]]);
895 /// ```
896 ///
897 /// Binds [`igraph_adjlist_init`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_adjlist_init).
898 pub fn adjlist_init(
899 &self,
900 mode: NeighborMode,
901 loops: Loops,
902 multiple: bool,
903 ) -> Result<AdjList> {
904 let collapse = collapse_rule(self, mode, loops, multiple);
905 let mut raw = MaybeUninit::<AdjList>::zeroed();
906 igraph_call!(igraph_adjlist_init(
907 self,
908 raw.as_mut_ptr(),
909 mode.into(),
910 loops.into(),
911 multiple || collapse.is_some()
912 ))?;
913 let mut al = unsafe { raw.assume_init() };
914 if let Some(max_loops) = collapse {
915 for (v, list) in al.iter_mut().enumerate() {
916 collapse_sorted(list, v as VertexId, max_loops);
917 }
918 }
919 Ok(al)
920 }
921
922 /// Adjacency list of the *complementer* graph, i.e. of the graph having
923 /// exactly the edges missing from this one
924 /// (`igraph_adjlist_init_complementer`).
925 ///
926 /// Multi-edges of the input are ignored and the lists are sorted.
927 ///
928 /// - `mode`: which neighbors *in the complementer* to list for directed
929 /// graphs (ignored for undirected ones);
930 /// - `loops`: [`Loops::None`] never lists `v` among its own neighbors;
931 /// [`Loops::Once`] lists it once if the graph has no loop on `v`;
932 /// [`Loops::Twice`] lists it twice in that case when `mode` is
933 /// [`NeighborMode::All`], and behaves as `Once` otherwise.
934 ///
935 /// Time complexity: O(|V|²+|E|).
936 ///
937 /// See also [`Graph::complementer`], which builds the complementer as a
938 /// graph.
939 ///
940 /// # Examples
941 /// ```
942 /// use igraph::prelude::*;
943 /// // The complement of the path 0 - 1 - 2 - 3 is the path 1 - 3 - 0 - 2.
944 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
945 /// let c = g.adjlist_init_complementer(NeighborMode::All, Loops::None).unwrap();
946 /// assert_eq!(c.to_vecs(), vec![vec![2, 3], vec![3], vec![0], vec![0, 1]]);
947 /// ```
948 ///
949 /// Binds [`igraph_adjlist_init_complementer`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_adjlist_init_complementer).
950 pub fn adjlist_init_complementer(&self, mode: NeighborMode, loops: Loops) -> Result<AdjList> {
951 let mut raw = MaybeUninit::<AdjList>::zeroed();
952 igraph_call!(igraph_adjlist_init_complementer(
953 self,
954 raw.as_mut_ptr(),
955 mode.into(),
956 loops.into()
957 ))?;
958 Ok(unsafe { raw.assume_init() })
959 }
960
961 /// Adjacency list *consistent* with an incidence list of this graph
962 /// (`igraph_adjlist_init_from_inclist`).
963 ///
964 /// Entry `i` of the list of vertex `v` is the other endpoint of the edge
965 /// at entry `i` of `inclist[v]`, so the two structures can be walked in
966 /// lockstep (neighbor and connecting edge together). The result is
967 /// independent of both the graph and the incidence list.
968 ///
969 /// Time complexity: O(|V|+|E|).
970 ///
971 /// # Errors
972 /// - [`ErrorKind::InvalidValue`] if the incidence list does not have one
973 /// entry per vertex of the graph;
974 /// - [`ErrorKind::InvalidEdgeId`] if it contains an id that is not an edge
975 /// of the graph (checked on the Rust side).
976 ///
977 /// # Examples
978 /// ```
979 /// use igraph::prelude::*;
980 /// let g = Graph::from_edges(&[(0, 1), (2, 0)], 3, true).unwrap();
981 /// let il = g.inclist_init(NeighborMode::All, Loops::Twice).unwrap();
982 /// let al = g.adjlist_init_from_inclist(&il).unwrap();
983 /// for v in 0..3 {
984 /// for (&e, &u) in il[v].iter().zip(&al[v]) {
985 /// let (a, b) = g.edge(e).unwrap();
986 /// assert!((a, b) == (v as i64, u) || (a, b) == (u, v as i64));
987 /// }
988 /// }
989 /// ```
990 ///
991 /// Binds [`igraph_adjlist_init_from_inclist`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_adjlist_init_from_inclist).
992 pub fn adjlist_init_from_inclist(&self, inclist: &IncList) -> Result<AdjList> {
993 if inclist.len() != self.vcount() {
994 return Err(Error::invalid(format!(
995 "incidence list has {} entries but the graph has {} vertices",
996 inclist.len(),
997 self.vcount()
998 )));
999 }
1000 let m = self.ecount() as u64;
1001 for (v, list) in inclist.iter().enumerate() {
1002 if let Some(&bad) = list.iter().find(|&&e| e < 0 || e as u64 >= m) {
1003 return Err(Error::new(
1004 ErrorKind::InvalidEdgeId,
1005 format!("the incidence list of vertex {v} contains the invalid edge id {bad}"),
1006 ));
1007 }
1008 }
1009 let mut raw = MaybeUninit::<AdjList>::zeroed();
1010 igraph_call!(igraph_adjlist_init_from_inclist(
1011 self,
1012 raw.as_mut_ptr(),
1013 inclist
1014 ))?;
1015 Ok(unsafe { raw.assume_init() })
1016 }
1017
1018 /// Creates a graph from an adjacency list (`igraph_adjlist`), the inverse
1019 /// of [`Graph::adjlist_init`].
1020 ///
1021 /// The graph has one vertex per list.
1022 ///
1023 /// - `mode`: [`NeighborMode::All`] creates an *undirected* graph;
1024 /// [`NeighborMode::Out`] a directed graph where each list holds the
1025 /// successors of its vertex; [`NeighborMode::In`] a directed graph where
1026 /// each list holds the predecessors.
1027 /// - `duplicate`: for undirected graphs only, whether each edge is listed
1028 /// twice (in the lists of both endpoints, as produced by
1029 /// [`Graph::adjlist_init`]; a self-loop then appears twice in its list)
1030 /// or just once.
1031 ///
1032 /// Converting a graph into an adjacency list, doing many structural
1033 /// edits on the lists and converting back costs O(|V|+|E|) overall,
1034 /// much less than repeated edge deletions and insertions on the graph.
1035 ///
1036 /// Time complexity: O(|V|+|E|).
1037 ///
1038 /// See also [`Graph::from_edges`] (from an edge list) and
1039 /// [`Graph::adjacency`] (from an adjacency matrix).
1040 ///
1041 /// # Errors
1042 /// - [`ErrorKind::InvalidValue`] if `duplicate` is set (and `mode` is
1043 /// [`NeighborMode::All`]) but the edges are not correctly listed twice:
1044 /// `u` must appear in the list of `w` as many times as `w` appears in
1045 /// the list of `u`, and loops must be listed an even number of times
1046 /// (checked on the Rust side: igraph itself only detects some of these
1047 /// cases and may otherwise silently invent or drop edges);
1048 /// - [`ErrorKind::InvalidVertexId`] if a list contains an id that is not
1049 /// one of the list's vertices (checked on the Rust side: igraph itself
1050 /// would silently add the missing vertices).
1051 ///
1052 /// # Examples
1053 /// ```
1054 /// use igraph::prelude::*;
1055 /// use igraph::adjlist::AdjList;
1056 /// // The undirected triangle, each edge listed from both of its ends.
1057 /// let al = AdjList::from(vec![vec![1, 2], vec![0, 2], vec![0, 1]]);
1058 /// let g = Graph::adjlist(&al, NeighborMode::All, true).unwrap();
1059 /// assert!(!g.is_directed());
1060 /// assert_eq!(g.ecount(), 3);
1061 /// // Round trip.
1062 /// assert_eq!(g.adjlist_init(NeighborMode::All, Loops::Twice, true).unwrap(), al);
1063 /// ```
1064 ///
1065 /// Binds [`igraph_adjlist`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_adjlist).
1066 pub fn adjlist(adjlist: &AdjList, mode: NeighborMode, duplicate: bool) -> Result<Graph> {
1067 // igraph would silently add vertices for too large ids: reject them.
1068 adjlist.check_ids()?;
1069 if duplicate && mode == NeighborMode::All {
1070 adjlist.check_duplicated()?;
1071 }
1072 Graph::init_with(|g| unsafe { igraph_adjlist(g, adjlist, mode.into(), duplicate) })
1073 }
1074
1075 /// Incidence list of the graph: for each vertex, the ids of its incident
1076 /// edges (`igraph_inclist_init`).
1077 ///
1078 /// The list is independent of the graph after creation.
1079 ///
1080 /// - `mode`: in directed graphs, out-edges ([`NeighborMode::Out`]),
1081 /// in-edges ([`NeighborMode::In`]) or both ([`NeighborMode::All`]);
1082 /// ignored for undirected graphs. With `Out` or `In` each edge id
1083 /// appears once in the whole structure, with `All` twice (once per
1084 /// endpoint).
1085 /// - `loops`: [`Loops::None`] drops loop edges, [`Loops::Once`] lists each
1086 /// loop edge once for its vertex, [`Loops::Twice`] twice (only for
1087 /// undirected graphs or `mode = All`).
1088 ///
1089 /// The list of vertex `v` holds the same ids as
1090 /// [`Graph::incident`]`(v, mode, loops)`. Pair it with
1091 /// [`Graph::adjlist_init_from_inclist`] to get the matching neighbors.
1092 ///
1093 /// Time complexity: O(|V|+|E|).
1094 ///
1095 /// # Examples
1096 /// ```
1097 /// use igraph::prelude::*;
1098 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 2)], 3, false).unwrap();
1099 /// let il = g.inclist_init(NeighborMode::All, Loops::Once).unwrap();
1100 /// assert_eq!(il.to_vecs(), vec![vec![0], vec![0, 1], vec![1, 2]]);
1101 /// ```
1102 ///
1103 /// Binds [`igraph_inclist_init`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_inclist_init).
1104 pub fn inclist_init(&self, mode: NeighborMode, loops: Loops) -> Result<IncList> {
1105 let mut raw = MaybeUninit::<IncList>::zeroed();
1106 igraph_call!(igraph_inclist_init(
1107 self,
1108 raw.as_mut_ptr(),
1109 mode.into(),
1110 loops.into()
1111 ))?;
1112 Ok(unsafe { raw.assume_init() })
1113 }
1114
1115 /// Lazy adjacency list of the graph (`igraph_lazy_adjlist_init`): the
1116 /// neighbors of a vertex are computed on its first
1117 /// [`get`](LazyAdjList::get) and then cached.
1118 ///
1119 /// The arguments have the same meaning as in [`Graph::adjlist_init`]; the
1120 /// lists are sorted. The lazy list borrows the graph, which therefore
1121 /// cannot be modified while the list is alive. When igraph already knows
1122 /// (from its property cache, e.g. after [`Graph::has_loop`] or
1123 /// [`Graph::has_multiple`]) that the graph has no self-loops or no
1124 /// multi-edges, it skips the corresponding filtering: see
1125 /// [`LazyAdjList::loops`] and [`LazyAdjList::multiple`]. The lists are
1126 /// always the same as those of [`Graph::adjlist_init`], whatever the
1127 /// state of the cache (this wrapper works around an igraph 1.0.0 and
1128 /// 1.0.1 bug that listed mutual pairs of a directed graph twice in mode
1129 /// `All` with `multiple = false` once the cache said "no multi-edges").
1130 ///
1131 /// Time complexity: O(|V|) for the initialization, O(d) for the first
1132 /// query of a vertex of degree d, O(1) afterwards.
1133 ///
1134 /// # Examples
1135 /// ```
1136 /// use igraph::prelude::*;
1137 /// let g = Graph::from_edges(&[(0, 1), (0, 2), (1, 2)], 3, true).unwrap();
1138 /// let mut lazy = g.lazy_adjlist_init(NeighborMode::In, Loops::Once, true).unwrap();
1139 /// assert_eq!(lazy.len(), 3);
1140 /// assert_eq!(lazy.get(2).unwrap(), &[0, 1]);
1141 /// assert_eq!(lazy.get(0).unwrap(), &[] as &[i64]);
1142 /// ```
1143 ///
1144 /// Binds [`igraph_lazy_adjlist_init`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_lazy_adjlist_init).
1145 pub fn lazy_adjlist_init(
1146 &self,
1147 mode: NeighborMode,
1148 loops: Loops,
1149 multiple: bool,
1150 ) -> Result<LazyAdjList<'_>> {
1151 let collapse = collapse_rule(self, mode, loops, multiple);
1152 let mut raw = MaybeUninit::<igraph_lazy_adjlist_t>::zeroed();
1153 igraph_call!(igraph_lazy_adjlist_init(
1154 self,
1155 raw.as_mut_ptr(),
1156 mode.into(),
1157 loops.into(),
1158 multiple || collapse.is_some()
1159 ))?;
1160 Ok(LazyAdjList {
1161 raw: unsafe { raw.assume_init() },
1162 collapse,
1163 _graph: PhantomData,
1164 })
1165 }
1166
1167 /// Lazy incidence list of the graph (`igraph_lazy_inclist_init`): the
1168 /// incident edges of a vertex are computed on its first
1169 /// [`get`](LazyIncList::get) and then cached.
1170 ///
1171 /// The arguments have the same meaning as in [`Graph::inclist_init`]. The
1172 /// lazy list borrows the graph.
1173 ///
1174 /// Time complexity: O(|V|) for the initialization, O(d) for the first
1175 /// query of a vertex of degree d, O(1) afterwards.
1176 ///
1177 /// # Examples
1178 /// ```
1179 /// use igraph::prelude::*;
1180 /// let g = Graph::from_edges(&[(0, 1), (0, 2), (1, 2)], 3, true).unwrap();
1181 /// let mut lazy = g.lazy_inclist_init(NeighborMode::Out, Loops::Once).unwrap();
1182 /// assert_eq!(lazy.get(0).unwrap(), &[0, 1]);
1183 /// ```
1184 ///
1185 /// Binds [`igraph_lazy_inclist_init`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_lazy_inclist_init).
1186 pub fn lazy_inclist_init(&self, mode: NeighborMode, loops: Loops) -> Result<LazyIncList<'_>> {
1187 let mut raw = MaybeUninit::<igraph_lazy_inclist_t>::zeroed();
1188 igraph_call!(igraph_lazy_inclist_init(
1189 self,
1190 raw.as_mut_ptr(),
1191 mode.into(),
1192 loops.into()
1193 ))?;
1194 Ok(LazyIncList {
1195 raw: unsafe { raw.assume_init() },
1196 _graph: PhantomData,
1197 })
1198 }
1199}
1200
1201/// Lazy adjacency list (`igraph_lazy_adjlist_t`) borrowing a graph: the
1202/// neighbors of each vertex are queried on first access and cached.
1203///
1204/// Create it with [`Graph::lazy_adjlist_init`] or [`LazyAdjList::new`]. Since
1205/// queries fill the cache, access needs `&mut self`.
1206pub struct LazyAdjList<'g> {
1207 raw: igraph_lazy_adjlist_t,
1208 /// Multi-edge collapsing done on the Rust side (see `collapse_rule`).
1209 collapse: Option<usize>,
1210 _graph: PhantomData<&'g Graph>,
1211}
1212
1213/// Lazy incidence list (`igraph_lazy_inclist_t`) borrowing a graph: the
1214/// incident edges of each vertex are queried on first access and cached.
1215///
1216/// Create it with [`Graph::lazy_inclist_init`] or [`LazyIncList::new`].
1217pub struct LazyIncList<'g> {
1218 raw: igraph_lazy_inclist_t,
1219 _graph: PhantomData<&'g Graph>,
1220}
1221
1222macro_rules! impl_lazy_list {
1223 (
1224 $ty:ident, $field:ident, $what:literal, $item:literal, c = $c:literal,
1225 get_real = $get_real:ident, destroy = $destroy:ident, clear = $clear:ident
1226 ) => {
1227 impl<'g> $ty<'g> {
1228 #[doc = concat!("Number of vertices of the ", $what, ", i.e. of the graph (`", $c, "_size`).")]
1229 ///
1230 #[doc = concat!("Binds [`", $c, "_size`](", "https://igraph.org/c/html/latest/igraph-Data-structures.html#", $c, "_size).")]
1231 pub fn len(&self) -> usize {
1232 self.raw.length.max(0) as usize
1233 }
1234
1235 /// Whether the underlying graph has no vertices.
1236 pub fn is_empty(&self) -> bool {
1237 self.len() == 0
1238 }
1239
1240 /// The graph this lazy list reads from.
1241 pub fn graph(&self) -> &'g Graph {
1242 // The pointer was set from a `&'g Graph` at construction.
1243 unsafe { &*self.raw.graph }
1244 }
1245
1246 /// The effective neighbor mode (always [`NeighborMode::All`] for
1247 /// undirected graphs).
1248 pub fn mode(&self) -> NeighborMode {
1249 NeighborMode::try_from(self.raw.mode).unwrap_or(NeighborMode::All)
1250 }
1251
1252 #[doc = concat!("Whether the ", $item, " of vertex `v` were already computed and cached.")]
1253 ///
1254 /// Returns `false` for out-of-range vertices. Time complexity: O(1).
1255 ///
1256 #[doc = concat!("Binds the [`", $c, "_has`](", "https://igraph.org/c/html/latest/igraph-Data-structures.html#", $c, "_has) macro.")]
1257 pub fn has(&self, v: VertexId) -> bool {
1258 match check_vertex(v, self.len()) {
1259 Ok(i) => unsafe { !(*self.raw.$field.add(i)).is_null() },
1260 Err(_) => false,
1261 }
1262 }
1263
1264 #[doc = concat!("The ", $item, " of vertex `v`, computed on the first call and cached afterwards.")]
1265 ///
1266 /// The returned slice borrows the lazy list mutably, so copy it
1267 /// (`.to_vec()`) if you need to query other vertices while using
1268 /// it. Time complexity: O(d) on the first call for a vertex of
1269 /// degree d, O(1) afterwards.
1270 ///
1271 #[doc = concat!("Binds the [`", $c, "_get`](", "https://igraph.org/c/html/latest/igraph-Data-structures.html#", $c, "_get) macro.")]
1272 ///
1273 /// # Errors
1274 /// [`ErrorKind::InvalidVertexId`] if `v` is out of range, or an
1275 /// igraph error if the list cannot be computed (out of memory).
1276 pub fn get(&mut self, v: VertexId) -> Result<&[igraph_int_t]> {
1277 Ok(self.get_mut(v)?.as_slice())
1278 }
1279
1280 #[doc = concat!("The ", $item, " of vertex `v` as a mutable [`VectorInt`], computed on first access.")]
1281 ///
1282 /// Modifying it changes only the cached copy, never the graph.
1283 ///
1284 /// # Errors
1285 /// As [`get`](Self::get).
1286 pub fn get_mut(&mut self, v: VertexId) -> Result<&mut VectorInt> {
1287 let i = check_vertex(v, self.len())?;
1288 ensure_init();
1289 let cached = unsafe { *self.raw.$field.add(i) };
1290 if !cached.is_null() {
1291 return Ok(unsafe { &mut *cached });
1292 }
1293 let ptr = unsafe { $get_real(&mut self.raw, v) };
1294 if ptr.is_null() {
1295 return Err(lazy_failure());
1296 }
1297 // The vector is owned by the lazy list, which we borrow mutably.
1298 let list = unsafe { &mut *ptr };
1299 self.after_compute(v, list);
1300 Ok(list)
1301 }
1302
1303 /// Computes (if needed) all the lists and copies them into nested
1304 /// Rust vectors.
1305 ///
1306 /// # Errors
1307 /// As [`get`](Self::get).
1308 pub fn to_vecs(&mut self) -> Result<Vec<Vec<igraph_int_t>>> {
1309 (0..self.len() as VertexId).map(|v| self.get(v).map(<[igraph_int_t]>::to_vec)).collect()
1310 }
1311
1312 #[doc = concat!("Forgets all the cached lists (`", stringify!($clear), "`); they will be recomputed on demand.")]
1313 ///
1314 /// Any edits made through [`get_mut`](Self::get_mut) are lost.
1315 ///
1316 #[doc = concat!("Binds [`", stringify!($clear), "`](", "https://igraph.org/c/html/latest/igraph-Data-structures.html#", stringify!($clear), ").")]
1317 pub fn clear(&mut self) {
1318 if !self.raw.$field.is_null() {
1319 unsafe { $clear(&mut self.raw) };
1320 }
1321 }
1322 }
1323
1324 impl Drop for $ty<'_> {
1325 #[doc = concat!("Frees the cached lists with `", stringify!($destroy), "`.")]
1326 fn drop(&mut self) {
1327 if !self.raw.$field.is_null() {
1328 unsafe { $destroy(&mut self.raw) };
1329 }
1330 }
1331 }
1332
1333 impl fmt::Debug for $ty<'_> {
1334 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1335 let cached = (0..self.len() as VertexId).filter(|&v| self.has(v)).count();
1336 f.debug_struct(stringify!($ty))
1337 .field("len", &self.len())
1338 .field("mode", &self.mode())
1339 .field("cached", &cached)
1340 .finish()
1341 }
1342 }
1343 };
1344}
1345
1346impl_lazy_list!(
1347 LazyAdjList,
1348 adjs,
1349 "lazy adjacency list",
1350 "neighbors",
1351 c = "igraph_lazy_adjlist",
1352 get_real = igraph_i_lazy_adjlist_get_real,
1353 destroy = igraph_lazy_adjlist_destroy,
1354 clear = igraph_lazy_adjlist_clear
1355);
1356impl_lazy_list!(
1357 LazyIncList,
1358 incs,
1359 "lazy incidence list",
1360 "incident edges",
1361 c = "igraph_lazy_inclist",
1362 get_real = igraph_i_lazy_inclist_get_real,
1363 destroy = igraph_lazy_inclist_destroy,
1364 clear = igraph_lazy_inclist_clear
1365);
1366
1367impl<'g> LazyAdjList<'g> {
1368 /// Post-processes a freshly computed list (see `collapse_rule`).
1369 fn after_compute(&self, v: VertexId, list: &mut VectorInt) {
1370 if let Some(max_loops) = self.collapse {
1371 collapse_sorted(list, v, max_loops);
1372 }
1373 }
1374
1375 /// Lazy adjacency list of `graph`: same as [`Graph::lazy_adjlist_init`].
1376 pub fn new(graph: &'g Graph, mode: NeighborMode, loops: Loops, multiple: bool) -> Result<Self> {
1377 graph.lazy_adjlist_init(mode, loops, multiple)
1378 }
1379
1380 /// The effective loop handling.
1381 ///
1382 /// This is the `loops` argument given at creation, unless igraph already
1383 /// knew that the graph has no self-loops: then it reports
1384 /// [`Loops::Twice`] (mode `All`) or [`Loops::Once`] (mode `In`/`Out`),
1385 /// which avoids a useless filtering pass and yields the same lists.
1386 pub fn loops(&self) -> Loops {
1387 Loops::try_from(self.raw.loops).unwrap_or(Loops::Twice)
1388 }
1389
1390 /// Whether multi-edges are kept.
1391 ///
1392 /// This is the `multiple` argument given at creation, except for an
1393 /// `Out` or `In` list of a directed graph that igraph already knew to
1394 /// have no multi-edges: then it is `true`, since there is nothing to
1395 /// collapse. (Lists gathering neighbors in both directions are always
1396 /// collapsed as requested; see [`Graph::adjlist_init`] for the igraph
1397 /// 1.0.0 and 1.0.1 bug this works around.)
1398 pub fn multiple(&self) -> bool {
1399 self.collapse.is_none() && self.raw.multiple
1400 }
1401}
1402
1403impl<'g> LazyIncList<'g> {
1404 /// Incidence lists need no post-processing.
1405 fn after_compute(&self, _v: VertexId, _list: &mut VectorInt) {}
1406
1407 /// Lazy incidence list of `graph`: same as [`Graph::lazy_inclist_init`].
1408 pub fn new(graph: &'g Graph, mode: NeighborMode, loops: Loops) -> Result<Self> {
1409 graph.lazy_inclist_init(mode, loops)
1410 }
1411
1412 /// The loop handling of this list, as given at creation.
1413 pub fn loops(&self) -> Loops {
1414 Loops::try_from(self.raw.loops).unwrap_or(Loops::Twice)
1415 }
1416}