1use super::error::{IgraphError, IgraphResult};
31
32pub type VertexId = u32;
35
36pub type EdgeId = u32;
39
40#[derive(Debug, Clone, Default)]
48pub struct Graph {
49 n: u32,
51 directed: bool,
53 from: Vec<VertexId>,
55 to: Vec<VertexId>,
57 oi: Vec<EdgeId>,
59 ii: Vec<EdgeId>,
61 os: Vec<u32>,
64 is: Vec<u32>,
66}
67
68impl Graph {
69 pub fn new(n: u32, directed: bool) -> IgraphResult<Self> {
74 let mut g = Self {
75 n: 0,
76 directed,
77 from: Vec::new(),
78 to: Vec::new(),
79 oi: Vec::new(),
80 ii: Vec::new(),
81 os: vec![0],
82 is: vec![0],
83 };
84 g.add_vertices(n)?;
85 Ok(g)
86 }
87
88 pub fn with_vertices(n: u32) -> Self {
93 Self::new(n, false).expect("with_vertices: n cannot exceed u32::MAX")
94 }
95
96 #[must_use]
98 pub fn vcount(&self) -> u32 {
99 self.n
100 }
101
102 #[must_use]
104 pub fn ecount(&self) -> usize {
105 self.from.len()
106 }
107
108 #[must_use]
110 pub fn is_directed(&self) -> bool {
111 self.directed
112 }
113
114 pub fn add_vertices(&mut self, nv: u32) -> IgraphResult<(VertexId, VertexId)> {
120 let new_n = self
121 .n
122 .checked_add(nv)
123 .ok_or(IgraphError::Internal("vertex count overflow"))?;
124 let first = self.n;
125 let ec = u32::try_from(self.ecount())
127 .map_err(|_| IgraphError::Internal("edge count exceeds u32::MAX"))?;
128 for _ in 0..nv {
129 self.os.push(ec);
130 self.is.push(ec);
131 }
132 self.n = new_n;
133 Ok((first, new_n.saturating_sub(1)))
134 }
135
136 pub fn add_edge(&mut self, u: VertexId, v: VertexId) -> IgraphResult<()> {
141 self.add_edges(std::iter::once((u, v)))
142 }
143
144 pub fn add_edges<I>(&mut self, edges: I) -> IgraphResult<()>
148 where
149 I: IntoIterator<Item = (VertexId, VertexId)>,
150 {
151 for (u, v) in edges {
152 self.check_vertex(u)?;
153 self.check_vertex(v)?;
154 if !self.directed && u > v {
156 self.from.push(v);
157 self.to.push(u);
158 } else {
159 self.from.push(u);
160 self.to.push(v);
161 }
162 }
163 self.rebuild_indexes()?;
164 Ok(())
165 }
166
167 pub fn neighbors(&self, v: VertexId) -> IgraphResult<Vec<VertexId>> {
176 self.check_vertex(v)?;
177 let v_idx = v as usize;
178 if self.directed {
179 let out_range = self.os[v_idx] as usize..self.os[v_idx + 1] as usize;
182 let out: Vec<VertexId> = self.oi[out_range]
183 .iter()
184 .map(|&e| self.to[e as usize])
185 .collect();
186 Ok(out)
187 } else {
188 let out_start = self.os[v_idx] as usize;
194 let out_end = self.os[v_idx + 1] as usize;
195 let in_start = self.is[v_idx] as usize;
196 let in_end = self.is[v_idx + 1] as usize;
197 let mut out = Vec::with_capacity((out_end - out_start) + (in_end - in_start));
198 let mut out_idx = out_start;
199 let mut in_idx = in_start;
200 while out_idx < out_end && in_idx < in_end {
201 let a = self.to[self.oi[out_idx] as usize];
202 let b = self.from[self.ii[in_idx] as usize];
203 if a <= b {
204 out.push(a);
205 out_idx += 1;
206 } else {
207 out.push(b);
208 in_idx += 1;
209 }
210 }
211 while out_idx < out_end {
212 out.push(self.to[self.oi[out_idx] as usize]);
213 out_idx += 1;
214 }
215 while in_idx < in_end {
216 out.push(self.from[self.ii[in_idx] as usize]);
217 in_idx += 1;
218 }
219 Ok(out)
220 }
221 }
222
223 pub fn degree(&self, v: VertexId) -> IgraphResult<usize> {
231 self.check_vertex(v)?;
232 let v_idx = v as usize;
233 let out = (self.os[v_idx + 1] - self.os[v_idx]) as usize;
234 if self.directed {
235 let in_count = (self.is[v_idx + 1] - self.is[v_idx]) as usize;
236 Ok(out + in_count)
237 } else {
238 let in_count = (self.is[v_idx + 1] - self.is[v_idx]) as usize;
242 Ok(out + in_count)
243 }
244 }
245
246 pub fn edge_source(&self, eid: EdgeId) -> IgraphResult<VertexId> {
253 self.check_edge(eid)?;
254 Ok(self.from[eid as usize])
255 }
256
257 pub fn edge_target(&self, eid: EdgeId) -> IgraphResult<VertexId> {
260 self.check_edge(eid)?;
261 Ok(self.to[eid as usize])
262 }
263
264 pub fn edge(&self, eid: EdgeId) -> IgraphResult<(VertexId, VertexId)> {
267 self.check_edge(eid)?;
268 let i = eid as usize;
269 Ok((self.from[i], self.to[i]))
270 }
271
272 pub fn edge_other(&self, eid: EdgeId, vid: VertexId) -> IgraphResult<VertexId> {
276 let (u, v) = self.edge(eid)?;
277 if vid == u {
278 Ok(v)
279 } else if vid == v {
280 Ok(u)
281 } else {
282 Err(IgraphError::InvalidArgument(format!(
283 "vertex {vid} is not an endpoint of edge {eid} ({u}, {v})"
284 )))
285 }
286 }
287
288 pub fn incident(&self, v: VertexId) -> IgraphResult<Vec<EdgeId>> {
304 self.check_vertex(v)?;
305 let v_idx = v as usize;
306 let out_range = self.os[v_idx] as usize..self.os[v_idx + 1] as usize;
307 if self.directed {
308 Ok(self.oi[out_range].to_vec())
309 } else {
310 let in_range = self.is[v_idx] as usize..self.is[v_idx + 1] as usize;
311 let mut out = Vec::with_capacity(out_range.len() + in_range.len());
312 out.extend_from_slice(&self.oi[out_range]);
313 out.extend_from_slice(&self.ii[in_range]);
314 Ok(out)
315 }
316 }
317
318 pub(crate) fn incident_in(&self, v: VertexId) -> IgraphResult<Vec<EdgeId>> {
325 self.check_vertex(v)?;
326 let v_idx = v as usize;
327 if self.directed {
328 let in_range = self.is[v_idx] as usize..self.is[v_idx + 1] as usize;
329 Ok(self.ii[in_range].to_vec())
330 } else {
331 self.incident(v)
332 }
333 }
334
335 pub fn get_eid(&self, from: VertexId, to: VertexId) -> IgraphResult<EdgeId> {
349 self.find_eid(from, to)?
350 .ok_or_else(|| IgraphError::InvalidArgument(format!("no edge between {from} and {to}")))
351 }
352
353 pub fn find_eid(&self, from: VertexId, to: VertexId) -> IgraphResult<Option<EdgeId>> {
360 self.check_vertex(from)?;
361 self.check_vertex(to)?;
362 if self.directed {
363 let range = self.os[from as usize] as usize..self.os[from as usize + 1] as usize;
365 for &e in &self.oi[range] {
366 if self.to[e as usize] == to {
367 return Ok(Some(e));
368 }
369 }
370 Ok(None)
371 } else {
372 let (lo, hi) = if from <= to { (from, to) } else { (to, from) };
375 let range = self.os[lo as usize] as usize..self.os[lo as usize + 1] as usize;
376 for &e in &self.oi[range] {
377 if self.to[e as usize] == hi {
378 return Ok(Some(e));
379 }
380 }
381 Ok(None)
382 }
383 }
384
385 pub fn get_all_eids_between(&self, from: VertexId, to: VertexId) -> IgraphResult<Vec<EdgeId>> {
394 self.check_vertex(from)?;
395 self.check_vertex(to)?;
396 let mut out = Vec::new();
397 if self.directed {
398 let range = self.os[from as usize] as usize..self.os[from as usize + 1] as usize;
399 for &e in &self.oi[range] {
400 if self.to[e as usize] == to {
401 out.push(e);
402 }
403 }
404 } else {
405 let (lo, hi) = if from <= to { (from, to) } else { (to, from) };
406 let range = self.os[lo as usize] as usize..self.os[lo as usize + 1] as usize;
407 for &e in &self.oi[range] {
408 if self.to[e as usize] == hi {
409 out.push(e);
410 }
411 }
412 }
413 out.sort_unstable();
414 Ok(out)
415 }
416
417 pub(crate) fn out_neighbors_vec(&self, v: VertexId) -> IgraphResult<Vec<VertexId>> {
425 self.check_vertex(v)?;
426 let v_idx = v as usize;
427 let range = self.os[v_idx] as usize..self.os[v_idx + 1] as usize;
428 Ok(self.oi[range]
429 .iter()
430 .map(|&e| self.to[e as usize])
431 .collect())
432 }
433
434 pub(crate) fn in_neighbors_vec(&self, v: VertexId) -> IgraphResult<Vec<VertexId>> {
441 self.check_vertex(v)?;
442 let v_idx = v as usize;
443 let range = self.is[v_idx] as usize..self.is[v_idx + 1] as usize;
444 Ok(self.ii[range]
445 .iter()
446 .map(|&e| self.from[e as usize])
447 .collect())
448 }
449
450 pub fn delete_edges(&mut self, edges: &[EdgeId]) -> IgraphResult<()> {
466 let m = self.ecount();
467 let m_u32 = u32::try_from(m).unwrap_or(u32::MAX);
468
469 for &eid in edges {
471 if (eid as usize) >= m {
472 return Err(IgraphError::EdgeOutOfRange { id: eid, m: m_u32 });
473 }
474 }
475 if edges.is_empty() {
476 return Ok(());
477 }
478
479 let mut remove = vec![false; m];
480 for &eid in edges {
481 remove[eid as usize] = true;
482 }
483
484 let mut new_from: Vec<VertexId> = Vec::with_capacity(m);
485 let mut new_to: Vec<VertexId> = Vec::with_capacity(m);
486 for (e, &is_removed) in remove.iter().enumerate() {
487 if !is_removed {
488 new_from.push(self.from[e]);
489 new_to.push(self.to[e]);
490 }
491 }
492 self.from = new_from;
493 self.to = new_to;
494 self.rebuild_indexes()
495 }
496
497 pub fn delete_vertices(&mut self, vertices: &[VertexId]) -> IgraphResult<()> {
508 self.delete_vertices_map(vertices).map(|_| ())
509 }
510
511 pub fn delete_vertices_map(
522 &mut self,
523 vertices: &[VertexId],
524 ) -> IgraphResult<(Vec<Option<VertexId>>, Vec<VertexId>)> {
525 let n_u32 = self.n;
526 let n = n_u32 as usize;
527
528 for &vid in vertices {
530 if vid >= n_u32 {
531 return Err(IgraphError::VertexOutOfRange { id: vid, n: n_u32 });
532 }
533 }
534
535 let mut remove = vec![false; n];
536 for &vid in vertices {
537 remove[vid as usize] = true;
538 }
539
540 let mut map: Vec<Option<VertexId>> = vec![None; n];
542 let mut invmap: Vec<VertexId> = Vec::new();
543 let mut next_new: u32 = 0;
544 for (i, &is_removed) in remove.iter().enumerate() {
545 if !is_removed {
546 let i_u32 = u32::try_from(i)
547 .map_err(|_| IgraphError::Internal("vertex index exceeds u32::MAX"))?;
548 map[i] = Some(next_new);
549 invmap.push(i_u32);
550 next_new = next_new
551 .checked_add(1)
552 .ok_or(IgraphError::Internal("new vertex count overflow"))?;
553 }
554 }
555
556 let m = self.ecount();
559 let mut new_from: Vec<VertexId> = Vec::with_capacity(m);
560 let mut new_to: Vec<VertexId> = Vec::with_capacity(m);
561 for (u, v) in self.from.iter().zip(self.to.iter()) {
562 if let (Some(nu), Some(nv)) = (map[*u as usize], map[*v as usize]) {
563 new_from.push(nu);
564 new_to.push(nv);
565 }
566 }
567
568 self.n = next_new;
569 self.from = new_from;
570 self.to = new_to;
571 self.rebuild_indexes()?;
572
573 Ok((map, invmap))
574 }
575
576 fn check_vertex(&self, v: VertexId) -> IgraphResult<()> {
577 if v >= self.n {
578 return Err(IgraphError::VertexOutOfRange { id: v, n: self.n });
579 }
580 Ok(())
581 }
582
583 fn check_edge(&self, eid: EdgeId) -> IgraphResult<()> {
584 let m = self.ecount();
585 let m_u32 = u32::try_from(m).unwrap_or(u32::MAX);
586 if (eid as usize) >= m {
587 return Err(IgraphError::EdgeOutOfRange { id: eid, m: m_u32 });
588 }
589 Ok(())
590 }
591
592 fn rebuild_indexes(&mut self) -> IgraphResult<()> {
610 let m = self.ecount();
611 let n = self.n as usize;
612
613 let mut tuples: Vec<(VertexId, VertexId, u32)> = (0..m)
619 .map(|e| {
620 Ok::<_, IgraphError>((
621 self.from[e],
622 self.to[e],
623 u32::try_from(e)
624 .map_err(|_| IgraphError::Internal("edge id exceeds u32::MAX"))?,
625 ))
626 })
627 .collect::<Result<_, _>>()?;
628 tuples.sort_unstable_by_key(|a| (a.0, a.1));
629 self.oi = tuples.iter().map(|t| t.2).collect();
630 self.os = vec![0u32; n + 1];
632 for &(u, _, _) in &tuples {
633 self.os[u as usize + 1] = self.os[u as usize + 1]
634 .checked_add(1)
635 .ok_or(IgraphError::Internal("degree overflow in rebuild_indexes"))?;
636 }
637 for i in 1..=n {
638 self.os[i] = self.os[i]
639 .checked_add(self.os[i - 1])
640 .ok_or(IgraphError::Internal("offset overflow in rebuild_indexes"))?;
641 }
642
643 let mut tuples: Vec<(VertexId, VertexId, u32)> = (0..m)
645 .map(|e| {
646 Ok::<_, IgraphError>((
647 self.to[e],
648 self.from[e],
649 u32::try_from(e)
650 .map_err(|_| IgraphError::Internal("edge id exceeds u32::MAX"))?,
651 ))
652 })
653 .collect::<Result<_, _>>()?;
654 tuples.sort_unstable_by_key(|a| (a.0, a.1));
655 self.ii = tuples.iter().map(|t| t.2).collect();
656 self.is = vec![0u32; n + 1];
657 for &(v, _, _) in &tuples {
658 self.is[v as usize + 1] = self.is[v as usize + 1]
659 .checked_add(1)
660 .ok_or(IgraphError::Internal("degree overflow in rebuild_indexes"))?;
661 }
662 for i in 1..=n {
663 self.is[i] = self.is[i]
664 .checked_add(self.is[i - 1])
665 .ok_or(IgraphError::Internal("offset overflow in rebuild_indexes"))?;
666 }
667
668 Ok(())
669 }
670}
671
672#[cfg(test)]
673mod tests {
674 use super::*;
675
676 #[test]
677 fn empty_graph_counts() {
678 let g = Graph::with_vertices(0);
679 assert_eq!(g.vcount(), 0);
680 assert_eq!(g.ecount(), 0);
681 assert!(!g.is_directed());
682 }
683
684 #[test]
685 fn new_directed_flag() {
686 let g = Graph::new(3, true).unwrap();
687 assert!(g.is_directed());
688 let g = Graph::new(3, false).unwrap();
689 assert!(!g.is_directed());
690 }
691
692 #[test]
693 fn add_vertices_then_edges() {
694 let mut g = Graph::with_vertices(3);
695 g.add_edge(0, 1).unwrap();
696 g.add_edge(1, 2).unwrap();
697 assert_eq!(g.vcount(), 3);
698 assert_eq!(g.ecount(), 2);
699 assert_eq!(g.degree(1).unwrap(), 2);
700 let mut nbrs = g.neighbors(1).unwrap();
701 nbrs.sort_unstable();
702 assert_eq!(nbrs, vec![0, 2]);
703 }
704
705 #[test]
706 fn out_of_range_vertex_errors() {
707 let mut g = Graph::with_vertices(2);
708 let err = g.add_edge(0, 5).unwrap_err();
709 assert!(matches!(err, IgraphError::VertexOutOfRange { id: 5, n: 2 }));
710 }
711
712 #[test]
713 fn self_loop_counted_correctly() {
714 let mut g = Graph::with_vertices(1);
715 g.add_edge(0, 0).unwrap();
716 assert_eq!(g.ecount(), 1);
717 assert_eq!(g.degree(0).unwrap(), 2);
719 let mut nbrs = g.neighbors(0).unwrap();
720 nbrs.sort_unstable();
721 assert_eq!(nbrs, vec![0, 0]);
722 }
723
724 #[test]
725 fn parallel_edges() {
726 let mut g = Graph::with_vertices(2);
727 g.add_edge(0, 1).unwrap();
728 g.add_edge(0, 1).unwrap();
729 assert_eq!(g.ecount(), 2);
730 assert_eq!(g.degree(0).unwrap(), 2);
731 assert_eq!(g.degree(1).unwrap(), 2);
732 }
733
734 #[test]
735 fn undirected_canonicalisation() {
736 let mut g = Graph::with_vertices(2);
738 g.add_edge(1, 0).unwrap();
739 g.add_edge(0, 1).unwrap();
740 assert_eq!(g.ecount(), 2);
741 let mut n0 = g.neighbors(0).unwrap();
743 let mut n1 = g.neighbors(1).unwrap();
744 n0.sort_unstable();
745 n1.sort_unstable();
746 assert_eq!(n0, vec![1, 1]);
747 assert_eq!(n1, vec![0, 0]);
748 }
749
750 #[test]
751 fn directed_neighbors_are_outgoing_only() {
752 let mut g = Graph::new(3, true).unwrap();
753 g.add_edge(0, 1).unwrap();
754 g.add_edge(2, 0).unwrap();
755 assert_eq!(g.neighbors(0).unwrap(), vec![1]);
757 assert_eq!(g.neighbors(2).unwrap(), vec![0]);
759 assert!(g.neighbors(1).unwrap().is_empty());
761 assert_eq!(g.degree(0).unwrap(), 2); assert_eq!(g.degree(1).unwrap(), 1); assert_eq!(g.degree(2).unwrap(), 1); }
766
767 #[test]
768 fn add_edges_batch_then_rebuild() {
769 let mut g = Graph::with_vertices(4);
770 g.add_edges(vec![(0, 1), (0, 2), (1, 2), (2, 3)]).unwrap();
771 assert_eq!(g.ecount(), 4);
772 assert_eq!(g.degree(0).unwrap(), 2);
774 assert_eq!(g.degree(1).unwrap(), 2);
775 assert_eq!(g.degree(2).unwrap(), 3);
776 assert_eq!(g.degree(3).unwrap(), 1);
777 }
778
779 #[test]
780 fn clone_is_deep() {
781 let mut g = Graph::with_vertices(3);
782 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
783 let g2 = g.clone();
784 g.add_edge(0, 2).unwrap();
786 assert_eq!(g.ecount(), 3);
787 assert_eq!(g2.ecount(), 2);
788 }
789
790 #[test]
791 fn os_invariant_is_monotone() {
792 let mut g = Graph::with_vertices(5);
793 g.add_edges(vec![(0, 1), (0, 2), (3, 4), (1, 2)]).unwrap();
794 for w in g.os.windows(2) {
796 assert!(w[0] <= w[1]);
797 }
798 assert_eq!(g.os[0], 0);
799 assert_eq!(*g.os.last().unwrap() as usize, g.ecount());
800 }
801
802 #[test]
803 fn vertex_out_of_range_when_adding_edge() {
804 let mut g = Graph::with_vertices(2);
805 let e = g.add_edge(2, 0).unwrap_err();
806 assert!(matches!(e, IgraphError::VertexOutOfRange { id: 2, n: 2 }));
807 assert_eq!(g.ecount(), 0);
809 }
810
811 #[test]
814 fn edge_endpoints_round_trip() {
815 let mut g = Graph::new(3, true).unwrap();
816 g.add_edges(vec![(0, 1), (2, 0), (1, 2)]).unwrap();
817 assert_eq!(g.edge(0).unwrap(), (0, 1));
819 assert_eq!(g.edge(1).unwrap(), (2, 0));
820 assert_eq!(g.edge(2).unwrap(), (1, 2));
821 assert_eq!(g.edge_source(1).unwrap(), 2);
822 assert_eq!(g.edge_target(1).unwrap(), 0);
823 }
824
825 #[test]
826 fn edge_other_endpoint() {
827 let mut g = Graph::with_vertices(3);
828 g.add_edge(0, 2).unwrap();
829 assert_eq!(g.edge_other(0, 0).unwrap(), 2);
830 assert_eq!(g.edge_other(0, 2).unwrap(), 0);
831 let err = g.edge_other(0, 1).unwrap_err();
833 assert!(matches!(err, IgraphError::InvalidArgument(_)));
834 }
835
836 #[test]
837 fn edge_out_of_range() {
838 let mut g = Graph::with_vertices(2);
839 g.add_edge(0, 1).unwrap();
840 let err = g.edge(5).unwrap_err();
841 assert!(matches!(err, IgraphError::EdgeOutOfRange { id: 5, m: 1 }));
842 }
843
844 #[test]
845 fn incident_returns_edge_ids_matching_neighbors_order() {
846 let mut g = Graph::with_vertices(4);
847 g.add_edges(vec![(0, 1), (0, 2), (3, 0)]).unwrap();
848 let eids = g.incident(0).unwrap();
849 let resolved: Vec<u32> = eids.iter().map(|&e| g.edge_other(e, 0).unwrap()).collect();
852 assert_eq!(resolved, g.neighbors(0).unwrap());
853 }
854
855 #[test]
856 fn incident_self_loop_appears_twice_undirected() {
857 let mut g = Graph::with_vertices(1);
858 g.add_edge(0, 0).unwrap();
859 let eids = g.incident(0).unwrap();
860 assert_eq!(eids, vec![0, 0]);
863 assert_eq!(g.degree(0).unwrap(), 2);
864 }
865
866 #[test]
867 fn incident_directed_returns_outgoing_only() {
868 let mut g = Graph::new(3, true).unwrap();
869 g.add_edges(vec![(0, 1), (2, 0)]).unwrap();
870 assert_eq!(g.incident(0).unwrap(), vec![0]);
872 assert_eq!(g.incident(2).unwrap(), vec![1]);
873 assert!(g.incident(1).unwrap().is_empty());
874 }
875
876 #[test]
877 fn get_eid_undirected_finds_edge_either_way() {
878 let mut g = Graph::with_vertices(3);
879 g.add_edge(0, 1).unwrap(); g.add_edge(1, 2).unwrap(); assert_eq!(g.get_eid(0, 1).unwrap(), 0);
882 assert_eq!(g.get_eid(1, 0).unwrap(), 0);
883 assert_eq!(g.get_eid(1, 2).unwrap(), 1);
884 assert_eq!(g.get_eid(2, 1).unwrap(), 1);
885 }
886
887 #[test]
888 fn get_eid_directed_respects_direction() {
889 let mut g = Graph::new(3, true).unwrap();
890 g.add_edge(0, 1).unwrap(); assert_eq!(g.get_eid(0, 1).unwrap(), 0);
892 assert!(g.get_eid(1, 0).is_err()); }
894
895 #[test]
896 fn find_eid_returns_none_for_missing() {
897 let mut g = Graph::with_vertices(3);
898 g.add_edge(0, 1).unwrap();
899 assert_eq!(g.find_eid(0, 2).unwrap(), None);
900 assert!(g.find_eid(0, 99).is_err()); }
902
903 #[test]
904 fn get_eid_self_loop() {
905 let mut g = Graph::with_vertices(2);
906 g.add_edge(0, 0).unwrap(); g.add_edge(0, 1).unwrap(); assert_eq!(g.get_eid(0, 0).unwrap(), 0);
909 assert_eq!(g.get_eid(0, 1).unwrap(), 1);
910 }
911
912 #[test]
913 fn get_all_eids_between_returns_all_parallel() {
914 let mut g = Graph::with_vertices(2);
915 g.add_edge(0, 1).unwrap(); g.add_edge(0, 1).unwrap(); g.add_edge(0, 1).unwrap(); let eids = g.get_all_eids_between(0, 1).unwrap();
919 assert_eq!(eids, vec![0, 1, 2]);
920 let eids = g.get_all_eids_between(1, 0).unwrap();
922 assert_eq!(eids, vec![0, 1, 2]);
923 }
924
925 #[test]
926 fn get_all_eids_between_directed_one_way_only() {
927 let mut g = Graph::new(2, true).unwrap();
928 g.add_edge(0, 1).unwrap(); g.add_edge(0, 1).unwrap(); assert_eq!(g.get_all_eids_between(0, 1).unwrap(), vec![0, 1]);
931 assert_eq!(g.get_all_eids_between(1, 0).unwrap(), Vec::<EdgeId>::new());
933 }
934
935 #[test]
936 fn get_eid_returns_lowest_id_for_parallel() {
937 let mut g = Graph::with_vertices(2);
941 g.add_edge(0, 1).unwrap(); g.add_edge(0, 1).unwrap(); assert_eq!(g.get_eid(0, 1).unwrap(), 0);
944 }
945
946 #[test]
949 fn delete_edges_empty_input_is_noop() {
950 let mut g = Graph::with_vertices(3);
951 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
952 g.delete_edges(&[]).unwrap();
953 assert_eq!(g.ecount(), 2);
954 assert_eq!(g.degree(1).unwrap(), 2);
955 }
956
957 #[test]
958 fn delete_edges_single_edge_undirected() {
959 let mut g = Graph::with_vertices(3);
960 g.add_edges(vec![(0, 1), (1, 2), (0, 2)]).unwrap();
961 g.delete_edges(&[1]).unwrap();
963 assert_eq!(g.ecount(), 2);
964 assert_eq!(g.find_eid(0, 1).unwrap(), Some(0));
966 assert_eq!(g.find_eid(0, 2).unwrap(), Some(1));
967 assert_eq!(g.find_eid(1, 2).unwrap(), None);
968 assert_eq!(g.degree(1).unwrap(), 1);
970 assert_eq!(g.degree(2).unwrap(), 1);
971 }
972
973 #[test]
974 fn delete_edges_duplicate_ids_tolerated() {
975 let mut g = Graph::with_vertices(3);
976 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
977 g.delete_edges(&[0, 0, 0]).unwrap();
978 assert_eq!(g.ecount(), 1);
979 assert_eq!(g.find_eid(1, 2).unwrap(), Some(0));
980 }
981
982 #[test]
983 fn delete_edges_all_edges_leaves_isolated_vertices() {
984 let mut g = Graph::with_vertices(3);
985 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
986 g.delete_edges(&[0, 1]).unwrap();
987 assert_eq!(g.ecount(), 0);
988 assert_eq!(g.vcount(), 3);
989 for v in 0..3 {
990 assert_eq!(g.degree(v).unwrap(), 0);
991 }
992 }
993
994 #[test]
995 fn delete_edges_out_of_range_errors_and_preserves_state() {
996 let mut g = Graph::with_vertices(3);
997 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
998 let err = g.delete_edges(&[5]).unwrap_err();
999 assert!(matches!(err, IgraphError::EdgeOutOfRange { id: 5, m: 2 }));
1000 assert_eq!(g.ecount(), 2);
1002 }
1003
1004 #[test]
1005 fn delete_edges_self_loop_directed() {
1006 let mut g = Graph::new(2, true).unwrap();
1007 g.add_edges(vec![(0, 0), (0, 1)]).unwrap();
1008 g.delete_edges(&[0]).unwrap(); assert_eq!(g.ecount(), 1);
1010 assert_eq!(g.degree(0).unwrap(), 1);
1011 assert_eq!(g.find_eid(0, 1).unwrap(), Some(0));
1012 }
1013
1014 #[test]
1015 fn delete_vertices_empty_input_is_noop() {
1016 let mut g = Graph::with_vertices(3);
1017 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
1018 g.delete_vertices(&[]).unwrap();
1019 assert_eq!(g.vcount(), 3);
1020 assert_eq!(g.ecount(), 2);
1021 }
1022
1023 #[test]
1024 fn delete_vertices_single_renumbers() {
1025 let mut g = Graph::with_vertices(4);
1026 g.add_edges(vec![(0, 1), (1, 2), (2, 3), (0, 3)]).unwrap();
1027 g.delete_vertices(&[1]).unwrap();
1030 assert_eq!(g.vcount(), 3);
1031 assert_eq!(g.ecount(), 2);
1032 assert!(g.find_eid(1, 2).unwrap().is_some());
1034 assert!(g.find_eid(0, 2).unwrap().is_some());
1035 assert_eq!(g.find_eid(0, 1).unwrap(), None);
1036 }
1037
1038 #[test]
1039 fn delete_vertices_duplicate_ids_tolerated() {
1040 let mut g = Graph::with_vertices(3);
1041 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
1042 g.delete_vertices(&[1, 1, 1]).unwrap();
1043 assert_eq!(g.vcount(), 2);
1044 assert_eq!(g.ecount(), 0);
1045 }
1046
1047 #[test]
1048 fn delete_vertices_all_yields_empty_graph() {
1049 let mut g = Graph::with_vertices(3);
1050 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
1051 g.delete_vertices(&[0, 1, 2]).unwrap();
1052 assert_eq!(g.vcount(), 0);
1053 assert_eq!(g.ecount(), 0);
1054 }
1055
1056 #[test]
1057 fn delete_vertices_out_of_range_errors_and_preserves_state() {
1058 let mut g = Graph::with_vertices(3);
1059 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
1060 let err = g.delete_vertices(&[5]).unwrap_err();
1061 assert!(matches!(err, IgraphError::VertexOutOfRange { id: 5, n: 3 }));
1062 assert_eq!(g.vcount(), 3);
1063 assert_eq!(g.ecount(), 2);
1064 }
1065
1066 #[test]
1067 fn delete_vertices_map_returns_correct_mappings() {
1068 let mut g = Graph::with_vertices(5);
1069 g.add_edges(vec![(0, 1), (1, 2), (2, 3), (3, 4)]).unwrap();
1070 let (map, invmap) = g.delete_vertices_map(&[1, 3]).unwrap();
1071 assert_eq!(map, vec![Some(0), None, Some(1), None, Some(2)]);
1073 assert_eq!(invmap, vec![0, 2, 4]);
1074 assert_eq!(g.vcount(), 3);
1075 assert_eq!(g.ecount(), 0);
1077 }
1078
1079 #[test]
1080 fn delete_vertices_directed_preserves_direction() {
1081 let mut g = Graph::new(4, true).unwrap();
1082 g.add_edges(vec![(0, 1), (1, 2), (2, 0), (3, 0)]).unwrap();
1083 g.delete_vertices(&[3]).unwrap();
1084 assert_eq!(g.vcount(), 3);
1085 assert!(g.is_directed());
1086 assert!(g.get_eid(0, 1).is_ok());
1088 assert!(g.get_eid(1, 0).is_err()); }
1090
1091 #[test]
1092 fn delete_vertices_self_loop_on_removed_vertex() {
1093 let mut g = Graph::with_vertices(3);
1094 g.add_edges(vec![(0, 0), (0, 1), (1, 2)]).unwrap();
1095 g.delete_vertices(&[0]).unwrap();
1096 assert_eq!(g.vcount(), 2);
1098 assert_eq!(g.ecount(), 1);
1099 assert!(g.find_eid(0, 1).unwrap().is_some());
1100 }
1101
1102 #[test]
1103 fn delete_vertices_preserves_parallel_edges() {
1104 let mut g = Graph::with_vertices(3);
1105 g.add_edges(vec![(0, 1), (0, 1), (1, 2)]).unwrap();
1106 g.delete_vertices(&[2]).unwrap();
1107 assert_eq!(g.vcount(), 2);
1108 assert_eq!(g.ecount(), 2); assert_eq!(g.degree(0).unwrap(), 2);
1110 assert_eq!(g.degree(1).unwrap(), 2);
1111 }
1112
1113 #[test]
1114 fn add_edges_after_delete_works() {
1115 let mut g = Graph::with_vertices(4);
1116 g.add_edges(vec![(0, 1), (1, 2), (2, 3)]).unwrap();
1117 g.delete_vertices(&[0]).unwrap(); g.add_edge(0, 2).unwrap();
1120 assert_eq!(g.ecount(), 3);
1121 assert_eq!(g.degree(0).unwrap(), 2); assert!(g.find_eid(0, 2).unwrap().is_some());
1123 }
1124}