1use super::cache::{
31 CachedProperty, PropertyCache, invalidate_after_add_edges, invalidate_after_add_vertices,
32};
33use super::error::{IgraphError, IgraphResult};
34
35pub type VertexId = u32;
38
39pub type EdgeId = u32;
42
43#[derive(Debug, Clone, Default)]
51pub struct Graph {
52 n: u32,
54 directed: bool,
56 from: Vec<VertexId>,
58 to: Vec<VertexId>,
60 oi: Vec<EdgeId>,
62 ii: Vec<EdgeId>,
64 os: Vec<u32>,
67 is: Vec<u32>,
69 cache: PropertyCache,
71}
72
73impl Graph {
74 pub fn new(n: u32, directed: bool) -> IgraphResult<Self> {
79 let mut g = Self {
80 n: 0,
81 directed,
82 from: Vec::new(),
83 to: Vec::new(),
84 oi: Vec::new(),
85 ii: Vec::new(),
86 os: vec![0],
87 is: vec![0],
88 cache: PropertyCache::new(),
89 };
90 g.add_vertices(n)?;
91 Ok(g)
92 }
93
94 pub fn with_vertices(n: u32) -> Self {
99 let len = n as usize + 1;
100 Self {
101 n,
102 directed: false,
103 from: Vec::new(),
104 to: Vec::new(),
105 oi: Vec::new(),
106 ii: Vec::new(),
107 os: vec![0; len],
108 is: vec![0; len],
109 cache: PropertyCache::new(),
110 }
111 }
112
113 #[must_use]
115 pub fn vcount(&self) -> u32 {
116 self.n
117 }
118
119 #[must_use]
121 pub fn ecount(&self) -> usize {
122 self.from.len()
123 }
124
125 #[must_use]
127 pub fn is_directed(&self) -> bool {
128 self.directed
129 }
130
131 pub fn add_vertices(&mut self, nv: u32) -> IgraphResult<(VertexId, VertexId)> {
137 let new_n = self
138 .n
139 .checked_add(nv)
140 .ok_or(IgraphError::Internal("vertex count overflow"))?;
141 let first = self.n;
142 let ec = u32::try_from(self.ecount())
144 .map_err(|_| IgraphError::Internal("edge count exceeds u32::MAX"))?;
145 for _ in 0..nv {
146 self.os.push(ec);
147 self.is.push(ec);
148 }
149 self.n = new_n;
150 if nv > 0 {
151 invalidate_after_add_vertices(&self.cache);
152 }
153 Ok((first, new_n.saturating_sub(1)))
154 }
155
156 pub fn add_edge(&mut self, u: VertexId, v: VertexId) -> IgraphResult<()> {
161 self.add_edges(std::iter::once((u, v)))
162 }
163
164 pub fn add_edges<I>(&mut self, edges: I) -> IgraphResult<()>
168 where
169 I: IntoIterator<Item = (VertexId, VertexId)>,
170 {
171 let m_before = self.ecount();
172 for (u, v) in edges {
173 self.check_vertex(u)?;
174 self.check_vertex(v)?;
175 if !self.directed && u > v {
177 self.from.push(v);
178 self.to.push(u);
179 } else {
180 self.from.push(u);
181 self.to.push(v);
182 }
183 }
184 self.rebuild_indexes()?;
185 if self.ecount() > m_before {
186 invalidate_after_add_edges(&self.cache);
187 }
188 Ok(())
189 }
190
191 pub fn neighbors(&self, v: VertexId) -> IgraphResult<Vec<VertexId>> {
200 self.check_vertex(v)?;
201 let v_idx = v as usize;
202 if self.directed {
203 let out_range = self.os[v_idx] as usize..self.os[v_idx + 1] as usize;
206 let out: Vec<VertexId> = self.oi[out_range]
207 .iter()
208 .map(|&e| self.to[e as usize])
209 .collect();
210 Ok(out)
211 } else {
212 let out_start = self.os[v_idx] as usize;
218 let out_end = self.os[v_idx + 1] as usize;
219 let in_start = self.is[v_idx] as usize;
220 let in_end = self.is[v_idx + 1] as usize;
221 let mut out = Vec::with_capacity((out_end - out_start) + (in_end - in_start));
222 let mut out_idx = out_start;
223 let mut in_idx = in_start;
224 while out_idx < out_end && in_idx < in_end {
225 let a = self.to[self.oi[out_idx] as usize];
226 let b = self.from[self.ii[in_idx] as usize];
227 if a <= b {
228 out.push(a);
229 out_idx += 1;
230 } else {
231 out.push(b);
232 in_idx += 1;
233 }
234 }
235 while out_idx < out_end {
236 out.push(self.to[self.oi[out_idx] as usize]);
237 out_idx += 1;
238 }
239 while in_idx < in_end {
240 out.push(self.from[self.ii[in_idx] as usize]);
241 in_idx += 1;
242 }
243 Ok(out)
244 }
245 }
246
247 pub fn degree(&self, v: VertexId) -> IgraphResult<usize> {
255 self.check_vertex(v)?;
256 let v_idx = v as usize;
257 let out = (self.os[v_idx + 1] - self.os[v_idx]) as usize;
258 if self.directed {
259 let in_count = (self.is[v_idx + 1] - self.is[v_idx]) as usize;
260 Ok(out + in_count)
261 } else {
262 let in_count = (self.is[v_idx + 1] - self.is[v_idx]) as usize;
266 Ok(out + in_count)
267 }
268 }
269
270 pub fn edge_source(&self, eid: EdgeId) -> IgraphResult<VertexId> {
277 self.check_edge(eid)?;
278 Ok(self.from[eid as usize])
279 }
280
281 pub fn edge_target(&self, eid: EdgeId) -> IgraphResult<VertexId> {
284 self.check_edge(eid)?;
285 Ok(self.to[eid as usize])
286 }
287
288 pub fn edge(&self, eid: EdgeId) -> IgraphResult<(VertexId, VertexId)> {
291 self.check_edge(eid)?;
292 let i = eid as usize;
293 Ok((self.from[i], self.to[i]))
294 }
295
296 pub fn edge_other(&self, eid: EdgeId, vid: VertexId) -> IgraphResult<VertexId> {
300 let (u, v) = self.edge(eid)?;
301 if vid == u {
302 Ok(v)
303 } else if vid == v {
304 Ok(u)
305 } else {
306 Err(IgraphError::InvalidArgument(format!(
307 "vertex {vid} is not an endpoint of edge {eid} ({u}, {v})"
308 )))
309 }
310 }
311
312 pub fn incident(&self, v: VertexId) -> IgraphResult<Vec<EdgeId>> {
328 self.check_vertex(v)?;
329 let v_idx = v as usize;
330 let out_range = self.os[v_idx] as usize..self.os[v_idx + 1] as usize;
331 if self.directed {
332 Ok(self.oi[out_range].to_vec())
333 } else {
334 let in_range = self.is[v_idx] as usize..self.is[v_idx + 1] as usize;
335 let mut out = Vec::with_capacity(out_range.len() + in_range.len());
336 out.extend_from_slice(&self.oi[out_range]);
337 out.extend_from_slice(&self.ii[in_range]);
338 Ok(out)
339 }
340 }
341
342 pub(crate) fn incident_in(&self, v: VertexId) -> IgraphResult<Vec<EdgeId>> {
349 self.check_vertex(v)?;
350 let v_idx = v as usize;
351 if self.directed {
352 let in_range = self.is[v_idx] as usize..self.is[v_idx + 1] as usize;
353 Ok(self.ii[in_range].to_vec())
354 } else {
355 self.incident(v)
356 }
357 }
358
359 pub fn get_eid(&self, from: VertexId, to: VertexId) -> IgraphResult<EdgeId> {
373 self.find_eid(from, to)?
374 .ok_or_else(|| IgraphError::InvalidArgument(format!("no edge between {from} and {to}")))
375 }
376
377 pub fn find_eid(&self, from: VertexId, to: VertexId) -> IgraphResult<Option<EdgeId>> {
384 self.check_vertex(from)?;
385 self.check_vertex(to)?;
386 if self.directed {
387 let range = self.os[from as usize] as usize..self.os[from as usize + 1] as usize;
389 for &e in &self.oi[range] {
390 if self.to[e as usize] == to {
391 return Ok(Some(e));
392 }
393 }
394 Ok(None)
395 } else {
396 let (lo, hi) = if from <= to { (from, to) } else { (to, from) };
399 let range = self.os[lo as usize] as usize..self.os[lo as usize + 1] as usize;
400 for &e in &self.oi[range] {
401 if self.to[e as usize] == hi {
402 return Ok(Some(e));
403 }
404 }
405 Ok(None)
406 }
407 }
408
409 pub fn get_all_eids_between(&self, from: VertexId, to: VertexId) -> IgraphResult<Vec<EdgeId>> {
418 self.check_vertex(from)?;
419 self.check_vertex(to)?;
420 let mut out = Vec::new();
421 if self.directed {
422 let range = self.os[from as usize] as usize..self.os[from as usize + 1] as usize;
423 for &e in &self.oi[range] {
424 if self.to[e as usize] == to {
425 out.push(e);
426 }
427 }
428 } else {
429 let (lo, hi) = if from <= to { (from, to) } else { (to, from) };
430 let range = self.os[lo as usize] as usize..self.os[lo as usize + 1] as usize;
431 for &e in &self.oi[range] {
432 if self.to[e as usize] == hi {
433 out.push(e);
434 }
435 }
436 }
437 out.sort_unstable();
438 Ok(out)
439 }
440
441 pub(crate) fn out_neighbors_vec(&self, v: VertexId) -> IgraphResult<Vec<VertexId>> {
449 self.check_vertex(v)?;
450 let v_idx = v as usize;
451 let range = self.os[v_idx] as usize..self.os[v_idx + 1] as usize;
452 Ok(self.oi[range]
453 .iter()
454 .map(|&e| self.to[e as usize])
455 .collect())
456 }
457
458 pub(crate) fn in_neighbors_vec(&self, v: VertexId) -> IgraphResult<Vec<VertexId>> {
465 self.check_vertex(v)?;
466 let v_idx = v as usize;
467 let range = self.is[v_idx] as usize..self.is[v_idx + 1] as usize;
468 Ok(self.ii[range]
469 .iter()
470 .map(|&e| self.from[e as usize])
471 .collect())
472 }
473
474 pub fn delete_edges(&mut self, edges: &[EdgeId]) -> IgraphResult<()> {
490 let m = self.ecount();
491 let m_u32 = u32::try_from(m).unwrap_or(u32::MAX);
492
493 for &eid in edges {
495 if (eid as usize) >= m {
496 return Err(IgraphError::EdgeOutOfRange { id: eid, m: m_u32 });
497 }
498 }
499 if edges.is_empty() {
500 return Ok(());
501 }
502
503 let mut remove = vec![false; m];
504 for &eid in edges {
505 remove[eid as usize] = true;
506 }
507
508 let mut new_from: Vec<VertexId> = Vec::with_capacity(m);
509 let mut new_to: Vec<VertexId> = Vec::with_capacity(m);
510 for (e, &is_removed) in remove.iter().enumerate() {
511 if !is_removed {
512 new_from.push(self.from[e]);
513 new_to.push(self.to[e]);
514 }
515 }
516 self.from = new_from;
517 self.to = new_to;
518 self.rebuild_indexes()?;
519 self.cache.invalidate_all();
520 Ok(())
521 }
522
523 pub fn delete_vertices(&mut self, vertices: &[VertexId]) -> IgraphResult<()> {
534 self.delete_vertices_map(vertices).map(|_| ())
535 }
536
537 pub fn delete_vertices_map(
548 &mut self,
549 vertices: &[VertexId],
550 ) -> IgraphResult<(Vec<Option<VertexId>>, Vec<VertexId>)> {
551 let n_u32 = self.n;
552 let n = n_u32 as usize;
553
554 for &vid in vertices {
556 if vid >= n_u32 {
557 return Err(IgraphError::VertexOutOfRange { id: vid, n: n_u32 });
558 }
559 }
560
561 let mut remove = vec![false; n];
562 for &vid in vertices {
563 remove[vid as usize] = true;
564 }
565
566 let mut map: Vec<Option<VertexId>> = vec![None; n];
568 let mut invmap: Vec<VertexId> = Vec::new();
569 let mut next_new: u32 = 0;
570 for (i, &is_removed) in remove.iter().enumerate() {
571 if !is_removed {
572 let i_u32 = u32::try_from(i)
573 .map_err(|_| IgraphError::Internal("vertex index exceeds u32::MAX"))?;
574 map[i] = Some(next_new);
575 invmap.push(i_u32);
576 next_new = next_new
577 .checked_add(1)
578 .ok_or(IgraphError::Internal("new vertex count overflow"))?;
579 }
580 }
581
582 let m = self.ecount();
585 let mut new_from: Vec<VertexId> = Vec::with_capacity(m);
586 let mut new_to: Vec<VertexId> = Vec::with_capacity(m);
587 for (u, v) in self.from.iter().zip(self.to.iter()) {
588 if let (Some(nu), Some(nv)) = (map[*u as usize], map[*v as usize]) {
589 new_from.push(nu);
590 new_to.push(nv);
591 }
592 }
593
594 self.n = next_new;
595 self.from = new_from;
596 self.to = new_to;
597 self.rebuild_indexes()?;
598 self.cache.invalidate_all();
599
600 Ok((map, invmap))
601 }
602
603 #[must_use]
618 pub fn cache_get(&self, prop: CachedProperty) -> Option<bool> {
619 self.cache.get(prop)
620 }
621
622 pub fn cache_set(&self, prop: CachedProperty, value: bool) {
631 self.cache.set(prop, value);
632 }
633
634 pub fn cache_invalidate(&self, prop: CachedProperty) {
641 self.cache.invalidate(prop);
642 }
643
644 pub fn cache_invalidate_all(&self) {
648 self.cache.invalidate_all();
649 }
650
651 fn check_vertex(&self, v: VertexId) -> IgraphResult<()> {
652 if v >= self.n {
653 return Err(IgraphError::VertexOutOfRange { id: v, n: self.n });
654 }
655 Ok(())
656 }
657
658 fn check_edge(&self, eid: EdgeId) -> IgraphResult<()> {
659 let m = self.ecount();
660 let m_u32 = u32::try_from(m).unwrap_or(u32::MAX);
661 if (eid as usize) >= m {
662 return Err(IgraphError::EdgeOutOfRange { id: eid, m: m_u32 });
663 }
664 Ok(())
665 }
666
667 fn rebuild_indexes(&mut self) -> IgraphResult<()> {
685 let m = self.ecount();
686 let n = self.n as usize;
687
688 let mut tuples: Vec<(VertexId, VertexId, u32)> = (0..m)
694 .map(|e| {
695 Ok::<_, IgraphError>((
696 self.from[e],
697 self.to[e],
698 u32::try_from(e)
699 .map_err(|_| IgraphError::Internal("edge id exceeds u32::MAX"))?,
700 ))
701 })
702 .collect::<Result<_, _>>()?;
703 tuples.sort_unstable_by_key(|a| (a.0, a.1));
704 self.oi = tuples.iter().map(|t| t.2).collect();
705 self.os = vec![0u32; n + 1];
707 for &(u, _, _) in &tuples {
708 self.os[u as usize + 1] = self.os[u as usize + 1]
709 .checked_add(1)
710 .ok_or(IgraphError::Internal("degree overflow in rebuild_indexes"))?;
711 }
712 for i in 1..=n {
713 self.os[i] = self.os[i]
714 .checked_add(self.os[i - 1])
715 .ok_or(IgraphError::Internal("offset overflow in rebuild_indexes"))?;
716 }
717
718 let mut tuples: Vec<(VertexId, VertexId, u32)> = (0..m)
720 .map(|e| {
721 Ok::<_, IgraphError>((
722 self.to[e],
723 self.from[e],
724 u32::try_from(e)
725 .map_err(|_| IgraphError::Internal("edge id exceeds u32::MAX"))?,
726 ))
727 })
728 .collect::<Result<_, _>>()?;
729 tuples.sort_unstable_by_key(|a| (a.0, a.1));
730 self.ii = tuples.iter().map(|t| t.2).collect();
731 self.is = vec![0u32; n + 1];
732 for &(v, _, _) in &tuples {
733 self.is[v as usize + 1] = self.is[v as usize + 1]
734 .checked_add(1)
735 .ok_or(IgraphError::Internal("degree overflow in rebuild_indexes"))?;
736 }
737 for i in 1..=n {
738 self.is[i] = self.is[i]
739 .checked_add(self.is[i - 1])
740 .ok_or(IgraphError::Internal("offset overflow in rebuild_indexes"))?;
741 }
742
743 Ok(())
744 }
745}
746
747#[cfg(test)]
748mod tests {
749 use super::*;
750
751 #[test]
752 fn empty_graph_counts() {
753 let g = Graph::with_vertices(0);
754 assert_eq!(g.vcount(), 0);
755 assert_eq!(g.ecount(), 0);
756 assert!(!g.is_directed());
757 }
758
759 #[test]
760 fn new_directed_flag() {
761 let g = Graph::new(3, true).unwrap();
762 assert!(g.is_directed());
763 let g = Graph::new(3, false).unwrap();
764 assert!(!g.is_directed());
765 }
766
767 #[test]
768 fn add_vertices_then_edges() {
769 let mut g = Graph::with_vertices(3);
770 g.add_edge(0, 1).unwrap();
771 g.add_edge(1, 2).unwrap();
772 assert_eq!(g.vcount(), 3);
773 assert_eq!(g.ecount(), 2);
774 assert_eq!(g.degree(1).unwrap(), 2);
775 let mut nbrs = g.neighbors(1).unwrap();
776 nbrs.sort_unstable();
777 assert_eq!(nbrs, vec![0, 2]);
778 }
779
780 #[test]
781 fn out_of_range_vertex_errors() {
782 let mut g = Graph::with_vertices(2);
783 let err = g.add_edge(0, 5).unwrap_err();
784 assert!(matches!(err, IgraphError::VertexOutOfRange { id: 5, n: 2 }));
785 }
786
787 #[test]
788 fn self_loop_counted_correctly() {
789 let mut g = Graph::with_vertices(1);
790 g.add_edge(0, 0).unwrap();
791 assert_eq!(g.ecount(), 1);
792 assert_eq!(g.degree(0).unwrap(), 2);
794 let mut nbrs = g.neighbors(0).unwrap();
795 nbrs.sort_unstable();
796 assert_eq!(nbrs, vec![0, 0]);
797 }
798
799 #[test]
800 fn parallel_edges() {
801 let mut g = Graph::with_vertices(2);
802 g.add_edge(0, 1).unwrap();
803 g.add_edge(0, 1).unwrap();
804 assert_eq!(g.ecount(), 2);
805 assert_eq!(g.degree(0).unwrap(), 2);
806 assert_eq!(g.degree(1).unwrap(), 2);
807 }
808
809 #[test]
810 fn undirected_canonicalisation() {
811 let mut g = Graph::with_vertices(2);
813 g.add_edge(1, 0).unwrap();
814 g.add_edge(0, 1).unwrap();
815 assert_eq!(g.ecount(), 2);
816 let mut n0 = g.neighbors(0).unwrap();
818 let mut n1 = g.neighbors(1).unwrap();
819 n0.sort_unstable();
820 n1.sort_unstable();
821 assert_eq!(n0, vec![1, 1]);
822 assert_eq!(n1, vec![0, 0]);
823 }
824
825 #[test]
826 fn directed_neighbors_are_outgoing_only() {
827 let mut g = Graph::new(3, true).unwrap();
828 g.add_edge(0, 1).unwrap();
829 g.add_edge(2, 0).unwrap();
830 assert_eq!(g.neighbors(0).unwrap(), vec![1]);
832 assert_eq!(g.neighbors(2).unwrap(), vec![0]);
834 assert!(g.neighbors(1).unwrap().is_empty());
836 assert_eq!(g.degree(0).unwrap(), 2); assert_eq!(g.degree(1).unwrap(), 1); assert_eq!(g.degree(2).unwrap(), 1); }
841
842 #[test]
843 fn add_edges_batch_then_rebuild() {
844 let mut g = Graph::with_vertices(4);
845 g.add_edges(vec![(0, 1), (0, 2), (1, 2), (2, 3)]).unwrap();
846 assert_eq!(g.ecount(), 4);
847 assert_eq!(g.degree(0).unwrap(), 2);
849 assert_eq!(g.degree(1).unwrap(), 2);
850 assert_eq!(g.degree(2).unwrap(), 3);
851 assert_eq!(g.degree(3).unwrap(), 1);
852 }
853
854 #[test]
855 fn clone_is_deep() {
856 let mut g = Graph::with_vertices(3);
857 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
858 let g2 = g.clone();
859 g.add_edge(0, 2).unwrap();
861 assert_eq!(g.ecount(), 3);
862 assert_eq!(g2.ecount(), 2);
863 }
864
865 #[test]
866 fn os_invariant_is_monotone() {
867 let mut g = Graph::with_vertices(5);
868 g.add_edges(vec![(0, 1), (0, 2), (3, 4), (1, 2)]).unwrap();
869 for w in g.os.windows(2) {
871 assert!(w[0] <= w[1]);
872 }
873 assert_eq!(g.os[0], 0);
874 assert_eq!(*g.os.last().unwrap() as usize, g.ecount());
875 }
876
877 #[test]
878 fn vertex_out_of_range_when_adding_edge() {
879 let mut g = Graph::with_vertices(2);
880 let e = g.add_edge(2, 0).unwrap_err();
881 assert!(matches!(e, IgraphError::VertexOutOfRange { id: 2, n: 2 }));
882 assert_eq!(g.ecount(), 0);
884 }
885
886 #[test]
889 fn edge_endpoints_round_trip() {
890 let mut g = Graph::new(3, true).unwrap();
891 g.add_edges(vec![(0, 1), (2, 0), (1, 2)]).unwrap();
892 assert_eq!(g.edge(0).unwrap(), (0, 1));
894 assert_eq!(g.edge(1).unwrap(), (2, 0));
895 assert_eq!(g.edge(2).unwrap(), (1, 2));
896 assert_eq!(g.edge_source(1).unwrap(), 2);
897 assert_eq!(g.edge_target(1).unwrap(), 0);
898 }
899
900 #[test]
901 fn edge_other_endpoint() {
902 let mut g = Graph::with_vertices(3);
903 g.add_edge(0, 2).unwrap();
904 assert_eq!(g.edge_other(0, 0).unwrap(), 2);
905 assert_eq!(g.edge_other(0, 2).unwrap(), 0);
906 let err = g.edge_other(0, 1).unwrap_err();
908 assert!(matches!(err, IgraphError::InvalidArgument(_)));
909 }
910
911 #[test]
912 fn edge_out_of_range() {
913 let mut g = Graph::with_vertices(2);
914 g.add_edge(0, 1).unwrap();
915 let err = g.edge(5).unwrap_err();
916 assert!(matches!(err, IgraphError::EdgeOutOfRange { id: 5, m: 1 }));
917 }
918
919 #[test]
920 fn incident_returns_edge_ids_matching_neighbors_order() {
921 let mut g = Graph::with_vertices(4);
922 g.add_edges(vec![(0, 1), (0, 2), (3, 0)]).unwrap();
923 let eids = g.incident(0).unwrap();
924 let resolved: Vec<u32> = eids.iter().map(|&e| g.edge_other(e, 0).unwrap()).collect();
927 assert_eq!(resolved, g.neighbors(0).unwrap());
928 }
929
930 #[test]
931 fn incident_self_loop_appears_twice_undirected() {
932 let mut g = Graph::with_vertices(1);
933 g.add_edge(0, 0).unwrap();
934 let eids = g.incident(0).unwrap();
935 assert_eq!(eids, vec![0, 0]);
938 assert_eq!(g.degree(0).unwrap(), 2);
939 }
940
941 #[test]
942 fn incident_directed_returns_outgoing_only() {
943 let mut g = Graph::new(3, true).unwrap();
944 g.add_edges(vec![(0, 1), (2, 0)]).unwrap();
945 assert_eq!(g.incident(0).unwrap(), vec![0]);
947 assert_eq!(g.incident(2).unwrap(), vec![1]);
948 assert!(g.incident(1).unwrap().is_empty());
949 }
950
951 #[test]
952 fn get_eid_undirected_finds_edge_either_way() {
953 let mut g = Graph::with_vertices(3);
954 g.add_edge(0, 1).unwrap(); g.add_edge(1, 2).unwrap(); assert_eq!(g.get_eid(0, 1).unwrap(), 0);
957 assert_eq!(g.get_eid(1, 0).unwrap(), 0);
958 assert_eq!(g.get_eid(1, 2).unwrap(), 1);
959 assert_eq!(g.get_eid(2, 1).unwrap(), 1);
960 }
961
962 #[test]
963 fn get_eid_directed_respects_direction() {
964 let mut g = Graph::new(3, true).unwrap();
965 g.add_edge(0, 1).unwrap(); assert_eq!(g.get_eid(0, 1).unwrap(), 0);
967 assert!(g.get_eid(1, 0).is_err()); }
969
970 #[test]
971 fn find_eid_returns_none_for_missing() {
972 let mut g = Graph::with_vertices(3);
973 g.add_edge(0, 1).unwrap();
974 assert_eq!(g.find_eid(0, 2).unwrap(), None);
975 assert!(g.find_eid(0, 99).is_err()); }
977
978 #[test]
979 fn get_eid_self_loop() {
980 let mut g = Graph::with_vertices(2);
981 g.add_edge(0, 0).unwrap(); g.add_edge(0, 1).unwrap(); assert_eq!(g.get_eid(0, 0).unwrap(), 0);
984 assert_eq!(g.get_eid(0, 1).unwrap(), 1);
985 }
986
987 #[test]
988 fn get_all_eids_between_returns_all_parallel() {
989 let mut g = Graph::with_vertices(2);
990 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();
994 assert_eq!(eids, vec![0, 1, 2]);
995 let eids = g.get_all_eids_between(1, 0).unwrap();
997 assert_eq!(eids, vec![0, 1, 2]);
998 }
999
1000 #[test]
1001 fn get_all_eids_between_directed_one_way_only() {
1002 let mut g = Graph::new(2, true).unwrap();
1003 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]);
1006 assert_eq!(g.get_all_eids_between(1, 0).unwrap(), Vec::<EdgeId>::new());
1008 }
1009
1010 #[test]
1011 fn get_eid_returns_lowest_id_for_parallel() {
1012 let mut g = Graph::with_vertices(2);
1016 g.add_edge(0, 1).unwrap(); g.add_edge(0, 1).unwrap(); assert_eq!(g.get_eid(0, 1).unwrap(), 0);
1019 }
1020
1021 #[test]
1024 fn delete_edges_empty_input_is_noop() {
1025 let mut g = Graph::with_vertices(3);
1026 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
1027 g.delete_edges(&[]).unwrap();
1028 assert_eq!(g.ecount(), 2);
1029 assert_eq!(g.degree(1).unwrap(), 2);
1030 }
1031
1032 #[test]
1033 fn delete_edges_single_edge_undirected() {
1034 let mut g = Graph::with_vertices(3);
1035 g.add_edges(vec![(0, 1), (1, 2), (0, 2)]).unwrap();
1036 g.delete_edges(&[1]).unwrap();
1038 assert_eq!(g.ecount(), 2);
1039 assert_eq!(g.find_eid(0, 1).unwrap(), Some(0));
1041 assert_eq!(g.find_eid(0, 2).unwrap(), Some(1));
1042 assert_eq!(g.find_eid(1, 2).unwrap(), None);
1043 assert_eq!(g.degree(1).unwrap(), 1);
1045 assert_eq!(g.degree(2).unwrap(), 1);
1046 }
1047
1048 #[test]
1049 fn delete_edges_duplicate_ids_tolerated() {
1050 let mut g = Graph::with_vertices(3);
1051 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
1052 g.delete_edges(&[0, 0, 0]).unwrap();
1053 assert_eq!(g.ecount(), 1);
1054 assert_eq!(g.find_eid(1, 2).unwrap(), Some(0));
1055 }
1056
1057 #[test]
1058 fn delete_edges_all_edges_leaves_isolated_vertices() {
1059 let mut g = Graph::with_vertices(3);
1060 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
1061 g.delete_edges(&[0, 1]).unwrap();
1062 assert_eq!(g.ecount(), 0);
1063 assert_eq!(g.vcount(), 3);
1064 for v in 0..3 {
1065 assert_eq!(g.degree(v).unwrap(), 0);
1066 }
1067 }
1068
1069 #[test]
1070 fn delete_edges_out_of_range_errors_and_preserves_state() {
1071 let mut g = Graph::with_vertices(3);
1072 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
1073 let err = g.delete_edges(&[5]).unwrap_err();
1074 assert!(matches!(err, IgraphError::EdgeOutOfRange { id: 5, m: 2 }));
1075 assert_eq!(g.ecount(), 2);
1077 }
1078
1079 #[test]
1080 fn delete_edges_self_loop_directed() {
1081 let mut g = Graph::new(2, true).unwrap();
1082 g.add_edges(vec![(0, 0), (0, 1)]).unwrap();
1083 g.delete_edges(&[0]).unwrap(); assert_eq!(g.ecount(), 1);
1085 assert_eq!(g.degree(0).unwrap(), 1);
1086 assert_eq!(g.find_eid(0, 1).unwrap(), Some(0));
1087 }
1088
1089 #[test]
1090 fn delete_vertices_empty_input_is_noop() {
1091 let mut g = Graph::with_vertices(3);
1092 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
1093 g.delete_vertices(&[]).unwrap();
1094 assert_eq!(g.vcount(), 3);
1095 assert_eq!(g.ecount(), 2);
1096 }
1097
1098 #[test]
1099 fn delete_vertices_single_renumbers() {
1100 let mut g = Graph::with_vertices(4);
1101 g.add_edges(vec![(0, 1), (1, 2), (2, 3), (0, 3)]).unwrap();
1102 g.delete_vertices(&[1]).unwrap();
1105 assert_eq!(g.vcount(), 3);
1106 assert_eq!(g.ecount(), 2);
1107 assert!(g.find_eid(1, 2).unwrap().is_some());
1109 assert!(g.find_eid(0, 2).unwrap().is_some());
1110 assert_eq!(g.find_eid(0, 1).unwrap(), None);
1111 }
1112
1113 #[test]
1114 fn delete_vertices_duplicate_ids_tolerated() {
1115 let mut g = Graph::with_vertices(3);
1116 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
1117 g.delete_vertices(&[1, 1, 1]).unwrap();
1118 assert_eq!(g.vcount(), 2);
1119 assert_eq!(g.ecount(), 0);
1120 }
1121
1122 #[test]
1123 fn delete_vertices_all_yields_empty_graph() {
1124 let mut g = Graph::with_vertices(3);
1125 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
1126 g.delete_vertices(&[0, 1, 2]).unwrap();
1127 assert_eq!(g.vcount(), 0);
1128 assert_eq!(g.ecount(), 0);
1129 }
1130
1131 #[test]
1132 fn delete_vertices_out_of_range_errors_and_preserves_state() {
1133 let mut g = Graph::with_vertices(3);
1134 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
1135 let err = g.delete_vertices(&[5]).unwrap_err();
1136 assert!(matches!(err, IgraphError::VertexOutOfRange { id: 5, n: 3 }));
1137 assert_eq!(g.vcount(), 3);
1138 assert_eq!(g.ecount(), 2);
1139 }
1140
1141 #[test]
1142 fn delete_vertices_map_returns_correct_mappings() {
1143 let mut g = Graph::with_vertices(5);
1144 g.add_edges(vec![(0, 1), (1, 2), (2, 3), (3, 4)]).unwrap();
1145 let (map, invmap) = g.delete_vertices_map(&[1, 3]).unwrap();
1146 assert_eq!(map, vec![Some(0), None, Some(1), None, Some(2)]);
1148 assert_eq!(invmap, vec![0, 2, 4]);
1149 assert_eq!(g.vcount(), 3);
1150 assert_eq!(g.ecount(), 0);
1152 }
1153
1154 #[test]
1155 fn delete_vertices_directed_preserves_direction() {
1156 let mut g = Graph::new(4, true).unwrap();
1157 g.add_edges(vec![(0, 1), (1, 2), (2, 0), (3, 0)]).unwrap();
1158 g.delete_vertices(&[3]).unwrap();
1159 assert_eq!(g.vcount(), 3);
1160 assert!(g.is_directed());
1161 assert!(g.get_eid(0, 1).is_ok());
1163 assert!(g.get_eid(1, 0).is_err()); }
1165
1166 #[test]
1167 fn delete_vertices_self_loop_on_removed_vertex() {
1168 let mut g = Graph::with_vertices(3);
1169 g.add_edges(vec![(0, 0), (0, 1), (1, 2)]).unwrap();
1170 g.delete_vertices(&[0]).unwrap();
1171 assert_eq!(g.vcount(), 2);
1173 assert_eq!(g.ecount(), 1);
1174 assert!(g.find_eid(0, 1).unwrap().is_some());
1175 }
1176
1177 #[test]
1178 fn delete_vertices_preserves_parallel_edges() {
1179 let mut g = Graph::with_vertices(3);
1180 g.add_edges(vec![(0, 1), (0, 1), (1, 2)]).unwrap();
1181 g.delete_vertices(&[2]).unwrap();
1182 assert_eq!(g.vcount(), 2);
1183 assert_eq!(g.ecount(), 2); assert_eq!(g.degree(0).unwrap(), 2);
1185 assert_eq!(g.degree(1).unwrap(), 2);
1186 }
1187
1188 #[test]
1189 fn add_edges_after_delete_works() {
1190 let mut g = Graph::with_vertices(4);
1191 g.add_edges(vec![(0, 1), (1, 2), (2, 3)]).unwrap();
1192 g.delete_vertices(&[0]).unwrap(); g.add_edge(0, 2).unwrap();
1195 assert_eq!(g.ecount(), 3);
1196 assert_eq!(g.degree(0).unwrap(), 2); assert!(g.find_eid(0, 2).unwrap().is_some());
1198 }
1199}