1use std::collections::BTreeMap;
21
22use crate::core::graph::EdgeId;
23use crate::core::{Graph, IgraphError, IgraphResult, VertexId};
24
25pub fn intersection(left: &Graph, right: &Graph) -> IgraphResult<Graph> {
63 if left.is_directed() != right.is_directed() {
64 return Err(IgraphError::InvalidArgument(
65 "intersection: cannot mix directed and undirected graphs".to_string(),
66 ));
67 }
68 let directed = left.is_directed();
69 let n = std::cmp::max(left.vcount(), right.vcount());
70
71 let canon = |u: VertexId, v: VertexId| -> (VertexId, VertexId) {
72 if directed || u <= v { (u, v) } else { (v, u) }
73 };
74
75 let mut count_left: BTreeMap<(VertexId, VertexId), u32> = BTreeMap::new();
76 let mut count_right: BTreeMap<(VertexId, VertexId), u32> = BTreeMap::new();
77
78 let m_l = u32::try_from(left.ecount())
79 .map_err(|_| IgraphError::Internal("ecount exceeds u32::MAX"))?;
80 for e in 0..m_l {
81 let (u, v) = left.edge(e as EdgeId)?;
82 *count_left.entry(canon(u, v)).or_insert(0) += 1;
83 }
84 let m_r = u32::try_from(right.ecount())
85 .map_err(|_| IgraphError::Internal("ecount exceeds u32::MAX"))?;
86 for e in 0..m_r {
87 let (u, v) = right.edge(e as EdgeId)?;
88 *count_right.entry(canon(u, v)).or_insert(0) += 1;
89 }
90
91 let mut edges: Vec<(VertexId, VertexId)> = Vec::new();
96 let (driver, lookup) = if count_left.len() <= count_right.len() {
97 (&count_left, &count_right)
98 } else {
99 (&count_right, &count_left)
100 };
101 for (k, &cd) in driver {
102 if let Some(&co) = lookup.get(k) {
103 let m = std::cmp::min(cd, co);
104 for _ in 0..m {
105 edges.push(*k);
106 }
107 }
108 }
109 edges.sort_unstable();
113
114 let mut out = Graph::new(n, directed)?;
115 out.add_edges(edges)?;
116 Ok(out)
117}
118
119pub fn intersection_many(graphs: &[&Graph]) -> IgraphResult<Graph> {
153 if graphs.is_empty() {
154 return Graph::new(0, true);
155 }
156
157 let directed = graphs[0].is_directed();
158 for g in &graphs[1..] {
159 if g.is_directed() != directed {
160 return Err(IgraphError::InvalidArgument(
161 "intersection_many: cannot mix directed and undirected graphs".to_string(),
162 ));
163 }
164 }
165
166 let n = graphs.iter().map(|g| g.vcount()).max().unwrap_or(0);
167
168 let canon = |u: VertexId, v: VertexId| -> (VertexId, VertexId) {
169 if directed || u <= v { (u, v) } else { (v, u) }
170 };
171
172 let mut result_counts: BTreeMap<(VertexId, VertexId), u32> = BTreeMap::new();
174 let m = graphs[0].ecount();
175 for eid in 0..m {
176 #[allow(clippy::cast_possible_truncation)]
177 let eid_u32 = eid as u32;
178 let (u, v) = graphs[0].edge(eid_u32)?;
179 *result_counts.entry(canon(u, v)).or_insert(0) += 1;
180 }
181
182 for g in &graphs[1..] {
184 let mut counts: BTreeMap<(VertexId, VertexId), u32> = BTreeMap::new();
185 let gm = g.ecount();
186 for eid in 0..gm {
187 #[allow(clippy::cast_possible_truncation)]
188 let eid_u32 = eid as u32;
189 let (u, v) = g.edge(eid_u32)?;
190 *counts.entry(canon(u, v)).or_insert(0) += 1;
191 }
192
193 result_counts.retain(|pair, mult| {
195 if let Some(&cnt) = counts.get(pair) {
196 *mult = std::cmp::min(*mult, cnt);
197 true
198 } else {
199 false
200 }
201 });
202 }
203
204 let mut edges: Vec<(VertexId, VertexId)> = Vec::new();
205 for (pair, mult) in &result_counts {
206 for _ in 0..*mult {
207 edges.push(*pair);
208 }
209 }
210
211 let mut out = Graph::new(n, directed)?;
212 out.add_edges(edges)?;
213 Ok(out)
214}
215
216#[cfg(test)]
217mod tests {
218 use super::*;
219
220 fn sorted_edges(g: &Graph) -> Vec<(VertexId, VertexId)> {
221 let m = u32::try_from(g.ecount()).unwrap();
222 let mut v: Vec<_> = (0..m).map(|e| g.edge(e).unwrap()).collect();
223 v.sort_unstable();
224 v
225 }
226
227 #[test]
228 fn empty_intersect_empty() {
229 let a = Graph::with_vertices(0);
230 let b = Graph::with_vertices(0);
231 let i = intersection(&a, &b).unwrap();
232 assert_eq!(i.vcount(), 0);
233 assert_eq!(i.ecount(), 0);
234 assert!(!i.is_directed());
235 }
236
237 #[test]
238 fn vcount_is_max_of_inputs() {
239 let a = Graph::with_vertices(3);
240 let b = Graph::with_vertices(7);
241 let i = intersection(&a, &b).unwrap();
242 assert_eq!(i.vcount(), 7);
243 assert_eq!(i.ecount(), 0);
244 }
245
246 #[test]
247 fn triangle_intersect_path_doc_example() {
248 let mut a = Graph::with_vertices(3);
249 a.add_edge(0, 1).unwrap();
250 a.add_edge(1, 2).unwrap();
251 a.add_edge(2, 0).unwrap();
252 let mut b = Graph::with_vertices(3);
253 b.add_edge(0, 1).unwrap();
254 b.add_edge(1, 2).unwrap();
255 let i = intersection(&a, &b).unwrap();
256 assert_eq!(i.vcount(), 3);
257 assert_eq!(i.ecount(), 2);
258 assert_eq!(sorted_edges(&i), vec![(0, 1), (1, 2)]);
259 }
260
261 #[test]
262 fn min_multiplicity_when_left_has_more() {
263 let mut a = Graph::with_vertices(2);
265 a.add_edge(0, 1).unwrap();
266 a.add_edge(0, 1).unwrap();
267 a.add_edge(0, 1).unwrap();
268 let mut b = Graph::with_vertices(2);
269 b.add_edge(0, 1).unwrap();
270 let i = intersection(&a, &b).unwrap();
271 assert_eq!(i.ecount(), 1);
272 }
273
274 #[test]
275 fn min_multiplicity_when_right_has_more() {
276 let mut a = Graph::with_vertices(2);
278 a.add_edge(0, 1).unwrap();
279 a.add_edge(0, 1).unwrap();
280 let mut b = Graph::with_vertices(2);
281 for _ in 0..5 {
282 b.add_edge(0, 1).unwrap();
283 }
284 let i = intersection(&a, &b).unwrap();
285 assert_eq!(i.ecount(), 2);
286 }
287
288 #[test]
289 fn disjoint_edge_sets_yields_empty() {
290 let mut a = Graph::with_vertices(4);
292 a.add_edge(0, 1).unwrap();
293 let mut b = Graph::with_vertices(4);
294 b.add_edge(2, 3).unwrap();
295 let i = intersection(&a, &b).unwrap();
296 assert_eq!(i.vcount(), 4);
297 assert_eq!(i.ecount(), 0);
298 }
299
300 #[test]
301 fn idempotent_with_self() {
302 let mut a = Graph::with_vertices(4);
304 a.add_edge(0, 1).unwrap();
305 a.add_edge(1, 2).unwrap();
306 a.add_edge(0, 2).unwrap();
307 a.add_edge(0, 2).unwrap(); let i = intersection(&a, &a).unwrap();
309 assert_eq!(i.vcount(), a.vcount());
310 assert_eq!(i.ecount(), a.ecount());
311 assert_eq!(sorted_edges(&i), sorted_edges(&a));
312 }
313
314 #[test]
315 fn directed_keeps_orientation_separate() {
316 let mut a = Graph::new(2, true).unwrap();
318 a.add_edge(0, 1).unwrap();
319 a.add_edge(1, 0).unwrap();
320 let mut b = Graph::new(2, true).unwrap();
321 b.add_edge(0, 1).unwrap();
322 let i = intersection(&a, &b).unwrap();
323 assert!(i.is_directed());
324 assert_eq!(i.ecount(), 1);
325 assert_eq!(i.edge(0).unwrap(), (0, 1));
326 }
327
328 #[test]
329 fn directed_min_multiplicity_per_orientation() {
330 let mut a = Graph::new(2, true).unwrap();
333 for _ in 0..3 {
334 a.add_edge(0, 1).unwrap();
335 }
336 for _ in 0..2 {
337 a.add_edge(1, 0).unwrap();
338 }
339 let mut b = Graph::new(2, true).unwrap();
340 for _ in 0..2 {
341 b.add_edge(0, 1).unwrap();
342 }
343 for _ in 0..4 {
344 b.add_edge(1, 0).unwrap();
345 }
346 let i = intersection(&a, &b).unwrap();
347 assert_eq!(i.ecount(), 4);
348 let s = sorted_edges(&i);
349 assert_eq!(s.iter().filter(|&&p| p == (0, 1)).count(), 2);
350 assert_eq!(s.iter().filter(|&&p| p == (1, 0)).count(), 2);
351 }
352
353 #[test]
354 fn loops_are_preserved_with_min_multiplicity() {
355 let mut a = Graph::with_vertices(1);
357 for _ in 0..3 {
358 a.add_edge(0, 0).unwrap();
359 }
360 let mut b = Graph::with_vertices(1);
361 b.add_edge(0, 0).unwrap();
362 let i = intersection(&a, &b).unwrap();
363 assert_eq!(i.ecount(), 1);
364 assert_eq!(i.edge(0).unwrap(), (0, 0));
365 }
366
367 #[test]
368 fn unaligned_vertex_sizes_use_max() {
369 let mut a = Graph::with_vertices(2);
371 a.add_edge(0, 1).unwrap();
372 let mut b = Graph::with_vertices(5);
373 b.add_edge(3, 4).unwrap();
374 let i = intersection(&a, &b).unwrap();
375 assert_eq!(i.vcount(), 5);
376 assert_eq!(i.ecount(), 0);
377 }
378
379 #[test]
380 fn mixed_directedness_errors() {
381 let a = Graph::with_vertices(2);
382 let b = Graph::new(2, true).unwrap();
383 assert!(intersection(&a, &b).is_err());
384 }
385
386 #[test]
387 fn undirected_canonicalises_swapped_endpoints() {
388 let mut a = Graph::with_vertices(2);
391 a.add_edge(1, 0).unwrap();
392 let mut b = Graph::with_vertices(2);
393 b.add_edge(0, 1).unwrap();
394 let i = intersection(&a, &b).unwrap();
395 assert_eq!(i.ecount(), 1);
396 }
397
398 #[test]
399 fn order_independent() {
400 let mut a = Graph::with_vertices(4);
403 a.add_edge(0, 1).unwrap();
404 a.add_edge(0, 1).unwrap();
405 a.add_edge(1, 2).unwrap();
406 a.add_edge(2, 3).unwrap();
407 let mut b = Graph::with_vertices(4);
408 b.add_edge(0, 1).unwrap();
409 b.add_edge(1, 2).unwrap();
410 b.add_edge(1, 2).unwrap();
411 let ab = intersection(&a, &b).unwrap();
412 let ba = intersection(&b, &a).unwrap();
413 assert_eq!(sorted_edges(&ab), sorted_edges(&ba));
414 }
415
416 #[test]
419 fn intersection_many_empty_list() {
420 let i = intersection_many(&[]).unwrap();
421 assert_eq!(i.vcount(), 0);
422 assert!(i.is_directed());
423 }
424
425 #[test]
426 fn intersection_many_single_graph() {
427 let mut a = Graph::with_vertices(3);
428 a.add_edge(0, 1).unwrap();
429 a.add_edge(1, 2).unwrap();
430 let i = intersection_many(&[&a]).unwrap();
431 assert_eq!(i.vcount(), 3);
432 assert_eq!(i.ecount(), 2);
433 assert_eq!(sorted_edges(&i), sorted_edges(&a));
434 }
435
436 #[test]
437 fn intersection_many_three_graphs() {
438 let mut a = Graph::with_vertices(3);
439 a.add_edge(0, 1).unwrap();
440 a.add_edge(1, 2).unwrap();
441 let mut b = Graph::with_vertices(3);
442 b.add_edge(0, 1).unwrap();
443 b.add_edge(2, 0).unwrap();
444 let mut c = Graph::with_vertices(3);
445 c.add_edge(0, 1).unwrap();
446 c.add_edge(1, 2).unwrap();
447 c.add_edge(2, 0).unwrap();
448
449 let i = intersection_many(&[&a, &b, &c]).unwrap();
450 assert_eq!(i.vcount(), 3);
451 assert_eq!(i.ecount(), 1); assert_eq!(sorted_edges(&i), vec![(0, 1)]);
453 }
454
455 #[test]
456 fn intersection_many_min_multiplicity() {
457 let mut a = Graph::with_vertices(2);
458 a.add_edge(0, 1).unwrap();
459 a.add_edge(0, 1).unwrap();
460 a.add_edge(0, 1).unwrap();
461 let mut b = Graph::with_vertices(2);
462 b.add_edge(0, 1).unwrap();
463 b.add_edge(0, 1).unwrap();
464 let mut c = Graph::with_vertices(2);
465 c.add_edge(0, 1).unwrap();
466 c.add_edge(0, 1).unwrap();
467 c.add_edge(0, 1).unwrap();
468 c.add_edge(0, 1).unwrap();
469
470 let i = intersection_many(&[&a, &b, &c]).unwrap();
471 assert_eq!(i.ecount(), 2); }
473
474 #[test]
475 fn intersection_many_no_common_edge() {
476 let mut a = Graph::with_vertices(3);
477 a.add_edge(0, 1).unwrap();
478 let mut b = Graph::with_vertices(3);
479 b.add_edge(1, 2).unwrap();
480 let mut c = Graph::with_vertices(3);
481 c.add_edge(2, 0).unwrap();
482
483 let i = intersection_many(&[&a, &b, &c]).unwrap();
484 assert_eq!(i.ecount(), 0);
485 }
486
487 #[test]
488 fn intersection_many_mixed_directedness_fails() {
489 let a = Graph::with_vertices(2);
490 let b = Graph::new(2, true).unwrap();
491 assert!(intersection_many(&[&a, &b]).is_err());
492 }
493
494 #[test]
495 fn intersection_many_directed() {
496 let mut a = Graph::new(3, true).unwrap();
497 a.add_edge(0, 1).unwrap();
498 a.add_edge(1, 2).unwrap();
499 let mut b = Graph::new(3, true).unwrap();
500 b.add_edge(0, 1).unwrap();
501 b.add_edge(2, 1).unwrap();
502
503 let i = intersection_many(&[&a, &b]).unwrap();
504 assert!(i.is_directed());
505 assert_eq!(i.ecount(), 1); assert_eq!(sorted_edges(&i), vec![(0, 1)]);
507 }
508}