zrx_graph/graph/iter/common_descendants.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 common descendants of a set of nodes.
27
28use std::collections::BTreeSet;
29
30use crate::graph::topology::Distance;
31use crate::graph::Graph;
32
33// ----------------------------------------------------------------------------
34// Structs
35// ----------------------------------------------------------------------------
36
37/// Iterator over common descendants of a set of nodes.
38pub struct CommonDescendants<'a> {
39 /// Distance matrix.
40 distance: &'a Distance,
41 /// Set of common descendants.
42 descendants: BTreeSet<usize>,
43}
44
45// ----------------------------------------------------------------------------
46// Implementations
47// ----------------------------------------------------------------------------
48
49impl<T> Graph<T> {
50 /// Creates an iterator over the common descendants of the set of nodes.
51 ///
52 /// # Panics
53 ///
54 /// Panics if a node does not exist, as this indicates that there's a bug
55 /// in the code that creates or uses the iterator. While the [`Builder`][]
56 /// is designed to be fallible to ensure the structure is valid, methods
57 /// that operate on [`Graph`] panic on violated invariants.
58 ///
59 /// [`Builder`]: crate::graph::Builder
60 ///
61 /// # Examples
62 ///
63 /// ```
64 /// # use std::error::Error;
65 /// # fn main() -> Result<(), Box<dyn Error>> {
66 /// use zrx_graph::Graph;
67 ///
68 /// // Create graph builder and add nodes
69 /// let mut builder = Graph::builder();
70 /// let a = builder.add_node("a");
71 /// let b = builder.add_node("b");
72 /// let c = builder.add_node("c");
73 ///
74 /// // Create edges between nodes
75 /// builder.add_edge(a, b)?;
76 /// builder.add_edge(a, c)?;
77 ///
78 /// // Create graph from builder
79 /// let graph = builder.build();
80 ///
81 /// // Create iterator over common descendants
82 /// for nodes in graph.common_descendants([a]) {
83 /// println!("{nodes:?}");
84 /// }
85 /// # Ok(())
86 /// # }
87 /// ```
88 pub fn common_descendants<N>(&self, nodes: N) -> CommonDescendants<'_>
89 where
90 N: AsRef<[usize]>,
91 {
92 let distance = self.topology.distance();
93 let nodes = nodes.as_ref();
94
95 // Compute common descendants by ensuring that each node in the given
96 // set of nodes is reachable from the current node being considered
97 let mut descendants = BTreeSet::new();
98 for descendant in self {
99 let mut iter = nodes.iter();
100 if iter.all(|&node| distance[node][descendant] != u8::MAX) {
101 descendants.insert(descendant);
102 }
103 }
104
105 // Create and return iterator
106 CommonDescendants {
107 distance: self.topology.distance(),
108 descendants,
109 }
110 }
111}
112
113// ----------------------------------------------------------------------------
114// Trait implementations
115// ----------------------------------------------------------------------------
116
117impl Iterator for CommonDescendants<'_> {
118 type Item = Vec<usize>;
119
120 /// Returns the next layer of common descendants.
121 fn next(&mut self) -> Option<Self::Item> {
122 if self.descendants.is_empty() {
123 return None;
124 }
125
126 // Compute the next layer of common descendants - all nodes that aren't
127 // descendants of any other remaining common descendant. This process is
128 // commonly referred to as peeling, where we iteratively remove layers
129 // from the set of common descendants.
130 let mut layer = Vec::new();
131 for &descendant in &self.descendants {
132 let mut iter = self.descendants.iter();
133 if !iter.any(|&node| {
134 node != descendant && self.distance[node][descendant] != u8::MAX
135 }) {
136 layer.push(descendant);
137 }
138 }
139
140 // Remove all nodes in the layer from the set of common descendants,
141 // and return the layer if it's not empty. Otherwise, we're done.
142 self.descendants.retain(|node| !layer.contains(node));
143 (!layer.is_empty()).then_some(layer)
144 }
145}
146
147// ----------------------------------------------------------------------------
148// Tests
149// ----------------------------------------------------------------------------
150
151#[cfg(test)]
152mod tests {
153
154 mod common_descendants {
155 use crate::graph;
156
157 #[test]
158 fn handles_graph() {
159 let graph = graph! {
160 "a" => "d",
161 "b" => "d", "b" => "e",
162 "c" => "f", "c" => "g",
163 "d" => "f", "d" => "g",
164 "e" => "g",
165 };
166 assert_eq!(
167 graph.common_descendants([0, 2]).collect::<Vec<_>>(),
168 vec![vec![1], vec![5, 6]]
169 );
170 }
171
172 #[test]
173 fn handles_multi_graph() {
174 let graph = graph! {
175 "a" => "d",
176 "b" => "d", "b" => "e", "b" => "e",
177 "c" => "f", "c" => "g",
178 "d" => "f", "d" => "g",
179 "e" => "g",
180 };
181 assert_eq!(
182 graph.common_descendants([0, 2]).collect::<Vec<_>>(),
183 vec![vec![1], vec![5, 6]]
184 );
185 }
186 }
187}