zrx_graph/graph/iter/ancestors.rs
1// Copyright (c) 2025-2026 Zensical and contributors
2
3// SPDX-License-Identifier: MIT
4// All contributions are certified under the DCO
5
6// Permission is hereby granted, free of charge, to any person obtaining a copy
7// of this software and associated documentation files (the "Software"), to
8// deal in the Software without restriction, including without limitation the
9// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10// sell copies of the Software, and to permit persons to whom the Software is
11// furnished to do so, subject to the following conditions:
12
13// The above copyright notice and this permission notice shall be included in
14// all copies or substantial portions of the Software.
15
16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
19// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22// IN THE SOFTWARE.
23
24// ----------------------------------------------------------------------------
25
26//! Iterator over ancestors of a node.
27
28use ahash::HashSet;
29
30use crate::graph::topology::Adjacency;
31use crate::graph::Graph;
32
33// ----------------------------------------------------------------------------
34// Structs
35// ----------------------------------------------------------------------------
36
37/// Iterator over ancestors of a node.
38pub struct Ancestors<'a> {
39 /// Incoming edges.
40 incoming: &'a Adjacency,
41 /// Stack for depth-first search.
42 stack: Vec<usize>,
43 /// Set of visited nodes.
44 visited: HashSet<usize>,
45}
46
47// ----------------------------------------------------------------------------
48// Implementations
49// ----------------------------------------------------------------------------
50
51impl<T> Graph<T> {
52 /// Creates an iterator over the ancestors of the given node.
53 ///
54 /// # Panics
55 ///
56 /// Panics if the node does not exist, as this indicates that there's a bug
57 /// in the code that creates or uses the iterator. While the [`Builder`][]
58 /// is designed to be fallible to ensure the structure is valid, methods
59 /// that operate on [`Graph`] panic on violated invariants.
60 ///
61 /// [`Builder`]: crate::graph::Builder
62 ///
63 /// # Examples
64 ///
65 /// ```
66 /// # use std::error::Error;
67 /// # fn main() -> Result<(), Box<dyn Error>> {
68 /// use zrx_graph::Graph;
69 ///
70 /// // Create graph builder and add nodes
71 /// let mut builder = Graph::builder();
72 /// let a = builder.add_node("a");
73 /// let b = builder.add_node("b");
74 /// let c = builder.add_node("c");
75 ///
76 /// // Create edges between nodes
77 /// builder.add_edge(a, b)?;
78 /// builder.add_edge(b, c)?;
79 ///
80 /// // Create graph from builder
81 /// let graph = builder.build();
82 ///
83 /// // Create iterator over ancestors
84 /// for node in graph.ancestors(c) {
85 /// println!("{node:?}");
86 /// }
87 /// # Ok(())
88 /// # }
89 /// ```
90 #[inline]
91 #[must_use]
92 pub fn ancestors(&self, node: usize) -> Ancestors<'_> {
93 let mut iter = Ancestors {
94 incoming: self.topology.incoming(),
95 stack: Vec::from([node]),
96 visited: HashSet::default(),
97 };
98
99 // Skip the initial node itself - it's simpler to just skip the initial
100 // node, so we can keep the iterator implementation plain and simple
101 iter.next();
102 iter
103 }
104}
105
106// ----------------------------------------------------------------------------
107// Trait implementations
108// ----------------------------------------------------------------------------
109
110impl Iterator for Ancestors<'_> {
111 type Item = usize;
112
113 /// Returns the next ancestor.
114 fn next(&mut self) -> Option<Self::Item> {
115 // Perform a depth-first search to find all ancestors of a node, by
116 // exploring them iteratively, not including the node itself
117 let node = self.stack.pop()?;
118 for &ancestor in self.incoming[node].iter().rev() {
119 // If we haven't visited this ancestor yet, we put it on the
120 // stack after marking it as visited and return it immediately
121 if self.visited.insert(ancestor) {
122 self.stack.push(ancestor);
123 }
124 }
125
126 // Return the next ancestor
127 Some(node)
128 }
129}
130
131// ----------------------------------------------------------------------------
132// Tests
133// ----------------------------------------------------------------------------
134
135#[cfg(test)]
136mod tests {
137
138 mod ancestors {
139 use crate::graph;
140
141 #[test]
142 fn handles_graph() {
143 let graph = graph! {
144 "a" => "b", "a" => "c",
145 "b" => "d", "b" => "e",
146 "c" => "f",
147 "d" => "g",
148 "e" => "g", "e" => "h",
149 "f" => "h",
150 "g" => "i",
151 "h" => "i",
152 };
153 for (node, ancestors) in [
154 (0, vec![]),
155 (1, vec![0]),
156 (2, vec![0]),
157 (3, vec![1, 0]),
158 (4, vec![1, 0]),
159 (5, vec![2, 0]),
160 (6, vec![3, 1, 0, 4]),
161 (7, vec![4, 1, 0, 5, 2]),
162 (8, vec![6, 3, 1, 0, 4, 7, 5, 2]),
163 ] {
164 assert_eq!(
165 graph.ancestors(node).collect::<Vec<_>>(),
166 ancestors
167 );
168 }
169 }
170
171 #[test]
172 fn handles_multi_graph() {
173 let graph = graph! {
174 "a" => "b", "a" => "c", "a" => "c",
175 "b" => "d", "b" => "e",
176 "c" => "f",
177 "d" => "g",
178 "e" => "g", "e" => "h",
179 "f" => "h",
180 "g" => "i",
181 "h" => "i",
182 };
183 for (node, ancestors) in [
184 (0, vec![]),
185 (1, vec![0]),
186 (2, vec![0]),
187 (3, vec![1, 0]),
188 (4, vec![1, 0]),
189 (5, vec![2, 0]),
190 (6, vec![3, 1, 0, 4]),
191 (7, vec![4, 1, 0, 5, 2]),
192 (8, vec![6, 3, 1, 0, 4, 7, 5, 2]),
193 ] {
194 assert_eq!(
195 graph.ancestors(node).collect::<Vec<_>>(),
196 ancestors
197 );
198 }
199 }
200 }
201}