Skip to main content

igraph/
cycles.rs

1//! Graph cycles: acyclicity, topological orders, feedback sets, cycle
2//! enumeration, cycle bases and Eulerian paths.
3//!
4//! This module binds the C headers `igraph_cycles.h` and `igraph_eulerian.h`,
5//! documented in the [Graph cycles](https://igraph.org/c/html/latest/igraph-Cycles.html)
6//! chapter of the igraph C manual. All functions are methods of [`Graph`].
7//!
8//! | Task | Method | Result |
9//! |------|--------|--------|
10//! | Is the graph a directed acyclic graph? | [`Graph::is_dag`] | `bool` |
11//! | Order the vertices of a DAG | [`Graph::topological_sorting`] | `Vec<VertexId>` |
12//! | Find *one* cycle, if any | [`Graph::find_cycle`] | `Option<`[`Cycle`]`>` |
13//! | List all simple cycles | [`Graph::simple_cycles`] | `Vec<`[`Cycle`]`>` |
14//! | Visit simple cycles lazily, with early stop | [`Graph::simple_cycles_callback`] | `()` |
15//! | Fundamental cycle basis (from a BFS tree) | [`Graph::fundamental_cycles`] | `Vec<Vec<EdgeId>>` |
16//! | Minimum weight cycle basis (Horton), see [`MinimumCycleBasisOptions`] | [`Graph::minimum_cycle_basis`] | `Vec<Vec<EdgeId>>` |
17//! | Edges whose removal breaks all cycles | [`Graph::feedback_arc_set`] | `Vec<EdgeId>` |
18//! | Vertices whose removal breaks all cycles | [`Graph::feedback_vertex_set`] | `Vec<VertexId>` |
19//! | Does an Eulerian path / cycle exist? | [`Graph::is_eulerian`] | [`EulerianStatus`] |
20//! | Find an Eulerian path | [`Graph::eulerian_path`] | [`EulerianWalk`] |
21//! | Find an Eulerian cycle | [`Graph::eulerian_cycle`] | [`EulerianWalk`] |
22//!
23//! Cycles are described both by their vertices and by their edges (see
24//! [`Cycle`]): with multi-edges, the vertex sequence alone would be ambiguous.
25//! Cycle bases are returned as edge-id lists only, as igraph does.
26//!
27//! Some functions ([`Graph::simple_cycles`], [`Graph::simple_cycles_callback`],
28//! [`Graph::fundamental_cycles`] and [`Graph::minimum_cycle_basis`]) are marked
29//! *experimental* in igraph 1.0 (both 1.0.0 and 1.0.1): their behavior may
30//! change in future releases of the C library. None of the C sources bound here
31//! changed between igraph 1.0.0 and 1.0.1.
32//!
33//! # See also
34//!
35//! Related functionality in other modules:
36//!
37//! - [`Graph::is_acyclic`], [`Graph::is_forest`] and [`Graph::is_tree`]
38//!   (structural properties): acyclicity tests that, unlike [`Graph::is_dag`],
39//!   also apply to undirected graphs.
40//! - [`Graph::girth`] and [`Graph::girth_with_cycle`]: the length of (and a
41//!   vertex list for) a *shortest* cycle, where [`Graph::find_cycle`] returns an
42//!   arbitrary one.
43//! - [`Graph::list_triangles`] and [`Graph::count_triangles`]: the cycles of
44//!   length 3, faster than [`Graph::simple_cycles`] with a length bound.
45//! - [`Graph::get_all_simple_paths`]: simple *paths* instead of cycles.
46//! - [`Graph::minimum_spanning_tree`] and [`Graph::connected_components`]: the
47//!   complement of a spanning forest (of a *maximum* weight one, if weighted)
48//!   is a minimum feedback arc set of an undirected graph, and the cycle space
49//!   has dimension |E| - |V| + #components.
50//! - [`Graph::transitive_closure`] and [`Graph::dfs`]: reachability and the
51//!   depth-first search underlying topological orders.
52//! - [`Graph::de_bruijn`] and [`Graph::kautz`]: balanced directed graphs, all
53//!   of which have Eulerian cycles.
54//!
55//! # Example
56//!
57//! Scheduling tasks with dependencies: a topological order exists exactly
58//! when the dependency graph has no cycle.
59//!
60//! ```
61//! use igraph::prelude::*;
62//!
63//! // 0: fetch, 1: configure, 2: build, 3: test, 4: package
64//! let mut deps = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (2, 4), (3, 4)], 5, true)?;
65//! assert!(deps.is_dag()?);
66//! assert_eq!(deps.topological_sorting(NeighborMode::Out)?, vec![0, 1, 2, 3, 4]);
67//!
68//! // Someone adds a circular dependency "package -> configure" ...
69//! deps.add_edge(4, 1)?;
70//! assert!(!deps.is_dag()?);
71//! let cycle = deps.find_cycle(NeighborMode::Out)?.expect("there is a cycle now");
72//! assert!(cycle.vertices.contains(&4) && cycle.vertices.contains(&1));
73//! // ... and removing the edges of a feedback arc set fixes it again.
74//! let fas = deps.feedback_arc_set(None, FasAlgorithm::ExactIp)?;
75//! assert_eq!(fas.len(), 1);
76//! deps.delete_edges(&fas)?;
77//! assert!(deps.is_dag()?);
78//! # Ok::<(), igraph::Error>(())
79//! ```
80
81use crate::{
82    constants::{FasAlgorithm, FvsAlgorithm, NeighborMode},
83    error::{Error, Result, catch_panic},
84    ffi::*,
85    graph::{EdgeId, VertexId},
86    igraph_call,
87    list::VectorIntList,
88    vector::{Vector, VectorInt},
89};
90use std::{ffi::c_void, ops::ControlFlow};
91
92#[cfg(doc)]
93use crate::graph::Graph;
94
95/// A cycle (closed walk) of a graph, given both as vertices and edges.
96///
97/// `edges[i]` connects `vertices[i]` and `vertices[(i + 1) % len]`: the first
98/// vertex is *not* repeated at the end, so `vertices.len() == edges.len()`,
99/// which is the length of the cycle. A self-loop is a cycle of length 1, and
100/// two parallel edges form a cycle of length 2 (when they can be traversed in
101/// opposite directions: undirected edges, mutual directed edges, or any
102/// directed edges with `NeighborMode::All`).
103#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
104pub struct Cycle {
105    /// The vertices of the cycle, in traversal order.
106    pub vertices: Vec<VertexId>,
107    /// The edges of the cycle, in traversal order.
108    pub edges: Vec<EdgeId>,
109}
110
111impl Cycle {
112    /// The length of the cycle, i.e. its number of edges.
113    pub fn len(&self) -> usize {
114        self.edges.len()
115    }
116
117    /// Whether the cycle has no edges (never the case for cycles returned by
118    /// this module).
119    pub fn is_empty(&self) -> bool {
120        self.edges.is_empty()
121    }
122}
123
124/// Options for [`Graph::simple_cycles`] and [`Graph::simple_cycles_callback`].
125///
126/// The default searches for all simple cycles of any length, following edge
127/// directions (`NeighborMode::Out`) in directed graphs. Lengths count edges,
128/// which for simple cycles equals the number of vertices.
129///
130/// ```
131/// use igraph::{cycles::SimpleCyclesOptions, prelude::*};
132/// let opts = SimpleCyclesOptions::default().with_max_length(4).with_max_results(10);
133/// assert_eq!(opts.max_cycle_length, Some(4));
134/// assert_eq!(opts.mode, NeighborMode::Out);
135/// ```
136#[derive(Debug, Clone, Copy, PartialEq, Eq)]
137pub struct SimpleCyclesOptions {
138    /// How edge directions are considered in directed graphs: `Out` follows
139    /// them, `In` follows them backwards, `All` ignores them. Ignored for
140    /// undirected graphs. Default: `Out`.
141    pub mode: NeighborMode,
142    /// Only report cycles with at least this many edges (`None`: no limit).
143    pub min_cycle_length: Option<usize>,
144    /// Only report cycles with at most this many edges (`None`: no limit).
145    /// Bounding the length also bounds the search, making it much faster.
146    pub max_cycle_length: Option<usize>,
147    /// Stop after this many cycles have been reported (`None`: no limit).
148    pub max_results: Option<usize>,
149}
150
151impl Default for SimpleCyclesOptions {
152    fn default() -> Self {
153        Self {
154            mode: NeighborMode::Out,
155            min_cycle_length: None,
156            max_cycle_length: None,
157            max_results: None,
158        }
159    }
160}
161
162impl SimpleCyclesOptions {
163    /// Sets [`mode`](Self::mode).
164    pub fn with_mode(mut self, mode: NeighborMode) -> Self {
165        self.mode = mode;
166        self
167    }
168
169    /// Sets [`min_cycle_length`](Self::min_cycle_length).
170    pub fn with_min_length(mut self, len: usize) -> Self {
171        self.min_cycle_length = Some(len);
172        self
173    }
174
175    /// Sets [`max_cycle_length`](Self::max_cycle_length).
176    pub fn with_max_length(mut self, len: usize) -> Self {
177        self.max_cycle_length = Some(len);
178        self
179    }
180
181    /// Sets [`max_results`](Self::max_results).
182    pub fn with_max_results(mut self, n: usize) -> Self {
183        self.max_results = Some(n);
184        self
185    }
186}
187
188/// Options for [`Graph::minimum_cycle_basis`].
189///
190/// The default computes an exact minimum cycle basis with each cycle listed
191/// in cycle order (`bfs_cutoff: None, complete: true, use_cycle_order: true`),
192/// the same defaults as the igraph R and Python interfaces.
193///
194/// ```
195/// use igraph::cycles::MinimumCycleBasisOptions;
196/// let fast = MinimumCycleBasisOptions::default().with_bfs_cutoff(3).with_complete(false);
197/// assert_eq!(fast.bfs_cutoff, Some(3));
198/// assert!(fast.use_cycle_order);
199/// ```
200#[derive(Debug, Clone, Copy, PartialEq, Eq)]
201pub struct MinimumCycleBasisOptions {
202    /// `None` computes an exact minimum basis. `Some(k)` limits the depth of
203    /// the BFS trees used to generate candidate cycles, which can speed up
204    /// the computation substantially: then only the returned cycles of length
205    /// at most `2k + 1` are guaranteed to belong to some minimum basis.
206    /// Default: `None`.
207    pub bfs_cutoff: Option<usize>,
208    /// Only used with a [`bfs_cutoff`](Self::bfs_cutoff). If `true`, a
209    /// complete basis is still returned (its cycles longer than `2k + 1` may
210    /// not be minimal); if `false`, only the cycles of length at most `2k + 1`
211    /// are returned, which is faster but does not span the whole cycle space.
212    /// Default: `true`.
213    pub complete: bool,
214    /// If `true`, the edge ids of each cycle are listed in the order they
215    /// appear along the cycle (at a small cost); if `false`, their order is
216    /// unspecified. Default: `true`.
217    pub use_cycle_order: bool,
218}
219
220impl Default for MinimumCycleBasisOptions {
221    fn default() -> Self {
222        Self {
223            bfs_cutoff: None,
224            complete: true,
225            use_cycle_order: true,
226        }
227    }
228}
229
230impl MinimumCycleBasisOptions {
231    /// Sets [`bfs_cutoff`](Self::bfs_cutoff) to `Some(k)`.
232    pub fn with_bfs_cutoff(mut self, k: usize) -> Self {
233        self.bfs_cutoff = Some(k);
234        self
235    }
236
237    /// Sets [`complete`](Self::complete).
238    pub fn with_complete(mut self, complete: bool) -> Self {
239        self.complete = complete;
240        self
241    }
242
243    /// Sets [`use_cycle_order`](Self::use_cycle_order).
244    pub fn with_cycle_order(mut self, use_cycle_order: bool) -> Self {
245        self.use_cycle_order = use_cycle_order;
246        self
247    }
248}
249
250/// Whether a graph has an Eulerian path and/or cycle, see [`Graph::is_eulerian`].
251#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
252pub struct EulerianStatus {
253    /// `true` if some walk traverses every edge exactly once.
254    pub has_path: bool,
255    /// `true` if some *closed* walk traverses every edge exactly once
256    /// (this implies `has_path`).
257    pub has_cycle: bool,
258}
259
260/// An Eulerian path or cycle, see [`Graph::eulerian_path`] and
261/// [`Graph::eulerian_cycle`].
262///
263/// `edges[i]` connects `vertices[i]` and `vertices[i + 1]`, so for a
264/// non-empty walk `vertices.len() == edges.len() + 1`; for a cycle the first
265/// and last vertices coincide. Every edge of the graph appears in `edges`
266/// exactly once.
267#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
268pub struct EulerianWalk {
269    /// The visited vertices, in order.
270    pub vertices: Vec<VertexId>,
271    /// The traversed edges, in order.
272    pub edges: Vec<EdgeId>,
273}
274
275/// `Option<usize>` limit → igraph's "negative means unlimited" convention.
276/// Values above `igraph_int_t::MAX` saturate (they are unreachable anyway),
277/// instead of wrapping to a negative "unlimited" value.
278fn limit(value: Option<usize>) -> igraph_int_t {
279    value.map_or(-1, |v| {
280        igraph_int_t::try_from(v).unwrap_or(igraph_int_t::MAX)
281    })
282}
283
284/// `Option<usize>` BFS cutoff → igraph's "negative means no cutoff" real.
285fn cutoff(value: Option<usize>) -> igraph_real_t {
286    value.map_or(-1.0, |c| c as igraph_real_t)
287}
288
289/// Pointer to an optional weight view, or NULL.
290fn opt_ptr(v: &Option<crate::vector::View<'_, Vector>>) -> *const igraph_vector_t {
291    v.as_ref().map_or(std::ptr::null(), |v| v.as_ptr())
292}
293
294struct CycleVisitor<'f, F> {
295    f: &'f mut F,
296    remaining: Option<usize>,
297}
298
299unsafe extern "C" fn cycle_trampoline<F>(
300    vertices: *const igraph_vector_int_t,
301    edges: *const igraph_vector_int_t,
302    arg: *mut c_void,
303) -> igraph_error_t
304where
305    F: FnMut(&[VertexId], &[EdgeId]) -> ControlFlow<()>,
306{
307    // The closure runs in a fresh level of igraph's "finally" stack:
308    // otherwise a failing igraph call made by the closure would free the
309    // temporaries of the running cycle search (a use-after-free in C).
310    // SAFETY: bookkeeping on igraph's thread-local finally stack; the
311    // matching EXIT runs below, as `catch_panic` never unwinds.
312    unsafe { IGRAPH_FINALLY_ENTER() };
313    let code = catch_panic(|| {
314        // SAFETY: `arg` is the `CycleVisitor<F>` passed by `simple_cycles_callback`,
315        // alive and exclusively borrowed for the whole C call; the vectors are
316        // valid for the duration of this callback.
317        let visitor = unsafe { &mut *(arg as *mut CycleVisitor<'_, F>) };
318        let (vertices, edges) = unsafe { ((*vertices).as_slice(), (*edges).as_slice()) };
319        // `remaining` is never `Some(0)` here: the wrapper returns early for a
320        // zero cap, and we answer IGRAPH_STOP as soon as it reaches zero.
321        let flow = (visitor.f)(vertices, edges);
322        if let Some(r) = visitor.remaining.as_mut() {
323            *r -= 1;
324        }
325        match flow {
326            ControlFlow::Break(()) => igraph_error_type_t_IGRAPH_STOP,
327            ControlFlow::Continue(()) if visitor.remaining == Some(0) => {
328                igraph_error_type_t_IGRAPH_STOP
329            }
330            ControlFlow::Continue(()) => igraph_error_type_t_IGRAPH_SUCCESS,
331        }
332    });
333    // SAFETY: closes the level opened above; any failed nested call has
334    // already freed its own objects of that level.
335    unsafe { IGRAPH_FINALLY_EXIT() };
336    code
337}
338
339impl igraph_t {
340    /// Checks whether the graph is a directed acyclic graph (DAG).
341    ///
342    /// A DAG is a directed graph without directed cycles (self-loops count as
343    /// cycles here). Undirected graphs are never DAGs: this returns `false` for
344    /// them. The result is cached inside the graph, so repeated calls without
345    /// modifications in between take O(1) time.
346    ///
347    /// Time complexity: O(|V|+|E|).
348    ///
349    /// See also [`is_acyclic`](Self::is_acyclic), which agrees with this
350    /// function on directed graphs but also tests undirected graphs for
351    /// cycles, and [`find_cycle`](Self::find_cycle), which returns a cycle
352    /// proving that a graph is not a DAG.
353    ///
354    /// Binds [`igraph_is_dag`](https://igraph.org/c/html/latest/igraph-Cycles.html#igraph_is_dag).
355    ///
356    /// # Examples
357    ///
358    /// ```
359    /// use igraph::prelude::*;
360    /// let chain = Graph::from_edges(&[(0, 1), (1, 2)], 3, true)?;
361    /// assert!(chain.is_dag()?);
362    /// let triangle = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 3, true)?;
363    /// assert!(!triangle.is_dag()?);
364    /// // Undirected graphs are not DAGs, even when they are trees.
365    /// let undirected = Graph::from_edges(&[(0, 1)], 2, false)?;
366    /// assert!(!undirected.is_dag()?);
367    /// assert!(undirected.is_acyclic()?); // ... but they can be acyclic
368    /// # Ok::<(), igraph::Error>(())
369    /// ```
370    pub fn is_dag(&self) -> Result<bool> {
371        let mut res = false;
372        igraph_call!(igraph_is_dag(self, &mut res))?;
373        Ok(res)
374    }
375
376    /// Computes a topological sorting of a directed acyclic graph.
377    ///
378    /// A topological sorting is a linear ordering of the vertices in which
379    /// every vertex comes before all the vertices it has edges to. Every DAG
380    /// has at least one, and possibly many; one of them is returned.
381    ///
382    /// With `mode = NeighborMode::Out` each vertex precedes its successors, so
383    /// the sources (no incoming edges) come first; with `NeighborMode::In` each
384    /// vertex precedes its predecessors, so the sinks come first.
385    /// Self-loops are ignored.
386    ///
387    /// Time complexity: O(|V|+|E|).
388    ///
389    /// See also [`is_dag`](Self::is_dag) to test beforehand whether an order
390    /// exists, [`feedback_arc_set`](Self::feedback_arc_set) to make a cyclic
391    /// graph sortable, and [`transitive_closure`](Self::transitive_closure)
392    /// for the full "must come before" relation.
393    ///
394    /// Binds [`igraph_topological_sorting`](https://igraph.org/c/html/latest/igraph-Cycles.html#igraph_topological_sorting).
395    ///
396    /// # Errors
397    ///
398    /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if the graph
399    /// contains a cycle (other than self-loops), if the graph is undirected,
400    /// or if `mode` is `NeighborMode::All`.
401    ///
402    /// # Examples
403    ///
404    /// The example graph from the igraph documentation (and Wikipedia):
405    ///
406    /// ```
407    /// use igraph::prelude::*;
408    /// let g = Graph::from_edges(
409    ///     &[(0, 3), (0, 4), (1, 3), (2, 4), (2, 7), (3, 5), (3, 6), (3, 7), (4, 6)],
410    ///     8,
411    ///     true,
412    /// )?;
413    /// assert_eq!(g.topological_sorting(NeighborMode::Out)?, vec![0, 1, 2, 3, 4, 5, 7, 6]);
414    /// assert_eq!(g.topological_sorting(NeighborMode::In)?, vec![5, 6, 7, 4, 3, 2, 0, 1]);
415    /// # Ok::<(), igraph::Error>(())
416    /// ```
417    pub fn topological_sorting(&self, mode: NeighborMode) -> Result<Vec<VertexId>> {
418        let mut res = VectorInt::new();
419        igraph_call!(igraph_topological_sorting(self, &mut res, mode.into()))?;
420        Ok(res.into())
421    }
422
423    /// Finds a single cycle of the graph, or `None` if the graph is acyclic.
424    ///
425    /// `mode` selects how edge directions are considered in directed graphs:
426    /// `Out` follows them, `In` follows them backwards (the returned cycle is
427    /// then listed against the edge directions) and `All` ignores them. It is
428    /// ignored for undirected graphs. Self-loops and multi-edges count as
429    /// cycles of length 1 and 2.
430    ///
431    /// igraph lists the vertices so that each edge *enters* the vertex at the
432    /// same position; this wrapper rotates them by one so that the result
433    /// follows the [`Cycle`] layout (`edges[i]` goes from `vertices[i]` to the next vertex), like
434    /// [`simple_cycles`](Self::simple_cycles) does.
435    ///
436    /// The cycle is not necessarily a shortest one: use
437    /// [`girth_with_cycle`](Self::girth_with_cycle) for that (undirected,
438    /// cycles of length at least 3), or [`simple_cycles`](Self::simple_cycles)
439    /// to list all cycles. [`is_acyclic`](Self::is_acyclic) only answers
440    /// whether a cycle exists.
441    ///
442    /// Time complexity: O(|V|+|E|).
443    ///
444    /// Binds [`igraph_find_cycle`](https://igraph.org/c/html/latest/igraph-Cycles.html#igraph_find_cycle).
445    ///
446    /// # Examples
447    ///
448    /// ```
449    /// use igraph::prelude::*;
450    /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (2, 0)], 5, true)?;
451    /// let c = g.find_cycle(NeighborMode::Out)?.unwrap();
452    /// assert_eq!(c.vertices, vec![0, 1, 2]);
453    /// assert_eq!(c.edges, vec![0, 1, 4]); // 0 -> 1 -> 2 -> 0
454    ///
455    /// let tree = Graph::from_edges(&[(0, 1), (0, 2)], 3, false)?;
456    /// assert_eq!(tree.find_cycle(NeighborMode::All)?, None);
457    ///
458    /// // Any cycle is at least as long as the girth.
459    /// let petersen = Graph::famous("Petersen")?;
460    /// let c = petersen.find_cycle(NeighborMode::All)?.unwrap();
461    /// assert!(c.len() >= petersen.girth()?.unwrap());
462    /// # Ok::<(), igraph::Error>(())
463    /// ```
464    pub fn find_cycle(&self, mode: NeighborMode) -> Result<Option<Cycle>> {
465        let mut vertices = VectorInt::new();
466        let mut edges = VectorInt::new();
467        igraph_call!(igraph_find_cycle(
468            self,
469            &mut vertices,
470            &mut edges,
471            mode.into()
472        ))?;
473        if edges.is_empty() {
474            Ok(None)
475        } else {
476            let mut vertices: Vec<VertexId> = vertices.into();
477            vertices.rotate_right(1);
478            Ok(Some(Cycle {
479                vertices,
480                edges: edges.into(),
481            }))
482        }
483    }
484
485    /// Lists the simple cycles of the graph (Johnson's algorithm).
486    ///
487    /// A simple cycle is a closed walk without repeated vertices. Each cycle
488    /// is reported once (in undirected graphs, the two traversal directions
489    /// of a cycle count as one). Self-loops are cycles of length 1; two
490    /// parallel edges give a cycle of length 2 when they can be traversed in
491    /// opposite directions (so not two parallel arcs `u -> v` with
492    /// `NeighborMode::Out`). The search can be restricted with
493    /// [`SimpleCyclesOptions`]: direction handling, minimum and maximum cycle
494    /// length and a cap on the number of results. The number of simple cycles
495    /// can grow exponentially with the size of the graph: bound the lengths or
496    /// the result count on large graphs, or use
497    /// [`simple_cycles_callback`](Self::simple_cycles_callback) to avoid
498    /// storing them.
499    ///
500    /// See also [`list_triangles`](Self::list_triangles) for the cycles of
501    /// length 3 only, [`find_cycle`](Self::find_cycle) for a single cycle, and
502    /// [`get_all_simple_paths`](Self::get_all_simple_paths) for simple paths.
503    ///
504    /// This function is *experimental* in igraph 1.0.
505    ///
506    /// Reference: Johnson DB, *Finding all the elementary circuits of a
507    /// directed graph*, SIAM J. Comput. 4(1):77-84 (1975).
508    ///
509    /// Binds [`igraph_simple_cycles`](https://igraph.org/c/html/latest/igraph-Cycles.html#igraph_simple_cycles).
510    ///
511    /// # Examples
512    ///
513    /// A square with one diagonal has three cycles: two triangles and the
514    /// outer square.
515    ///
516    /// ```
517    /// use igraph::{cycles::SimpleCyclesOptions, prelude::*};
518    /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0), (0, 2)], 4, false)?;
519    /// let cycles = g.simple_cycles(&SimpleCyclesOptions::default())?;
520    /// let mut lengths: Vec<usize> = cycles.iter().map(|c| c.len()).collect();
521    /// lengths.sort();
522    /// assert_eq!(lengths, vec![3, 3, 4]);
523    /// // Only the triangles:
524    /// let triangles = g.simple_cycles(&SimpleCyclesOptions::default().with_max_length(3))?;
525    /// assert_eq!(triangles.len(), 2);
526    /// # Ok::<(), igraph::Error>(())
527    /// ```
528    pub fn simple_cycles(&self, options: &SimpleCyclesOptions) -> Result<Vec<Cycle>> {
529        if options.max_results == Some(0) {
530            return Ok(Vec::new());
531        }
532        let mut vertices = VectorIntList::new();
533        let mut edges = VectorIntList::new();
534        igraph_call!(igraph_simple_cycles(
535            self,
536            &mut vertices,
537            &mut edges,
538            options.mode.into(),
539            limit(options.min_cycle_length),
540            limit(options.max_cycle_length),
541            limit(options.max_results)
542        ))?;
543        Ok(vertices
544            .iter()
545            .zip(edges.iter())
546            .map(|(v, e)| Cycle {
547                vertices: v.to_vec(),
548                edges: e.to_vec(),
549            })
550            .collect())
551    }
552
553    /// Visits the simple cycles of the graph with a closure (Johnson's
554    /// algorithm), without storing them.
555    ///
556    /// This is the streaming variant of [`simple_cycles`](Self::simple_cycles),
557    /// with the same semantics and options. For each cycle, `f` is called with
558    /// its vertices and edges (see [`Cycle`] for their layout); it returns
559    /// [`ControlFlow::Continue`] to keep searching or [`ControlFlow::Break`]
560    /// to stop the search early (this is not an error). The search also stops
561    /// after [`max_results`](SimpleCyclesOptions::max_results) cycles.
562    ///
563    /// If `f` panics, the search is aborted and the panic is resumed in the
564    /// caller once igraph has cleaned up.
565    ///
566    /// This function is *experimental* in igraph 1.0.
567    ///
568    /// Binds [`igraph_simple_cycles_callback`](https://igraph.org/c/html/latest/igraph-Cycles.html#igraph_simple_cycles_callback).
569    ///
570    /// # Examples
571    ///
572    /// Is there a cycle through vertex 3 in the complete graph `K5`? Stop at
573    /// the first one.
574    ///
575    /// ```
576    /// use igraph::{cycles::SimpleCyclesOptions, prelude::*};
577    /// use std::ops::ControlFlow;
578    /// let k5 = Graph::full(5, false, false)?;
579    /// let mut total = 0;
580    /// k5.simple_cycles_callback(&SimpleCyclesOptions::default(), |_, _| {
581    ///     total += 1;
582    ///     ControlFlow::Continue(())
583    /// })?;
584    /// assert_eq!(total, 37); // 10 triangles + 15 squares + 12 pentagons
585    ///
586    /// let mut found = None;
587    /// k5.simple_cycles_callback(&SimpleCyclesOptions::default(), |vs, _| {
588    ///     if vs.contains(&3) {
589    ///         found = Some(vs.to_vec());
590    ///         return ControlFlow::Break(());
591    ///     }
592    ///     ControlFlow::Continue(())
593    /// })?;
594    /// assert!(found.unwrap().contains(&3));
595    /// # Ok::<(), igraph::Error>(())
596    /// ```
597    pub fn simple_cycles_callback<F>(&self, options: &SimpleCyclesOptions, mut f: F) -> Result<()>
598    where
599        F: FnMut(&[VertexId], &[EdgeId]) -> ControlFlow<()>,
600    {
601        if options.max_results == Some(0) {
602            return Ok(());
603        }
604        let mut visitor = CycleVisitor {
605            f: &mut f,
606            remaining: options.max_results,
607        };
608        igraph_call!(igraph_simple_cycles_callback(
609            self,
610            options.mode.into(),
611            limit(options.min_cycle_length),
612            limit(options.max_cycle_length),
613            Some(cycle_trampoline::<F>),
614            &mut visitor as *mut CycleVisitor<'_, F> as *mut c_void
615        ))
616    }
617
618    /// Computes a fundamental cycle basis, from breadth-first search trees.
619    ///
620    /// Every edge not in a BFS spanning forest closes exactly one cycle with
621    /// the tree edges: these *fundamental cycles* form a basis of the cycle
622    /// space, whose dimension is the cyclomatic number |E| - |V| + c (c being
623    /// the number of connected components). Each cycle is returned as a list
624    /// of edge ids, in cycle order. Edge directions are ignored; multi-edges
625    /// and self-loops are supported.
626    ///
627    /// - `start`: `None` returns a complete basis; `Some(v)` returns only the
628    ///   fundamental cycles of the BFS tree rooted at `v`, i.e. of the
629    ///   (weakly) connected component of `v`.
630    /// - `bfs_cutoff`: `None` returns a complete basis; `Some(k)` limits the
631    ///   BFS depth, so that only cycles of length at most `2k + 1` are found.
632    ///
633    /// Time complexity: O(|V|+|E|). This function is *experimental* in igraph
634    /// 1.0. (The C function also takes a `weights` argument, currently unused
635    /// by igraph, so it is not exposed.)
636    ///
637    /// See also [`minimum_cycle_basis`](Self::minimum_cycle_basis) for a basis
638    /// of shortest total length, and
639    /// [`connected_components`](Self::connected_components) for `c`.
640    ///
641    /// Binds [`igraph_fundamental_cycles`](https://igraph.org/c/html/latest/igraph-Cycles.html#igraph_fundamental_cycles).
642    ///
643    /// # Errors
644    ///
645    /// [`ErrorKind::InvalidVertexId`](crate::ErrorKind::InvalidVertexId) if
646    /// `start` is not a vertex of the graph.
647    ///
648    /// # Examples
649    ///
650    /// ```
651    /// use igraph::prelude::*;
652    /// // Two triangles sharing the edge 0-2: the cycle space has dimension 5 - 4 + 1 = 2.
653    /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3), (3, 0)], 4, false)?;
654    /// let basis = g.fundamental_cycles(None, None)?;
655    /// assert_eq!(basis.len(), 2);
656    ///
657    /// // In general: one basis cycle per edge outside a spanning forest.
658    /// let karate = Graph::famous("Zachary")?;
659    /// let c = karate.connected_components(Connectedness::Weak)?.count;
660    /// let dim = karate.ecount() - karate.vcount() + c;
661    /// assert_eq!(karate.fundamental_cycles(None, None)?.len(), dim); // 78 - 34 + 1
662    /// # Ok::<(), igraph::Error>(())
663    /// ```
664    pub fn fundamental_cycles(
665        &self,
666        start: Option<VertexId>,
667        bfs_cutoff: Option<usize>,
668    ) -> Result<Vec<Vec<EdgeId>>> {
669        if let Some(v) = start
670            && (v < 0 || v as usize >= self.vcount())
671        {
672            return Err(Error::new(
673                crate::ErrorKind::InvalidVertexId,
674                format!("invalid start vertex {v} for fundamental cycles"),
675            ));
676        }
677        let mut res = VectorIntList::new();
678        igraph_call!(igraph_fundamental_cycles(
679            self,
680            std::ptr::null(),
681            &mut res,
682            start.unwrap_or(-1),
683            cutoff(bfs_cutoff)
684        ))?;
685        Ok(res.to_vecs())
686    }
687
688    /// Computes a minimum weight cycle basis (modified Horton algorithm).
689    ///
690    /// A minimum cycle basis is a basis of the cycle space whose total length
691    /// is as small as possible; its cycles are returned as edge-id lists,
692    /// sorted by increasing length. Edge directions are ignored; multi-edges
693    /// and self-loops are supported.
694    ///
695    /// The search is tuned by [`MinimumCycleBasisOptions`]: an optional BFS
696    /// cutoff trading exactness for speed, whether a cut-off basis should
697    /// still be completed, and whether cycles are listed in cycle order.
698    ///
699    /// This function is *experimental* in igraph 1.0. (The C function also
700    /// takes a `weights` argument, currently unused by igraph, so it is not
701    /// exposed.)
702    ///
703    /// For a simple graph with at least one cycle, the first (shortest) basis
704    /// cycle has the length of the [`girth`](Self::girth). See also
705    /// [`fundamental_cycles`](Self::fundamental_cycles), a faster but usually
706    /// longer basis.
707    ///
708    /// Reference: Horton JD, *A polynomial-time algorithm to find the shortest
709    /// cycle basis of a graph*, SIAM J. Comput. 16(2):358-366 (1987).
710    ///
711    /// Binds [`igraph_minimum_cycle_basis`](https://igraph.org/c/html/latest/igraph-Cycles.html#igraph_minimum_cycle_basis).
712    ///
713    /// # Examples
714    ///
715    /// ```
716    /// use igraph::{cycles::MinimumCycleBasisOptions, prelude::*};
717    /// // A square with a diagonal: the minimum basis is made of the two triangles.
718    /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0), (0, 2)], 4, false)?;
719    /// let basis = g.minimum_cycle_basis(&MinimumCycleBasisOptions::default())?;
720    /// assert_eq!(basis.iter().map(Vec::len).collect::<Vec<_>>(), vec![3, 3]);
721    /// // The outer square is the sum (symmetric difference) of the two
722    /// // triangles: the shared diagonal, edge 4, cancels out.
723    /// let mut parity = [false; 5];
724    /// for &e in basis.concat().iter() {
725    ///     parity[e as usize] ^= true;
726    /// }
727    /// assert_eq!(parity, [true, true, true, true, false]);
728    ///
729    /// // The Petersen graph has girth 5: its 15 - 10 + 1 = 6 basis cycles are pentagons.
730    /// let petersen = Graph::famous("Petersen")?;
731    /// let basis = petersen.minimum_cycle_basis(&MinimumCycleBasisOptions::default())?;
732    /// assert_eq!(basis.iter().map(Vec::len).collect::<Vec<_>>(), vec![5; 6]);
733    /// # Ok::<(), igraph::Error>(())
734    /// ```
735    pub fn minimum_cycle_basis(
736        &self,
737        options: &MinimumCycleBasisOptions,
738    ) -> Result<Vec<Vec<EdgeId>>> {
739        let mut res = VectorIntList::new();
740        igraph_call!(igraph_minimum_cycle_basis(
741            self,
742            std::ptr::null(),
743            &mut res,
744            cutoff(options.bfs_cutoff),
745            options.complete,
746            options.use_cycle_order
747        ))?;
748        Ok(res.to_vecs())
749    }
750
751    /// Finds a feedback arc set: edges whose removal makes the graph acyclic.
752    ///
753    /// One is usually interested in a *minimum* feedback arc set, i.e. one of
754    /// smallest total weight (`weights`, one per edge, or `None` for unit
755    /// weights; weights are meant to be non-negative). For undirected graphs
756    /// this is easy: the complement of a maximum weight spanning forest (and
757    /// `algo` is ignored). For directed
758    /// graphs the problem is NP-complete and `algo` selects the method:
759    ///
760    /// - [`FasAlgorithm::ExactIp`]: exact minimum via integer programming,
761    ///   picking the best available formulation (currently `ExactIpCg`).
762    ///   Exponential in the worst case.
763    /// - [`FasAlgorithm::ExactIpCg`]: exact, set-cover formulation with
764    ///   incremental cycle (constraint) generation.
765    /// - [`FasAlgorithm::ExactIpTi`]: exact, topological-order formulation
766    ///   with triangle inequalities; usually much slower.
767    /// - [`FasAlgorithm::ApproxEades`]: the linear time O(|E|) heuristic of
768    ///   Eades, Lin and Smyth (1993), returning fewer than |E|/2 - |V|/6 edges.
769    ///
770    /// References: Eades P, Lin X, Smyth WF, Inf. Proc. Letters 47(6):319-323
771    /// (1993); Baharev A et al., ACM J. Exp. Algorithmics 26:1-28 (2021).
772    ///
773    /// Time complexity: depends on `algo` (see above).
774    ///
775    /// See also [`feedback_vertex_set`](Self::feedback_vertex_set) for the
776    /// vertex version, and, for undirected graphs,
777    /// [`minimum_spanning_tree`](Self::minimum_spanning_tree): the edges not
778    /// in a *maximum* weight spanning forest form a minimum feedback arc set.
779    /// Delete the returned edges at once with
780    /// [`delete_edges`](Self::delete_edges).
781    ///
782    /// Binds [`igraph_feedback_arc_set`](https://igraph.org/c/html/latest/igraph-Cycles.html#igraph_feedback_arc_set).
783    ///
784    /// # Errors
785    ///
786    /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if the
787    /// weight vector has the wrong length or invalid values;
788    /// [`ErrorKind::Unimplemented`](crate::ErrorKind::Unimplemented) for the
789    /// exact methods when igraph was built without GLPK.
790    ///
791    /// # Examples
792    ///
793    /// The graph of igraph's own example program:
794    ///
795    /// ```
796    /// use igraph::prelude::*;
797    /// let edges = [(0, 1), (1, 2), (2, 0), (2, 3), (2, 4), (0, 4), (4, 3), (5, 0), (6, 5)];
798    /// let g = Graph::from_edges(&edges, 7, true)?;
799    /// assert_eq!(g.feedback_arc_set(None, FasAlgorithm::ExactIp)?, vec![0]);
800    /// // Make edge 0 expensive to cut: another edge of the triangle goes.
801    /// let w = [3.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0];
802    /// let fas = g.feedback_arc_set(Some(&w), FasAlgorithm::ExactIp)?;
803    /// assert!(fas == [1] || fas == [2]);
804    /// # Ok::<(), igraph::Error>(())
805    /// ```
806    pub fn feedback_arc_set(
807        &self,
808        weights: Option<&[f64]>,
809        algo: FasAlgorithm,
810    ) -> Result<Vec<EdgeId>> {
811        if let Some(w) = weights
812            && w.len() != self.ecount()
813        {
814            return Err(Error::invalid(format!(
815                "weight vector length ({}) must match the number of edges ({})",
816                w.len(),
817                self.ecount()
818            )));
819        }
820        let w = weights.map(Vector::view);
821        let mut res = VectorInt::new();
822        igraph_call!(igraph_feedback_arc_set(
823            self,
824            &mut res,
825            opt_ptr(&w),
826            algo.into()
827        ))?;
828        Ok(res.into())
829    }
830
831    /// Finds a minimum feedback vertex set: vertices whose removal makes the
832    /// graph acyclic.
833    ///
834    /// Minimizes the total vertex weight (`vertex_weights`, one per vertex, or
835    /// `None` for unit weights). The problem is NP-complete both for directed
836    /// and undirected graphs; the only method, [`FvsAlgorithm::ExactIp`], uses
837    /// integer programming with incremental cycle generation (like
838    /// [`FasAlgorithm::ExactIpCg`]) and is exponential in the worst case.
839    /// Self-loops count as cycles, so looped vertices are always included.
840    ///
841    /// Time complexity: exponential in the worst case. See also
842    /// [`feedback_arc_set`](Self::feedback_arc_set), and
843    /// [`delete_vertices`](Self::delete_vertices) or
844    /// [`induced_subgraph`](Self::induced_subgraph) to remove the vertices.
845    ///
846    /// Binds [`igraph_feedback_vertex_set`](https://igraph.org/c/html/latest/igraph-Cycles.html#igraph_feedback_vertex_set).
847    ///
848    /// # Errors
849    ///
850    /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if the
851    /// weight vector has the wrong length or invalid values;
852    /// [`ErrorKind::Unimplemented`](crate::ErrorKind::Unimplemented) when
853    /// igraph was built without GLPK.
854    ///
855    /// # Examples
856    ///
857    /// ```
858    /// use igraph::prelude::*;
859    /// // Two triangles sharing vertex 0 (a "bowtie"): removing 0 breaks both.
860    /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (0, 3), (3, 4), (4, 0)], 5, false)?;
861    /// assert_eq!(g.feedback_vertex_set(None, FvsAlgorithm::ExactIp)?, vec![0]);
862    ///
863    /// // The Petersen graph needs three vertices removed to become a forest.
864    /// let mut petersen = Graph::famous("Petersen")?;
865    /// let fvs = petersen.feedback_vertex_set(None, FvsAlgorithm::ExactIp)?;
866    /// assert_eq!(fvs.len(), 3);
867    /// petersen.delete_vertices(&fvs)?;
868    /// assert!(petersen.is_forest(NeighborMode::All)?);
869    /// # Ok::<(), igraph::Error>(())
870    /// ```
871    pub fn feedback_vertex_set(
872        &self,
873        vertex_weights: Option<&[f64]>,
874        algo: FvsAlgorithm,
875    ) -> Result<Vec<VertexId>> {
876        if let Some(w) = vertex_weights
877            && w.len() != self.vcount()
878        {
879            return Err(Error::invalid(format!(
880                "vertex weight vector length ({}) must match the number of vertices ({})",
881                w.len(),
882                self.vcount()
883            )));
884        }
885        let w = vertex_weights.map(Vector::view);
886        let mut res = VectorInt::new();
887        igraph_call!(igraph_feedback_vertex_set(
888            self,
889            &mut res,
890            opt_ptr(&w),
891            algo.into()
892        ))?;
893        Ok(res.into())
894    }
895
896    /// Checks whether the graph has an Eulerian path and/or an Eulerian cycle.
897    ///
898    /// An Eulerian path traverses every edge exactly once; an Eulerian cycle
899    /// is a closed one. By Euler's theorem, a connected (ignoring isolated
900    /// vertices) undirected graph has an Eulerian cycle iff all degrees are
901    /// even, and a path iff at most two degrees are odd. A directed graph
902    /// whose edges lie in a single weakly connected component has an Eulerian
903    /// cycle iff every in-degree equals the out-degree, and a path iff this
904    /// holds except for at most one start vertex (out-degree one larger) and
905    /// one end vertex (in-degree one larger). A graph without edges trivially
906    /// has both.
907    ///
908    /// Time complexity: O(|V|+|E|).
909    ///
910    /// See also [`degree`](Self::degree) and
911    /// [`is_connected`](Self::is_connected), the ingredients of Euler's
912    /// theorem, and [`eulerian_path`](Self::eulerian_path) /
913    /// [`eulerian_cycle`](Self::eulerian_cycle) to construct the walks.
914    ///
915    /// Binds [`igraph_is_eulerian`](https://igraph.org/c/html/latest/igraph-Cycles.html#igraph_is_eulerian).
916    ///
917    /// # Examples
918    ///
919    /// The seven bridges of Königsberg have no Eulerian path:
920    ///
921    /// ```
922    /// use igraph::prelude::*;
923    /// // 0: island Kneiphof, 1: north bank, 2: south bank, 3: east island Lomse
924    /// let bridges = [(0, 1), (0, 1), (0, 2), (0, 2), (0, 3), (1, 3), (2, 3)];
925    /// let konigsberg = Graph::from_edges(&bridges, 4, false)?;
926    /// let status = konigsberg.is_eulerian()?;
927    /// assert!(!status.has_path && !status.has_cycle);
928    /// # Ok::<(), igraph::Error>(())
929    /// ```
930    pub fn is_eulerian(&self) -> Result<EulerianStatus> {
931        let mut has_path = false;
932        let mut has_cycle = false;
933        igraph_call!(igraph_is_eulerian(self, &mut has_path, &mut has_cycle))?;
934        Ok(EulerianStatus {
935            has_path,
936            has_cycle,
937        })
938    }
939
940    /// Finds an Eulerian path, traversing every edge exactly once
941    /// (Hierholzer's algorithm).
942    ///
943    /// If the graph has no edges, an empty walk is returned. When the graph
944    /// also has an Eulerian cycle, the returned path is necessarily closed
945    /// (an Eulerian path with distinct ends exists only when exactly two
946    /// vertices have odd degree, or unbalanced in/out-degrees if directed).
947    ///
948    /// Time complexity: O(|V|+|E|).
949    ///
950    /// Binds [`igraph_eulerian_path`](https://igraph.org/c/html/latest/igraph-Cycles.html#igraph_eulerian_path).
951    ///
952    /// # Errors
953    ///
954    /// [`ErrorKind::NoSolution`](crate::ErrorKind::NoSolution) if the graph has
955    /// no Eulerian path (check first with [`is_eulerian`](Self::is_eulerian)).
956    ///
957    /// # Examples
958    ///
959    /// Drawing the "house of Santa Claus" without lifting the pen:
960    ///
961    /// ```
962    /// use igraph::prelude::*;
963    /// // square 0-1-2-3, both diagonals, and the roof 2-4-3
964    /// let house = [(0, 1), (1, 2), (2, 3), (3, 0), (0, 2), (1, 3), (2, 4), (4, 3)];
965    /// let g = Graph::from_edges(&house, 5, false)?;
966    /// let walk = g.eulerian_path()?;
967    /// assert_eq!(walk.edges.len(), 8);
968    /// assert_eq!(walk.vertices.len(), 9);
969    /// // It must start and end at the two odd-degree corners 0 and 1.
970    /// let ends = [walk.vertices[0], walk.vertices[8]];
971    /// assert!(ends == [0, 1] || ends == [1, 0]);
972    /// # Ok::<(), igraph::Error>(())
973    /// ```
974    pub fn eulerian_path(&self) -> Result<EulerianWalk> {
975        let mut edges = VectorInt::new();
976        let mut vertices = VectorInt::new();
977        igraph_call!(igraph_eulerian_path(self, &mut edges, &mut vertices))?;
978        Ok(EulerianWalk {
979            vertices: vertices.into(),
980            edges: edges.into(),
981        })
982    }
983
984    /// Finds an Eulerian cycle, a closed walk traversing every edge exactly
985    /// once (Hierholzer's algorithm).
986    ///
987    /// The first and last returned vertices coincide. If the graph has no
988    /// edges, an empty walk is returned.
989    ///
990    /// Time complexity: O(|V|+|E|).
991    ///
992    /// Binds [`igraph_eulerian_cycle`](https://igraph.org/c/html/latest/igraph-Cycles.html#igraph_eulerian_cycle).
993    ///
994    /// # Errors
995    ///
996    /// [`ErrorKind::NoSolution`](crate::ErrorKind::NoSolution) if the graph has
997    /// no Eulerian cycle.
998    ///
999    /// # Examples
1000    ///
1001    /// ```
1002    /// use igraph::prelude::*;
1003    /// let triangle = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 3, false)?;
1004    /// let c = triangle.eulerian_cycle()?;
1005    /// assert_eq!(c.edges, vec![0, 1, 2]);
1006    /// assert_eq!(c.vertices, vec![0, 1, 2, 0]);
1007    ///
1008    /// let path = Graph::from_edges(&[(0, 1), (1, 2)], 3, false)?;
1009    /// assert_eq!(path.eulerian_cycle().unwrap_err().kind(), ErrorKind::NoSolution);
1010    ///
1011    /// // An Eulerian cycle of the binary de Bruijn graph B(2, 2) traverses all
1012    /// // 8 three-bit words once: reading one bit per step gives a de Bruijn
1013    /// // sequence, which contains every 3-bit word (cyclically) exactly once.
1014    /// let b = Graph::de_bruijn(2, 2)?;
1015    /// let tour = b.eulerian_cycle()?;
1016    /// let bits: Vec<i64> = tour.vertices[1..].iter().map(|v| v & 1).collect();
1017    /// let mut words: Vec<i64> =
1018    ///     (0..8).map(|i| 4 * bits[i] + 2 * bits[(i + 1) % 8] + bits[(i + 2) % 8]).collect();
1019    /// words.sort();
1020    /// assert_eq!(words, (0..8).collect::<Vec<_>>());
1021    /// # Ok::<(), igraph::Error>(())
1022    /// ```
1023    pub fn eulerian_cycle(&self) -> Result<EulerianWalk> {
1024        let mut edges = VectorInt::new();
1025        let mut vertices = VectorInt::new();
1026        igraph_call!(igraph_eulerian_cycle(self, &mut edges, &mut vertices))?;
1027        Ok(EulerianWalk {
1028            vertices: vertices.into(),
1029            edges: edges.into(),
1030        })
1031    }
1032}