Skip to main content

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}