1use std::collections::VecDeque;
15
16use crate::core::{Graph, IgraphError, IgraphResult, VertexId};
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum NeighborhoodMode {
24 Out,
27 In,
31 All,
34}
35
36pub fn neighborhood_size(graph: &Graph, order: i32) -> IgraphResult<Vec<u32>> {
66 neighborhood_size_with_mode(graph, order, NeighborhoodMode::All, 0)
67}
68
69pub fn neighborhood_size_with_mode(
112 graph: &Graph,
113 order: i32,
114 mode: NeighborhoodMode,
115 mindist: i32,
116) -> IgraphResult<Vec<u32>> {
117 if mindist < 0 {
118 return Err(IgraphError::InvalidArgument(format!(
119 "minimum distance must not be negative, got {mindist}"
120 )));
121 }
122 if order >= 0 && mindist > order {
123 return Err(IgraphError::InvalidArgument(format!(
124 "minimum distance must not exceed neighbourhood order ({order}), got {mindist}"
125 )));
126 }
127
128 let n = graph.vcount();
129 if n == 0 {
130 return Ok(Vec::new());
131 }
132 let n_us = n as usize;
133
134 let inf_order = order < 0;
138 let effective_order: i64 = if inf_order {
139 i64::from(n)
140 } else {
141 i64::from(order)
142 };
143 let mindist_i64 = i64::from(mindist);
144
145 let directed = graph.is_directed();
146 let mut added: Vec<u32> = vec![0; n_us];
149 let mut queue: VecDeque<(VertexId, i64)> = VecDeque::new();
150 let mut result: Vec<u32> = vec![0; n_us];
151
152 for src in 0..n {
153 let marker = src + 1;
154 added[src as usize] = marker;
155 let mut size: u32 = u32::from(mindist_i64 == 0);
156 queue.clear();
157 if effective_order > 0 {
158 queue.push_back((src, 0));
159 }
160
161 while let Some((actnode, actdist)) = queue.pop_front() {
162 let neis = neighbours_for(graph, actnode, mode, directed)?;
163 if actdist < effective_order - 1 {
164 for nei in neis {
165 if added[nei as usize] != marker {
166 added[nei as usize] = marker;
167 queue.push_back((nei, actdist + 1));
168 if actdist + 1 >= mindist_i64 {
169 size = size
170 .checked_add(1)
171 .ok_or(IgraphError::Internal("neighborhood size overflowed u32"))?;
172 }
173 }
174 }
175 } else {
176 for nei in neis {
178 if added[nei as usize] != marker {
179 added[nei as usize] = marker;
180 if actdist + 1 >= mindist_i64 {
181 size = size
182 .checked_add(1)
183 .ok_or(IgraphError::Internal("neighborhood size overflowed u32"))?;
184 }
185 }
186 }
187 }
188 }
189
190 result[src as usize] = size;
191 }
192
193 Ok(result)
194}
195
196pub fn neighborhood(graph: &Graph, order: i32) -> IgraphResult<Vec<Vec<u32>>> {
223 neighborhood_with_mode(graph, order, NeighborhoodMode::All, 0)
224}
225
226pub fn neighborhood_with_mode(
263 graph: &Graph,
264 order: i32,
265 mode: NeighborhoodMode,
266 mindist: i32,
267) -> IgraphResult<Vec<Vec<u32>>> {
268 if mindist < 0 {
269 return Err(IgraphError::InvalidArgument(format!(
270 "minimum distance must not be negative, got {mindist}"
271 )));
272 }
273 if order >= 0 && mindist > order {
274 return Err(IgraphError::InvalidArgument(format!(
275 "minimum distance must not exceed neighbourhood order ({order}), got {mindist}"
276 )));
277 }
278
279 let n = graph.vcount();
280 if n == 0 {
281 return Ok(Vec::new());
282 }
283 let n_us = n as usize;
284
285 let inf_order = order < 0;
286 let effective_order: i64 = if inf_order {
287 i64::from(n)
288 } else {
289 i64::from(order)
290 };
291 let mindist_i64 = i64::from(mindist);
292
293 let directed = graph.is_directed();
294 let mut added: Vec<u32> = vec![0; n_us];
295 let mut queue: VecDeque<(VertexId, i64)> = VecDeque::new();
296 let mut result: Vec<Vec<u32>> = Vec::with_capacity(n_us);
297
298 for src in 0..n {
299 let marker = src + 1;
300 added[src as usize] = marker;
301 let mut tmp: Vec<u32> = Vec::new();
302 if mindist_i64 == 0 {
303 tmp.push(src);
304 }
305 queue.clear();
306 if effective_order > 0 {
307 queue.push_back((src, 0));
308 }
309
310 while let Some((actnode, actdist)) = queue.pop_front() {
311 let neis = neighbours_for(graph, actnode, mode, directed)?;
312 if actdist < effective_order - 1 {
313 for nei in neis {
314 if added[nei as usize] != marker {
315 added[nei as usize] = marker;
316 queue.push_back((nei, actdist + 1));
317 if actdist + 1 >= mindist_i64 {
318 tmp.push(nei);
319 }
320 }
321 }
322 } else {
323 for nei in neis {
324 if added[nei as usize] != marker {
325 added[nei as usize] = marker;
326 if actdist + 1 >= mindist_i64 {
327 tmp.push(nei);
328 }
329 }
330 }
331 }
332 }
333
334 result.push(tmp);
335 }
336
337 Ok(result)
338}
339
340pub fn neighborhood_graphs(graph: &Graph, order: i32) -> IgraphResult<Vec<Graph>> {
374 neighborhood_graphs_with_mode(graph, order, NeighborhoodMode::All, 0)
375}
376
377pub fn neighborhood_graphs_with_mode(
411 graph: &Graph,
412 order: i32,
413 mode: NeighborhoodMode,
414 mindist: i32,
415) -> IgraphResult<Vec<Graph>> {
416 use crate::algorithms::operators::induced_subgraph::induced_subgraph;
417
418 let neighborhoods = neighborhood_with_mode(graph, order, mode, mindist)?;
419 let n = graph.vcount();
420 let mut result: Vec<Graph> = Vec::with_capacity(n as usize);
421
422 for vids in &neighborhoods {
423 if vids.len() == n as usize {
424 result.push(graph.clone());
425 } else {
426 let sub = induced_subgraph(graph, vids)?;
427 result.push(sub.graph);
428 }
429 }
430
431 Ok(result)
432}
433
434fn neighbours_for(
437 graph: &Graph,
438 v: VertexId,
439 mode: NeighborhoodMode,
440 directed: bool,
441) -> IgraphResult<Vec<VertexId>> {
442 if !directed {
443 return graph.neighbors(v);
444 }
445 match mode {
446 NeighborhoodMode::Out => graph.out_neighbors_vec(v),
447 NeighborhoodMode::In => graph.in_neighbors_vec(v),
448 NeighborhoodMode::All => {
449 let mut out = graph.out_neighbors_vec(v)?;
450 out.extend(graph.in_neighbors_vec(v)?);
451 Ok(out)
452 }
453 }
454}
455
456#[cfg(test)]
457mod tests {
458 use super::*;
459 use crate::Graph;
460
461 fn c_ref_graph() -> Graph {
465 let mut g = Graph::new(6, true).unwrap();
466 for (u, v) in [
467 (0, 1),
468 (0, 2),
469 (1, 1),
470 (1, 3),
471 (2, 0),
472 (2, 3),
473 (3, 4),
474 (3, 4),
475 ] {
476 g.add_edge(u, v).unwrap();
477 }
478 g
479 }
480
481 #[test]
482 fn empty_graph_returns_empty_vector() {
483 let g = Graph::with_vertices(0);
484 assert!(neighborhood_size(&g, 1).unwrap().is_empty());
485 }
486
487 #[test]
488 fn singleton_order_0_is_one() {
489 let g = Graph::with_vertices(1);
490 assert_eq!(neighborhood_size(&g, 0).unwrap(), vec![1]);
491 assert_eq!(neighborhood_size(&g, 5).unwrap(), vec![1]);
492 }
493
494 #[test]
495 fn no_edges_only_self_at_any_order() {
496 let g = Graph::with_vertices(4);
497 assert_eq!(neighborhood_size(&g, 0).unwrap(), vec![1, 1, 1, 1]);
498 assert_eq!(neighborhood_size(&g, 5).unwrap(), vec![1, 1, 1, 1]);
499 }
500
501 #[test]
502 fn ring_p5_matches_python_reference_order_1() {
503 let mut g = Graph::with_vertices(5);
506 for (u, v) in [(0, 1), (1, 2), (2, 3), (3, 4)] {
507 g.add_edge(u, v).unwrap();
508 }
509 assert_eq!(neighborhood_size(&g, 1).unwrap(), vec![2, 3, 3, 3, 2]);
510 }
511
512 #[test]
513 fn ring_p10_matches_python_order_1() {
514 let mut g = Graph::with_vertices(10);
515 for i in 0..9 {
516 g.add_edge(i, i + 1).unwrap();
517 }
518 assert_eq!(
519 neighborhood_size(&g, 1).unwrap(),
520 vec![2, 3, 3, 3, 3, 3, 3, 3, 3, 2]
521 );
522 }
523
524 #[test]
525 fn ring_p10_matches_python_order_3() {
526 let mut g = Graph::with_vertices(10);
527 for i in 0..9 {
528 g.add_edge(i, i + 1).unwrap();
529 }
530 assert_eq!(
531 neighborhood_size(&g, 3).unwrap(),
532 vec![4, 5, 6, 7, 7, 7, 7, 6, 5, 4]
533 );
534 }
535
536 #[test]
537 fn ring_p10_order_3_mindist_2_matches_python() {
538 let mut g = Graph::with_vertices(10);
539 for i in 0..9 {
540 g.add_edge(i, i + 1).unwrap();
541 }
542 assert_eq!(
543 neighborhood_size_with_mode(&g, 3, NeighborhoodMode::All, 2).unwrap(),
544 vec![2, 2, 3, 4, 4, 4, 4, 3, 2, 2]
545 );
546 }
547
548 #[test]
549 fn c_ref_order_0_is_self_only() {
550 let g = c_ref_graph();
551 assert_eq!(neighborhood_size(&g, 0).unwrap(), vec![1, 1, 1, 1, 1, 1]);
553 }
554
555 #[test]
556 fn c_ref_order_1_all_mode() {
557 let g = c_ref_graph();
558 assert_eq!(neighborhood_size(&g, 1).unwrap(), vec![3, 3, 3, 4, 2, 1]);
560 }
561
562 #[test]
563 fn c_ref_order_1_in_mode() {
564 let g = c_ref_graph();
565 assert_eq!(
567 neighborhood_size_with_mode(&g, 1, NeighborhoodMode::In, 0).unwrap(),
568 vec![2, 2, 2, 3, 2, 1]
569 );
570 }
571
572 #[test]
573 fn c_ref_order_10_all_mode_saturates() {
574 let g = c_ref_graph();
575 assert_eq!(neighborhood_size(&g, 10).unwrap(), vec![5, 5, 5, 5, 5, 1]);
577 }
578
579 #[test]
580 fn c_ref_order_2_mindist_2_out_mode() {
581 let g = c_ref_graph();
582 assert_eq!(
584 neighborhood_size_with_mode(&g, 2, NeighborhoodMode::Out, 2).unwrap(),
585 vec![1, 1, 2, 0, 0, 0]
586 );
587 }
588
589 #[test]
590 fn c_ref_order_4_mindist_4_all_mode_all_zero() {
591 let g = c_ref_graph();
592 assert_eq!(
594 neighborhood_size_with_mode(&g, 4, NeighborhoodMode::All, 4).unwrap(),
595 vec![0, 0, 0, 0, 0, 0]
596 );
597 }
598
599 #[test]
600 fn c_ref_infinite_order_out_mode() {
601 let g = c_ref_graph();
602 assert_eq!(
604 neighborhood_size_with_mode(&g, -1, NeighborhoodMode::Out, 0).unwrap(),
605 vec![5, 3, 5, 2, 1, 1]
606 );
607 }
608
609 #[test]
610 fn c_ref_infinite_order_mindist_2_out_mode() {
611 let g = c_ref_graph();
612 assert_eq!(
614 neighborhood_size_with_mode(&g, -1, NeighborhoodMode::Out, 2).unwrap(),
615 vec![2, 1, 2, 0, 0, 0]
616 );
617 }
618
619 #[test]
620 fn c_ref_infinite_order_mindist_2_in_mode() {
621 let g = c_ref_graph();
622 assert_eq!(
624 neighborhood_size_with_mode(&g, -1, NeighborhoodMode::In, 2).unwrap(),
625 vec![0, 1, 0, 1, 3, 0]
626 );
627 }
628
629 #[test]
630 fn negative_mindist_errors() {
631 let g = Graph::with_vertices(3);
632 match neighborhood_size_with_mode(&g, 2, NeighborhoodMode::All, -1) {
633 Err(IgraphError::InvalidArgument(msg)) => assert!(msg.contains("negative")),
634 other => panic!("expected InvalidArgument for negative mindist, got {other:?}"),
635 }
636 }
637
638 #[test]
639 fn mindist_exceeding_finite_order_errors() {
640 let g = Graph::with_vertices(3);
641 match neighborhood_size_with_mode(&g, 2, NeighborhoodMode::All, 3) {
642 Err(IgraphError::InvalidArgument(msg)) => assert!(msg.contains("exceed")),
643 other => panic!("expected InvalidArgument for mindist > order, got {other:?}"),
644 }
645 }
646
647 #[test]
648 fn infinite_order_allows_any_mindist() {
649 let mut g = Graph::with_vertices(3);
651 g.add_edge(0, 1).unwrap();
652 g.add_edge(1, 2).unwrap();
653 assert_eq!(
655 neighborhood_size_with_mode(&g, -1, NeighborhoodMode::All, 10).unwrap(),
656 vec![0, 0, 0]
657 );
658 }
659
660 #[test]
661 fn k4_complete_undirected_order_1() {
662 let mut g = Graph::with_vertices(4);
663 for u in 0..4 {
664 for v in (u + 1)..4 {
665 g.add_edge(u, v).unwrap();
666 }
667 }
668 assert_eq!(neighborhood_size(&g, 1).unwrap(), vec![4, 4, 4, 4]);
670 }
671
672 #[test]
673 fn directed_star_out_in_modes() {
674 let mut g = Graph::new(4, true).unwrap();
676 for v in [1, 2, 3] {
677 g.add_edge(0, v).unwrap();
678 }
679 assert_eq!(
681 neighborhood_size_with_mode(&g, -1, NeighborhoodMode::Out, 0).unwrap(),
682 vec![4, 1, 1, 1]
683 );
684 assert_eq!(
686 neighborhood_size_with_mode(&g, -1, NeighborhoodMode::In, 0).unwrap(),
687 vec![1, 2, 2, 2]
688 );
689 assert_eq!(
691 neighborhood_size_with_mode(&g, -1, NeighborhoodMode::All, 0).unwrap(),
692 vec![4, 4, 4, 4]
693 );
694 }
695
696 #[test]
697 fn self_loop_does_not_inflate_count() {
698 let mut g = Graph::with_vertices(3);
699 g.add_edge(0, 0).unwrap();
700 g.add_edge(0, 1).unwrap();
701 g.add_edge(1, 2).unwrap();
702 assert_eq!(neighborhood_size(&g, 1).unwrap(), vec![2, 3, 2]);
704 }
705
706 #[test]
707 fn multi_edge_does_not_double_count() {
708 let mut g = Graph::with_vertices(3);
709 g.add_edge(0, 1).unwrap();
710 g.add_edge(0, 1).unwrap();
711 g.add_edge(1, 2).unwrap();
712 assert_eq!(neighborhood_size(&g, 1).unwrap(), vec![2, 3, 2]);
713 }
714
715 #[test]
716 fn mindist_equals_order_counts_frontier_only() {
717 let mut g = Graph::with_vertices(5);
719 for (u, v) in [(0, 1), (1, 2), (2, 3), (3, 4)] {
720 g.add_edge(u, v).unwrap();
721 }
722 assert_eq!(
724 neighborhood_size_with_mode(&g, 2, NeighborhoodMode::All, 2).unwrap(),
725 vec![1, 1, 2, 1, 1]
726 );
727 }
728
729 fn sorted(mut v: Vec<u32>) -> Vec<u32> {
737 v.sort_unstable();
738 v
739 }
740
741 fn sorted_all(nbh: Vec<Vec<u32>>) -> Vec<Vec<u32>> {
742 nbh.into_iter().map(sorted).collect()
743 }
744
745 #[test]
746 fn neighborhood_empty_graph_returns_empty() {
747 let g = Graph::with_vertices(0);
748 assert!(neighborhood(&g, 1).unwrap().is_empty());
749 }
750
751 #[test]
752 fn neighborhood_singleton_order_0_is_self_only() {
753 let g = Graph::with_vertices(1);
754 assert_eq!(neighborhood(&g, 0).unwrap(), vec![vec![0]]);
755 assert_eq!(neighborhood(&g, 5).unwrap(), vec![vec![0]]);
756 }
757
758 #[test]
759 fn neighborhood_no_edges_returns_singletons() {
760 let g = Graph::with_vertices(3);
761 let n0 = neighborhood(&g, 0).unwrap();
762 assert_eq!(n0, vec![vec![0], vec![1], vec![2]]);
763 let n5 = neighborhood(&g, 5).unwrap();
764 assert_eq!(n5, vec![vec![0], vec![1], vec![2]]);
765 }
766
767 #[test]
768 fn neighborhood_p5_order_1_set_eq() {
769 let mut g = Graph::with_vertices(5);
771 for (u, v) in [(0, 1), (1, 2), (2, 3), (3, 4)] {
772 g.add_edge(u, v).unwrap();
773 }
774 let got = sorted_all(neighborhood(&g, 1).unwrap());
775 let exp: Vec<Vec<u32>> = vec![
776 vec![0, 1],
777 vec![0, 1, 2],
778 vec![1, 2, 3],
779 vec![2, 3, 4],
780 vec![3, 4],
781 ];
782 assert_eq!(got, exp);
783 }
784
785 #[test]
786 fn neighborhood_p5_order_2_set_eq() {
787 let mut g = Graph::with_vertices(5);
788 for (u, v) in [(0, 1), (1, 2), (2, 3), (3, 4)] {
789 g.add_edge(u, v).unwrap();
790 }
791 let got = sorted_all(neighborhood(&g, 2).unwrap());
792 let exp: Vec<Vec<u32>> = vec![
793 vec![0, 1, 2],
794 vec![0, 1, 2, 3],
795 vec![0, 1, 2, 3, 4],
796 vec![1, 2, 3, 4],
797 vec![2, 3, 4],
798 ];
799 assert_eq!(got, exp);
800 }
801
802 #[test]
803 fn neighborhood_c_ref_order_0_is_self_only() {
804 let g = c_ref_graph();
806 let got = neighborhood(&g, 0).unwrap();
807 let exp: Vec<Vec<u32>> = (0..6).map(|i| vec![i]).collect();
808 assert_eq!(got, exp);
809 }
810
811 #[test]
812 fn neighborhood_c_ref_order_1_all_set_eq() {
813 let g = c_ref_graph();
815 let got = sorted_all(neighborhood(&g, 1).unwrap());
816 let exp: Vec<Vec<u32>> = vec![
817 vec![0, 1, 2],
818 vec![0, 1, 3],
819 vec![0, 2, 3],
820 vec![1, 2, 3, 4],
821 vec![3, 4],
822 vec![5],
823 ];
824 assert_eq!(got, exp);
825 }
826
827 #[test]
828 fn neighborhood_c_ref_order_1_in_set_eq() {
829 let g = c_ref_graph();
831 let got = sorted_all(neighborhood_with_mode(&g, 1, NeighborhoodMode::In, 0).unwrap());
832 let exp: Vec<Vec<u32>> = vec![
833 vec![0, 2],
834 vec![0, 1],
835 vec![0, 2],
836 vec![1, 2, 3],
837 vec![3, 4],
838 vec![5],
839 ];
840 assert_eq!(got, exp);
841 }
842
843 #[test]
844 fn neighborhood_c_ref_order_10_all_saturates_set_eq() {
845 let g = c_ref_graph();
848 let got = sorted_all(neighborhood(&g, 10).unwrap());
849 let big: Vec<u32> = (0..5).collect();
850 let exp: Vec<Vec<u32>> = vec![
851 big.clone(),
852 big.clone(),
853 big.clone(),
854 big.clone(),
855 big,
856 vec![5],
857 ];
858 assert_eq!(got, exp);
859 }
860
861 #[test]
862 fn neighborhood_c_ref_order_2_mindist_2_out_set_eq() {
863 let g = c_ref_graph();
865 let got = sorted_all(neighborhood_with_mode(&g, 2, NeighborhoodMode::Out, 2).unwrap());
866 let exp: Vec<Vec<u32>> = vec![vec![3], vec![4], vec![1, 4], vec![], vec![], vec![]];
867 assert_eq!(got, exp);
868 }
869
870 #[test]
871 fn neighborhood_c_ref_order_4_mindist_4_all_empty() {
872 let g = c_ref_graph();
874 let got = neighborhood_with_mode(&g, 4, NeighborhoodMode::All, 4).unwrap();
875 let exp: Vec<Vec<u32>> = vec![vec![]; 6];
876 assert_eq!(got, exp);
877 }
878
879 #[test]
880 fn neighborhood_c_ref_infinite_order_out_set_eq() {
881 let g = c_ref_graph();
883 let got = sorted_all(neighborhood_with_mode(&g, -1, NeighborhoodMode::Out, 0).unwrap());
884 let exp: Vec<Vec<u32>> = vec![
885 vec![0, 1, 2, 3, 4],
886 vec![1, 3, 4],
887 vec![0, 1, 2, 3, 4],
888 vec![3, 4],
889 vec![4],
890 vec![5],
891 ];
892 assert_eq!(got, exp);
893 }
894
895 #[test]
896 fn neighborhood_c_ref_infinite_mindist_2_out_set_eq() {
897 let g = c_ref_graph();
899 let got = sorted_all(neighborhood_with_mode(&g, -1, NeighborhoodMode::Out, 2).unwrap());
900 let exp: Vec<Vec<u32>> = vec![vec![3, 4], vec![4], vec![1, 4], vec![], vec![], vec![]];
901 assert_eq!(got, exp);
902 }
903
904 #[test]
905 fn neighborhood_c_ref_infinite_mindist_2_in_set_eq() {
906 let g = c_ref_graph();
908 let got = sorted_all(neighborhood_with_mode(&g, -1, NeighborhoodMode::In, 2).unwrap());
909 let exp: Vec<Vec<u32>> = vec![vec![], vec![2], vec![], vec![0], vec![0, 1, 2], vec![]];
910 assert_eq!(got, exp);
911 }
912
913 #[test]
914 fn neighborhood_negative_mindist_errors() {
915 let g = Graph::with_vertices(3);
916 match neighborhood_with_mode(&g, 2, NeighborhoodMode::All, -1) {
917 Err(IgraphError::InvalidArgument(msg)) => assert!(msg.contains("negative")),
918 other => panic!("expected InvalidArgument for negative mindist, got {other:?}"),
919 }
920 }
921
922 #[test]
923 fn neighborhood_mindist_exceeds_order_errors() {
924 let g = Graph::with_vertices(3);
925 match neighborhood_with_mode(&g, 2, NeighborhoodMode::All, 3) {
926 Err(IgraphError::InvalidArgument(msg)) => assert!(msg.contains("exceed")),
927 other => panic!("expected InvalidArgument, got {other:?}"),
928 }
929 }
930
931 #[test]
932 fn neighborhood_lengths_match_neighborhood_size() {
933 let g = c_ref_graph();
936 for order in [0_i32, 1, 2, 3, 10, -1] {
937 for &mode in &[
938 NeighborhoodMode::Out,
939 NeighborhoodMode::In,
940 NeighborhoodMode::All,
941 ] {
942 for mindist in [0_i32, 1, 2] {
943 if order >= 0 && mindist > order {
944 continue;
945 }
946 let sizes = neighborhood_size_with_mode(&g, order, mode, mindist).unwrap();
947 let lists = neighborhood_with_mode(&g, order, mode, mindist).unwrap();
948 let list_lens: Vec<u32> = lists
949 .iter()
950 .map(|v| u32::try_from(v.len()).unwrap())
951 .collect();
952 assert_eq!(
953 sizes, list_lens,
954 "size/list-len mismatch at order={order} mode={mode:?} mindist={mindist}",
955 );
956 }
957 }
958 }
959 }
960
961 #[test]
962 fn neighborhood_self_loop_not_double_visited() {
963 let mut g = Graph::with_vertices(3);
964 g.add_edge(0, 0).unwrap();
965 g.add_edge(0, 1).unwrap();
966 g.add_edge(1, 2).unwrap();
967 let got = sorted_all(neighborhood(&g, 1).unwrap());
968 assert_eq!(got, vec![vec![0, 1], vec![0, 1, 2], vec![1, 2]]);
969 }
970
971 #[test]
972 fn neighborhood_multi_edge_not_double_visited() {
973 let mut g = Graph::with_vertices(3);
974 g.add_edge(0, 1).unwrap();
975 g.add_edge(0, 1).unwrap();
976 g.add_edge(1, 2).unwrap();
977 let got = sorted_all(neighborhood(&g, 1).unwrap());
978 assert_eq!(got, vec![vec![0, 1], vec![0, 1, 2], vec![1, 2]]);
979 }
980
981 #[test]
982 fn neighborhood_mindist_1_excludes_self() {
983 let mut g = Graph::with_vertices(5);
985 for (u, v) in [(0, 1), (1, 2), (2, 3), (3, 4)] {
986 g.add_edge(u, v).unwrap();
987 }
988 let got = sorted_all(neighborhood_with_mode(&g, 1, NeighborhoodMode::All, 1).unwrap());
989 assert_eq!(
990 got,
991 vec![vec![1], vec![0, 2], vec![1, 3], vec![2, 4], vec![3]]
992 );
993 for list in &got {
994 for (i, neighbors) in got.iter().enumerate() {
995 let i_u32 = u32::try_from(i).unwrap();
996 if std::ptr::eq(list, neighbors) {
997 assert!(!list.contains(&i_u32), "mindist=1 should exclude self");
998 }
999 }
1000 }
1001 }
1002
1003 #[test]
1006 fn neighborhood_graphs_empty_graph() {
1007 let g = Graph::with_vertices(0);
1008 let gs = neighborhood_graphs(&g, 1).unwrap();
1009 assert!(gs.is_empty());
1010 }
1011
1012 #[test]
1013 fn neighborhood_graphs_isolated_vertices() {
1014 let g = Graph::with_vertices(3);
1015 let gs = neighborhood_graphs(&g, 1).unwrap();
1016 assert_eq!(gs.len(), 3);
1017 for sub in &gs {
1018 assert_eq!(sub.vcount(), 1);
1019 assert_eq!(sub.ecount(), 0);
1020 }
1021 }
1022
1023 #[test]
1024 fn neighborhood_graphs_path_order_1() {
1025 let mut g = Graph::with_vertices(4);
1027 g.add_edge(0, 1).unwrap();
1028 g.add_edge(1, 2).unwrap();
1029 g.add_edge(2, 3).unwrap();
1030
1031 let gs = neighborhood_graphs(&g, 1).unwrap();
1032 assert_eq!(gs.len(), 4);
1033 assert_eq!(gs[0].vcount(), 2);
1035 assert_eq!(gs[0].ecount(), 1);
1036 assert_eq!(gs[1].vcount(), 3);
1038 assert_eq!(gs[1].ecount(), 2);
1039 assert_eq!(gs[2].vcount(), 3);
1041 assert_eq!(gs[2].ecount(), 2);
1042 assert_eq!(gs[3].vcount(), 2);
1044 assert_eq!(gs[3].ecount(), 1);
1045 }
1046
1047 #[test]
1048 fn neighborhood_graphs_order_0_is_singletons() {
1049 let mut g = Graph::with_vertices(3);
1050 g.add_edge(0, 1).unwrap();
1051 g.add_edge(1, 2).unwrap();
1052
1053 let gs = neighborhood_graphs(&g, 0).unwrap();
1054 assert_eq!(gs.len(), 3);
1055 for sub in &gs {
1056 assert_eq!(sub.vcount(), 1);
1057 assert_eq!(sub.ecount(), 0);
1058 }
1059 }
1060
1061 #[test]
1062 fn neighborhood_graphs_complete_graph_order_1() {
1063 let mut g = Graph::with_vertices(4);
1065 g.add_edge(0, 1).unwrap();
1066 g.add_edge(0, 2).unwrap();
1067 g.add_edge(0, 3).unwrap();
1068 g.add_edge(1, 2).unwrap();
1069 g.add_edge(1, 3).unwrap();
1070 g.add_edge(2, 3).unwrap();
1071
1072 let gs = neighborhood_graphs(&g, 1).unwrap();
1073 assert_eq!(gs.len(), 4);
1074 for sub in &gs {
1075 assert_eq!(sub.vcount(), 4);
1077 assert_eq!(sub.ecount(), 6);
1078 }
1079 }
1080
1081 #[test]
1082 fn neighborhood_graphs_directed_out_mode() {
1083 let mut g = Graph::new(4, true).unwrap();
1085 g.add_edge(0, 1).unwrap();
1086 g.add_edge(0, 2).unwrap();
1087 g.add_edge(0, 3).unwrap();
1088
1089 let gs = neighborhood_graphs_with_mode(&g, 1, NeighborhoodMode::Out, 0).unwrap();
1090 assert_eq!(gs.len(), 4);
1091 assert_eq!(gs[0].vcount(), 4);
1093 assert_eq!(gs[0].ecount(), 3);
1094 assert_eq!(gs[1].vcount(), 1);
1096 assert_eq!(gs[1].ecount(), 0);
1097 }
1098
1099 #[test]
1100 fn neighborhood_graphs_directed_in_mode() {
1101 let mut g = Graph::new(4, true).unwrap();
1103 g.add_edge(0, 1).unwrap();
1104 g.add_edge(0, 2).unwrap();
1105 g.add_edge(0, 3).unwrap();
1106
1107 let gs = neighborhood_graphs_with_mode(&g, 1, NeighborhoodMode::In, 0).unwrap();
1108 assert_eq!(gs[0].vcount(), 1);
1110 assert_eq!(gs[0].ecount(), 0);
1111 assert_eq!(gs[1].vcount(), 2);
1113 assert_eq!(gs[1].ecount(), 1);
1114 }
1115
1116 #[test]
1117 fn neighborhood_graphs_mindist_excludes_close_vertices() {
1118 let mut g = Graph::with_vertices(4);
1120 g.add_edge(0, 1).unwrap();
1121 g.add_edge(1, 2).unwrap();
1122 g.add_edge(2, 3).unwrap();
1123
1124 let gs = neighborhood_graphs_with_mode(&g, 2, NeighborhoodMode::All, 1).unwrap();
1126 assert_eq!(gs[0].vcount(), 2);
1128 assert_eq!(gs[0].ecount(), 1);
1129 assert_eq!(gs[1].vcount(), 3);
1133 assert_eq!(gs[1].ecount(), 1);
1134 }
1135
1136 #[test]
1137 fn neighborhood_graphs_infinite_order_returns_full_component() {
1138 let mut g = Graph::with_vertices(5);
1140 g.add_edge(0, 1).unwrap();
1141 g.add_edge(1, 2).unwrap();
1142 g.add_edge(3, 4).unwrap();
1143
1144 let gs = neighborhood_graphs(&g, -1).unwrap();
1145 assert_eq!(gs[0].vcount(), 3);
1147 assert_eq!(gs[0].ecount(), 2);
1148 assert_eq!(gs[3].vcount(), 2);
1150 assert_eq!(gs[3].ecount(), 1);
1151 }
1152
1153 #[test]
1154 fn neighborhood_graphs_preserves_directedness() {
1155 let mut g = Graph::new(3, true).unwrap();
1156 g.add_edge(0, 1).unwrap();
1157 g.add_edge(1, 2).unwrap();
1158
1159 let gs = neighborhood_graphs_with_mode(&g, 1, NeighborhoodMode::All, 0).unwrap();
1160 for sub in &gs {
1161 assert!(sub.is_directed());
1162 }
1163 }
1164}