rust_igraph/algorithms/connectivity/
subcomponent.rs1use crate::core::{Graph, IgraphError, IgraphResult, VertexId};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum SubcomponentMode {
11 Out,
13 In,
15 All,
17}
18
19pub fn subcomponent(
58 graph: &Graph,
59 source: VertexId,
60 mode: SubcomponentMode,
61) -> IgraphResult<Vec<VertexId>> {
62 let n = graph.vcount();
63 if source >= n {
64 return Err(IgraphError::VertexOutOfRange { id: source, n });
65 }
66
67 if n == 0 {
68 return Ok(Vec::new());
69 }
70
71 let adj = build_adj(graph, mode)?;
73
74 let mut visited = vec![false; n as usize];
76 let mut result = Vec::new();
77 let mut queue = std::collections::VecDeque::new();
78
79 visited[source as usize] = true;
80 queue.push_back(source);
81
82 while let Some(v) = queue.pop_front() {
83 result.push(v);
84 for &nb in &adj[v as usize] {
85 if !visited[nb as usize] {
86 visited[nb as usize] = true;
87 queue.push_back(nb);
88 }
89 }
90 }
91
92 Ok(result)
93}
94
95fn build_adj(graph: &Graph, mode: SubcomponentMode) -> IgraphResult<Vec<Vec<VertexId>>> {
96 let n = graph.vcount() as usize;
97 let ecount = graph.ecount();
98 let mut adj: Vec<Vec<VertexId>> = vec![Vec::new(); n];
99
100 for eid in 0..ecount {
101 #[allow(clippy::cast_possible_truncation)]
102 let (src, tgt) = graph.edge(eid as u32)?;
103 if src == tgt {
104 continue;
105 }
106
107 if !graph.is_directed() || mode == SubcomponentMode::All {
108 adj[src as usize].push(tgt);
109 adj[tgt as usize].push(src);
110 } else if mode == SubcomponentMode::Out {
111 adj[src as usize].push(tgt);
112 } else {
113 adj[tgt as usize].push(src);
115 }
116 }
117
118 Ok(adj)
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124
125 #[test]
126 fn test_out_of_range() {
127 let g = Graph::with_vertices(3);
128 assert!(subcomponent(&g, 5, SubcomponentMode::All).is_err());
129 }
130
131 #[test]
132 fn test_single_vertex() {
133 let g = Graph::with_vertices(1);
134 let r = subcomponent(&g, 0, SubcomponentMode::All).unwrap();
135 assert_eq!(r, vec![0]);
136 }
137
138 #[test]
139 fn test_disconnected_undirected() {
140 let mut g = Graph::with_vertices(4);
141 g.add_edge(0, 1).unwrap();
142 g.add_edge(2, 3).unwrap();
143 let r = subcomponent(&g, 0, SubcomponentMode::All).unwrap();
144 assert_eq!(r.len(), 2);
145 assert!(r.contains(&0));
146 assert!(r.contains(&1));
147 }
148
149 #[test]
150 fn test_connected_undirected() {
151 let mut g = Graph::with_vertices(4);
152 g.add_edge(0, 1).unwrap();
153 g.add_edge(1, 2).unwrap();
154 g.add_edge(2, 3).unwrap();
155 let r = subcomponent(&g, 0, SubcomponentMode::All).unwrap();
156 assert_eq!(r.len(), 4);
157 }
158
159 #[test]
160 fn test_directed_out() {
161 let mut g = Graph::new(4, true).unwrap();
162 g.add_edge(0, 1).unwrap();
163 g.add_edge(1, 2).unwrap();
164 g.add_edge(3, 0).unwrap();
165 let r = subcomponent(&g, 0, SubcomponentMode::Out).unwrap();
166 assert_eq!(r, vec![0, 1, 2]);
167 }
168
169 #[test]
170 fn test_directed_in() {
171 let mut g = Graph::new(4, true).unwrap();
172 g.add_edge(0, 1).unwrap();
173 g.add_edge(1, 2).unwrap();
174 g.add_edge(3, 0).unwrap();
175 let r = subcomponent(&g, 2, SubcomponentMode::In).unwrap();
176 assert_eq!(r, vec![2, 1, 0, 3]);
177 }
178
179 #[test]
180 fn test_directed_all() {
181 let mut g = Graph::new(5, true).unwrap();
182 g.add_edge(0, 1).unwrap();
183 g.add_edge(2, 1).unwrap();
184 g.add_edge(3, 4).unwrap();
185 let r = subcomponent(&g, 0, SubcomponentMode::All).unwrap();
186 assert_eq!(r.len(), 3);
187 assert!(r.contains(&0));
188 assert!(r.contains(&1));
189 assert!(r.contains(&2));
190 }
191
192 #[test]
193 fn test_isolated_vertex() {
194 let g = Graph::with_vertices(5);
195 let r = subcomponent(&g, 3, SubcomponentMode::All).unwrap();
196 assert_eq!(r, vec![3]);
197 }
198
199 #[test]
200 fn test_self_loop_ignored() {
201 let mut g = Graph::new(3, true).unwrap();
202 g.add_edge(0, 0).unwrap();
203 g.add_edge(0, 1).unwrap();
204 let r = subcomponent(&g, 0, SubcomponentMode::Out).unwrap();
205 assert_eq!(r, vec![0, 1]);
206 }
207
208 #[test]
209 fn test_cycle_directed_out() {
210 let mut g = Graph::new(3, true).unwrap();
211 g.add_edge(0, 1).unwrap();
212 g.add_edge(1, 2).unwrap();
213 g.add_edge(2, 0).unwrap();
214 let r = subcomponent(&g, 0, SubcomponentMode::Out).unwrap();
215 assert_eq!(r.len(), 3);
216 }
217
218 #[test]
219 fn test_bfs_order() {
220 let mut g = Graph::new(4, true).unwrap();
221 g.add_edge(0, 1).unwrap();
222 g.add_edge(0, 2).unwrap();
223 g.add_edge(1, 3).unwrap();
224 let r = subcomponent(&g, 0, SubcomponentMode::Out).unwrap();
225 assert_eq!(r[0], 0);
226 assert!(r[1] == 1 || r[1] == 2);
228 }
229
230 #[test]
231 fn test_undirected_mode_ignored() {
232 let mut g = Graph::with_vertices(3);
233 g.add_edge(0, 1).unwrap();
234 g.add_edge(1, 2).unwrap();
235 let out = subcomponent(&g, 0, SubcomponentMode::Out).unwrap();
237 let in_ = subcomponent(&g, 0, SubcomponentMode::In).unwrap();
238 let all = subcomponent(&g, 0, SubcomponentMode::All).unwrap();
239 assert_eq!(out.len(), 3);
240 assert_eq!(in_.len(), 3);
241 assert_eq!(all.len(), 3);
242 }
243}