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 Self::new(n, false).expect("with_vertices: n cannot exceed u32::MAX")
100 }
101
102 #[must_use]
104 pub fn vcount(&self) -> u32 {
105 self.n
106 }
107
108 #[must_use]
110 pub fn ecount(&self) -> usize {
111 self.from.len()
112 }
113
114 #[must_use]
116 pub fn is_directed(&self) -> bool {
117 self.directed
118 }
119
120 pub fn add_vertices(&mut self, nv: u32) -> IgraphResult<(VertexId, VertexId)> {
126 let new_n = self
127 .n
128 .checked_add(nv)
129 .ok_or(IgraphError::Internal("vertex count overflow"))?;
130 let first = self.n;
131 let ec = u32::try_from(self.ecount())
133 .map_err(|_| IgraphError::Internal("edge count exceeds u32::MAX"))?;
134 for _ in 0..nv {
135 self.os.push(ec);
136 self.is.push(ec);
137 }
138 self.n = new_n;
139 if nv > 0 {
140 invalidate_after_add_vertices(&self.cache);
141 }
142 Ok((first, new_n.saturating_sub(1)))
143 }
144
145 pub fn add_edge(&mut self, u: VertexId, v: VertexId) -> IgraphResult<()> {
150 self.add_edges(std::iter::once((u, v)))
151 }
152
153 pub fn add_edges<I>(&mut self, edges: I) -> IgraphResult<()>
157 where
158 I: IntoIterator<Item = (VertexId, VertexId)>,
159 {
160 let m_before = self.ecount();
161 for (u, v) in edges {
162 self.check_vertex(u)?;
163 self.check_vertex(v)?;
164 if !self.directed && u > v {
166 self.from.push(v);
167 self.to.push(u);
168 } else {
169 self.from.push(u);
170 self.to.push(v);
171 }
172 }
173 self.rebuild_indexes()?;
174 if self.ecount() > m_before {
175 invalidate_after_add_edges(&self.cache);
176 }
177 Ok(())
178 }
179
180 pub fn neighbors(&self, v: VertexId) -> IgraphResult<Vec<VertexId>> {
189 self.check_vertex(v)?;
190 let v_idx = v as usize;
191 if self.directed {
192 let out_range = self.os[v_idx] as usize..self.os[v_idx + 1] as usize;
195 let out: Vec<VertexId> = self.oi[out_range]
196 .iter()
197 .map(|&e| self.to[e as usize])
198 .collect();
199 Ok(out)
200 } else {
201 let out_start = self.os[v_idx] as usize;
207 let out_end = self.os[v_idx + 1] as usize;
208 let in_start = self.is[v_idx] as usize;
209 let in_end = self.is[v_idx + 1] as usize;
210 let mut out = Vec::with_capacity((out_end - out_start) + (in_end - in_start));
211 let mut out_idx = out_start;
212 let mut in_idx = in_start;
213 while out_idx < out_end && in_idx < in_end {
214 let a = self.to[self.oi[out_idx] as usize];
215 let b = self.from[self.ii[in_idx] as usize];
216 if a <= b {
217 out.push(a);
218 out_idx += 1;
219 } else {
220 out.push(b);
221 in_idx += 1;
222 }
223 }
224 while out_idx < out_end {
225 out.push(self.to[self.oi[out_idx] as usize]);
226 out_idx += 1;
227 }
228 while in_idx < in_end {
229 out.push(self.from[self.ii[in_idx] as usize]);
230 in_idx += 1;
231 }
232 Ok(out)
233 }
234 }
235
236 pub fn degree(&self, v: VertexId) -> IgraphResult<usize> {
244 self.check_vertex(v)?;
245 let v_idx = v as usize;
246 let out = (self.os[v_idx + 1] - self.os[v_idx]) as usize;
247 if self.directed {
248 let in_count = (self.is[v_idx + 1] - self.is[v_idx]) as usize;
249 Ok(out + in_count)
250 } else {
251 let in_count = (self.is[v_idx + 1] - self.is[v_idx]) as usize;
255 Ok(out + in_count)
256 }
257 }
258
259 pub fn edge_source(&self, eid: EdgeId) -> IgraphResult<VertexId> {
266 self.check_edge(eid)?;
267 Ok(self.from[eid as usize])
268 }
269
270 pub fn edge_target(&self, eid: EdgeId) -> IgraphResult<VertexId> {
273 self.check_edge(eid)?;
274 Ok(self.to[eid as usize])
275 }
276
277 pub fn edge(&self, eid: EdgeId) -> IgraphResult<(VertexId, VertexId)> {
280 self.check_edge(eid)?;
281 let i = eid as usize;
282 Ok((self.from[i], self.to[i]))
283 }
284
285 pub fn edge_other(&self, eid: EdgeId, vid: VertexId) -> IgraphResult<VertexId> {
289 let (u, v) = self.edge(eid)?;
290 if vid == u {
291 Ok(v)
292 } else if vid == v {
293 Ok(u)
294 } else {
295 Err(IgraphError::InvalidArgument(format!(
296 "vertex {vid} is not an endpoint of edge {eid} ({u}, {v})"
297 )))
298 }
299 }
300
301 pub fn incident(&self, v: VertexId) -> IgraphResult<Vec<EdgeId>> {
317 self.check_vertex(v)?;
318 let v_idx = v as usize;
319 let out_range = self.os[v_idx] as usize..self.os[v_idx + 1] as usize;
320 if self.directed {
321 Ok(self.oi[out_range].to_vec())
322 } else {
323 let in_range = self.is[v_idx] as usize..self.is[v_idx + 1] as usize;
324 let mut out = Vec::with_capacity(out_range.len() + in_range.len());
325 out.extend_from_slice(&self.oi[out_range]);
326 out.extend_from_slice(&self.ii[in_range]);
327 Ok(out)
328 }
329 }
330
331 pub(crate) fn incident_in(&self, v: VertexId) -> IgraphResult<Vec<EdgeId>> {
338 self.check_vertex(v)?;
339 let v_idx = v as usize;
340 if self.directed {
341 let in_range = self.is[v_idx] as usize..self.is[v_idx + 1] as usize;
342 Ok(self.ii[in_range].to_vec())
343 } else {
344 self.incident(v)
345 }
346 }
347
348 pub fn get_eid(&self, from: VertexId, to: VertexId) -> IgraphResult<EdgeId> {
362 self.find_eid(from, to)?
363 .ok_or_else(|| IgraphError::InvalidArgument(format!("no edge between {from} and {to}")))
364 }
365
366 pub fn find_eid(&self, from: VertexId, to: VertexId) -> IgraphResult<Option<EdgeId>> {
373 self.check_vertex(from)?;
374 self.check_vertex(to)?;
375 if self.directed {
376 let range = self.os[from as usize] as usize..self.os[from as usize + 1] as usize;
378 for &e in &self.oi[range] {
379 if self.to[e as usize] == to {
380 return Ok(Some(e));
381 }
382 }
383 Ok(None)
384 } else {
385 let (lo, hi) = if from <= to { (from, to) } else { (to, from) };
388 let range = self.os[lo as usize] as usize..self.os[lo as usize + 1] as usize;
389 for &e in &self.oi[range] {
390 if self.to[e as usize] == hi {
391 return Ok(Some(e));
392 }
393 }
394 Ok(None)
395 }
396 }
397
398 pub fn get_all_eids_between(&self, from: VertexId, to: VertexId) -> IgraphResult<Vec<EdgeId>> {
407 self.check_vertex(from)?;
408 self.check_vertex(to)?;
409 let mut out = Vec::new();
410 if self.directed {
411 let range = self.os[from as usize] as usize..self.os[from as usize + 1] as usize;
412 for &e in &self.oi[range] {
413 if self.to[e as usize] == to {
414 out.push(e);
415 }
416 }
417 } else {
418 let (lo, hi) = if from <= to { (from, to) } else { (to, from) };
419 let range = self.os[lo as usize] as usize..self.os[lo as usize + 1] as usize;
420 for &e in &self.oi[range] {
421 if self.to[e as usize] == hi {
422 out.push(e);
423 }
424 }
425 }
426 out.sort_unstable();
427 Ok(out)
428 }
429
430 pub(crate) fn out_neighbors_vec(&self, v: VertexId) -> IgraphResult<Vec<VertexId>> {
438 self.check_vertex(v)?;
439 let v_idx = v as usize;
440 let range = self.os[v_idx] as usize..self.os[v_idx + 1] as usize;
441 Ok(self.oi[range]
442 .iter()
443 .map(|&e| self.to[e as usize])
444 .collect())
445 }
446
447 pub(crate) fn in_neighbors_vec(&self, v: VertexId) -> IgraphResult<Vec<VertexId>> {
454 self.check_vertex(v)?;
455 let v_idx = v as usize;
456 let range = self.is[v_idx] as usize..self.is[v_idx + 1] as usize;
457 Ok(self.ii[range]
458 .iter()
459 .map(|&e| self.from[e as usize])
460 .collect())
461 }
462
463 pub fn delete_edges(&mut self, edges: &[EdgeId]) -> IgraphResult<()> {
479 let m = self.ecount();
480 let m_u32 = u32::try_from(m).unwrap_or(u32::MAX);
481
482 for &eid in edges {
484 if (eid as usize) >= m {
485 return Err(IgraphError::EdgeOutOfRange { id: eid, m: m_u32 });
486 }
487 }
488 if edges.is_empty() {
489 return Ok(());
490 }
491
492 let mut remove = vec![false; m];
493 for &eid in edges {
494 remove[eid as usize] = true;
495 }
496
497 let mut new_from: Vec<VertexId> = Vec::with_capacity(m);
498 let mut new_to: Vec<VertexId> = Vec::with_capacity(m);
499 for (e, &is_removed) in remove.iter().enumerate() {
500 if !is_removed {
501 new_from.push(self.from[e]);
502 new_to.push(self.to[e]);
503 }
504 }
505 self.from = new_from;
506 self.to = new_to;
507 self.rebuild_indexes()?;
508 self.cache.invalidate_all();
509 Ok(())
510 }
511
512 pub fn delete_vertices(&mut self, vertices: &[VertexId]) -> IgraphResult<()> {
523 self.delete_vertices_map(vertices).map(|_| ())
524 }
525
526 pub fn delete_vertices_map(
537 &mut self,
538 vertices: &[VertexId],
539 ) -> IgraphResult<(Vec<Option<VertexId>>, Vec<VertexId>)> {
540 let n_u32 = self.n;
541 let n = n_u32 as usize;
542
543 for &vid in vertices {
545 if vid >= n_u32 {
546 return Err(IgraphError::VertexOutOfRange { id: vid, n: n_u32 });
547 }
548 }
549
550 let mut remove = vec![false; n];
551 for &vid in vertices {
552 remove[vid as usize] = true;
553 }
554
555 let mut map: Vec<Option<VertexId>> = vec![None; n];
557 let mut invmap: Vec<VertexId> = Vec::new();
558 let mut next_new: u32 = 0;
559 for (i, &is_removed) in remove.iter().enumerate() {
560 if !is_removed {
561 let i_u32 = u32::try_from(i)
562 .map_err(|_| IgraphError::Internal("vertex index exceeds u32::MAX"))?;
563 map[i] = Some(next_new);
564 invmap.push(i_u32);
565 next_new = next_new
566 .checked_add(1)
567 .ok_or(IgraphError::Internal("new vertex count overflow"))?;
568 }
569 }
570
571 let m = self.ecount();
574 let mut new_from: Vec<VertexId> = Vec::with_capacity(m);
575 let mut new_to: Vec<VertexId> = Vec::with_capacity(m);
576 for (u, v) in self.from.iter().zip(self.to.iter()) {
577 if let (Some(nu), Some(nv)) = (map[*u as usize], map[*v as usize]) {
578 new_from.push(nu);
579 new_to.push(nv);
580 }
581 }
582
583 self.n = next_new;
584 self.from = new_from;
585 self.to = new_to;
586 self.rebuild_indexes()?;
587 self.cache.invalidate_all();
588
589 Ok((map, invmap))
590 }
591
592 #[must_use]
607 pub fn cache_get(&self, prop: CachedProperty) -> Option<bool> {
608 self.cache.get(prop)
609 }
610
611 pub fn cache_set(&self, prop: CachedProperty, value: bool) {
620 self.cache.set(prop, value);
621 }
622
623 pub fn cache_invalidate(&self, prop: CachedProperty) {
630 self.cache.invalidate(prop);
631 }
632
633 pub fn cache_invalidate_all(&self) {
637 self.cache.invalidate_all();
638 }
639
640 fn check_vertex(&self, v: VertexId) -> IgraphResult<()> {
641 if v >= self.n {
642 return Err(IgraphError::VertexOutOfRange { id: v, n: self.n });
643 }
644 Ok(())
645 }
646
647 fn check_edge(&self, eid: EdgeId) -> IgraphResult<()> {
648 let m = self.ecount();
649 let m_u32 = u32::try_from(m).unwrap_or(u32::MAX);
650 if (eid as usize) >= m {
651 return Err(IgraphError::EdgeOutOfRange { id: eid, m: m_u32 });
652 }
653 Ok(())
654 }
655
656 fn rebuild_indexes(&mut self) -> IgraphResult<()> {
674 let m = self.ecount();
675 let n = self.n as usize;
676
677 let mut tuples: Vec<(VertexId, VertexId, u32)> = (0..m)
683 .map(|e| {
684 Ok::<_, IgraphError>((
685 self.from[e],
686 self.to[e],
687 u32::try_from(e)
688 .map_err(|_| IgraphError::Internal("edge id exceeds u32::MAX"))?,
689 ))
690 })
691 .collect::<Result<_, _>>()?;
692 tuples.sort_unstable_by_key(|a| (a.0, a.1));
693 self.oi = tuples.iter().map(|t| t.2).collect();
694 self.os = vec![0u32; n + 1];
696 for &(u, _, _) in &tuples {
697 self.os[u as usize + 1] = self.os[u as usize + 1]
698 .checked_add(1)
699 .ok_or(IgraphError::Internal("degree overflow in rebuild_indexes"))?;
700 }
701 for i in 1..=n {
702 self.os[i] = self.os[i]
703 .checked_add(self.os[i - 1])
704 .ok_or(IgraphError::Internal("offset overflow in rebuild_indexes"))?;
705 }
706
707 let mut tuples: Vec<(VertexId, VertexId, u32)> = (0..m)
709 .map(|e| {
710 Ok::<_, IgraphError>((
711 self.to[e],
712 self.from[e],
713 u32::try_from(e)
714 .map_err(|_| IgraphError::Internal("edge id exceeds u32::MAX"))?,
715 ))
716 })
717 .collect::<Result<_, _>>()?;
718 tuples.sort_unstable_by_key(|a| (a.0, a.1));
719 self.ii = tuples.iter().map(|t| t.2).collect();
720 self.is = vec![0u32; n + 1];
721 for &(v, _, _) in &tuples {
722 self.is[v as usize + 1] = self.is[v as usize + 1]
723 .checked_add(1)
724 .ok_or(IgraphError::Internal("degree overflow in rebuild_indexes"))?;
725 }
726 for i in 1..=n {
727 self.is[i] = self.is[i]
728 .checked_add(self.is[i - 1])
729 .ok_or(IgraphError::Internal("offset overflow in rebuild_indexes"))?;
730 }
731
732 Ok(())
733 }
734}
735
736#[cfg(test)]
737mod tests {
738 use super::*;
739
740 #[test]
741 fn empty_graph_counts() {
742 let g = Graph::with_vertices(0);
743 assert_eq!(g.vcount(), 0);
744 assert_eq!(g.ecount(), 0);
745 assert!(!g.is_directed());
746 }
747
748 #[test]
749 fn new_directed_flag() {
750 let g = Graph::new(3, true).unwrap();
751 assert!(g.is_directed());
752 let g = Graph::new(3, false).unwrap();
753 assert!(!g.is_directed());
754 }
755
756 #[test]
757 fn add_vertices_then_edges() {
758 let mut g = Graph::with_vertices(3);
759 g.add_edge(0, 1).unwrap();
760 g.add_edge(1, 2).unwrap();
761 assert_eq!(g.vcount(), 3);
762 assert_eq!(g.ecount(), 2);
763 assert_eq!(g.degree(1).unwrap(), 2);
764 let mut nbrs = g.neighbors(1).unwrap();
765 nbrs.sort_unstable();
766 assert_eq!(nbrs, vec![0, 2]);
767 }
768
769 #[test]
770 fn out_of_range_vertex_errors() {
771 let mut g = Graph::with_vertices(2);
772 let err = g.add_edge(0, 5).unwrap_err();
773 assert!(matches!(err, IgraphError::VertexOutOfRange { id: 5, n: 2 }));
774 }
775
776 #[test]
777 fn self_loop_counted_correctly() {
778 let mut g = Graph::with_vertices(1);
779 g.add_edge(0, 0).unwrap();
780 assert_eq!(g.ecount(), 1);
781 assert_eq!(g.degree(0).unwrap(), 2);
783 let mut nbrs = g.neighbors(0).unwrap();
784 nbrs.sort_unstable();
785 assert_eq!(nbrs, vec![0, 0]);
786 }
787
788 #[test]
789 fn parallel_edges() {
790 let mut g = Graph::with_vertices(2);
791 g.add_edge(0, 1).unwrap();
792 g.add_edge(0, 1).unwrap();
793 assert_eq!(g.ecount(), 2);
794 assert_eq!(g.degree(0).unwrap(), 2);
795 assert_eq!(g.degree(1).unwrap(), 2);
796 }
797
798 #[test]
799 fn undirected_canonicalisation() {
800 let mut g = Graph::with_vertices(2);
802 g.add_edge(1, 0).unwrap();
803 g.add_edge(0, 1).unwrap();
804 assert_eq!(g.ecount(), 2);
805 let mut n0 = g.neighbors(0).unwrap();
807 let mut n1 = g.neighbors(1).unwrap();
808 n0.sort_unstable();
809 n1.sort_unstable();
810 assert_eq!(n0, vec![1, 1]);
811 assert_eq!(n1, vec![0, 0]);
812 }
813
814 #[test]
815 fn directed_neighbors_are_outgoing_only() {
816 let mut g = Graph::new(3, true).unwrap();
817 g.add_edge(0, 1).unwrap();
818 g.add_edge(2, 0).unwrap();
819 assert_eq!(g.neighbors(0).unwrap(), vec![1]);
821 assert_eq!(g.neighbors(2).unwrap(), vec![0]);
823 assert!(g.neighbors(1).unwrap().is_empty());
825 assert_eq!(g.degree(0).unwrap(), 2); assert_eq!(g.degree(1).unwrap(), 1); assert_eq!(g.degree(2).unwrap(), 1); }
830
831 #[test]
832 fn add_edges_batch_then_rebuild() {
833 let mut g = Graph::with_vertices(4);
834 g.add_edges(vec![(0, 1), (0, 2), (1, 2), (2, 3)]).unwrap();
835 assert_eq!(g.ecount(), 4);
836 assert_eq!(g.degree(0).unwrap(), 2);
838 assert_eq!(g.degree(1).unwrap(), 2);
839 assert_eq!(g.degree(2).unwrap(), 3);
840 assert_eq!(g.degree(3).unwrap(), 1);
841 }
842
843 #[test]
844 fn clone_is_deep() {
845 let mut g = Graph::with_vertices(3);
846 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
847 let g2 = g.clone();
848 g.add_edge(0, 2).unwrap();
850 assert_eq!(g.ecount(), 3);
851 assert_eq!(g2.ecount(), 2);
852 }
853
854 #[test]
855 fn os_invariant_is_monotone() {
856 let mut g = Graph::with_vertices(5);
857 g.add_edges(vec![(0, 1), (0, 2), (3, 4), (1, 2)]).unwrap();
858 for w in g.os.windows(2) {
860 assert!(w[0] <= w[1]);
861 }
862 assert_eq!(g.os[0], 0);
863 assert_eq!(*g.os.last().unwrap() as usize, g.ecount());
864 }
865
866 #[test]
867 fn vertex_out_of_range_when_adding_edge() {
868 let mut g = Graph::with_vertices(2);
869 let e = g.add_edge(2, 0).unwrap_err();
870 assert!(matches!(e, IgraphError::VertexOutOfRange { id: 2, n: 2 }));
871 assert_eq!(g.ecount(), 0);
873 }
874
875 #[test]
878 fn edge_endpoints_round_trip() {
879 let mut g = Graph::new(3, true).unwrap();
880 g.add_edges(vec![(0, 1), (2, 0), (1, 2)]).unwrap();
881 assert_eq!(g.edge(0).unwrap(), (0, 1));
883 assert_eq!(g.edge(1).unwrap(), (2, 0));
884 assert_eq!(g.edge(2).unwrap(), (1, 2));
885 assert_eq!(g.edge_source(1).unwrap(), 2);
886 assert_eq!(g.edge_target(1).unwrap(), 0);
887 }
888
889 #[test]
890 fn edge_other_endpoint() {
891 let mut g = Graph::with_vertices(3);
892 g.add_edge(0, 2).unwrap();
893 assert_eq!(g.edge_other(0, 0).unwrap(), 2);
894 assert_eq!(g.edge_other(0, 2).unwrap(), 0);
895 let err = g.edge_other(0, 1).unwrap_err();
897 assert!(matches!(err, IgraphError::InvalidArgument(_)));
898 }
899
900 #[test]
901 fn edge_out_of_range() {
902 let mut g = Graph::with_vertices(2);
903 g.add_edge(0, 1).unwrap();
904 let err = g.edge(5).unwrap_err();
905 assert!(matches!(err, IgraphError::EdgeOutOfRange { id: 5, m: 1 }));
906 }
907
908 #[test]
909 fn incident_returns_edge_ids_matching_neighbors_order() {
910 let mut g = Graph::with_vertices(4);
911 g.add_edges(vec![(0, 1), (0, 2), (3, 0)]).unwrap();
912 let eids = g.incident(0).unwrap();
913 let resolved: Vec<u32> = eids.iter().map(|&e| g.edge_other(e, 0).unwrap()).collect();
916 assert_eq!(resolved, g.neighbors(0).unwrap());
917 }
918
919 #[test]
920 fn incident_self_loop_appears_twice_undirected() {
921 let mut g = Graph::with_vertices(1);
922 g.add_edge(0, 0).unwrap();
923 let eids = g.incident(0).unwrap();
924 assert_eq!(eids, vec![0, 0]);
927 assert_eq!(g.degree(0).unwrap(), 2);
928 }
929
930 #[test]
931 fn incident_directed_returns_outgoing_only() {
932 let mut g = Graph::new(3, true).unwrap();
933 g.add_edges(vec![(0, 1), (2, 0)]).unwrap();
934 assert_eq!(g.incident(0).unwrap(), vec![0]);
936 assert_eq!(g.incident(2).unwrap(), vec![1]);
937 assert!(g.incident(1).unwrap().is_empty());
938 }
939
940 #[test]
941 fn get_eid_undirected_finds_edge_either_way() {
942 let mut g = Graph::with_vertices(3);
943 g.add_edge(0, 1).unwrap(); g.add_edge(1, 2).unwrap(); assert_eq!(g.get_eid(0, 1).unwrap(), 0);
946 assert_eq!(g.get_eid(1, 0).unwrap(), 0);
947 assert_eq!(g.get_eid(1, 2).unwrap(), 1);
948 assert_eq!(g.get_eid(2, 1).unwrap(), 1);
949 }
950
951 #[test]
952 fn get_eid_directed_respects_direction() {
953 let mut g = Graph::new(3, true).unwrap();
954 g.add_edge(0, 1).unwrap(); assert_eq!(g.get_eid(0, 1).unwrap(), 0);
956 assert!(g.get_eid(1, 0).is_err()); }
958
959 #[test]
960 fn find_eid_returns_none_for_missing() {
961 let mut g = Graph::with_vertices(3);
962 g.add_edge(0, 1).unwrap();
963 assert_eq!(g.find_eid(0, 2).unwrap(), None);
964 assert!(g.find_eid(0, 99).is_err()); }
966
967 #[test]
968 fn get_eid_self_loop() {
969 let mut g = Graph::with_vertices(2);
970 g.add_edge(0, 0).unwrap(); g.add_edge(0, 1).unwrap(); assert_eq!(g.get_eid(0, 0).unwrap(), 0);
973 assert_eq!(g.get_eid(0, 1).unwrap(), 1);
974 }
975
976 #[test]
977 fn get_all_eids_between_returns_all_parallel() {
978 let mut g = Graph::with_vertices(2);
979 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();
983 assert_eq!(eids, vec![0, 1, 2]);
984 let eids = g.get_all_eids_between(1, 0).unwrap();
986 assert_eq!(eids, vec![0, 1, 2]);
987 }
988
989 #[test]
990 fn get_all_eids_between_directed_one_way_only() {
991 let mut g = Graph::new(2, true).unwrap();
992 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]);
995 assert_eq!(g.get_all_eids_between(1, 0).unwrap(), Vec::<EdgeId>::new());
997 }
998
999 #[test]
1000 fn get_eid_returns_lowest_id_for_parallel() {
1001 let mut g = Graph::with_vertices(2);
1005 g.add_edge(0, 1).unwrap(); g.add_edge(0, 1).unwrap(); assert_eq!(g.get_eid(0, 1).unwrap(), 0);
1008 }
1009
1010 #[test]
1013 fn delete_edges_empty_input_is_noop() {
1014 let mut g = Graph::with_vertices(3);
1015 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
1016 g.delete_edges(&[]).unwrap();
1017 assert_eq!(g.ecount(), 2);
1018 assert_eq!(g.degree(1).unwrap(), 2);
1019 }
1020
1021 #[test]
1022 fn delete_edges_single_edge_undirected() {
1023 let mut g = Graph::with_vertices(3);
1024 g.add_edges(vec![(0, 1), (1, 2), (0, 2)]).unwrap();
1025 g.delete_edges(&[1]).unwrap();
1027 assert_eq!(g.ecount(), 2);
1028 assert_eq!(g.find_eid(0, 1).unwrap(), Some(0));
1030 assert_eq!(g.find_eid(0, 2).unwrap(), Some(1));
1031 assert_eq!(g.find_eid(1, 2).unwrap(), None);
1032 assert_eq!(g.degree(1).unwrap(), 1);
1034 assert_eq!(g.degree(2).unwrap(), 1);
1035 }
1036
1037 #[test]
1038 fn delete_edges_duplicate_ids_tolerated() {
1039 let mut g = Graph::with_vertices(3);
1040 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
1041 g.delete_edges(&[0, 0, 0]).unwrap();
1042 assert_eq!(g.ecount(), 1);
1043 assert_eq!(g.find_eid(1, 2).unwrap(), Some(0));
1044 }
1045
1046 #[test]
1047 fn delete_edges_all_edges_leaves_isolated_vertices() {
1048 let mut g = Graph::with_vertices(3);
1049 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
1050 g.delete_edges(&[0, 1]).unwrap();
1051 assert_eq!(g.ecount(), 0);
1052 assert_eq!(g.vcount(), 3);
1053 for v in 0..3 {
1054 assert_eq!(g.degree(v).unwrap(), 0);
1055 }
1056 }
1057
1058 #[test]
1059 fn delete_edges_out_of_range_errors_and_preserves_state() {
1060 let mut g = Graph::with_vertices(3);
1061 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
1062 let err = g.delete_edges(&[5]).unwrap_err();
1063 assert!(matches!(err, IgraphError::EdgeOutOfRange { id: 5, m: 2 }));
1064 assert_eq!(g.ecount(), 2);
1066 }
1067
1068 #[test]
1069 fn delete_edges_self_loop_directed() {
1070 let mut g = Graph::new(2, true).unwrap();
1071 g.add_edges(vec![(0, 0), (0, 1)]).unwrap();
1072 g.delete_edges(&[0]).unwrap(); assert_eq!(g.ecount(), 1);
1074 assert_eq!(g.degree(0).unwrap(), 1);
1075 assert_eq!(g.find_eid(0, 1).unwrap(), Some(0));
1076 }
1077
1078 #[test]
1079 fn delete_vertices_empty_input_is_noop() {
1080 let mut g = Graph::with_vertices(3);
1081 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
1082 g.delete_vertices(&[]).unwrap();
1083 assert_eq!(g.vcount(), 3);
1084 assert_eq!(g.ecount(), 2);
1085 }
1086
1087 #[test]
1088 fn delete_vertices_single_renumbers() {
1089 let mut g = Graph::with_vertices(4);
1090 g.add_edges(vec![(0, 1), (1, 2), (2, 3), (0, 3)]).unwrap();
1091 g.delete_vertices(&[1]).unwrap();
1094 assert_eq!(g.vcount(), 3);
1095 assert_eq!(g.ecount(), 2);
1096 assert!(g.find_eid(1, 2).unwrap().is_some());
1098 assert!(g.find_eid(0, 2).unwrap().is_some());
1099 assert_eq!(g.find_eid(0, 1).unwrap(), None);
1100 }
1101
1102 #[test]
1103 fn delete_vertices_duplicate_ids_tolerated() {
1104 let mut g = Graph::with_vertices(3);
1105 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
1106 g.delete_vertices(&[1, 1, 1]).unwrap();
1107 assert_eq!(g.vcount(), 2);
1108 assert_eq!(g.ecount(), 0);
1109 }
1110
1111 #[test]
1112 fn delete_vertices_all_yields_empty_graph() {
1113 let mut g = Graph::with_vertices(3);
1114 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
1115 g.delete_vertices(&[0, 1, 2]).unwrap();
1116 assert_eq!(g.vcount(), 0);
1117 assert_eq!(g.ecount(), 0);
1118 }
1119
1120 #[test]
1121 fn delete_vertices_out_of_range_errors_and_preserves_state() {
1122 let mut g = Graph::with_vertices(3);
1123 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
1124 let err = g.delete_vertices(&[5]).unwrap_err();
1125 assert!(matches!(err, IgraphError::VertexOutOfRange { id: 5, n: 3 }));
1126 assert_eq!(g.vcount(), 3);
1127 assert_eq!(g.ecount(), 2);
1128 }
1129
1130 #[test]
1131 fn delete_vertices_map_returns_correct_mappings() {
1132 let mut g = Graph::with_vertices(5);
1133 g.add_edges(vec![(0, 1), (1, 2), (2, 3), (3, 4)]).unwrap();
1134 let (map, invmap) = g.delete_vertices_map(&[1, 3]).unwrap();
1135 assert_eq!(map, vec![Some(0), None, Some(1), None, Some(2)]);
1137 assert_eq!(invmap, vec![0, 2, 4]);
1138 assert_eq!(g.vcount(), 3);
1139 assert_eq!(g.ecount(), 0);
1141 }
1142
1143 #[test]
1144 fn delete_vertices_directed_preserves_direction() {
1145 let mut g = Graph::new(4, true).unwrap();
1146 g.add_edges(vec![(0, 1), (1, 2), (2, 0), (3, 0)]).unwrap();
1147 g.delete_vertices(&[3]).unwrap();
1148 assert_eq!(g.vcount(), 3);
1149 assert!(g.is_directed());
1150 assert!(g.get_eid(0, 1).is_ok());
1152 assert!(g.get_eid(1, 0).is_err()); }
1154
1155 #[test]
1156 fn delete_vertices_self_loop_on_removed_vertex() {
1157 let mut g = Graph::with_vertices(3);
1158 g.add_edges(vec![(0, 0), (0, 1), (1, 2)]).unwrap();
1159 g.delete_vertices(&[0]).unwrap();
1160 assert_eq!(g.vcount(), 2);
1162 assert_eq!(g.ecount(), 1);
1163 assert!(g.find_eid(0, 1).unwrap().is_some());
1164 }
1165
1166 #[test]
1167 fn delete_vertices_preserves_parallel_edges() {
1168 let mut g = Graph::with_vertices(3);
1169 g.add_edges(vec![(0, 1), (0, 1), (1, 2)]).unwrap();
1170 g.delete_vertices(&[2]).unwrap();
1171 assert_eq!(g.vcount(), 2);
1172 assert_eq!(g.ecount(), 2); assert_eq!(g.degree(0).unwrap(), 2);
1174 assert_eq!(g.degree(1).unwrap(), 2);
1175 }
1176
1177 #[test]
1178 fn add_edges_after_delete_works() {
1179 let mut g = Graph::with_vertices(4);
1180 g.add_edges(vec![(0, 1), (1, 2), (2, 3)]).unwrap();
1181 g.delete_vertices(&[0]).unwrap(); g.add_edge(0, 2).unwrap();
1184 assert_eq!(g.ecount(), 3);
1185 assert_eq!(g.degree(0).unwrap(), 2); assert!(g.find_eid(0, 2).unwrap().is_some());
1187 }
1188}