Skip to main content

igraph/
constructors.rs

1//! Deterministic graph generators (`igraph_constructors.h`).
2//!
3//! This module turns igraph's *deterministic constructors* into associated
4//! functions of [`Graph`] returning [`Result`]`<Graph>`: given the same
5//! arguments they always build the very same graph, with the same vertex and
6//! edge ids. (Random generators live in [`crate::games`].)
7//!
8//! ```
9//! use igraph::prelude::*;
10//!
11//! // The Petersen graph, three ways: by name, as G(5, 2), and from its LCF code.
12//! let by_name = Graph::famous("Petersen").unwrap();
13//! let gp = Graph::generalized_petersen(5, 2).unwrap();
14//! assert_eq!((by_name.vcount(), by_name.ecount()), (10, 15));
15//! assert_eq!((gp.vcount(), gp.ecount()), (10, 15));
16//! // Both are 3-regular.
17//! for g in [&by_name, &gp] {
18//!     let degrees = g.degree(VertexSelector::All, NeighborMode::All, Loops::Twice).unwrap();
19//!     assert!(degrees.iter().all(|&d| d == 3));
20//! }
21//!
22//! // A 3x4 grid, a 6-cycle and a star with 5 leaves.
23//! let grid = Graph::square_lattice(&[3, 4], 1, false, false, None).unwrap();
24//! assert_eq!((grid.vcount(), grid.ecount()), (12, 17));
25//! let c6 = Graph::cycle_graph(6, false, false).unwrap();
26//! assert_eq!(c6.ecount(), 6);
27//! let star = Graph::star(6, StarMode::Undirected, 0).unwrap();
28//! assert_eq!(star.degree_of(0, NeighborMode::All, Loops::Twice).unwrap(), 5);
29//! ```
30//!
31//! # Provided functionality
32//!
33//! | Family | Functions |
34//! |---|---|
35//! | From matrices | [`Graph::adjacency`], [`Graph::weighted_adjacency`], [`Graph::sparse_adjacency`], [`Graph::sparse_weighted_adjacency`] |
36//! | From edge lists | [`Graph::small`] (see also [`Graph::from_edges`]) |
37//! | Paths, cycles, stars | [`Graph::ring`], [`Graph::path_graph`], [`Graph::cycle_graph`], [`Graph::star`], [`Graph::wheel`] |
38//! | Complete graphs | [`Graph::full`], [`Graph::full_citation`], [`Graph::full_multipartite`], [`Graph::turan`] |
39//! | Lattices | [`Graph::square_lattice`], [`Graph::triangular_lattice`], [`Graph::hexagonal_lattice`], [`Graph::hypercube`] |
40//! | Trees | [`Graph::kary_tree`], [`Graph::symmetric_tree`], [`Graph::regular_tree`], [`Graph::tree_from_parent_vector`], [`Graph::from_prufer`] |
41//! | Circulant-like | [`Graph::circulant`], [`Graph::generalized_petersen`], [`Graph::lcf`], [`Graph::extended_chordal_ring`] |
42//! | Word graphs | [`Graph::de_bruijn`], [`Graph::kautz`] |
43//! | Named graphs | [`Graph::famous`] (with [`FamousGraph`]), [`Graph::atlas`], [`Graph::mycielski_graph`] |
44//! | Degree sequences | [`Graph::realize_degree_sequence`], [`Graph::realize_bipartite_degree_sequence`] |
45//! | Derived graphs | [`Graph::linegraph`] |
46//!
47//! The corresponding chapter of the C documentation is
48//! [Deterministic graph generators](https://igraph.org/c/html/latest/igraph-Generators.html).
49//!
50//! # Conventions
51//!
52//! - Vertex counts and sizes are `usize`; values that may legitimately be
53//!   negative (shifts, parent ids, degrees coming from
54//!   [`Graph::degree`]) are `i64`.
55//! - Every function returns an [`Error`] rather than panicking on invalid
56//!   input; most errors are [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue).
57//!
58//! # See also
59//!
60//! | Need | Where |
61//! |---|---|
62//! | Random graphs (Erdős–Rényi, random regular, random trees, degree sequences, ...) | [`crate::games`], e.g. [`Graph::erdos_renyi_game_gnm`], [`Graph::k_regular_game`], [`Graph::tree_game`], [`Graph::degree_sequence_game`] |
63//! | The inverse conversions (graph → matrix, tree → Prüfer code) | [`Graph::get_adjacency`], [`Graph::get_adjacency_sparse`], [`Graph::to_prufer`] in [`crate::conversion`] |
64//! | Bipartite constructors | [`Graph::full_bipartite`], [`BipartiteGraph`](crate::bipartite::BipartiteGraph) in [`crate::bipartite`] |
65//! | Graphs derived from other graphs | [`Graph::complementer`], [`Graph::mycielskian`], [`Graph::disjoint_union`] in [`crate::operators`] |
66//! | Is a degree sequence realizable at all? | [`is_graphical`](crate::mixing::is_graphical), [`is_bigraphical`](crate::mixing::is_bigraphical) |
67//! | Comparing generated graphs up to relabelling | [`Graph::isomorphic`], [`Graph::count_automorphisms`] in [`crate::isomorphism`] |
68//! | Drawing them | [`Graph::layout_circle`] (rings), [`Graph::layout_star`], [`Graph::layout_grid`] (lattices), [`Graph::layout_reingold_tilford`] (trees) in [`crate::layout`] |
69//! | Reading graphs from files | [`crate::foreign`] |
70//!
71//! ```
72//! use igraph::prelude::*;
73//!
74//! // LCF notation and the named graph agree up to relabelling: the Heawood
75//! // graph two ways.
76//! let heawood = Graph::famous("Heawood")?;
77//! assert!(Graph::lcf(14, &[5, -5], 7)?.isomorphic(&heawood)?);
78//! // Its automorphism group PGL(2, 7) has order 336, and its girth is 6.
79//! assert_eq!(heawood.count_automorphisms(None)?, 336.0);
80//! assert_eq!(heawood.girth()?, Some(6));
81//!
82//! // Round trip through the adjacency matrix of the conversion module.
83//! let a = heawood.get_adjacency(GetAdjacency::Both, None, Loops::Twice)?;
84//! assert_eq!(Graph::adjacency(&a, Adjacency::Undirected, Loops::Twice)?, heawood);
85//! # Ok::<(), igraph::Error>(())
86//! ```
87//!
88//! # Differences from the C library
89//!
90//! A few defects of igraph 1.0.0 and 1.0.1 (the generator sources are
91//! unchanged in 1.0.1) are worked around on the Rust side, so the wrappers
92//! behave as documented:
93//!
94//! - [`Graph::sparse_adjacency`] / [`Graph::sparse_weighted_adjacency`]
95//!   agree with their dense counterparts even when an entry below the
96//!   diagonal has no mirror entry (igraph drops it in the unweighted `Max`
97//!   mode and in the weighted `Max`, `Min` and `Plus` modes), and when
98//!   explicit zeros are given in the `Undirected` mode (igraph's structural
99//!   symmetry test would reject them).
100//! - [`Graph::adjacency`] and [`Graph::sparse_adjacency`] reject NaN,
101//!   infinite, negative and fractional edge counts (igraph casts them to
102//!   integers unchecked).
103//! - [`Graph::star`] and [`Graph::wheel`] with `n = 1` give the singleton
104//!   graph (igraph returns the null graph).
105//! - [`Graph::lcf`] with `n = 0` gives the null graph (igraph divides by
106//!   zero), and LCF shifts and chordal ring offsets are reduced modulo the
107//!   number of vertices (igraph adds them without overflow checks).
108//! - [`Graph::extended_chordal_ring`] accepts a chord matrix without columns
109//!   (igraph divides by the number of columns).
110
111use crate::{
112    constants::{Adjacency, Loops, NeighborMode, RealizeDegseq, StarMode, TreeMode, WheelMode},
113    error::{Error, Result},
114    ffi::*,
115    graph::{Graph, VertexId},
116    linalg::SparseMat,
117    matrix::{Matrix, MatrixInt},
118    vector::{Vector, VectorBool, VectorInt},
119};
120use std::{collections::BTreeMap, ffi::CString};
121
122/// Converts a count to `igraph_int_t`, failing on overflow.
123fn int(n: usize, what: &str) -> Result<igraph_int_t> {
124    igraph_int_t::try_from(n).map_err(|_| Error::invalid(format!("{what} is too large: {n}")))
125}
126
127/// `base^exp` in exact integer arithmetic, `None` on overflow.
128///
129/// igraph's de Bruijn and Kautz generators compute these powers as doubles
130/// and convert them to `igraph_int_t` *before* checking the range, which is
131/// undefined behaviour for results of 2^63 and more: callers check here first.
132fn checked_ipow(base: igraph_int_t, exp: igraph_int_t) -> Option<igraph_int_t> {
133    match base {
134        0 => Some(if exp == 0 { 1 } else { 0 }),
135        1 => Some(1),
136        _ => base.checked_pow(u32::try_from(exp).ok()?),
137    }
138}
139
140/// For hexagon-shaped lattices (`dims.len() == 3`) igraph computes row counts,
141/// lengths and starts from sums of the sizes without overflow checks. Rejects
142/// sizes for which `2 * (d0 + d1 + d2) + 4`, a bound on all of them, overflows.
143fn check_hex_shape(dims: &[igraph_int_t], what: &str) -> Result<()> {
144    if dims.len() == 3 {
145        dims.iter()
146            .try_fold(0 as igraph_int_t, |acc, &d| acc.checked_add(d))
147            .and_then(|sum| sum.checked_mul(2))
148            .and_then(|b| b.checked_add(4))
149            .ok_or_else(|| {
150                Error::invalid(format!("Lattice dimensions {dims:?} too large for {what}."))
151            })?;
152    }
153    Ok(())
154}
155
156/// Converts a slice of counts into an owned igraph integer vector.
157fn int_vector(values: &[usize], what: &str) -> Result<VectorInt> {
158    values
159        .iter()
160        .map(|&v| int(v, what))
161        .collect::<Result<Vec<_>>>()
162        .map(VectorInt::from)
163}
164
165/// In igraph 1.0.0 and 1.0.1, `igraph_star` (and thus `igraph_wheel`) builds the graph
166/// from its edge list with no explicit vertex count, so `n = 1` yields the
167/// null graph. Adds the missing vertices, if any.
168fn fix_single_vertex(mut g: Graph, n: igraph_int_t) -> Result<Graph> {
169    let missing = usize::try_from(n).unwrap_or(0).saturating_sub(g.vcount());
170    if missing > 0 {
171        g.add_vertices(missing)?;
172    }
173    Ok(g)
174}
175
176/// Which kinds of edges a degree sequence realization may use
177/// (`igraph_edge_type_sw_t` flags, including their combination), taken by
178/// [`Graph::realize_degree_sequence`] and
179/// [`Graph::realize_bipartite_degree_sequence`].
180///
181/// Re-exported from [`constants`](crate::constants::AllowedEdgeTypes); it is
182/// the same type as the one taken by
183/// [`is_graphical`](crate::mixing::is_graphical), so the same value can first
184/// test and then realize a degree sequence:
185///
186/// ```
187/// use igraph::{constructors::AllowedEdgeTypes, mixing::is_graphical, prelude::*};
188/// let degrees = [4, 2];
189/// for allowed in [AllowedEdgeTypes::MULTI, AllowedEdgeTypes::ALL] {
190///     let graphical = is_graphical(&degrees, None, allowed).unwrap();
191///     let realized =
192///         Graph::realize_degree_sequence(&degrees, None, allowed, RealizeDegseq::Smallest);
193///     assert_eq!(graphical, realized.is_ok());
194/// }
195/// // Without self-loops, vertex 0 cannot place 4 stubs on its only neighbour's 2.
196/// assert!(!is_graphical(&degrees, None, AllowedEdgeTypes::MULTI).unwrap());
197/// ```
198pub use crate::constants::AllowedEdgeTypes;
199
200macro_rules! famous_graphs {
201    ($( $(#[$meta:meta])* $variant:ident = $name:literal, $n:literal, $m:literal; )+) => {
202        /// The named graphs known to [`Graph::famous`].
203        ///
204        /// Each variant documents its size; [`FamousGraph::name`] gives the
205        /// name understood by igraph, and [`FamousGraph::ALL`] lists them all.
206        /// `FamousGraph` implements `AsRef<str>`, so it can be passed to
207        /// [`Graph::famous`] directly:
208        ///
209        /// ```
210        /// use igraph::{constructors::FamousGraph, prelude::*};
211        /// let g = Graph::famous(FamousGraph::Heawood).unwrap();
212        /// assert_eq!((g.vcount(), g.ecount()), FamousGraph::Heawood.size());
213        /// ```
214        #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
215        pub enum FamousGraph {
216            $( $(#[$meta])* $variant ),+
217        }
218
219        impl FamousGraph {
220            /// All the famous graphs, in the order of the igraph documentation
221            /// (alphabetical, except that `Noperfectmatching` precedes `Nonline`).
222            pub const ALL: &'static [FamousGraph] = &[$(FamousGraph::$variant),+];
223
224            /// The name igraph uses for this graph (matching is case insensitive).
225            pub fn name(self) -> &'static str {
226                match self { $(FamousGraph::$variant => $name),+ }
227            }
228
229            /// The `(vertex count, edge count)` of this graph.
230            pub fn size(self) -> (usize, usize) {
231                match self { $(FamousGraph::$variant => ($n, $m)),+ }
232            }
233        }
234    };
235}
236
237famous_graphs! {
238    /// The bull graph: a triangle with two pendant "horns" (5 vertices, 5 edges).
239    Bull = "Bull", 5, 5;
240    /// The Chvátal graph: the smallest triangle-free, 4-chromatic, 4-regular graph (12, 24).
241    Chvatal = "Chvatal", 12, 24;
242    /// The Coxeter graph: a non-Hamiltonian cubic symmetric graph (28, 42).
243    Coxeter = "Coxeter", 28, 42;
244    /// The skeleton of the cube (8, 12).
245    Cubical = "Cubical", 8, 12;
246    /// The diamond: two triangles sharing an edge (4, 5).
247    Diamond = "Diamond", 4, 5;
248    /// The skeleton of the dodecahedron (20, 30).
249    Dodecahedron = "Dodecahedron", 20, 30;
250    /// The Folkman graph: the smallest semisymmetric graph (20, 40).
251    Folkman = "Folkman", 20, 40;
252    /// The Franklin graph, related to colorings of the Klein bottle (12, 18).
253    Franklin = "Franklin", 12, 18;
254    /// The Frucht graph: the smallest cubic graph with no non-trivial automorphism (12, 18).
255    Frucht = "Frucht", 12, 18;
256    /// The Grötzsch graph: triangle-free with chromatic number 4 (11, 20).
257    Grotzsch = "Grotzsch", 11, 20;
258    /// The Heawood graph: the 6-cage, the smallest cubic graph of girth 6 (14, 21).
259    Heawood = "Heawood", 14, 21;
260    /// The Herschel graph: the smallest non-Hamiltonian polyhedral graph (11, 18).
261    Herschel = "Herschel", 11, 18;
262    /// The house graph: a triangle on top of a square (5, 6).
263    House = "House", 5, 6;
264    /// The house graph with an X in the square (5, 8).
265    HouseX = "HouseX", 5, 8;
266    /// The skeleton of the icosahedron (12, 30).
267    Icosahedron = "Icosahedron", 12, 30;
268    /// Krackhardt's kite social network (10, 18).
269    KrackhardtKite = "Krackhardt_Kite", 10, 18;
270    /// The Levi graph: a 4-arc transitive cubic graph (30, 45).
271    Levi = "Levi", 30, 45;
272    /// The McGee graph: the unique 3-regular 7-cage (24, 36).
273    McGee = "McGee", 24, 36;
274    /// The Meredith graph: 4-regular, 4-connected and non-Hamiltonian (70, 140).
275    Meredith = "Meredith", 70, 140;
276    /// A connected graph without a perfect matching (16, 27).
277    NoPerfectMatching = "Noperfectmatching", 16, 27;
278    /// The disjoint union of the 9 forbidden subgraphs of line graphs (50, 72).
279    Nonline = "Nonline", 50, 72;
280    /// The skeleton of the octahedron (6, 12).
281    Octahedron = "Octahedron", 6, 12;
282    /// The Petersen graph: 3-regular, the smallest hypohamiltonian graph (10, 15).
283    Petersen = "Petersen", 10, 15;
284    /// The Robertson graph: the unique (4,5)-cage (19, 38).
285    Robertson = "Robertson", 19, 38;
286    /// A smallest non-trivial graph whose automorphism group is cyclic (9, 15).
287    SmallestCyclicGroup = "Smallestcyclicgroup", 9, 15;
288    /// The skeleton of the tetrahedron, i.e. `K_4` (4, 6).
289    Tetrahedron = "Tetrahedron", 4, 6;
290    /// The Thomassen graph: the smallest hypotraceable graph (34, 52).
291    Thomassen = "Thomassen", 34, 52;
292    /// The Tutte graph: a counterexample to Tait's Hamiltonian conjecture (46, 69).
293    Tutte = "Tutte", 46, 69;
294    /// A triangle-free, uniquely 3-colorable graph (12, 22).
295    Uniquely3Colorable = "Uniquely3colorable", 12, 22;
296    /// The Walther graph: an identity graph (25, 31).
297    Walther = "Walther", 25, 31;
298    /// Zachary's karate club social network (34, 78).
299    Zachary = "Zachary", 34, 78;
300}
301
302impl AsRef<str> for FamousGraph {
303    fn as_ref(&self) -> &str {
304        self.name()
305    }
306}
307
308impl std::fmt::Display for FamousGraph {
309    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
310        f.write_str(self.name())
311    }
312}
313
314/// Checks that `x` is a valid *edge count* for the unweighted adjacency
315/// constructors: finite, non-negative and integral.
316///
317/// igraph converts matrix entries to integers with a plain C cast, which is
318/// undefined behaviour for NaN, infinities and out-of-range values, and
319/// silently truncates fractions; all of these are rejected here.
320fn check_edge_count(x: f64, row: usize, col: usize) -> Result<()> {
321    // 2^63: the first double that does not fit into an igraph_int_t.
322    const LIMIT: f64 = 9_223_372_036_854_775_808.0;
323    if x.is_finite() && (0.0..LIMIT).contains(&x) && x.fract() == 0.0 {
324        Ok(())
325    } else {
326        Err(Error::invalid(format!(
327            "adjacency matrix entry ({row}, {col}) = {x} is not a non-negative integer edge count"
328        )))
329    }
330}
331
332/// Validates every entry of a dense matrix with [`check_edge_count`].
333fn check_edge_counts(matrix: &Matrix) -> Result<()> {
334    let nrow = matrix.nrow();
335    // Column-major storage: element k is at (k % nrow, k / nrow).
336    for (k, &x) in matrix.as_slice().iter().enumerate() {
337        check_edge_count(x, k % nrow, k / nrow)?;
338    }
339    Ok(())
340}
341
342/// Whether the sparse routines of `mode` may combine `A[i][j]` with `A[j][i]`
343/// while only visiting the stored entries of the upper triangle.
344///
345/// In igraph 1.0.0 and 1.0.1 these routines (`igraph_sparse_adjacency` in
346/// the `MAX` mode, `igraph_sparse_weighted_adjacency` in the `MAX`, `MIN`
347/// and `PLUS` modes) skip every stored entry below the diagonal, so an entry
348/// `A[i][j]` (`i > j`) whose mirror `A[j][i]` is not stored is silently lost
349/// (the dense routines do not have this problem). Storing an explicit zero
350/// at the mirror position restores the documented semantics. (The unweighted
351/// `MIN` mode loses nothing, since `min(x, 0) = 0` there, and the unweighted
352/// `PLUS` mode visits every entry; padding is harmless for both.)
353fn needs_mirror_padding(mode: Adjacency) -> bool {
354    matches!(mode, Adjacency::Max | Adjacency::Min | Adjacency::Plus)
355}
356
357/// Builds the `n x n` column-compressed adjacency matrix for `mode` from
358/// triplets, storing exactly the nonzero entries of the matrix they
359/// describe: duplicated positions are summed on the Rust side, and positions
360/// whose sum is zero are left out. The latter matters for
361/// [`Adjacency::Undirected`], where igraph's symmetry test is *structural*
362/// (an explicitly stored zero without a stored mirror makes the matrix
363/// "non-symmetric"), while the dense routines only compare values.
364///
365/// When [`needs_mirror_padding`] holds, an explicit zero is then stored at
366/// `(j, i)` for every stored `(i, j)` whose mirror position is empty. With
367/// `counts`, every given value and every sum must be an edge count (see
368/// [`check_edge_count`]).
369///
370/// No position is stored twice, so the number of edges igraph's weighted
371/// routines emit never exceeds the number of nonzero entries they allocate
372/// room for.
373fn compressed_adjacency(
374    n: usize,
375    entries: &[(VertexId, VertexId, f64)],
376    mode: Adjacency,
377    counts: bool,
378) -> Result<SparseMat> {
379    let n_int = int(n, "the number of vertices")?;
380    for &(i, j, _) in entries {
381        if !(0..n_int).contains(&i) || !(0..n_int).contains(&j) {
382            return Err(Error::new(
383                crate::error::ErrorKind::InvalidVertexId,
384                format!("matrix entry ({i}, {j}) out of bounds for {n} vertices"),
385            ));
386        }
387    }
388    // In bounds (checked above), so all the `as usize` casts below are lossless.
389    if counts {
390        for &(i, j, x) in entries {
391            check_edge_count(x, i as usize, j as usize)?;
392        }
393    }
394    let mut summed: BTreeMap<(VertexId, VertexId), f64> = BTreeMap::new();
395    for &(i, j, x) in entries {
396        *summed.entry((i, j)).or_insert(0.0) += x;
397    }
398    // `0.0 == -0.0`; NaN sums are kept, as a dense matrix would keep them.
399    summed.retain(|_, x| *x != 0.0);
400    if counts {
401        for (&(i, j), &x) in &summed {
402            check_edge_count(x, i as usize, j as usize)?;
403        }
404    }
405    let padding: Vec<(VertexId, VertexId)> = if needs_mirror_padding(mode) {
406        summed
407            .keys()
408            .filter(|&&(i, j)| i != j && !summed.contains_key(&(j, i)))
409            .map(|&(i, j)| (j, i))
410            .collect()
411    } else {
412        Vec::new()
413    };
414    let stored = summed.len() + padding.len();
415    let mut triplet = SparseMat::with_capacity(n, n, stored.max(1))?;
416    for (&(i, j), &x) in &summed {
417        triplet.entry(i as usize, j as usize, x)?;
418    }
419    for &(i, j) in &padding {
420        triplet.entry(i as usize, j as usize, 0.0)?;
421    }
422    triplet.compress()
423}
424
425/// Deterministic generators, see the [module docs](self).
426impl igraph_t {
427    /// Creates a graph from an adjacency matrix.
428    ///
429    /// Row/column `i` of the square matrix becomes vertex `i`; entries are
430    /// *edge counts* (non-negative integers), interpreted according to
431    /// `mode` (`A[i][j]` is the element in row `i`, column `j`):
432    ///
433    /// - [`Adjacency::Directed`]: directed graph with `A[i][j]` edges `i -> j`;
434    /// - [`Adjacency::Undirected`]: undirected graph, the matrix must be symmetric;
435    /// - [`Adjacency::Max`] / [`Adjacency::Min`] / [`Adjacency::Plus`]:
436    ///   `max`, `min` or sum of `A[i][j]` and `A[j][i]` undirected edges;
437    /// - [`Adjacency::Upper`] / [`Adjacency::Lower`]: only the upper / lower
438    ///   triangle (diagonal included) is used.
439    ///
440    /// `loops` says how the diagonal is read: [`Loops::None`] ignores it,
441    /// [`Loops::Once`] reads it as the number of self-loops, [`Loops::Twice`]
442    /// as *twice* that number (the usual degree convention for undirected
443    /// graphs; odd values are an error). In the [`Adjacency::Directed`],
444    /// [`Adjacency::Upper`] and [`Adjacency::Lower`] modes `Twice` is treated
445    /// as `Once`: a directed loop adds one to both the in- and the
446    /// out-degree, and a triangle only holds the diagonal once. Edge
447    /// ordering is not specified.
448    ///
449    /// Binds [`igraph_adjacency`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_adjacency).
450    /// Time complexity: O(|V|²).
451    ///
452    /// See also [`Graph::get_adjacency`], the inverse conversion, and
453    /// [`sparse_adjacency`](Self::sparse_adjacency) for large sparse graphs.
454    ///
455    /// # Errors
456    /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) for a
457    /// non-square matrix, entries that are not non-negative integers
458    /// (negative, fractional, NaN or infinite; checked on the Rust side, since
459    /// igraph would truncate them with an unchecked cast), a non-symmetric
460    /// matrix with [`Adjacency::Undirected`], or an odd diagonal with
461    /// [`Loops::Twice`] in the modes that honour it.
462    ///
463    /// # Examples
464    /// ```
465    /// use igraph::prelude::*;
466    /// let a = Matrix::from_rows(&[[0.0, 1.0, 1.0], [1.0, 0.0, 0.0], [1.0, 0.0, 2.0]]).unwrap();
467    /// let g = Graph::adjacency(&a, Adjacency::Undirected, Loops::Twice).unwrap();
468    /// // Two edges plus one self-loop on vertex 2 (its diagonal entry counts it twice).
469    /// assert_eq!(g.ecount(), 3);
470    /// assert_eq!(g.degree_of(2, NeighborMode::All, Loops::Twice).unwrap(), 3);
471    /// // `get_adjacency` gives the matrix back (with the same loop convention).
472    /// assert_eq!(g.get_adjacency(GetAdjacency::Both, None, Loops::Twice).unwrap(), a);
473    /// ```
474    pub fn adjacency(matrix: &Matrix, mode: Adjacency, loops: Loops) -> Result<Graph> {
475        check_edge_counts(matrix)?;
476        Graph::init_with(|g| unsafe { igraph_adjacency(g, matrix, mode.into(), loops.into()) })
477    }
478
479    /// Creates a weighted graph from a weighted adjacency matrix, returning
480    /// the graph and its edge weights (indexed by edge id).
481    ///
482    /// Zero entries mean "no edge" (negative weights are allowed). The modes
483    /// are as in [`adjacency`](Self::adjacency), except that at most one edge
484    /// is created per vertex pair: [`Adjacency::Undirected`] requires a
485    /// symmetric matrix, and `Max`/`Min`/`Plus` combine the two weights
486    /// `A[i][j]` and `A[j][i]` into the weight of a single edge. For the
487    /// diagonal, [`Loops::None`] ignores it, [`Loops::Once`] takes the entry
488    /// as the loop weight and [`Loops::Twice`] as twice the loop weight
489    /// (halving it; `Twice` is treated as `Once` in the
490    /// [`Adjacency::Directed`], [`Adjacency::Upper`] and [`Adjacency::Lower`]
491    /// modes).
492    ///
493    /// Binds [`igraph_weighted_adjacency`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_weighted_adjacency).
494    /// Time complexity: O(|V|²).
495    ///
496    /// See also [`Graph::get_adjacency`] with `weights`, the inverse conversion.
497    ///
498    /// # Errors
499    /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) for a
500    /// non-square matrix, or a non-symmetric one with [`Adjacency::Undirected`].
501    ///
502    /// # Examples
503    /// ```
504    /// use igraph::prelude::*;
505    /// let a = Matrix::from_rows(&[[0.0, 2.5, 0.0], [0.0, 0.0, -1.0], [4.0, 0.0, 0.0]]).unwrap();
506    /// let (g, w) = Graph::weighted_adjacency(&a, Adjacency::Directed, Loops::None).unwrap();
507    /// let mut edges: Vec<_> = g.edge_list().into_iter().zip(w).collect();
508    /// edges.sort_by_key(|e| e.0);
509    /// assert_eq!(edges, vec![((0, 1), 2.5), ((1, 2), -1.0), ((2, 0), 4.0)]);
510    /// ```
511    pub fn weighted_adjacency(
512        matrix: &Matrix,
513        mode: Adjacency,
514        loops: Loops,
515    ) -> Result<(Graph, Vec<f64>)> {
516        let mut weights = Vector::new();
517        let g = Graph::init_with(|g| unsafe {
518            igraph_weighted_adjacency(g, matrix, mode.into(), &mut weights, loops.into())
519        })?;
520        Ok((g, weights.into()))
521    }
522
523    /// Creates a graph on `n` vertices from a *sparse* adjacency matrix given
524    /// as `(row, column, value)` triplets.
525    ///
526    /// This is the sparse counterpart of [`adjacency`](Self::adjacency), with
527    /// the same `mode` and `loops` semantics and the same result; entries not
528    /// listed are zero, and repeated `(row, column)` pairs are summed. The
529    /// triplets are summed up and compressed into igraph's column-compressed
530    /// sparse matrix before the call.
531    ///
532    /// igraph 1.0.0 and 1.0.1 lose the entries below the diagonal whose
533    /// mirror entry is absent in the [`Adjacency::Max`] mode (and in the
534    /// `Max`/`Min`/`Plus` modes of the weighted variant), and their
535    /// [`Adjacency::Undirected`] mode rejects an explicitly stored zero
536    /// without a stored mirror as non-symmetric. This wrapper stores only the
537    /// nonzero sums, plus explicit zeros at the missing mirror positions where
538    /// needed, so that the result always agrees with the dense
539    /// [`adjacency`](Self::adjacency).
540    ///
541    /// Binds [`igraph_sparse_adjacency`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_sparse_adjacency).
542    /// Time complexity: O(|E|), plus O(t log t) for summing up the `t`
543    /// triplets.
544    ///
545    /// See also [`Graph::get_adjacency_sparse`], whose
546    /// [`entries`](crate::conversion::CooMatrix::entries) are exactly the
547    /// triplets this function takes.
548    ///
549    /// # Errors
550    /// [`ErrorKind::InvalidVertexId`](crate::ErrorKind::InvalidVertexId) if a
551    /// triplet lies outside the `n × n` matrix;
552    /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if a value
553    /// (or the sum of the values given for one position) is not a
554    /// non-negative integer; otherwise as
555    /// [`adjacency`](Self::adjacency).
556    ///
557    /// # Examples
558    /// ```
559    /// use igraph::prelude::*;
560    /// // A directed 1000-cycle, without ever allocating a dense 1000 x 1000 matrix.
561    /// let n = 1000;
562    /// let entries: Vec<_> = (0..n as i64).map(|i| (i, (i + 1) % n as i64, 1.0)).collect();
563    /// let g = Graph::sparse_adjacency(n, &entries, Adjacency::Directed, Loops::None).unwrap();
564    /// assert_eq!((g.vcount(), g.ecount()), (1000, 1000));
565    /// assert!(g.is_directed());
566    ///
567    /// // Round trip through the sparse adjacency matrix of the conversion module.
568    /// let karate = Graph::famous("Zachary").unwrap();
569    /// let coo = karate.get_adjacency_sparse(GetAdjacency::Upper, None, Loops::Twice).unwrap();
570    /// let back = Graph::sparse_adjacency(34, &coo.entries, Adjacency::Upper, Loops::Twice).unwrap();
571    /// assert!(back.isomorphic(&karate).unwrap());
572    /// assert_eq!(back.ecount(), 78);
573    /// ```
574    pub fn sparse_adjacency(
575        n: usize,
576        entries: &[(VertexId, VertexId, f64)],
577        mode: Adjacency,
578        loops: Loops,
579    ) -> Result<Graph> {
580        let mut sparse = compressed_adjacency(n, entries, mode, true)?;
581        Graph::init_with(|g| unsafe {
582            igraph_sparse_adjacency(g, &mut sparse, mode.into(), loops.into())
583        })
584    }
585
586    /// Creates a weighted graph on `n` vertices from a sparse weighted
587    /// adjacency matrix given as `(row, column, weight)` triplets, returning
588    /// the graph and its edge weights.
589    ///
590    /// The sparse counterpart of [`weighted_adjacency`](Self::weighted_adjacency);
591    /// repeated `(row, column)` pairs are summed, explicit zeros mean no edge,
592    /// and the result agrees with the dense version (see the note about
593    /// mirror entries in [`sparse_adjacency`](Self::sparse_adjacency)).
594    ///
595    /// Binds [`igraph_sparse_weighted_adjacency`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_sparse_weighted_adjacency).
596    /// Time complexity: O(|E|), plus O(t log t) for summing up the `t`
597    /// triplets.
598    ///
599    /// # Errors
600    /// [`ErrorKind::InvalidVertexId`](crate::ErrorKind::InvalidVertexId) if a
601    /// triplet lies outside the `n × n` matrix; otherwise as
602    /// [`weighted_adjacency`](Self::weighted_adjacency) (any real weight is
603    /// accepted).
604    ///
605    /// # Examples
606    /// ```
607    /// use igraph::prelude::*;
608    /// let entries = [(0, 1, 0.5), (1, 0, 0.5), (1, 2, 3.0), (2, 1, 3.0)];
609    /// let (g, w) =
610    ///     Graph::sparse_weighted_adjacency(3, &entries, Adjacency::Undirected, Loops::None)
611    ///         .unwrap();
612    /// assert_eq!(g.ecount(), 2);
613    /// assert_eq!(w.iter().sum::<f64>(), 3.5);
614    /// ```
615    pub fn sparse_weighted_adjacency(
616        n: usize,
617        entries: &[(VertexId, VertexId, f64)],
618        mode: Adjacency,
619        loops: Loops,
620    ) -> Result<(Graph, Vec<f64>)> {
621        let mut sparse = compressed_adjacency(n, entries, mode, false)?;
622        let mut weights = Vector::new();
623        let g = Graph::init_with(|g| unsafe {
624            igraph_sparse_weighted_adjacency(
625                g,
626                &mut sparse,
627                mode.into(),
628                &mut weights,
629                loops.into(),
630            )
631        })?;
632        Ok((g, weights.into()))
633    }
634
635    /// Shorthand to create a small graph from a flat edge list
636    /// `[from0, to0, from1, to1, ...]`, in the spirit of `igraph_small`.
637    ///
638    /// The C function `igraph_small` is variadic and terminated by `-1`; in
639    /// Rust a slice literal does the same job safely. The graph has
640    /// `max(n, largest id + 1)` vertices. This is the same as
641    /// [`Graph::from_flat_edges`], provided for familiarity with the C API;
642    /// [`Graph::from_edges`] takes `(from, to)` pairs instead.
643    ///
644    /// Binds [`igraph_create`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_create)
645    /// (the non-variadic equivalent of
646    /// [`igraph_small`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_small)).
647    ///
648    /// # Errors
649    /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) for an odd
650    /// number of ids, [`ErrorKind::InvalidVertexId`](crate::ErrorKind::InvalidVertexId)
651    /// for negative ids.
652    ///
653    /// # Examples
654    /// ```
655    /// use igraph::prelude::*;
656    /// let bowtie = Graph::small(5, false, &[0, 1, 1, 2, 2, 0, 2, 3, 3, 4, 4, 2]).unwrap();
657    /// assert_eq!(bowtie.degree_of(2, NeighborMode::All, Loops::Twice).unwrap(), 4);
658    /// ```
659    pub fn small(n: usize, directed: bool, edges: &[VertexId]) -> Result<Graph> {
660        Graph::from_flat_edges(edges, n, directed)
661    }
662
663    /// Creates a star graph: vertex `center` connected to all the other
664    /// `n - 1` vertices.
665    ///
666    /// `mode` chooses between an undirected star ([`StarMode::Undirected`]),
667    /// edges pointing out of ([`StarMode::Out`]) or into ([`StarMode::In`])
668    /// the center, or mutual directed edges ([`StarMode::Mutual`]).
669    ///
670    /// Binds [`igraph_star`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_star).
671    /// Time complexity: O(|V|).
672    ///
673    /// See also [`wheel`](Self::wheel), and [`Graph::layout_star`] to draw it.
674    ///
675    /// The star on a single vertex is that vertex alone (igraph 1.0.0 and 1.0.1
676    /// return the null graph there; this wrapper adds the missing vertex).
677    ///
678    /// # Errors
679    /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if `center`
680    /// is not a vertex of the graph, in particular always for `n = 0`.
681    ///
682    /// # Examples
683    /// ```
684    /// use igraph::prelude::*;
685    /// let s = Graph::star(5, StarMode::In, 2).unwrap();
686    /// assert_eq!(s.degree_of(2, NeighborMode::In, Loops::Twice).unwrap(), 4);
687    /// assert_eq!(s.degree_of(2, NeighborMode::Out, Loops::Twice).unwrap(), 0);
688    /// ```
689    pub fn star(n: usize, mode: StarMode, center: VertexId) -> Result<Graph> {
690        let n = int(n, "the number of vertices")?;
691        let g = Graph::init_with(|g| unsafe { igraph_star(g, n, mode.into(), center) })?;
692        fix_single_vertex(g, n)
693    }
694
695    /// Creates a wheel graph: a star (the spokes) plus a cycle through the
696    /// `n - 1` non-center vertices (the rim).
697    ///
698    /// `mode` orients the spokes like in [`star`](Self::star); in the directed
699    /// modes the rim is a directed cycle (mutual for [`WheelMode::Mutual`]).
700    /// Note that the wheels on 2 and 3 vertices are not simple (they contain a
701    /// self-loop and a multi-edge, respectively).
702    ///
703    /// Binds [`igraph_wheel`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_wheel).
704    /// Time complexity: O(|V|).
705    ///
706    /// The wheel on one vertex is the singleton graph.
707    ///
708    /// # Errors
709    /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if `center`
710    /// is not a vertex of the graph (always for `n = 0`).
711    ///
712    /// # Examples
713    /// ```
714    /// use igraph::prelude::*;
715    /// let w = Graph::wheel(6, WheelMode::Undirected, 0).unwrap();
716    /// assert_eq!(w.ecount(), 10); // 5 spokes + 5 rim edges
717    /// ```
718    pub fn wheel(n: usize, mode: WheelMode, center: VertexId) -> Result<Graph> {
719        let n = int(n, "the number of vertices")?;
720        let g = Graph::init_with(|g| unsafe { igraph_wheel(g, n, mode.into(), center) })?;
721        fix_single_vertex(g, n)
722    }
723
724    /// The `dim`-dimensional hypercube graph `Q_dim`.
725    ///
726    /// It has `2^dim` vertices and `dim * 2^(dim-1)` edges; two vertices are
727    /// adjacent when the binary representations of their ids differ in
728    /// exactly one bit. Directed edges point from lower to higher ids.
729    ///
730    /// Binds [`igraph_hypercube`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_hypercube).
731    /// Time complexity: O(2^dim).
732    ///
733    /// See also [`square_lattice`](Self::square_lattice): `Q_dim` is the
734    /// `2 x 2 x ... x 2` lattice.
735    ///
736    /// # Errors
737    /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if
738    /// `dim > 57`, so that the edge count would not fit into half the range
739    /// of `igraph_int_t` (smaller but still huge dimensions fail with an
740    /// out-of-memory error).
741    ///
742    /// # Examples
743    /// ```
744    /// use igraph::prelude::*;
745    /// let q3 = Graph::hypercube(3, false).unwrap();
746    /// assert_eq!((q3.vcount(), q3.ecount()), (8, 12));
747    /// assert_eq!(q3.neighbors(0, NeighborMode::All).unwrap(), vec![1, 2, 4]);
748    /// assert!(q3.isomorphic(&Graph::famous("Cubical").unwrap()).unwrap());
749    /// ```
750    pub fn hypercube(dim: usize, directed: bool) -> Result<Graph> {
751        let dim = int(dim, "the dimension")?;
752        Graph::init_with(|g| unsafe { igraph_hypercube(g, dim, directed) })
753    }
754
755    /// Creates an arbitrary-dimensional square lattice (grid).
756    ///
757    /// `dims` gives the size along each dimension (an empty slice gives the
758    /// singleton graph). The vertex at position `(i_1, i_2, ..., i_d)` gets id
759    /// `i_1 + n_1 * i_2 + n_1 * n_2 * i_3 + ...`. Vertices within `nei` steps
760    /// of each other are connected (`nei = 1` is the usual grid).
761    /// `periodic`, when given, must have one flag per dimension, making the
762    /// lattice wrap around (a torus) along the flagged dimensions.
763    /// When `directed`, edges point from lower to higher ids unless `mutual`
764    /// (or periodicity) is set.
765    ///
766    /// Binds [`igraph_square_lattice`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_square_lattice).
767    /// Time complexity: O(|V| + |E|) for `nei < 2`.
768    ///
769    /// See also [`Graph::layout_grid`], which places a 2D lattice on its grid
770    /// (pass `Some(dims[0])` as the width), and
771    /// [`triangular_lattice`](Self::triangular_lattice) /
772    /// [`hexagonal_lattice`](Self::hexagonal_lattice).
773    ///
774    /// # Errors
775    /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if
776    /// `periodic` and `dims` have different lengths.
777    ///
778    /// # Examples
779    /// ```
780    /// use igraph::prelude::*;
781    /// // A 4x4 torus is 4-regular.
782    /// let torus = Graph::square_lattice(&[4, 4], 1, false, false, Some(&[true, true])).unwrap();
783    /// assert_eq!(torus.ecount(), 32);
784    /// let deg = torus.degree(VertexSelector::All, NeighborMode::All, Loops::Twice).unwrap();
785    /// assert!(deg.iter().all(|&d| d == 4));
786    /// // Vertex (x, y) of a 5 x 3 grid has id x + 5 y, which `layout_grid` puts at (x, y).
787    /// let grid = Graph::square_lattice(&[5, 3], 1, false, false, None).unwrap();
788    /// let layout = grid.layout_grid(Some(5)).unwrap();
789    /// assert_eq!((layout[(7, 0)], layout[(7, 1)]), (2.0, 1.0));
790    /// ```
791    pub fn square_lattice(
792        dims: &[usize],
793        nei: usize,
794        directed: bool,
795        mutual: bool,
796        periodic: Option<&[bool]>,
797    ) -> Result<Graph> {
798        if let Some(p) = periodic
799            && p.len() != dims.len()
800        {
801            return Err(Error::invalid(format!(
802                "periodic has {} flags but the lattice has {} dimensions",
803                p.len(),
804                dims.len()
805            )));
806        }
807        let dims = int_vector(dims, "a lattice dimension")?;
808        let nei = int(nei, "the neighborhood order")?;
809        let periodic = periodic.map(VectorBool::view);
810        let periodic_ptr = periodic.as_ref().map_or(std::ptr::null(), |v| v.as_ptr());
811        Graph::init_with(|g| unsafe {
812            igraph_square_lattice(g, &dims, nei, directed, mutual, periodic_ptr)
813        })
814    }
815
816    /// Creates a cycle graph `C_n` (`circular = true`) or a path graph `P_n`
817    /// (`circular = false`).
818    ///
819    /// In directed graphs all edges follow the same orientation along the
820    /// ring, or are mutual when `mutual` is set (ignored when undirected).
821    /// For `n` = 1 or 2 the cycle is not simple (a self-loop, or two parallel
822    /// edges).
823    ///
824    /// Binds [`igraph_ring`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_ring).
825    /// Time complexity: O(|V|).
826    ///
827    /// See also [`circulant`](Self::circulant) for rings with chords, and
828    /// [`Graph::layout_circle`] to draw it.
829    ///
830    /// # Examples
831    /// ```
832    /// use igraph::prelude::*;
833    /// let ring = Graph::ring(5, true, false, true).unwrap();
834    /// assert_eq!(ring.edge_list(), vec![(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)]);
835    /// ```
836    pub fn ring(n: usize, directed: bool, mutual: bool, circular: bool) -> Result<Graph> {
837        let n = int(n, "the number of vertices")?;
838        Graph::init_with(|g| unsafe { igraph_ring(g, n, directed, mutual, circular) })
839    }
840
841    /// The path graph `P_n` on `n` vertices: `0 - 1 - ... - (n-1)`.
842    ///
843    /// A convenience form of [`ring`](Self::ring) with `circular = false`;
844    /// `mutual` adds both directions in directed graphs.
845    ///
846    /// Binds [`igraph_path_graph`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_path_graph).
847    /// Time complexity: O(|V|).
848    ///
849    /// # Examples
850    /// ```
851    /// use igraph::prelude::*;
852    /// let p = Graph::path_graph(4, true, true).unwrap();
853    /// assert_eq!(p.ecount(), 6); // 3 links, both directions
854    /// ```
855    pub fn path_graph(n: usize, directed: bool, mutual: bool) -> Result<Graph> {
856        let n = int(n, "the number of vertices")?;
857        Graph::init_with(|g| unsafe { igraph_path_graph(g, n, directed, mutual) })
858    }
859
860    /// The cycle graph `C_n` on `n` vertices.
861    ///
862    /// A convenience form of [`ring`](Self::ring) with `circular = true`. For
863    /// `n` = 1 or 2 the result has a self-loop or parallel edges.
864    ///
865    /// Binds [`igraph_cycle_graph`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_cycle_graph).
866    /// Time complexity: O(|V|).
867    ///
868    /// # Examples
869    /// ```
870    /// use igraph::prelude::*;
871    /// let c = Graph::cycle_graph(7, false, false).unwrap();
872    /// assert_eq!((c.vcount(), c.ecount()), (7, 7));
873    /// ```
874    pub fn cycle_graph(n: usize, directed: bool, mutual: bool) -> Result<Graph> {
875        let n = int(n, "the number of vertices")?;
876        Graph::init_with(|g| unsafe { igraph_cycle_graph(g, n, directed, mutual) })
877    }
878
879    /// Creates a `children`-ary tree on `n` vertices, filled level by level
880    /// in breadth-first order (vertex `i`'s children are
881    /// `children*i + 1 ..= children*i + children`).
882    ///
883    /// For a complete tree with `l` levels below the root use
884    /// `n = (children^(l+1) - 1) / (children - 1)`. `mode` gives the edge
885    /// orientation (parent → child for [`TreeMode::Out`]). `n = 0` gives the
886    /// null graph.
887    ///
888    /// Binds [`igraph_kary_tree`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_kary_tree).
889    /// Time complexity: O(|V| + |E|).
890    ///
891    /// See also [`Graph::tree_game`] for uniformly random trees and
892    /// [`Graph::layout_reingold_tilford`] to draw trees.
893    ///
894    /// # Errors
895    /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if
896    /// `children` is zero.
897    ///
898    /// # Examples
899    /// ```
900    /// use igraph::prelude::*;
901    /// let t = Graph::kary_tree(7, 2, TreeMode::Out).unwrap(); // a complete binary tree
902    /// assert_eq!(t.neighbors(1, NeighborMode::Out).unwrap(), vec![3, 4]);
903    /// assert!(t.is_tree(NeighborMode::Out).unwrap());
904    /// ```
905    pub fn kary_tree(n: usize, children: usize, mode: TreeMode) -> Result<Graph> {
906        let n = int(n, "the number of vertices")?;
907        let children = int(children, "the number of children")?;
908        Graph::init_with(|g| unsafe { igraph_kary_tree(g, n, children, mode.into()) })
909    }
910
911    /// Creates a symmetric tree where every vertex at distance `d` from the
912    /// root has `branches[d]` children.
913    ///
914    /// The tree has `1 + b_0 + b_0 b_1 + ...` vertices, numbered in
915    /// breadth-first order from the root `0`.
916    ///
917    /// Binds [`igraph_symmetric_tree`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_symmetric_tree).
918    /// Time complexity: O(|V| + |E|).
919    ///
920    /// An empty `branches` gives the singleton graph.
921    ///
922    /// # Errors
923    /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if a
924    /// branch count is zero.
925    ///
926    /// # Examples
927    /// ```
928    /// use igraph::prelude::*;
929    /// let t = Graph::symmetric_tree(&[3, 2], TreeMode::Undirected).unwrap();
930    /// assert_eq!(t.vcount(), 1 + 3 + 3 * 2);
931    /// ```
932    pub fn symmetric_tree(branches: &[usize], mode: TreeMode) -> Result<Graph> {
933        let branches = int_vector(branches, "a branching count")?;
934        Graph::init_with(|g| unsafe { igraph_symmetric_tree(g, &branches, mode.into()) })
935    }
936
937    /// Creates a regular tree (Bethe lattice) of height `h` in which every
938    /// non-leaf vertex has total degree `k`.
939    ///
940    /// Unlike a [`kary_tree`](Self::kary_tree), the root has `k` children and
941    /// the other internal vertices `k - 1`, so that all internal degrees are
942    /// equal. `h` is the distance between the root and the leaves.
943    ///
944    /// Binds [`igraph_regular_tree`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_regular_tree).
945    /// Time complexity: O(|V| + |E|).
946    ///
947    /// # Errors
948    /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) unless
949    /// `h >= 1` and `k >= 2`.
950    ///
951    /// # Examples
952    /// ```
953    /// use igraph::prelude::*;
954    /// let t = Graph::regular_tree(2, 3, TreeMode::Undirected).unwrap();
955    /// assert_eq!(t.vcount(), 1 + 3 + 3 * 2);
956    /// ```
957    pub fn regular_tree(h: usize, k: usize, mode: TreeMode) -> Result<Graph> {
958        let h = int(h, "the height")?;
959        let k = int(k, "the degree")?;
960        Graph::init_with(|g| unsafe { igraph_regular_tree(g, h, k, mode.into()) })
961    }
962
963    /// Builds a tree or forest from a parent vector: `parents[v]` is the
964    /// parent of vertex `v`, or a negative value if `v` is a root.
965    ///
966    /// Such vectors are produced by BFS/DFS traversals, shortest path trees,
967    /// dominator trees, etc. The graph has `parents.len()` vertices; with
968    /// [`TreeMode::Out`] edges point from parents to children, with
969    /// [`TreeMode::In`] from children to parents.
970    ///
971    /// Binds [`igraph_tree_from_parent_vector`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_tree_from_parent_vector).
972    /// Time complexity: O(n).
973    ///
974    /// See also [`Graph::bfs_simple`] and the other traversals of
975    /// [`crate::visitor`], whose `parents` give such vectors (map `None` to `-1`).
976    ///
977    /// # Errors
978    /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if the
979    /// vector encodes a cycle or a self-loop,
980    /// [`ErrorKind::InvalidVertexId`](crate::ErrorKind::InvalidVertexId) for
981    /// out-of-range parents.
982    ///
983    /// # Examples
984    /// ```
985    /// use igraph::prelude::*;
986    /// // Two trees: 0 <- {1, 2}, 2 <- 3, and the lone root 4.
987    /// let f = Graph::tree_from_parent_vector(&[-1, 0, 0, 2, -1], TreeMode::Out).unwrap();
988    /// assert_eq!(f.ecount(), 3);
989    /// assert_eq!(f.neighbors(0, NeighborMode::Out).unwrap(), vec![1, 2]);
990    ///
991    /// // The BFS tree of the Petersen graph: 1 root, 3 children, 6 grandchildren.
992    /// let petersen = Graph::famous("Petersen").unwrap();
993    /// let bfs = petersen.bfs_simple(0, NeighborMode::All).unwrap();
994    /// let parents: Vec<i64> = bfs.parents.iter().map(|p| p.unwrap_or(-1)).collect();
995    /// let tree = Graph::tree_from_parent_vector(&parents, TreeMode::Out).unwrap();
996    /// assert!(tree.is_tree(NeighborMode::Out).unwrap());
997    /// assert_eq!(tree.ecount(), 9);
998    /// ```
999    pub fn tree_from_parent_vector(parents: &[VertexId], mode: TreeMode) -> Result<Graph> {
1000        let parents = VectorInt::view(parents);
1001        Graph::init_with(|g| unsafe {
1002            igraph_tree_from_parent_vector(g, parents.as_ptr(), mode.into())
1003        })
1004    }
1005
1006    /// Builds the labelled tree encoded by a Prüfer sequence.
1007    ///
1008    /// A sequence of length `n - 2` with entries in `0..n` encodes a unique
1009    /// tree on `n` vertices (Cayley's formula counts `n^(n-2)` of them); a
1010    /// vertex appears in the sequence exactly `degree - 1` times.
1011    ///
1012    /// Binds [`igraph_from_prufer`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_from_prufer).
1013    /// Time complexity: O(|V|).
1014    ///
1015    /// See also [`Graph::to_prufer`], the inverse conversion.
1016    ///
1017    /// # Errors
1018    /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) for an
1019    /// invalid sequence (entries out of range).
1020    ///
1021    /// # Examples
1022    /// ```
1023    /// use igraph::prelude::*;
1024    /// let t = Graph::from_prufer(&[3, 3, 3]).unwrap(); // the star K_{1,4} centered at 3
1025    /// assert_eq!(t.degree_of(3, NeighborMode::All, Loops::Twice).unwrap(), 4);
1026    /// assert_eq!(t.to_prufer().unwrap(), vec![3, 3, 3]);
1027    /// ```
1028    pub fn from_prufer(prufer: &[VertexId]) -> Result<Graph> {
1029        let prufer = VectorInt::view(prufer);
1030        Graph::init_with(|g| unsafe { igraph_from_prufer(g, prufer.as_ptr()) })
1031    }
1032
1033    /// Creates the complete graph on `n` vertices.
1034    ///
1035    /// Directed complete graphs have both `i -> j` and `j -> i`; with `loops`
1036    /// every vertex also gets a single self-loop.
1037    ///
1038    /// Binds [`igraph_full`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_full).
1039    /// Time complexity: O(|V|²).
1040    ///
1041    /// See also [`Graph::is_complete`], [`Graph::complementer`] (the
1042    /// complement of `K_n` is the empty graph) and [`Graph::full_bipartite`].
1043    ///
1044    /// # Examples
1045    /// ```
1046    /// use igraph::prelude::*;
1047    /// assert_eq!(Graph::full(5, false, false).unwrap().ecount(), 10);
1048    /// assert_eq!(Graph::full(5, true, false).unwrap().ecount(), 20);
1049    /// assert_eq!(Graph::full(5, false, true).unwrap().ecount(), 15);
1050    /// ```
1051    pub fn full(n: usize, directed: bool, loops: bool) -> Result<Graph> {
1052        let n = int(n, "the number of vertices")?;
1053        Graph::init_with(|g| unsafe { igraph_full(g, n, directed, loops) })
1054    }
1055
1056    /// Creates a complete multipartite graph with partitions of the given
1057    /// `sizes`, returning the graph and the partition index of each vertex.
1058    ///
1059    /// Vertices are numbered partition by partition; every pair of vertices
1060    /// in different partitions is connected. In directed graphs, `mode`
1061    /// [`NeighborMode::Out`] points edges from lower to higher partitions,
1062    /// [`NeighborMode::In`] the opposite, and [`NeighborMode::All`] creates
1063    /// mutual edges.
1064    ///
1065    /// Binds [`igraph_full_multipartite`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_full_multipartite).
1066    /// Time complexity: O(|V| + |E|).
1067    ///
1068    /// See also [`Graph::full_bipartite`] for two partitions with boolean
1069    /// vertex types, and [`turan`](Self::turan).
1070    ///
1071    /// # Examples
1072    /// ```
1073    /// use igraph::prelude::*;
1074    /// let (k233, types) =
1075    ///     Graph::full_multipartite(&[2, 3, 3], false, NeighborMode::All).unwrap();
1076    /// assert_eq!(k233.ecount(), 2 * 3 + 2 * 3 + 3 * 3);
1077    /// assert_eq!(types, vec![0, 0, 1, 1, 1, 2, 2, 2]);
1078    /// ```
1079    pub fn full_multipartite(
1080        sizes: &[usize],
1081        directed: bool,
1082        mode: NeighborMode,
1083    ) -> Result<(Graph, Vec<i64>)> {
1084        let sizes = int_vector(sizes, "a partition size")?;
1085        let mut types = VectorInt::new();
1086        let g = Graph::init_with(|g| unsafe {
1087            igraph_full_multipartite(g, &mut types, &sizes, directed, mode.into())
1088        })?;
1089        Ok((g, types.into()))
1090    }
1091
1092    /// Creates the Turán graph `T(n, r)`, returning the graph and the
1093    /// partition index of each vertex.
1094    ///
1095    /// It is the complete `r`-partite graph on `n` vertices with partition
1096    /// sizes as equal as possible: by Turán's theorem, the densest graph on
1097    /// `n` vertices without a clique of size `r + 1`. It is undirected; `n = 0`
1098    /// gives the null graph, and `r > n` gives the complete graph.
1099    ///
1100    /// Binds [`igraph_turan`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_turan).
1101    /// Time complexity: O(|V| + |E|).
1102    ///
1103    /// # Errors
1104    /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if `r` is zero.
1105    ///
1106    /// # Examples
1107    /// ```
1108    /// use igraph::prelude::*;
1109    /// let (t, types) = Graph::turan(6, 3).unwrap(); // the octahedron K_{2,2,2}
1110    /// assert_eq!(t.ecount(), 12);
1111    /// assert_eq!(types, vec![0, 0, 1, 1, 2, 2]);
1112    /// // Turán's theorem: no clique on r + 1 = 4 vertices.
1113    /// assert_eq!(t.clique_number().unwrap(), 3);
1114    /// ```
1115    pub fn turan(n: usize, r: usize) -> Result<(Graph, Vec<i64>)> {
1116        let n = int(n, "the number of vertices")?;
1117        let r = int(r, "the number of partitions")?;
1118        let mut types = VectorInt::new();
1119        let g = Graph::init_with(|g| unsafe { igraph_turan(g, &mut types, n, r) })?;
1120        Ok((g, types.into()))
1121    }
1122
1123    /// Creates a full citation graph: the complete directed acyclic graph in
1124    /// which `i -> j` is an edge exactly when `j < i`.
1125    ///
1126    /// With `directed = false` it is just the complete graph.
1127    ///
1128    /// Binds [`igraph_full_citation`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_full_citation).
1129    /// Time complexity: O(|V|²).
1130    ///
1131    /// # Examples
1132    /// ```
1133    /// use igraph::prelude::*;
1134    /// let g = Graph::full_citation(4, true).unwrap();
1135    /// assert_eq!(g.neighbors(3, NeighborMode::Out).unwrap(), vec![0, 1, 2]);
1136    /// assert!(g.neighbors(0, NeighborMode::Out).unwrap().is_empty());
1137    /// ```
1138    pub fn full_citation(n: usize, directed: bool) -> Result<Graph> {
1139        let n = int(n, "the number of vertices")?;
1140        Graph::init_with(|g| unsafe { igraph_full_citation(g, n, directed) })
1141    }
1142
1143    /// Creates graph number `number` of *An Atlas of Graphs* (Read and
1144    /// Wilson, 1998).
1145    ///
1146    /// The atlas holds all 1253 simple undirected unlabelled graphs on 0 to 7
1147    /// vertices, ordered by number of vertices, then number of edges, then
1148    /// degree sequence (lexicographically, e.g. 111223 < 112222), then
1149    /// increasing number of automorphisms. Graphs on 0, 1, ..., 7 vertices
1150    /// start at numbers 0, 1, 2, 4, 8, 19, 53 and 209.
1151    ///
1152    /// Binds [`igraph_atlas`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_atlas).
1153    /// Time complexity: O(|V| + |E|).
1154    ///
1155    /// See also [`Graph::isomorphic`] and [`Graph::canonical_permutation`] to
1156    /// locate a given small graph in the atlas.
1157    ///
1158    /// # Errors
1159    /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if
1160    /// `number > 1252`.
1161    ///
1162    /// # Examples
1163    /// ```
1164    /// use igraph::prelude::*;
1165    /// let k7 = Graph::atlas(1252).unwrap(); // the last one is K_7
1166    /// assert_eq!((k7.vcount(), k7.ecount()), (7, 21));
1167    /// ```
1168    pub fn atlas(number: usize) -> Result<Graph> {
1169        let number = int(number, "the atlas number")?;
1170        Graph::init_with(|g| unsafe { igraph_atlas(g, number) })
1171    }
1172
1173    /// Creates an extended chordal ring: a cycle on `nodes` vertices plus
1174    /// chords described by the rows of `w`.
1175    ///
1176    /// For each row `L` of length `p` (all rows have the same length, which
1177    /// must divide `nodes`), vertex `i` is connected to vertex
1178    /// `(i + L[i mod p]) mod nodes`. Entries may be negative. The result is
1179    /// not simplified: duplicate chords (and chords along the cycle) produce
1180    /// multi-edges, and offsets that are multiples of `nodes` self-loops.
1181    /// Note that igraph's definition differs from the one in Kotsis (1993).
1182    ///
1183    /// Binds [`igraph_extended_chordal_ring`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_extended_chordal_ring).
1184    /// Time complexity: O(|V| + |E|).
1185    ///
1186    /// # Errors
1187    /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if
1188    /// `nodes < 3`, rows have different lengths, or the row length does not
1189    /// divide `nodes`.
1190    ///
1191    /// # Examples
1192    /// ```
1193    /// use igraph::prelude::*;
1194    /// // One row [3]: each vertex also links 3 steps ahead; on 6 vertices the
1195    /// // three "diameters" appear twice, giving the utility graph K_{3,3} plus duplicates.
1196    /// let g = Graph::extended_chordal_ring(6, &[[3]], false).unwrap();
1197    /// assert_eq!(g.ecount(), 6 + 6);
1198    /// ```
1199    pub fn extended_chordal_ring<R: AsRef<[i64]>>(
1200        nodes: usize,
1201        w: &[R],
1202        directed: bool,
1203    ) -> Result<Graph> {
1204        let nodes = int(nodes, "the number of vertices")?;
1205        if nodes < 3 {
1206            return Err(Error::invalid(format!(
1207                "an extended chordal ring has at least 3 vertices, got {nodes}"
1208            )));
1209        }
1210        // igraph divides by the number of columns: an empty chord matrix
1211        // (no rows, or only empty rows) is passed as a 0 x 1 matrix, meaning
1212        // "no chords". Offsets are reduced modulo `nodes`, which keeps their
1213        // meaning and avoids the unchecked `i + offset` overflow in C.
1214        let w = if w.iter().all(|row| row.as_ref().is_empty()) {
1215            MatrixInt::zeros(0, 1)
1216        } else {
1217            let rows: Vec<Vec<i64>> = w
1218                .iter()
1219                .map(|row| row.as_ref().iter().map(|&x| x.rem_euclid(nodes)).collect())
1220                .collect();
1221            MatrixInt::from_rows(&rows)?
1222        };
1223        Graph::init_with(|g| unsafe { igraph_extended_chordal_ring(g, nodes, &w, directed) })
1224    }
1225
1226    /// The line graph `L(G)` of this graph: one vertex per edge (edge `i`
1227    /// becomes vertex `i`).
1228    ///
1229    /// For undirected graphs, two vertices of `L(G)` are adjacent when the
1230    /// corresponding edges share an endpoint (twice, if they share both
1231    /// endpoints in a multigraph; the single vertex of a self-loop counts as
1232    /// two endpoints, so a self-loop and an edge incident to it are joined
1233    /// twice). For directed graphs, `e -> f` is an edge when the target of
1234    /// `e` is the source of `f`. Self-loops are self-adjacent and get a single
1235    /// self-loop in `L(G)`, in both cases.
1236    ///
1237    /// Binds [`igraph_linegraph`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_linegraph).
1238    /// Time complexity: O(|V| + |E|).
1239    ///
1240    /// See also the other graph transformations of [`crate::operators`].
1241    ///
1242    /// # Examples
1243    /// ```
1244    /// use igraph::prelude::*;
1245    /// // The line graph of the star K_{1,4} is K_4.
1246    /// let l = Graph::star(5, StarMode::Undirected, 0).unwrap().linegraph().unwrap();
1247    /// assert_eq!((l.vcount(), l.ecount()), (4, 6));
1248    /// ```
1249    pub fn linegraph(&self) -> Result<Graph> {
1250        Graph::init_with(|l| unsafe { igraph_linegraph(self, l) })
1251    }
1252
1253    /// The de Bruijn graph `B(m, n)`: vertices are the `m^n` strings of
1254    /// length `n` over an alphabet of `m` letters, with an edge `v -> w` when
1255    /// `w` is obtained by dropping the first letter of `v` and appending one.
1256    ///
1257    /// Vertex ids are the strings read as base-`m` numbers. Every vertex has
1258    /// in- and out-degree `m` (loops included), so the graph has `m^(n+1)`
1259    /// edges and is Eulerian; its Eulerian circuits spell de Bruijn sequences.
1260    ///
1261    /// `B(m, 0)` is the singleton graph and `B(0, n)` for `n > 0` the null
1262    /// graph.
1263    ///
1264    /// Binds [`igraph_de_bruijn`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_de_bruijn).
1265    /// Time complexity: O(|V| + |E|).
1266    ///
1267    /// See also [`Graph::eulerian_cycle`] to spell a de Bruijn sequence (as
1268    /// in the example below) and [`kautz`](Self::kautz).
1269    ///
1270    /// # Errors
1271    /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if the
1272    /// number of vertices `m^n` does not fit in an `i64`, and
1273    /// [`ErrorKind::Overflow`](crate::ErrorKind::Overflow) if the `m^(n+1)`
1274    /// edges do not fit in igraph's edge vector (both checked before calling
1275    /// igraph).
1276    ///
1277    /// # Examples
1278    /// ```
1279    /// use igraph::prelude::*;
1280    /// let b = Graph::de_bruijn(2, 3).unwrap();
1281    /// assert_eq!((b.vcount(), b.ecount()), (8, 16));
1282    /// // "011" -> "110" and "111"
1283    /// assert_eq!(b.neighbors(0b011, NeighborMode::Out).unwrap(), vec![0b110, 0b111]);
1284    ///
1285    /// // An Eulerian circuit of B(2, 3) spells a de Bruijn sequence of order 4:
1286    /// // each vertex appends its last letter, and all 16 4-bit words occur
1287    /// // exactly once as cyclic substrings.
1288    /// let walk = b.eulerian_cycle().unwrap();
1289    /// let seq: Vec<i64> = walk.vertices[1..].iter().map(|v| v % 2).collect();
1290    /// assert_eq!(seq.len(), 16);
1291    /// let words: std::collections::BTreeSet<i64> = (0..16)
1292    ///     .map(|i| (0..4).fold(0, |acc, j| 2 * acc + seq[(i + j) % 16]))
1293    ///     .collect();
1294    /// assert_eq!(words.len(), 16);
1295    /// ```
1296    pub fn de_bruijn(m: usize, n: usize) -> Result<Graph> {
1297        let m = int(m, "the alphabet size")?;
1298        let n = int(n, "the string length")?;
1299        if n > 0 && m > 0 {
1300            let nodes = checked_ipow(m, n).ok_or_else(|| {
1301                Error::invalid(format!(
1302                    "Parameters ({m}, {n}) too large for De Bruijn graph."
1303                ))
1304            })?;
1305            // igraph then needs `2 * m * m^n` edge endpoints (it reports
1306            // `IGRAPH_EOVERFLOW` otherwise). Checking it here also keeps
1307            // `m^n` far below 2^63: for `m` close to `i64::MAX` and `n = 1`,
1308            // `pow(m, 1)` rounds up to 2^63 as a double, and igraph's
1309            // conversion back to an integer would be undefined behaviour.
1310            nodes
1311                .checked_mul(m)
1312                .and_then(|e| e.checked_mul(2))
1313                .ok_or_else(|| {
1314                    Error::new(
1315                        crate::error::ErrorKind::Overflow,
1316                        format!("Parameters ({m}, {n}) too large for De Bruijn graph."),
1317                    )
1318                })?;
1319        }
1320        Graph::init_with(|g| unsafe { igraph_de_bruijn(g, m, n) })
1321    }
1322
1323    /// The Kautz graph `K(m, n)`: vertices are the strings of length `n + 1`
1324    /// over an alphabet of `m + 1` letters with no two equal consecutive
1325    /// letters; `v -> w` when `w` is `v` shifted by one letter.
1326    ///
1327    /// It has `(m+1) m^n` vertices, each with in- and out-degree `m`, and no
1328    /// self-loops. Degenerate cases: `K(m, 0)` is the complete directed graph
1329    /// on `m + 1` vertices, and `K(0, n)` for `n > 0` is the null graph.
1330    ///
1331    /// Binds [`igraph_kautz`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_kautz).
1332    /// Time complexity: roughly O(|V| + |E|).
1333    ///
1334    /// # Errors
1335    /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if the
1336    /// graph would be too large (`m^n` or `(m+1)^(n+1)` does not fit in an
1337    /// `i64`; checked before calling igraph).
1338    ///
1339    /// # Examples
1340    /// ```
1341    /// use igraph::prelude::*;
1342    /// let k = Graph::kautz(2, 1).unwrap();
1343    /// assert_eq!((k.vcount(), k.ecount()), (6, 12));
1344    /// ```
1345    pub fn kautz(m: usize, n: usize) -> Result<Graph> {
1346        let m = int(m, "m")?;
1347        let n = int(n, "n")?;
1348        // `K(m, 0)` is the complete digraph on `m + 1` vertices; otherwise igraph
1349        // needs `m^n` and `(m + 1)^(n + 1)`.
1350        let fits = match (m, n) {
1351            (_, 0) => m.checked_add(1).is_some(),
1352            (0, _) => true,
1353            _ => {
1354                checked_ipow(m, n).is_some()
1355                    && m.checked_add(1)
1356                        .zip(n.checked_add(1))
1357                        .and_then(|(b, e)| checked_ipow(b, e))
1358                        .is_some()
1359            }
1360        };
1361        if !fits {
1362            return Err(Error::invalid(format!(
1363                "Parameters ({m}, {n}) too large for Kautz graph."
1364            )));
1365        }
1366        Graph::init_with(|g| unsafe { igraph_kautz(g, m, n) })
1367    }
1368
1369    /// The circulant graph `C_n(shifts)`: vertex `j` is connected to
1370    /// `(j + s) mod n` for every shift `s`.
1371    ///
1372    /// Shifts may be negative; shifts that are multiples of `n` are ignored
1373    /// and no multi-edges or self-loops are created.
1374    ///
1375    /// Binds [`igraph_circulant`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_circulant).
1376    /// Time complexity: O(|V| |shifts|).
1377    ///
1378    /// See also [`extended_chordal_ring`](Self::extended_chordal_ring), which
1379    /// allows position-dependent chords and multi-edges, and
1380    /// [`Graph::k_regular_game`] for *random* regular graphs.
1381    ///
1382    /// # Examples
1383    /// ```
1384    /// use igraph::prelude::*;
1385    /// // C_5(1, 2) is K_5.
1386    /// assert_eq!(Graph::circulant(5, &[1, 2], false).unwrap().ecount(), 10);
1387    /// ```
1388    pub fn circulant(n: usize, shifts: &[i64], directed: bool) -> Result<Graph> {
1389        let n = int(n, "the number of vertices")?;
1390        let shifts = VectorInt::view(shifts);
1391        Graph::init_with(|g| unsafe { igraph_circulant(g, n, shifts.as_ptr(), directed) })
1392    }
1393
1394    /// The generalized Petersen graph `G(n, k)`: an outer `n`-cycle
1395    /// `v_0 .. v_(n-1)` (ids `0..n`), an inner circulant `u_i ~ u_(i+k mod n)`
1396    /// (ids `n..2n`) and the spokes `v_i ~ u_i`.
1397    ///
1398    /// It has `2n` vertices and `3n` edges and is cubic. `G(5, 2)` is the
1399    /// Petersen graph, `G(4, 1)` the cube, `G(10, 3)` the Desargues graph.
1400    ///
1401    /// Binds [`igraph_generalized_petersen`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_generalized_petersen).
1402    /// Time complexity: O(|V|).
1403    ///
1404    /// # Errors
1405    /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) unless
1406    /// `n >= 3` and `0 < k < n / 2`.
1407    ///
1408    /// # Examples
1409    /// ```
1410    /// use igraph::prelude::*;
1411    /// let desargues = Graph::generalized_petersen(10, 3).unwrap();
1412    /// assert_eq!((desargues.vcount(), desargues.ecount()), (20, 30));
1413    /// let petersen = Graph::generalized_petersen(5, 2).unwrap();
1414    /// assert!(petersen.isomorphic(&Graph::famous("Petersen").unwrap()).unwrap());
1415    /// ```
1416    pub fn generalized_petersen(n: usize, k: usize) -> Result<Graph> {
1417        let n = int(n, "the number of vertices")?;
1418        let k = int(k, "the shift")?;
1419        Graph::init_with(|g| unsafe { igraph_generalized_petersen(g, n, k) })
1420    }
1421
1422    /// Creates a named graph, such as `"Petersen"` or `"Zachary"`.
1423    ///
1424    /// The name is case insensitive; the supported graphs (and their sizes)
1425    /// are listed by [`FamousGraph`], which can be passed directly. Some names
1426    /// have aliases in igraph: `Dodecahedral`, `Icosahedral`, `Octahedral`,
1427    /// `Tetrahedral` and `Groetzsch`.
1428    ///
1429    /// Binds [`igraph_famous`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_famous).
1430    /// Time complexity: O(|V| + |E|).
1431    ///
1432    /// See also [`atlas`](Self::atlas) for all small graphs, and
1433    /// [`crate::foreign`] to read graphs from files.
1434    ///
1435    /// # Errors
1436    /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) for an
1437    /// unknown name (or one containing a NUL byte).
1438    ///
1439    /// # Examples
1440    /// ```
1441    /// use igraph::{constructors::FamousGraph, prelude::*};
1442    /// let karate = Graph::famous("zachary").unwrap();
1443    /// assert_eq!((karate.vcount(), karate.ecount()), (34, 78));
1444    /// let kite = Graph::famous(FamousGraph::KrackhardtKite).unwrap();
1445    /// assert_eq!(kite.vcount(), 10);
1446    /// assert_eq!(Graph::famous("Unicorn").unwrap_err().kind(), ErrorKind::InvalidValue);
1447    /// // The Frucht graph is cubic but has no symmetry at all.
1448    /// let frucht = Graph::famous(FamousGraph::Frucht).unwrap();
1449    /// assert_eq!(frucht.count_automorphisms(None).unwrap(), 1.0);
1450    /// ```
1451    pub fn famous(name: impl AsRef<str>) -> Result<Graph> {
1452        let name = CString::new(name.as_ref())
1453            .map_err(|_| Error::invalid("graph names cannot contain NUL bytes"))?;
1454        Graph::init_with(|g| unsafe { igraph_famous(g, name.as_ptr()) })
1455    }
1456
1457    /// Creates a graph from LCF (Lederberg–Coxeter–Frucht) notation
1458    /// `[shifts]^repeats` on `n` vertices.
1459    ///
1460    /// The graph is the cycle `0 - 1 - ... - (n-1) - 0` plus, going around
1461    /// the cycle, a chord from vertex `i` to `i + s` for the shifts `s`
1462    /// repeated `repeats` times. Normally `n = shifts.len() * repeats`, and the
1463    /// result is a cubic Hamiltonian graph. The result is always simple:
1464    /// duplicate chords are merged and loops (shifts that are multiples of
1465    /// `n`) dropped. Shifts are taken modulo `n`, so any `i64` is accepted;
1466    /// `n = 0` gives the null graph.
1467    ///
1468    /// Binds [`igraph_lcf`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_lcf)
1469    /// (and covers the variadic `igraph_lcf_small`).
1470    /// Time complexity: O(|V| + |E|).
1471    ///
1472    /// See also [`generalized_petersen`](Self::generalized_petersen) and
1473    /// [`famous`](Self::famous) for other ways to build well-known cubic graphs.
1474    ///
1475    /// # Examples
1476    /// ```
1477    /// use igraph::prelude::*;
1478    /// // The Heawood graph is [5, -5]^7.
1479    /// let h = Graph::lcf(14, &[5, -5], 7).unwrap();
1480    /// assert_eq!((h.vcount(), h.ecount()), (14, 21));
1481    /// assert!(h.isomorphic(&Graph::famous("Heawood").unwrap()).unwrap());
1482    /// ```
1483    pub fn lcf(n: usize, shifts: &[i64], repeats: usize) -> Result<Graph> {
1484        let n = int(n, "the number of vertices")?;
1485        let repeats = int(repeats, "the number of repeats")?;
1486        if n == 0 {
1487            // igraph 1.0.0 and 1.0.1 compute `i % n` for every chord: with n = 0 and a
1488            // non-empty chord list it dies with a division by zero (SIGFPE).
1489            return Graph::empty(0, false);
1490        }
1491        // igraph computes `n + i + shift` without overflow checks (UB in C
1492        // for huge shifts), and rejects shifts below `-(n + i)`; reducing
1493        // them modulo `n` keeps the meaning and avoids both problems.
1494        let shifts: VectorInt = shifts.iter().map(|&s| s.rem_euclid(n)).collect();
1495        Graph::init_with(|g| unsafe { igraph_lcf(g, n, &shifts, repeats) })
1496    }
1497
1498    /// Builds a graph realizing the given degree sequence, deterministically.
1499    ///
1500    /// With `in_degrees = None` an undirected graph with degrees
1501    /// `out_degrees` is created, otherwise a directed graph with the given
1502    /// out- and in-degrees. Simple graphs are built with the Havel–Hakimi
1503    /// (undirected) or Kleitman–Wang (directed) algorithm: repeatedly pick a
1504    /// vertex and connect all its stubs to the vertices with the largest
1505    /// remaining degrees. Multigraphs use an analogous one-edge-at-a-time
1506    /// procedure; with self-loops allowed, leftover stubs become loops on a
1507    /// single vertex.
1508    ///
1509    /// `allowed` selects the kind of graph (directed graphs support only
1510    /// [`AllowedEdgeTypes::SIMPLE`]; [`AllowedEdgeTypes::LOOPS`] alone is not
1511    /// implemented). `method` selects the vertex order:
1512    /// [`RealizeDegseq::Smallest`] (smallest remaining degree first; in the
1513    /// undirected case it yields a *connected* graph whenever one exists, so
1514    /// it builds a tree from tree degrees), [`RealizeDegseq::Largest`]
1515    /// (strongly assortative, often disconnected) or
1516    /// [`RealizeDegseq::Index`] (in vertex order).
1517    ///
1518    /// Binds [`igraph_realize_degree_sequence`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_realize_degree_sequence).
1519    /// Time complexity: O(V + α(V) E) for simple undirected graphs.
1520    ///
1521    /// See also [`is_graphical`](crate::mixing::is_graphical), which only
1522    /// decides whether a realization exists (it takes the same
1523    /// [`AllowedEdgeTypes`] flags), and
1524    /// [`Graph::degree_sequence_game`] for *random* realizations.
1525    ///
1526    /// # Errors
1527    /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if the
1528    /// sequence is not graphical for the requested kind of graph, or lengths
1529    /// or sums of the directed sequences differ;
1530    /// [`ErrorKind::Unimplemented`](crate::ErrorKind::Unimplemented) for
1531    /// unsupported combinations.
1532    ///
1533    /// # Examples
1534    /// ```
1535    /// use igraph::{constructors::AllowedEdgeTypes, prelude::*};
1536    /// let degrees = [3, 3, 2, 2, 2, 1, 1];
1537    /// let g = Graph::realize_degree_sequence(
1538    ///     &degrees, None, AllowedEdgeTypes::SIMPLE, RealizeDegseq::Smallest,
1539    /// ).unwrap();
1540    /// assert_eq!(g.degree(VertexSelector::All, NeighborMode::All, Loops::Twice).unwrap(), degrees);
1541    /// // [3, 3] cannot be realized as a simple graph...
1542    /// assert!(Graph::realize_degree_sequence(
1543    ///     &[3, 3], None, AllowedEdgeTypes::SIMPLE, RealizeDegseq::Smallest).is_err());
1544    /// // ... but it can as a multigraph: three parallel edges.
1545    /// let m = Graph::realize_degree_sequence(
1546    ///     &[3, 3], None, AllowedEdgeTypes::MULTI, RealizeDegseq::Smallest).unwrap();
1547    /// assert_eq!(m.ecount(), 3);
1548    /// ```
1549    pub fn realize_degree_sequence(
1550        out_degrees: &[i64],
1551        in_degrees: Option<&[i64]>,
1552        allowed: impl Into<AllowedEdgeTypes>,
1553        method: RealizeDegseq,
1554    ) -> Result<Graph> {
1555        let out = VectorInt::view(out_degrees);
1556        let ind = in_degrees.map(VectorInt::view);
1557        let ind_ptr = ind.as_ref().map_or(std::ptr::null(), |v| v.as_ptr());
1558        let allowed = igraph_edge_type_sw_t::from(allowed.into());
1559        Graph::init_with(|g| unsafe {
1560            igraph_realize_degree_sequence(g, out.as_ptr(), ind_ptr, allowed, method.into())
1561        })
1562    }
1563
1564    /// Builds a bipartite graph realizing the bidegree sequence
1565    /// `(degrees1, degrees2)`, deterministically.
1566    ///
1567    /// Vertices `0..degrees1.len()` form the first partition, followed by
1568    /// the second one. A Havel–Hakimi-like algorithm is used; `allowed` is
1569    /// [`AllowedEdgeTypes::SIMPLE`] or [`AllowedEdgeTypes::MULTI`] (a
1570    /// bipartite graph has no self-loops, so igraph ignores the loops flag:
1571    /// `LOOPS` acts as `SIMPLE`, `ALL` as `MULTI`), and
1572    /// `method` has the same meaning as in
1573    /// [`realize_degree_sequence`](Self::realize_degree_sequence) (with
1574    /// [`RealizeDegseq::Smallest`] the result is connected whenever the
1575    /// sequence is potentially connected).
1576    ///
1577    /// Binds [`igraph_realize_bipartite_degree_sequence`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_realize_bipartite_degree_sequence).
1578    ///
1579    /// See also [`is_bigraphical`](crate::mixing::is_bigraphical), which only
1580    /// decides whether a realization exists.
1581    ///
1582    /// # Errors
1583    /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if the
1584    /// bidegree sequence cannot be realized.
1585    ///
1586    /// # Examples
1587    /// ```
1588    /// use igraph::{constructors::AllowedEdgeTypes, prelude::*};
1589    /// // Three students, two projects: who works on what.
1590    /// let g = Graph::realize_bipartite_degree_sequence(
1591    ///     &[1, 2, 1], &[2, 2], AllowedEdgeTypes::SIMPLE, RealizeDegseq::Smallest,
1592    /// ).unwrap();
1593    /// assert_eq!((g.vcount(), g.ecount()), (5, 4));
1594    /// assert!(g.is_bipartite().unwrap());
1595    /// ```
1596    pub fn realize_bipartite_degree_sequence(
1597        degrees1: &[i64],
1598        degrees2: &[i64],
1599        allowed: impl Into<AllowedEdgeTypes>,
1600        method: RealizeDegseq,
1601    ) -> Result<Graph> {
1602        let d1 = VectorInt::view(degrees1);
1603        let d2 = VectorInt::view(degrees2);
1604        let allowed = igraph_edge_type_sw_t::from(allowed.into());
1605        Graph::init_with(|g| unsafe {
1606            igraph_realize_bipartite_degree_sequence(
1607                g,
1608                d1.as_ptr(),
1609                d2.as_ptr(),
1610                allowed,
1611                method.into(),
1612            )
1613        })
1614    }
1615
1616    /// Creates a triangular lattice of the given shape.
1617    ///
1618    /// Vertices are points `(i, j)` connected to `(i+1, j)`, `(i, j+1)` and
1619    /// `(i-1, j+1)` when present, so degrees are at most 6. `dims` of length
1620    /// 1 gives a triangle with `dims[0]` vertices per side, length 2 a
1621    /// "quasi-rectangle" with sides of `dims[0]` and `dims[1]` vertices,
1622    /// length 3 a hexagon with the given side lengths. Vertices are ordered
1623    /// row by row. This is the planar dual of
1624    /// [`hexagonal_lattice`](Self::hexagonal_lattice) with the same `dims`.
1625    ///
1626    /// Binds [`igraph_triangular_lattice`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_triangular_lattice).
1627    /// Time complexity: O(|V|).
1628    ///
1629    /// # Errors
1630    /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) unless
1631    /// `dims` has length 1, 2 or 3, or if a hexagon shape is so large that
1632    /// its row sizes overflow an `i64` (checked before calling igraph).
1633    ///
1634    /// # Examples
1635    /// ```
1636    /// use igraph::prelude::*;
1637    /// let t = Graph::triangular_lattice(&[5], false, false).unwrap();
1638    /// assert_eq!((t.vcount(), t.ecount()), (15, 30));
1639    /// ```
1640    pub fn triangular_lattice(dims: &[usize], directed: bool, mutual: bool) -> Result<Graph> {
1641        let dims = int_vector(dims, "a lattice dimension")?;
1642        check_hex_shape(&dims, "a triangular lattice")?;
1643        Graph::init_with(|g| unsafe { igraph_triangular_lattice(g, &dims, directed, mutual) })
1644    }
1645
1646    /// Creates a hexagonal (honeycomb) lattice of the given shape.
1647    ///
1648    /// `dims` is interpreted as in
1649    /// [`triangular_lattice`](Self::triangular_lattice), but counts
1650    /// *hexagons*: the 6-cycles of the result correspond one-to-one to the
1651    /// vertices of the triangular lattice with the same `dims`. Degrees are
1652    /// at most 3.
1653    ///
1654    /// Binds [`igraph_hexagonal_lattice`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_hexagonal_lattice).
1655    /// Time complexity: O(|V|).
1656    ///
1657    /// # Errors
1658    /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) unless
1659    /// `dims` has length 1, 2 or 3, or if a hexagon shape is so large that
1660    /// its row sizes overflow an `i64` (checked before calling igraph).
1661    ///
1662    /// # Examples
1663    /// ```
1664    /// use igraph::prelude::*;
1665    /// let benzene = Graph::hexagonal_lattice(&[1], false, false).unwrap();
1666    /// assert_eq!((benzene.vcount(), benzene.ecount()), (6, 6));
1667    /// ```
1668    pub fn hexagonal_lattice(dims: &[usize], directed: bool, mutual: bool) -> Result<Graph> {
1669        let dims = int_vector(dims, "a lattice dimension")?;
1670        check_hex_shape(&dims, "a hexagonal lattice")?;
1671        Graph::init_with(|g| unsafe { igraph_hexagonal_lattice(g, &dims, directed, mutual) })
1672    }
1673
1674    /// The Mycielski graph `M_k`: triangle-free with chromatic number `k`.
1675    ///
1676    /// Obtained by iterating the Mycielski construction: `M_0` is the null
1677    /// graph, `M_1` a single vertex, `M_2` an edge, `M_3` the 5-cycle, `M_4`
1678    /// the Grötzsch graph. For `k > 1`, `M_k` has `3 * 2^(k-2) - 1` vertices
1679    /// and `(7 * 3^(k-2) + 1) / 2 - 3 * 2^(k-2)` edges.
1680    ///
1681    /// This function is marked *experimental* in igraph 1.0.x.
1682    ///
1683    /// Binds [`igraph_mycielski_graph`](https://igraph.org/c/html/latest/igraph-Generators.html#igraph_mycielski_graph).
1684    /// Time complexity: O(3^k).
1685    ///
1686    /// See also [`Graph::mycielskian`], which applies the construction to an
1687    /// arbitrary graph.
1688    ///
1689    /// # Examples
1690    /// ```
1691    /// use igraph::prelude::*;
1692    /// let m4 = Graph::mycielski_graph(4).unwrap();
1693    /// assert_eq!((m4.vcount(), m4.ecount()), (11, 20));
1694    /// assert!(m4.isomorphic(&Graph::famous("Grotzsch").unwrap()).unwrap());
1695    /// assert_eq!(m4.count_triangles().unwrap(), 0.0);
1696    /// ```
1697    pub fn mycielski_graph(k: usize) -> Result<Graph> {
1698        let k = int(k, "the order")?;
1699        Graph::init_with(|g| unsafe { igraph_mycielski_graph(g, k) })
1700    }
1701}