rust_igraph/algorithms/properties/
degree.rs1use crate::core::{Graph, IgraphResult, VertexId};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum DegreeMode {
12 Out,
14 In,
16 All,
18}
19
20pub fn degree_sequence(graph: &Graph, mode: DegreeMode) -> IgraphResult<Vec<u32>> {
54 let n = graph.vcount() as usize;
55 let ecount = graph.ecount();
56 let mut degrees = vec![0u32; n];
57
58 for eid in 0..ecount {
59 #[allow(clippy::cast_possible_truncation)]
60 let (src, tgt) = graph.edge(eid as u32)?;
61
62 if graph.is_directed() {
63 match mode {
64 DegreeMode::Out => {
65 degrees[src as usize] += 1;
66 }
67 DegreeMode::In => {
68 degrees[tgt as usize] += 1;
69 }
70 DegreeMode::All => {
71 degrees[src as usize] += 1;
72 if src != tgt {
73 degrees[tgt as usize] += 1;
74 }
75 }
76 }
77 } else {
78 degrees[src as usize] += 1;
79 degrees[tgt as usize] += 1;
80 }
81 }
82
83 Ok(degrees)
84}
85
86pub fn max_degree(graph: &Graph, mode: DegreeMode) -> IgraphResult<u32> {
103 let degrees = degree_sequence(graph, mode)?;
104 Ok(degrees.into_iter().max().unwrap_or(0))
105}
106
107pub fn min_degree(graph: &Graph, mode: DegreeMode) -> IgraphResult<u32> {
124 let degrees = degree_sequence(graph, mode)?;
125 Ok(degrees.into_iter().min().unwrap_or(0))
126}
127
128pub fn max_degree_vertex(graph: &Graph, mode: DegreeMode) -> IgraphResult<Option<VertexId>> {
144 let degrees = degree_sequence(graph, mode)?;
145 Ok(degrees
146 .iter()
147 .enumerate()
148 .max_by_key(|(_, d)| *d)
149 .map(|(i, _)| {
150 #[allow(clippy::cast_possible_truncation)]
151 let v = i as u32;
152 v
153 }))
154}
155
156#[cfg(test)]
157mod tests {
158 use super::*;
159
160 #[test]
161 fn test_empty_graph() {
162 let g = Graph::with_vertices(0);
163 let deg = degree_sequence(&g, DegreeMode::All).unwrap();
164 assert!(deg.is_empty());
165 assert_eq!(max_degree(&g, DegreeMode::All).unwrap(), 0);
166 assert_eq!(min_degree(&g, DegreeMode::All).unwrap(), 0);
167 }
168
169 #[test]
170 fn test_isolated_vertices() {
171 let g = Graph::with_vertices(5);
172 let deg = degree_sequence(&g, DegreeMode::All).unwrap();
173 assert_eq!(deg, vec![0, 0, 0, 0, 0]);
174 }
175
176 #[test]
177 fn test_undirected_path() {
178 let mut g = Graph::with_vertices(4);
179 g.add_edge(0, 1).unwrap();
180 g.add_edge(1, 2).unwrap();
181 g.add_edge(2, 3).unwrap();
182 let deg = degree_sequence(&g, DegreeMode::All).unwrap();
183 assert_eq!(deg, vec![1, 2, 2, 1]);
184 }
185
186 #[test]
187 fn test_undirected_star() {
188 let mut g = Graph::with_vertices(5);
189 for i in 1..5u32 {
190 g.add_edge(0, i).unwrap();
191 }
192 let deg = degree_sequence(&g, DegreeMode::All).unwrap();
193 assert_eq!(deg, vec![4, 1, 1, 1, 1]);
194 assert_eq!(max_degree(&g, DegreeMode::All).unwrap(), 4);
195 assert_eq!(min_degree(&g, DegreeMode::All).unwrap(), 1);
196 }
197
198 #[test]
199 fn test_directed_out() {
200 let mut g = Graph::new(3, true).unwrap();
201 g.add_edge(0, 1).unwrap();
202 g.add_edge(0, 2).unwrap();
203 g.add_edge(1, 2).unwrap();
204 let deg = degree_sequence(&g, DegreeMode::Out).unwrap();
205 assert_eq!(deg, vec![2, 1, 0]);
206 }
207
208 #[test]
209 fn test_directed_in() {
210 let mut g = Graph::new(3, true).unwrap();
211 g.add_edge(0, 1).unwrap();
212 g.add_edge(0, 2).unwrap();
213 g.add_edge(1, 2).unwrap();
214 let deg = degree_sequence(&g, DegreeMode::In).unwrap();
215 assert_eq!(deg, vec![0, 1, 2]);
216 }
217
218 #[test]
219 fn test_directed_all() {
220 let mut g = Graph::new(3, true).unwrap();
221 g.add_edge(0, 1).unwrap();
222 g.add_edge(0, 2).unwrap();
223 g.add_edge(1, 2).unwrap();
224 let deg = degree_sequence(&g, DegreeMode::All).unwrap();
225 assert_eq!(deg, vec![2, 2, 2]);
226 }
227
228 #[test]
229 fn test_self_loop_undirected() {
230 let mut g = Graph::with_vertices(2);
231 g.add_edge(0, 0).unwrap();
232 g.add_edge(0, 1).unwrap();
233 let deg = degree_sequence(&g, DegreeMode::All).unwrap();
234 assert_eq!(deg, vec![3, 1]);
236 }
237
238 #[test]
239 fn test_self_loop_directed_out() {
240 let mut g = Graph::new(2, true).unwrap();
241 g.add_edge(0, 0).unwrap();
242 g.add_edge(0, 1).unwrap();
243 let deg = degree_sequence(&g, DegreeMode::Out).unwrap();
244 assert_eq!(deg, vec![2, 0]);
245 }
246
247 #[test]
248 fn test_self_loop_directed_in() {
249 let mut g = Graph::new(2, true).unwrap();
250 g.add_edge(0, 0).unwrap();
251 g.add_edge(0, 1).unwrap();
252 let deg = degree_sequence(&g, DegreeMode::In).unwrap();
253 assert_eq!(deg, vec![1, 1]);
254 }
255
256 #[test]
257 fn test_self_loop_directed_all() {
258 let mut g = Graph::new(2, true).unwrap();
259 g.add_edge(0, 0).unwrap();
260 g.add_edge(0, 1).unwrap();
261 let deg = degree_sequence(&g, DegreeMode::All).unwrap();
262 assert_eq!(deg, vec![2, 1]);
264 }
265
266 #[test]
267 fn test_max_degree_vertex_basic() {
268 let mut g = Graph::with_vertices(4);
269 g.add_edge(0, 1).unwrap();
270 g.add_edge(0, 2).unwrap();
271 g.add_edge(0, 3).unwrap();
272 assert_eq!(max_degree_vertex(&g, DegreeMode::All).unwrap(), Some(0));
273 }
274
275 #[test]
276 fn test_max_degree_vertex_empty() {
277 let g = Graph::with_vertices(0);
278 assert_eq!(max_degree_vertex(&g, DegreeMode::All).unwrap(), None);
279 }
280
281 #[test]
282 fn test_complete_graph() {
283 let mut g = Graph::with_vertices(5);
284 for i in 0..5u32 {
285 for j in (i + 1)..5 {
286 g.add_edge(i, j).unwrap();
287 }
288 }
289 let deg = degree_sequence(&g, DegreeMode::All).unwrap();
290 assert!(deg.iter().all(|&d| d == 4));
291 assert_eq!(max_degree(&g, DegreeMode::All).unwrap(), 4);
292 assert_eq!(min_degree(&g, DegreeMode::All).unwrap(), 4);
293 }
294
295 #[test]
296 fn test_multi_edges() {
297 let mut g = Graph::with_vertices(2);
298 g.add_edge(0, 1).unwrap();
299 g.add_edge(0, 1).unwrap();
300 g.add_edge(0, 1).unwrap();
301 let deg = degree_sequence(&g, DegreeMode::All).unwrap();
302 assert_eq!(deg, vec![3, 3]);
303 }
304}