1use std::collections::VecDeque;
20
21use crate::algorithms::paths::dijkstra::DijkstraMode;
22use crate::core::graph::EdgeId;
23use crate::core::{Graph, IgraphError, IgraphResult, VertexId};
24
25fn validate_weights(graph: &Graph, weights: &[f64]) -> IgraphResult<()> {
26 let m = graph.ecount();
27 if weights.len() != m {
28 return Err(IgraphError::InvalidArgument(format!(
29 "weights vector size ({}) differs from edge count ({})",
30 weights.len(),
31 m
32 )));
33 }
34 for (e, &w) in weights.iter().enumerate() {
35 if w.is_nan() {
36 return Err(IgraphError::InvalidArgument(format!(
37 "weight at edge {e} is NaN"
38 )));
39 }
40 }
41 Ok(())
42}
43
44fn incident_for_mode(graph: &Graph, v: VertexId, mode: DijkstraMode) -> IgraphResult<Vec<EdgeId>> {
45 if !graph.is_directed() {
46 return graph.incident(v);
47 }
48 match mode {
49 DijkstraMode::Out => graph.incident(v),
50 DijkstraMode::In => graph.incident_in(v),
51 DijkstraMode::All => {
52 let mut out = graph.incident(v)?;
53 out.extend(graph.incident_in(v)?);
54 Ok(out)
55 }
56 }
57}
58
59pub fn bellman_ford_distances(
95 graph: &Graph,
96 source: VertexId,
97 weights: &[f64],
98) -> IgraphResult<Vec<Option<f64>>> {
99 bellman_ford_distances_with_mode(graph, source, weights, DijkstraMode::Out)
100}
101
102pub fn bellman_ford_distances_with_mode(
128 graph: &Graph,
129 source: VertexId,
130 weights: &[f64],
131 mode: DijkstraMode,
132) -> IgraphResult<Vec<Option<f64>>> {
133 let n = graph.vcount();
134 if source >= n {
135 return Err(IgraphError::VertexOutOfRange { id: source, n });
136 }
137 validate_weights(graph, weights)?;
138
139 let n_usize = n as usize;
140 let mut dist: Vec<f64> = vec![f64::INFINITY; n_usize];
141 dist[source as usize] = 0.0;
142
143 let mut queue: VecDeque<VertexId> = VecDeque::with_capacity(n_usize);
146 let mut in_queue: Vec<bool> = vec![true; n_usize];
147 let mut num_queued: Vec<u32> = vec![0; n_usize];
148 for v in 0..n {
149 queue.push_back(v);
150 }
151
152 let n_u32 = n;
153 while let Some(j) = queue.pop_front() {
154 let j_idx = j as usize;
155 in_queue[j_idx] = false;
156 num_queued[j_idx] = num_queued[j_idx]
157 .checked_add(1)
158 .ok_or(IgraphError::Internal("num_queued overflow"))?;
159 if num_queued[j_idx] > n_u32 {
162 return Err(IgraphError::InvalidArgument(
163 "negative cycle reachable from source while running Bellman-Ford".to_string(),
164 ));
165 }
166
167 if !dist[j_idx].is_finite() {
169 continue;
170 }
171
172 let incidents = incident_for_mode(graph, j, mode)?;
173 for eid in incidents {
174 let w = weights[eid as usize];
175 if w == f64::INFINITY {
177 continue;
178 }
179 let target = graph.edge_other(eid, j)?;
180 let target_idx = target as usize;
181 let altdist = dist[j_idx] + w;
182 if altdist < dist[target_idx] {
183 dist[target_idx] = altdist;
184 if !in_queue[target_idx] {
185 in_queue[target_idx] = true;
186 queue.push_back(target);
187 }
188 }
189 }
190 }
191
192 Ok(dist
193 .into_iter()
194 .map(|d| if d.is_finite() { Some(d) } else { None })
195 .collect())
196}
197
198pub fn bellman_ford_path_to(
228 graph: &Graph,
229 source: VertexId,
230 target: VertexId,
231 weights: &[f64],
232) -> IgraphResult<Option<(Vec<VertexId>, Vec<EdgeId>)>> {
233 bellman_ford_path_to_with_mode(graph, source, target, weights, DijkstraMode::Out)
234}
235
236pub fn bellman_ford_path_to_with_mode(
260 graph: &Graph,
261 source: VertexId,
262 target: VertexId,
263 weights: &[f64],
264 mode: DijkstraMode,
265) -> IgraphResult<Option<(Vec<VertexId>, Vec<EdgeId>)>> {
266 let n = graph.vcount();
267 if source >= n {
268 return Err(IgraphError::VertexOutOfRange { id: source, n });
269 }
270 if target >= n {
271 return Err(IgraphError::VertexOutOfRange { id: target, n });
272 }
273 validate_weights(graph, weights)?;
274
275 if source == target {
276 return Ok(Some((vec![source], vec![])));
277 }
278
279 let n_usize = n as usize;
280 let mut dist: Vec<f64> = vec![f64::INFINITY; n_usize];
281 dist[source as usize] = 0.0;
282
283 let mut parent_edge: Vec<Option<EdgeId>> = vec![None; n_usize];
285
286 let mut queue: VecDeque<VertexId> = VecDeque::with_capacity(n_usize);
287 let mut in_queue: Vec<bool> = vec![true; n_usize];
288 let mut num_queued: Vec<u32> = vec![0; n_usize];
289 for v in 0..n {
290 queue.push_back(v);
291 }
292
293 while let Some(j) = queue.pop_front() {
294 let j_idx = j as usize;
295 in_queue[j_idx] = false;
296 num_queued[j_idx] = num_queued[j_idx]
297 .checked_add(1)
298 .ok_or(IgraphError::Internal("num_queued overflow"))?;
299 if num_queued[j_idx] > n {
300 return Err(IgraphError::InvalidArgument(
301 "negative cycle reachable from source while running Bellman-Ford".to_string(),
302 ));
303 }
304
305 if !dist[j_idx].is_finite() {
306 continue;
307 }
308
309 let incidents = incident_for_mode(graph, j, mode)?;
310 for eid in incidents {
311 let w = weights[eid as usize];
312 if w == f64::INFINITY {
313 continue;
314 }
315 let neighbor = graph.edge_other(eid, j)?;
316 let neighbor_idx = neighbor as usize;
317 let altdist = dist[j_idx] + w;
318 if altdist < dist[neighbor_idx] {
319 dist[neighbor_idx] = altdist;
320 parent_edge[neighbor_idx] = Some(eid);
321 if !in_queue[neighbor_idx] {
322 in_queue[neighbor_idx] = true;
323 queue.push_back(neighbor);
324 }
325 }
326 }
327 }
328
329 if !dist[target as usize].is_finite() {
331 return Ok(None);
332 }
333
334 let mut edges_rev: Vec<EdgeId> = Vec::new();
336 let mut vertices_rev: Vec<VertexId> = Vec::new();
337 let mut cur = target;
338 vertices_rev.push(cur);
339 while cur != source {
340 let eid = parent_edge[cur as usize].ok_or(IgraphError::Internal(
341 "bellman_ford_path_to: missing parent edge in path reconstruction",
342 ))?;
343 edges_rev.push(eid);
344 cur = graph.edge_other(eid, cur)?;
345 vertices_rev.push(cur);
346 }
347
348 vertices_rev.reverse();
349 edges_rev.reverse();
350 Ok(Some((vertices_rev, edges_rev)))
351}
352
353pub type BellmanFordPathEntry = Option<(Vec<VertexId>, Vec<EdgeId>)>;
356
357pub fn bellman_ford_paths(
396 graph: &Graph,
397 source: VertexId,
398 targets: &[VertexId],
399 weights: &[f64],
400) -> IgraphResult<Vec<BellmanFordPathEntry>> {
401 bellman_ford_paths_with_mode(graph, source, targets, weights, DijkstraMode::Out)
402}
403
404pub fn bellman_ford_paths_with_mode(
424 graph: &Graph,
425 source: VertexId,
426 targets: &[VertexId],
427 weights: &[f64],
428 mode: DijkstraMode,
429) -> IgraphResult<Vec<BellmanFordPathEntry>> {
430 let n = graph.vcount();
431 if source >= n {
432 return Err(IgraphError::VertexOutOfRange { id: source, n });
433 }
434 for &t in targets {
435 if t >= n {
436 return Err(IgraphError::VertexOutOfRange { id: t, n });
437 }
438 }
439 validate_weights(graph, weights)?;
440
441 let n_usize = n as usize;
442
443 let mut dist: Vec<f64> = vec![f64::INFINITY; n_usize];
445 dist[source as usize] = 0.0;
446
447 let mut parent_edge: Vec<Option<EdgeId>> = vec![None; n_usize];
448
449 let mut queue: VecDeque<VertexId> = VecDeque::with_capacity(n_usize);
450 let mut in_queue: Vec<bool> = vec![true; n_usize];
451 let mut num_queued: Vec<u32> = vec![0; n_usize];
452 for v in 0..n {
453 queue.push_back(v);
454 }
455
456 while let Some(j) = queue.pop_front() {
457 let j_idx = j as usize;
458 in_queue[j_idx] = false;
459 num_queued[j_idx] = num_queued[j_idx]
460 .checked_add(1)
461 .ok_or(IgraphError::Internal("num_queued overflow"))?;
462 if num_queued[j_idx] > n {
463 return Err(IgraphError::InvalidArgument(
464 "negative cycle reachable from source while running Bellman-Ford".to_string(),
465 ));
466 }
467
468 if !dist[j_idx].is_finite() {
469 continue;
470 }
471
472 let incidents = incident_for_mode(graph, j, mode)?;
473 for eid in incidents {
474 let w = weights[eid as usize];
475 if w == f64::INFINITY {
476 continue;
477 }
478 let neighbor = graph.edge_other(eid, j)?;
479 let neighbor_idx = neighbor as usize;
480 let altdist = dist[j_idx] + w;
481 if altdist < dist[neighbor_idx] {
482 dist[neighbor_idx] = altdist;
483 parent_edge[neighbor_idx] = Some(eid);
484 if !in_queue[neighbor_idx] {
485 in_queue[neighbor_idx] = true;
486 queue.push_back(neighbor);
487 }
488 }
489 }
490 }
491
492 let mut results: Vec<Option<(Vec<VertexId>, Vec<EdgeId>)>> = Vec::with_capacity(targets.len());
494 for &target in targets {
495 if target == source {
496 results.push(Some((vec![source], vec![])));
497 continue;
498 }
499 if !dist[target as usize].is_finite() {
500 results.push(None);
501 continue;
502 }
503 let mut edges_rev: Vec<EdgeId> = Vec::new();
504 let mut vertices_rev: Vec<VertexId> = Vec::new();
505 let mut cur = target;
506 vertices_rev.push(cur);
507 while cur != source {
508 let eid = parent_edge[cur as usize].ok_or(IgraphError::Internal(
509 "bellman_ford_paths: missing parent edge in path reconstruction",
510 ))?;
511 edges_rev.push(eid);
512 cur = graph.edge_other(eid, cur)?;
513 vertices_rev.push(cur);
514 }
515 vertices_rev.reverse();
516 edges_rev.reverse();
517 results.push(Some((vertices_rev, edges_rev)));
518 }
519
520 Ok(results)
521}
522
523#[cfg(test)]
524mod tests {
525 use super::*;
526
527 #[test]
528 fn positive_weights_match_dijkstra_triangle() {
529 let mut g = Graph::with_vertices(3);
532 g.add_edge(0, 1).unwrap();
533 g.add_edge(0, 2).unwrap();
534 g.add_edge(1, 2).unwrap();
535 let weights = [1.0, 4.0, 2.0];
536 let bf = bellman_ford_distances(&g, 0, &weights).unwrap();
537 assert_eq!(bf, vec![Some(0.0), Some(1.0), Some(3.0)]);
538 }
539
540 #[test]
541 fn negative_edge_directed_no_cycle() {
542 let mut g = Graph::new(3, true).unwrap();
544 g.add_edge(0, 1).unwrap();
545 g.add_edge(1, 2).unwrap();
546 let d = bellman_ford_distances(&g, 0, &[3.0, -1.0]).unwrap();
547 assert_eq!(d, vec![Some(0.0), Some(3.0), Some(2.0)]);
548 }
549
550 #[test]
551 fn unreachable_vertex_is_none() {
552 let mut g = Graph::with_vertices(4);
554 g.add_edge(0, 1).unwrap();
555 g.add_edge(2, 3).unwrap();
556 let d = bellman_ford_distances(&g, 0, &[1.0, 1.0]).unwrap();
557 assert_eq!(d, vec![Some(0.0), Some(1.0), None, None]);
558 }
559
560 #[test]
561 fn negative_cycle_directed_errors() {
562 let mut g = Graph::new(3, true).unwrap();
564 g.add_edge(0, 1).unwrap();
565 g.add_edge(1, 2).unwrap();
566 g.add_edge(2, 0).unwrap();
567 let err = bellman_ford_distances(&g, 0, &[1.0, 1.0, -3.0]).unwrap_err();
568 assert!(matches!(err, IgraphError::InvalidArgument(_)));
569 }
570
571 #[test]
572 fn negative_cycle_unreachable_does_not_error() {
573 let mut g = Graph::new(4, true).unwrap();
579 g.add_edge(0, 1).unwrap();
580 g.add_edge(2, 3).unwrap();
581 g.add_edge(3, 2).unwrap();
582 let d = bellman_ford_distances(&g, 0, &[1.0, -1.0, -1.0]).unwrap();
583 assert_eq!(d, vec![Some(0.0), Some(1.0), None, None]);
584 }
585
586 #[test]
587 fn zero_weights_ok() {
588 let mut g = Graph::with_vertices(3);
589 g.add_edge(0, 1).unwrap();
590 g.add_edge(1, 2).unwrap();
591 let d = bellman_ford_distances(&g, 0, &[0.0, 0.0]).unwrap();
592 assert_eq!(d, vec![Some(0.0), Some(0.0), Some(0.0)]);
593 }
594
595 #[test]
596 fn source_out_of_range_errors() {
597 let g = Graph::with_vertices(3);
598 let err = bellman_ford_distances(&g, 99, &[]).unwrap_err();
599 assert!(matches!(
600 err,
601 IgraphError::VertexOutOfRange { id: 99, n: 3 }
602 ));
603 }
604
605 #[test]
606 fn weights_size_mismatch_errors() {
607 let mut g = Graph::with_vertices(2);
608 g.add_edge(0, 1).unwrap();
609 let err = bellman_ford_distances(&g, 0, &[1.0, 2.0]).unwrap_err();
610 assert!(matches!(err, IgraphError::InvalidArgument(_)));
611 }
612
613 #[test]
614 fn nan_weight_errors() {
615 let mut g = Graph::with_vertices(2);
616 g.add_edge(0, 1).unwrap();
617 let err = bellman_ford_distances(&g, 0, &[f64::NAN]).unwrap_err();
618 assert!(matches!(err, IgraphError::InvalidArgument(_)));
619 }
620
621 #[test]
622 fn in_mode_walks_reverse_edges() {
623 let mut g = Graph::new(3, true).unwrap();
625 g.add_edge(0, 1).unwrap();
626 g.add_edge(1, 2).unwrap();
627 let d = bellman_ford_distances_with_mode(&g, 2, &[3.0, -1.0], DijkstraMode::In).unwrap();
628 assert_eq!(d, vec![Some(2.0), Some(-1.0), Some(0.0)]);
630 }
631
632 #[test]
633 fn all_mode_ignores_direction() {
634 let mut g = Graph::new(2, true).unwrap();
636 g.add_edge(0, 1).unwrap();
637 let d = bellman_ford_distances_with_mode(&g, 1, &[5.0], DijkstraMode::All).unwrap();
638 assert_eq!(d, vec![Some(5.0), Some(0.0)]);
639 }
640
641 #[test]
642 fn infinity_weight_ignored() {
643 let mut g = Graph::with_vertices(3);
645 g.add_edge(0, 1).unwrap();
646 g.add_edge(0, 2).unwrap();
647 let d = bellman_ford_distances(&g, 0, &[1.0, f64::INFINITY]).unwrap();
648 assert_eq!(d, vec![Some(0.0), Some(1.0), None]);
649 }
650
651 #[test]
654 fn path_to_simple_directed() {
655 let mut g = Graph::new(4, true).unwrap();
656 g.add_edge(0, 1).unwrap(); g.add_edge(1, 2).unwrap(); g.add_edge(0, 2).unwrap(); g.add_edge(2, 3).unwrap(); let w = [3.0, -1.0, 5.0, 1.0];
661 let (vs, es) = bellman_ford_path_to(&g, 0, 3, &w).unwrap().unwrap();
662 assert_eq!(vs, vec![0, 1, 2, 3]);
663 assert_eq!(es, vec![0, 1, 3]);
664 }
665
666 #[test]
667 fn path_to_source_equals_target() {
668 let mut g = Graph::with_vertices(3);
669 g.add_edge(0, 1).unwrap();
670 let (vs, es) = bellman_ford_path_to(&g, 0, 0, &[1.0]).unwrap().unwrap();
671 assert_eq!(vs, vec![0]);
672 assert!(es.is_empty());
673 }
674
675 #[test]
676 fn path_to_unreachable() {
677 let mut g = Graph::new(3, true).unwrap();
678 g.add_edge(0, 1).unwrap();
679 let result = bellman_ford_path_to(&g, 0, 2, &[1.0]).unwrap();
680 assert!(result.is_none());
681 }
682
683 #[test]
684 fn path_to_negative_cycle_errors() {
685 let mut g = Graph::new(3, true).unwrap();
686 g.add_edge(0, 1).unwrap();
687 g.add_edge(1, 2).unwrap();
688 g.add_edge(2, 0).unwrap();
689 let err = bellman_ford_path_to(&g, 0, 2, &[1.0, 1.0, -3.0]).unwrap_err();
690 assert!(matches!(err, IgraphError::InvalidArgument(_)));
691 }
692
693 #[test]
694 fn path_to_prefers_negative_shortcut() {
695 let mut g = Graph::new(3, true).unwrap();
697 g.add_edge(0, 1).unwrap(); g.add_edge(1, 2).unwrap(); g.add_edge(0, 2).unwrap(); let (vs, es) = bellman_ford_path_to(&g, 0, 2, &[1.0, -1.0, 5.0])
701 .unwrap()
702 .unwrap();
703 assert_eq!(vs, vec![0, 1, 2]);
704 assert_eq!(es, vec![0, 1]);
705 }
706
707 #[test]
708 fn path_to_with_in_mode() {
709 let mut g = Graph::new(3, true).unwrap();
711 g.add_edge(0, 1).unwrap(); g.add_edge(1, 2).unwrap(); let (vs, es) = bellman_ford_path_to_with_mode(&g, 2, 0, &[3.0, -1.0], DijkstraMode::In)
714 .unwrap()
715 .unwrap();
716 assert_eq!(vs, vec![2, 1, 0]);
717 assert_eq!(es, vec![1, 0]);
718 }
719
720 #[test]
721 fn path_to_undirected_negative_cycle() {
722 let mut g = Graph::with_vertices(3);
725 g.add_edge(0, 1).unwrap(); g.add_edge(1, 2).unwrap(); g.add_edge(0, 2).unwrap(); let err = bellman_ford_path_to(&g, 0, 2, &[2.0, -1.0, 5.0]).unwrap_err();
729 assert!(matches!(err, IgraphError::InvalidArgument(_)));
730 }
731
732 #[test]
733 fn path_to_multiple_hops() {
734 let mut g = Graph::new(4, true).unwrap();
736 g.add_edge(0, 1).unwrap(); g.add_edge(1, 2).unwrap(); g.add_edge(2, 3).unwrap(); g.add_edge(0, 3).unwrap(); let (vs, es) = bellman_ford_path_to(&g, 0, 3, &[1.0, 1.0, 1.0, 10.0])
741 .unwrap()
742 .unwrap();
743 assert_eq!(vs, vec![0, 1, 2, 3]);
744 assert_eq!(es, vec![0, 1, 2]);
745 }
746
747 #[test]
750 fn paths_multi_target_directed() {
751 let mut g = Graph::new(4, true).unwrap();
752 g.add_edge(0, 1).unwrap(); g.add_edge(1, 2).unwrap(); g.add_edge(0, 2).unwrap(); g.add_edge(2, 3).unwrap(); let w = [3.0, -1.0, 5.0, 1.0];
757 let results = bellman_ford_paths(&g, 0, &[1, 2, 3], &w).unwrap();
758 assert_eq!(results.len(), 3);
759 let (vs, es) = results[0].as_ref().unwrap();
761 assert_eq!(*vs, vec![0, 1]);
762 assert_eq!(*es, vec![0]);
763 let (vs, es) = results[1].as_ref().unwrap();
765 assert_eq!(*vs, vec![0, 1, 2]);
766 assert_eq!(*es, vec![0, 1]);
767 let (vs, es) = results[2].as_ref().unwrap();
769 assert_eq!(*vs, vec![0, 1, 2, 3]);
770 assert_eq!(*es, vec![0, 1, 3]);
771 }
772
773 #[test]
774 fn paths_source_in_targets() {
775 let mut g = Graph::new(3, true).unwrap();
776 g.add_edge(0, 1).unwrap();
777 g.add_edge(1, 2).unwrap();
778 let results = bellman_ford_paths(&g, 0, &[0, 2], &[1.0, 1.0]).unwrap();
779 let (vs, es) = results[0].as_ref().unwrap();
781 assert_eq!(*vs, vec![0]);
782 assert!(es.is_empty());
783 let (vs, es) = results[1].as_ref().unwrap();
785 assert_eq!(*vs, vec![0, 1, 2]);
786 assert_eq!(*es, vec![0, 1]);
787 }
788
789 #[test]
790 fn paths_unreachable_target() {
791 let mut g = Graph::new(4, true).unwrap();
792 g.add_edge(0, 1).unwrap();
793 let results = bellman_ford_paths(&g, 0, &[1, 2, 3], &[1.0]).unwrap();
795 assert!(results[0].is_some());
796 assert!(results[1].is_none());
797 assert!(results[2].is_none());
798 }
799
800 #[test]
801 fn paths_empty_targets() {
802 let g = Graph::with_vertices(3);
803 let results = bellman_ford_paths(&g, 0, &[], &[]).unwrap();
804 assert!(results.is_empty());
805 }
806
807 #[test]
808 fn paths_negative_cycle_errors() {
809 let mut g = Graph::new(3, true).unwrap();
810 g.add_edge(0, 1).unwrap();
811 g.add_edge(1, 2).unwrap();
812 g.add_edge(2, 0).unwrap();
813 let err = bellman_ford_paths(&g, 0, &[2], &[1.0, 1.0, -3.0]).unwrap_err();
814 assert!(matches!(err, IgraphError::InvalidArgument(_)));
815 }
816
817 #[test]
818 fn paths_duplicate_target() {
819 let mut g = Graph::new(3, true).unwrap();
820 g.add_edge(0, 1).unwrap();
821 g.add_edge(1, 2).unwrap();
822 let results = bellman_ford_paths(&g, 0, &[2, 2], &[1.0, 1.0]).unwrap();
823 assert_eq!(results.len(), 2);
824 assert_eq!(results[0], results[1]);
825 }
826
827 #[test]
828 fn paths_with_in_mode() {
829 let mut g = Graph::new(3, true).unwrap();
830 g.add_edge(0, 1).unwrap(); g.add_edge(1, 2).unwrap(); let results =
833 bellman_ford_paths_with_mode(&g, 2, &[0, 1], &[3.0, -1.0], DijkstraMode::In).unwrap();
834 let (vs, es) = results[1].as_ref().unwrap();
836 assert_eq!(*vs, vec![2, 1]);
837 assert_eq!(*es, vec![1]);
838 let (vs, es) = results[0].as_ref().unwrap();
840 assert_eq!(*vs, vec![2, 1, 0]);
841 assert_eq!(*es, vec![1, 0]);
842 }
843
844 #[test]
845 fn paths_agrees_with_path_to() {
846 let mut g = Graph::new(5, true).unwrap();
847 g.add_edge(0, 1).unwrap(); g.add_edge(1, 2).unwrap(); g.add_edge(0, 3).unwrap(); g.add_edge(3, 4).unwrap(); g.add_edge(2, 4).unwrap(); let w = [2.0, -1.0, 3.0, 1.0, 1.0];
853 let multi = bellman_ford_paths(&g, 0, &[1, 2, 3, 4], &w).unwrap();
854 for (i, &target) in [1u32, 2, 3, 4].iter().enumerate() {
855 let single = bellman_ford_path_to(&g, 0, target, &w).unwrap();
856 assert_eq!(multi[i], single, "mismatch for target {target}");
857 }
858 }
859
860 #[test]
861 fn paths_target_out_of_range() {
862 let g = Graph::with_vertices(3);
863 let err = bellman_ford_paths(&g, 0, &[99], &[]).unwrap_err();
864 assert!(matches!(
865 err,
866 IgraphError::VertexOutOfRange { id: 99, n: 3 }
867 ));
868 }
869}