Skip to main content

igraph/
list.rs

1//! Owned typed lists (`igraph_typed_list_pmt.h`): [`VectorIntList`],
2//! [`VectorList`], [`MatrixList`], [`GraphList`] and [`BitsetList`].
3//!
4//! igraph returns collections of vectors (paths, cliques, components, ...)
5//! and of graphs (decompositions, ...) through these list types. Each list
6//! owns its items: it frees them on [`Drop`], can be indexed (`list[i]`),
7//! iterated by reference, and converted into standard Rust collections.
8//!
9//! The safe wrappers of the crate usually convert them for you, e.g.
10//! [`Graph::maximal_cliques`](crate::Graph::maximal_cliques) returns a
11//! `Vec<Vec<i64>>` and [`Graph::decompose`](crate::Graph::decompose) a
12//! `Vec<Graph>`; the list types matter when calling raw FFI functions or
13//! when a wrapper takes a list as input.
14//!
15//! ```
16//! use igraph::prelude::*;
17//!
18//! let list = VectorIntList::from_iter([vec![0, 1], vec![2, 3, 4]]);
19//! assert_eq!(list.len(), 2);
20//! assert_eq!(list[1].as_slice(), &[2, 3, 4]);
21//! let nested: Vec<Vec<i64>> = list.to_vecs();
22//! assert_eq!(nested, vec![vec![0, 1], vec![2, 3, 4]]);
23//! ```
24//!
25//! Every list supports `push`, `pop`, `insert`, `remove`, `swap_remove`,
26//! `replace`, `swap`, `reverse`, `permute`, `truncate`, `clear`,
27//! `sort_by`/`sort_by_key`, `push_copy`, `reserve`/`capacity`, mutable
28//! indexing and iteration. Lists of vectors can also be sorted and
29//! deduplicated with igraph's own comparators:
30//!
31//! ```
32//! use igraph::prelude::*;
33//!
34//! let mut cliques = VectorIntList::from_iter([vec![2, 3], vec![0, 1, 2], vec![2, 3], vec![0, 1]]);
35//! cliques.sort(); // lexicographic, a prefix first
36//! cliques.dedup();
37//! assert_eq!(cliques.to_vecs(), vec![vec![0, 1], vec![0, 1, 2], vec![2, 3]]);
38//! cliques.sort_by_key(|c| std::cmp::Reverse(c.len()));
39//! assert_eq!(cliques[0], vec![0, 1, 2]);
40//! ```
41
42use crate::ffi::*;
43use std::{fmt, mem::MaybeUninit, ops::Index};
44
45/// Owned list of integer vectors (`igraph_vector_int_list_t`).
46pub type VectorIntList = igraph_vector_int_list_t;
47/// Owned list of real vectors (`igraph_vector_list_t`).
48pub type VectorList = igraph_vector_list_t;
49/// Owned list of real matrices (`igraph_matrix_list_t`).
50pub type MatrixList = igraph_matrix_list_t;
51/// Owned list of graphs (`igraph_graph_list_t`).
52pub type GraphList = igraph_graph_list_t;
53/// Owned list of bitsets (`igraph_bitset_list_t`), see [`crate::bitset`].
54pub type BitsetList = igraph_bitset_list_t;
55
56macro_rules! impl_list {
57    ($ty:ident, $item:ident, init = $init:ident, init_copy = $init_copy:ident,
58     destroy = $destroy:ident, push_back = $push:ident, pop_back = $pop:ident,
59     remove = $remove:ident) => {
60        impl $ty {
61            /// Creates an empty list
62            /// ([`igraph_*_list_init`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_vector_list_init)).
63            pub fn new() -> Self {
64                crate::error::ensure_init();
65                let mut raw = MaybeUninit::<Self>::uninit();
66                crate::error::check(unsafe { $init(raw.as_mut_ptr(), 0) })
67                    .expect("igraph failed to allocate a list");
68                unsafe { raw.assume_init() }
69            }
70
71            /// Number of items.
72            pub fn len(&self) -> usize {
73                if self.stor_begin.is_null() {
74                    0
75                } else {
76                    unsafe { self.end.offset_from(self.stor_begin) as usize }
77                }
78            }
79
80            /// Whether the list is empty.
81            pub fn is_empty(&self) -> bool {
82                self.len() == 0
83            }
84
85            /// The items as a slice of the C item type.
86            pub fn as_slice(&self) -> &[$item] {
87                if self.stor_begin.is_null() {
88                    &[]
89                } else {
90                    unsafe { std::slice::from_raw_parts(self.stor_begin, self.len()) }
91                }
92            }
93
94            /// The items as a mutable slice of the C item type.
95            pub fn as_mut_slice(&mut self) -> &mut [$item] {
96                if self.stor_begin.is_null() {
97                    &mut []
98                } else {
99                    let len = self.len();
100                    unsafe { std::slice::from_raw_parts_mut(self.stor_begin, len) }
101                }
102            }
103
104            /// The item at `index`, if any.
105            pub fn get(&self, index: usize) -> Option<&$item> {
106                self.as_slice().get(index)
107            }
108
109            /// Iterates over the items by reference.
110            pub fn iter(&self) -> std::slice::Iter<'_, $item> {
111                self.as_slice().iter()
112            }
113
114            /// Appends an item, transferring its ownership to the list
115            /// ([`igraph_*_list_push_back`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_vector_list_push_back)).
116            pub fn push(&mut self, mut item: $item) {
117                crate::error::check(unsafe { $push(self, &mut item) })
118                    .expect("igraph failed to grow a list");
119                // The list now owns the item's storage.
120                std::mem::forget(item);
121            }
122
123            /// Removes and returns the last item, if any; the caller owns it
124            /// ([`igraph_*_list_pop_back`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_vector_list_pop_back)).
125            pub fn pop(&mut self) -> Option<$item> {
126                if self.is_empty() {
127                    None
128                } else {
129                    Some(unsafe { $pop(self) })
130                }
131            }
132
133            /// Removes the item at `index`, shifting the following ones, and
134            /// returns it (ownership is transferred back to the caller)
135            /// ([`igraph_*_list_remove`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_vector_list_remove)).
136            ///
137            /// # Panics
138            /// If `index >= len`.
139            pub fn remove(&mut self, index: usize) -> $item {
140                assert!(index < self.len(), "list index {index} out of bounds");
141                let mut out = MaybeUninit::<$item>::uninit();
142                crate::error::check(unsafe {
143                    $remove(self, index as igraph_int_t, out.as_mut_ptr())
144                })
145                .expect("igraph failed to remove a list item");
146                unsafe { out.assume_init() }
147            }
148
149            /// Converts into a [`Vec`] of owned items, without copying them.
150            pub fn into_vec(mut self) -> Vec<$item> {
151                let mut items = Vec::with_capacity(self.len());
152                while let Some(item) = self.pop() {
153                    items.push(item);
154                }
155                items.reverse();
156                items
157            }
158        }
159
160        impl Drop for $ty {
161            fn drop(&mut self) {
162                if !self.stor_begin.is_null() {
163                    unsafe { $destroy(self) };
164                    self.stor_begin = std::ptr::null_mut();
165                }
166            }
167        }
168
169        impl Default for $ty {
170            fn default() -> Self {
171                Self::new()
172            }
173        }
174
175        impl Clone for $ty {
176            fn clone(&self) -> Self {
177                crate::error::ensure_init();
178                let mut raw = MaybeUninit::<Self>::uninit();
179                crate::error::check(unsafe { $init_copy(raw.as_mut_ptr(), self) })
180                    .expect("igraph failed to copy a list");
181                unsafe { raw.assume_init() }
182            }
183        }
184
185        impl Index<usize> for $ty {
186            type Output = $item;
187            fn index(&self, index: usize) -> &$item {
188                &self.as_slice()[index]
189            }
190        }
191
192        impl<'a> IntoIterator for &'a $ty {
193            type Item = &'a $item;
194            type IntoIter = std::slice::Iter<'a, $item>;
195            fn into_iter(self) -> Self::IntoIter {
196                self.iter()
197            }
198        }
199
200        impl IntoIterator for $ty {
201            type Item = $item;
202            type IntoIter = std::vec::IntoIter<$item>;
203            fn into_iter(self) -> Self::IntoIter {
204                self.into_vec().into_iter()
205            }
206        }
207
208        impl FromIterator<$item> for $ty {
209            fn from_iter<I: IntoIterator<Item = $item>>(iter: I) -> Self {
210                let mut list = Self::new();
211                for item in iter {
212                    list.push(item);
213                }
214                list
215            }
216        }
217
218        impl From<Vec<$item>> for $ty {
219            fn from(items: Vec<$item>) -> Self {
220                items.into_iter().collect()
221            }
222        }
223
224        unsafe impl Send for $ty {}
225    };
226}
227
228impl_list!(
229    igraph_vector_int_list_t,
230    igraph_vector_int_t,
231    init = igraph_vector_int_list_init,
232    init_copy = igraph_vector_int_list_init_copy,
233    destroy = igraph_vector_int_list_destroy,
234    push_back = igraph_vector_int_list_push_back,
235    pop_back = igraph_vector_int_list_pop_back,
236    remove = igraph_vector_int_list_remove
237);
238impl_list!(
239    igraph_vector_list_t,
240    igraph_vector_t,
241    init = igraph_vector_list_init,
242    init_copy = igraph_vector_list_init_copy,
243    destroy = igraph_vector_list_destroy,
244    push_back = igraph_vector_list_push_back,
245    pop_back = igraph_vector_list_pop_back,
246    remove = igraph_vector_list_remove
247);
248impl_list!(
249    igraph_matrix_list_t,
250    igraph_matrix_t,
251    init = igraph_matrix_list_init,
252    init_copy = igraph_matrix_list_init_copy,
253    destroy = igraph_matrix_list_destroy,
254    push_back = igraph_matrix_list_push_back,
255    pop_back = igraph_matrix_list_pop_back,
256    remove = igraph_matrix_list_remove
257);
258impl_list!(
259    igraph_graph_list_t,
260    igraph_t,
261    init = igraph_graph_list_init,
262    init_copy = igraph_graph_list_init_copy,
263    destroy = igraph_graph_list_destroy,
264    push_back = igraph_graph_list_push_back,
265    pop_back = igraph_graph_list_pop_back,
266    remove = igraph_graph_list_remove
267);
268
269impl_list!(
270    igraph_bitset_list_t,
271    igraph_bitset_t,
272    init = igraph_bitset_list_init,
273    init_copy = igraph_bitset_list_init_copy,
274    destroy = igraph_bitset_list_destroy,
275    push_back = igraph_bitset_list_push_back,
276    pop_back = igraph_bitset_list_pop_back,
277    remove = igraph_bitset_list_remove
278);
279
280unsafe impl Sync for igraph_bitset_list_t {}
281unsafe impl Sync for igraph_vector_int_list_t {}
282unsafe impl Sync for igraph_vector_list_t {}
283unsafe impl Sync for igraph_matrix_list_t {}
284
285macro_rules! impl_vec_conversions {
286    ($ty:ident, $elem:ty, $vec:ident) => {
287        impl $ty {
288            /// Copies the items into nested [`Vec`]s.
289            pub fn to_vecs(&self) -> Vec<Vec<$elem>> {
290                self.iter().map(|v| v.to_vec()).collect()
291            }
292        }
293
294        impl FromIterator<Vec<$elem>> for $ty {
295            fn from_iter<I: IntoIterator<Item = Vec<$elem>>>(iter: I) -> Self {
296                iter.into_iter().map($vec::from).collect()
297            }
298        }
299
300        impl<'a> FromIterator<&'a [$elem]> for $ty {
301            fn from_iter<I: IntoIterator<Item = &'a [$elem]>>(iter: I) -> Self {
302                iter.into_iter().map($vec::from_slice).collect()
303            }
304        }
305
306        impl From<&[Vec<$elem>]> for $ty {
307            fn from(items: &[Vec<$elem>]) -> Self {
308                items.iter().map(|v| $vec::from_slice(v)).collect()
309            }
310        }
311
312        impl From<$ty> for Vec<Vec<$elem>> {
313            fn from(list: $ty) -> Self {
314                list.to_vecs()
315            }
316        }
317
318        impl PartialEq for $ty {
319            fn eq(&self, other: &Self) -> bool {
320                self.as_slice() == other.as_slice()
321            }
322        }
323    };
324}
325
326impl_vec_conversions!(igraph_vector_int_list_t, igraph_int_t, igraph_vector_int_t);
327impl_vec_conversions!(igraph_vector_list_t, igraph_real_t, igraph_vector_t);
328
329// ---------------------------------------------------------------------------
330// More list operations (`igraph_typed_list_pmt.h`), for every list type.
331// ---------------------------------------------------------------------------
332
333macro_rules! impl_list_extra {
334    (
335        $ty:ident, $item:ident,
336        insert = $insert:ident, replace = $replace:ident, remove_fast = $remove_fast:ident,
337        clear = $clear:ident, reverse = $reverse:ident, permute = $permute:ident,
338        capacity = $capacity:ident, reserve = $reserve:ident, swap_elements = $swap_elements:ident,
339        push_back_copy = $push_back_copy:ident
340    ) => {
341        impl $ty {
342            /// Inserts `item` at position `pos`, shifting the following items;
343            /// the list takes ownership of it
344            /// ([`igraph_*_list_insert`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_vector_list_insert)).
345            ///
346            /// # Panics
347            /// If `pos > len`.
348            pub fn insert(&mut self, pos: usize, mut item: $item) {
349                let len = self.len();
350                assert!(
351                    pos <= len,
352                    "list insertion index {pos} out of bounds (len {len})"
353                );
354                crate::error::check(unsafe { $insert(self, pos as igraph_int_t, &mut item) })
355                    .expect("igraph failed to grow a list");
356                // The list now owns the item's storage.
357                std::mem::forget(item);
358            }
359
360            /// Replaces the item at `pos` with `item`, returning the old one
361            /// ([`igraph_*_list_replace`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_vector_list_replace)).
362            ///
363            /// # Panics
364            /// If `pos >= len`.
365            pub fn replace(&mut self, pos: usize, mut item: $item) -> $item {
366                let len = self.len();
367                assert!(pos < len, "list index {pos} out of bounds (len {len})");
368                // igraph swaps the two structs: `item` now holds the old one.
369                unsafe { $replace(self, pos as igraph_int_t, &mut item) };
370                item
371            }
372
373            /// Removes the item at `pos` in O(1), moving the last item into
374            /// its place, and returns it
375            /// ([`igraph_*_list_remove_fast`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_vector_list_remove_fast)).
376            ///
377            /// # Panics
378            /// If `pos >= len`.
379            pub fn swap_remove(&mut self, pos: usize) -> $item {
380                let len = self.len();
381                assert!(pos < len, "list index {pos} out of bounds (len {len})");
382                let mut out = MaybeUninit::<$item>::uninit();
383                crate::error::check(unsafe {
384                    $remove_fast(self, pos as igraph_int_t, out.as_mut_ptr())
385                })
386                .expect("igraph failed to remove a list item");
387                unsafe { out.assume_init() }
388            }
389
390            /// Destroys all the items, keeping the storage
391            /// ([`igraph_*_list_clear`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_vector_list_clear)).
392            pub fn clear(&mut self) {
393                if !self.stor_begin.is_null() {
394                    unsafe { $clear(self) };
395                }
396            }
397
398            /// Keeps the first `len` items, destroying the others.
399            pub fn truncate(&mut self, len: usize) {
400                while self.len() > len {
401                    drop(self.pop());
402                }
403            }
404
405            /// Reverses the order of the items
406            /// (`igraph_*_list_reverse`, undocumented in `igraph_vector_list.h`).
407            pub fn reverse(&mut self) {
408                if !self.is_empty() {
409                    crate::error::check(unsafe { $reverse(self) })
410                        .expect("igraph_*_list_reverse cannot fail");
411                }
412            }
413
414            /// Swaps the items at positions `i` and `j`
415            /// ([`igraph_*_list_swap_elements`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_vector_list_swap_elements)).
416            ///
417            /// # Panics
418            /// If an index is out of bounds.
419            pub fn swap(&mut self, i: usize, j: usize) {
420                let len = self.len();
421                assert!(
422                    i < len && j < len,
423                    "list indices ({i}, {j}) out of bounds (len {len})"
424                );
425                unsafe { $swap_elements(self, i as igraph_int_t, j as igraph_int_t) };
426            }
427
428            /// Reorders the items so that the item at `index[i]` moves to
429            /// position `i`
430            /// ([`igraph_*_list_permute`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_vector_list_permute)).
431            ///
432            /// # Errors
433            /// [`ErrorKind::InvalidValue`](crate::error::ErrorKind::InvalidValue)
434            /// unless `index` is a permutation of `0..len` (igraph 1.0.0 and
435            /// 1.0.1 do not check it, and would leak or double free items with
436            /// an invalid one).
437            pub fn permute(&mut self, index: &[igraph_int_t]) -> crate::error::Result<()> {
438                let len = self.len();
439                if index.len() != len {
440                    return Err(crate::error::Error::invalid(format!(
441                        "permutation of length {} for a list of length {len}",
442                        index.len()
443                    )));
444                }
445                let mut seen = vec![false; len];
446                for &i in index {
447                    if i < 0 || i as usize >= len || std::mem::replace(&mut seen[i as usize], true)
448                    {
449                        return Err(crate::error::Error::invalid(format!(
450                            "invalid or repeated position {i} in a permutation of length {len}"
451                        )));
452                    }
453                }
454                if len == 0 {
455                    return Ok(());
456                }
457                let idx = crate::vector::VectorInt::view(index);
458                crate::igraph_call!($permute(self, idx.as_ptr()))
459            }
460
461            /// Sorts the items with a comparator, like [`slice::sort_by`]
462            /// (stable; the items are moved, never copied).
463            pub fn sort_by(&mut self, compare: impl FnMut(&$item, &$item) -> std::cmp::Ordering) {
464                self.as_mut_slice().sort_by(compare);
465            }
466
467            /// Sorts the items by a key, like [`slice::sort_by_key`].
468            pub fn sort_by_key<K: Ord>(&mut self, key: impl FnMut(&$item) -> K) {
469                self.as_mut_slice().sort_by_key(key);
470            }
471
472            /// Number of items the list can hold without reallocating
473            /// ([`igraph_*_list_capacity`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_vector_list_capacity)).
474            pub fn capacity(&self) -> usize {
475                if self.stor_begin.is_null() {
476                    0
477                } else {
478                    unsafe { $capacity(self) as usize }
479                }
480            }
481
482            /// Reserves storage for at least `capacity` items in total
483            /// ([`igraph_*_list_reserve`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_vector_list_reserve)).
484            pub fn reserve(&mut self, capacity: usize) {
485                crate::error::check(unsafe { $reserve(self, crate::error::int_size(capacity)) })
486                    .expect("igraph failed to reserve list storage");
487            }
488
489            /// Appends a deep copy of `item`
490            /// ([`igraph_*_list_push_back_copy`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_vector_list_push_back_copy)).
491            pub fn push_copy(&mut self, item: &$item) {
492                crate::error::check(unsafe { $push_back_copy(self, item) })
493                    .expect("igraph failed to grow a list");
494            }
495
496            /// The item at `index`, mutably, if any.
497            pub fn get_mut(&mut self, index: usize) -> Option<&mut $item> {
498                self.as_mut_slice().get_mut(index)
499            }
500
501            /// The first item, if any.
502            pub fn first(&self) -> Option<&$item> {
503                self.as_slice().first()
504            }
505
506            /// The last item, if any.
507            pub fn last(&self) -> Option<&$item> {
508                self.as_slice().last()
509            }
510
511            /// Iterates over the items mutably.
512            pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, $item> {
513                self.as_mut_slice().iter_mut()
514            }
515        }
516
517        impl std::ops::IndexMut<usize> for $ty {
518            fn index_mut(&mut self, index: usize) -> &mut $item {
519                &mut self.as_mut_slice()[index]
520            }
521        }
522
523        impl<'a> IntoIterator for &'a mut $ty {
524            type Item = &'a mut $item;
525            type IntoIter = std::slice::IterMut<'a, $item>;
526            fn into_iter(self) -> Self::IntoIter {
527                self.iter_mut()
528            }
529        }
530
531        impl Extend<$item> for $ty {
532            fn extend<I: IntoIterator<Item = $item>>(&mut self, iter: I) {
533                for item in iter {
534                    self.push(item);
535                }
536            }
537        }
538    };
539}
540
541impl_list_extra!(
542    igraph_vector_int_list_t,
543    igraph_vector_int_t,
544    insert = igraph_vector_int_list_insert,
545    replace = igraph_vector_int_list_replace,
546    remove_fast = igraph_vector_int_list_remove_fast,
547    clear = igraph_vector_int_list_clear,
548    reverse = igraph_vector_int_list_reverse,
549    permute = igraph_vector_int_list_permute,
550    capacity = igraph_vector_int_list_capacity,
551    reserve = igraph_vector_int_list_reserve,
552    swap_elements = igraph_vector_int_list_swap_elements,
553    push_back_copy = igraph_vector_int_list_push_back_copy
554);
555impl_list_extra!(
556    igraph_vector_list_t,
557    igraph_vector_t,
558    insert = igraph_vector_list_insert,
559    replace = igraph_vector_list_replace,
560    remove_fast = igraph_vector_list_remove_fast,
561    clear = igraph_vector_list_clear,
562    reverse = igraph_vector_list_reverse,
563    permute = igraph_vector_list_permute,
564    capacity = igraph_vector_list_capacity,
565    reserve = igraph_vector_list_reserve,
566    swap_elements = igraph_vector_list_swap_elements,
567    push_back_copy = igraph_vector_list_push_back_copy
568);
569impl_list_extra!(
570    igraph_matrix_list_t,
571    igraph_matrix_t,
572    insert = igraph_matrix_list_insert,
573    replace = igraph_matrix_list_replace,
574    remove_fast = igraph_matrix_list_remove_fast,
575    clear = igraph_matrix_list_clear,
576    reverse = igraph_matrix_list_reverse,
577    permute = igraph_matrix_list_permute,
578    capacity = igraph_matrix_list_capacity,
579    reserve = igraph_matrix_list_reserve,
580    swap_elements = igraph_matrix_list_swap_elements,
581    push_back_copy = igraph_matrix_list_push_back_copy
582);
583impl_list_extra!(
584    igraph_bitset_list_t,
585    igraph_bitset_t,
586    insert = igraph_bitset_list_insert,
587    replace = igraph_bitset_list_replace,
588    remove_fast = igraph_bitset_list_remove_fast,
589    clear = igraph_bitset_list_clear,
590    reverse = igraph_bitset_list_reverse,
591    permute = igraph_bitset_list_permute,
592    capacity = igraph_bitset_list_capacity,
593    reserve = igraph_bitset_list_reserve,
594    swap_elements = igraph_bitset_list_swap_elements,
595    push_back_copy = igraph_bitset_list_push_back_copy
596);
597impl_list_extra!(
598    igraph_graph_list_t,
599    igraph_t,
600    insert = igraph_graph_list_insert,
601    replace = igraph_graph_list_replace,
602    remove_fast = igraph_graph_list_remove_fast,
603    clear = igraph_graph_list_clear,
604    reverse = igraph_graph_list_reverse,
605    permute = igraph_graph_list_permute,
606    capacity = igraph_graph_list_capacity,
607    reserve = igraph_graph_list_reserve,
608    swap_elements = igraph_graph_list_swap_elements,
609    push_back_copy = igraph_graph_list_push_back_copy
610);
611
612// ---------------------------------------------------------------------------
613// Ordering of vector lists, with igraph's own comparators.
614// ---------------------------------------------------------------------------
615
616macro_rules! impl_vec_list_order {
617    (
618        $ty:ident, $item:ident,
619        sort = $sort:ident, sort_ind = $sort_ind:ident, dedup = $dedup:ident,
620        lex_cmp = $lex_cmp:ident, colex_cmp = $colex_cmp:ident, all_e = $all_e:ident
621    ) => {
622        impl $ty {
623            /// Sorts the vectors lexicographically (a proper prefix comes
624            /// first)
625            /// ([`igraph_*_list_sort`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_vector_list_sort)
626            /// with [`igraph_vector_lex_cmp`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_vector_lex_cmp)).
627            pub fn sort(&mut self) {
628                if !self.is_empty() {
629                    unsafe { $sort(self, Some($lex_cmp)) };
630                }
631            }
632
633            /// Sorts the vectors colexicographically, i.e. comparing them
634            /// from their last elements
635            /// (`igraph_*_list_sort` with `igraph_vector_colex_cmp`).
636            pub fn sort_colex(&mut self) {
637                if !self.is_empty() {
638                    unsafe { $sort(self, Some($colex_cmp)) };
639                }
640            }
641
642            /// The permutation that sorts the list lexicographically, without
643            /// modifying it; pass it to `permute` to sort
644            /// ([`igraph_*_list_sort_ind`](https://igraph.org/c/html/latest/igraph-Data-structures.html#igraph_vector_list_sort_ind)).
645            pub fn sort_ind(&self) -> Vec<igraph_int_t> {
646                if self.is_empty() {
647                    return Vec::new();
648                }
649                let mut ind = crate::vector::VectorInt::new();
650                // `sort_ind` takes a mutable pointer but does not modify the
651                // list; a copy keeps `&self` honest at the price of O(n) copies.
652                let mut copy = self.clone();
653                crate::error::check(unsafe { $sort_ind(&mut copy, &mut ind, Some($lex_cmp)) })
654                    .expect("igraph failed to sort a list");
655                ind.into()
656            }
657
658            /// Removes consecutive equal vectors, keeping the first of each
659            /// run; on a sorted list it removes all duplicates, like
660            /// [`Vec::dedup`]
661            /// (`igraph_*_list_remove_consecutive_duplicates`, undocumented in `igraph_vector_list.h`).
662            pub fn dedup(&mut self) {
663                if !self.is_empty() {
664                    unsafe { $dedup(self, Some($all_e)) };
665                }
666            }
667        }
668    };
669}
670
671impl_vec_list_order!(
672    igraph_vector_int_list_t,
673    igraph_vector_int_t,
674    sort = igraph_vector_int_list_sort,
675    sort_ind = igraph_vector_int_list_sort_ind,
676    dedup = igraph_vector_int_list_remove_consecutive_duplicates,
677    lex_cmp = igraph_vector_int_lex_cmp,
678    colex_cmp = igraph_vector_int_colex_cmp,
679    all_e = igraph_vector_int_all_e
680);
681impl_vec_list_order!(
682    igraph_vector_list_t,
683    igraph_vector_t,
684    sort = igraph_vector_list_sort,
685    sort_ind = igraph_vector_list_sort_ind,
686    dedup = igraph_vector_list_remove_consecutive_duplicates,
687    lex_cmp = igraph_vector_lex_cmp,
688    colex_cmp = igraph_vector_colex_cmp,
689    all_e = igraph_vector_all_e
690);
691
692impl igraph_graph_list_t {
693    /// Sets whether the empty graphs created by igraph when *growing* this
694    /// list (e.g. by `igraph_graph_list_resize`) are directed
695    /// (`igraph_graph_list_set_directed`, undocumented in `igraph_graph_list.h`).
696    pub fn set_directed(&mut self, directed: bool) {
697        // igraph 1.0.0 and 1.0.1 declare, but do not export,
698        // `igraph_graph_list_set_directed`, which only sets this field.
699        self.directed = directed;
700    }
701}
702
703// Graphs are not `Sync`, so neither is a list of graphs; the other lists are.
704impl fmt::Display for igraph_vector_int_list_t {
705    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
706        f.debug_list()
707            .entries(self.iter().map(|v| v.as_slice()))
708            .finish()
709    }
710}
711
712impl fmt::Display for igraph_vector_list_t {
713    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
714        f.debug_list()
715            .entries(self.iter().map(|v| v.as_slice()))
716            .finish()
717    }
718}