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(
116 graph: &Graph,
117 source: VertexId,
118 weights: &[f64],
119 mode: DijkstraMode,
120) -> IgraphResult<Vec<Option<f64>>> {
121 let n = graph.vcount();
122 if source >= n {
123 return Err(IgraphError::VertexOutOfRange { id: source, n });
124 }
125 validate_weights(graph, weights)?;
126
127 let n_usize = n as usize;
128 let mut dist: Vec<f64> = vec![f64::INFINITY; n_usize];
129 dist[source as usize] = 0.0;
130
131 let mut queue: VecDeque<VertexId> = VecDeque::with_capacity(n_usize);
134 let mut in_queue: Vec<bool> = vec![true; n_usize];
135 let mut num_queued: Vec<u32> = vec![0; n_usize];
136 for v in 0..n {
137 queue.push_back(v);
138 }
139
140 let n_u32 = n;
141 while let Some(j) = queue.pop_front() {
142 let j_idx = j as usize;
143 in_queue[j_idx] = false;
144 num_queued[j_idx] = num_queued[j_idx]
145 .checked_add(1)
146 .ok_or(IgraphError::Internal("num_queued overflow"))?;
147 if num_queued[j_idx] > n_u32 {
150 return Err(IgraphError::InvalidArgument(
151 "negative cycle reachable from source while running Bellman-Ford".to_string(),
152 ));
153 }
154
155 if !dist[j_idx].is_finite() {
157 continue;
158 }
159
160 let incidents = incident_for_mode(graph, j, mode)?;
161 for eid in incidents {
162 let w = weights[eid as usize];
163 if w == f64::INFINITY {
165 continue;
166 }
167 let target = graph.edge_other(eid, j)?;
168 let target_idx = target as usize;
169 let altdist = dist[j_idx] + w;
170 if altdist < dist[target_idx] {
171 dist[target_idx] = altdist;
172 if !in_queue[target_idx] {
173 in_queue[target_idx] = true;
174 queue.push_back(target);
175 }
176 }
177 }
178 }
179
180 Ok(dist
181 .into_iter()
182 .map(|d| if d.is_finite() { Some(d) } else { None })
183 .collect())
184}
185
186pub fn bellman_ford_path_to(
216 graph: &Graph,
217 source: VertexId,
218 target: VertexId,
219 weights: &[f64],
220) -> IgraphResult<Option<(Vec<VertexId>, Vec<EdgeId>)>> {
221 bellman_ford_path_to_with_mode(graph, source, target, weights, DijkstraMode::Out)
222}
223
224pub fn bellman_ford_path_to_with_mode(
235 graph: &Graph,
236 source: VertexId,
237 target: VertexId,
238 weights: &[f64],
239 mode: DijkstraMode,
240) -> IgraphResult<Option<(Vec<VertexId>, Vec<EdgeId>)>> {
241 let n = graph.vcount();
242 if source >= n {
243 return Err(IgraphError::VertexOutOfRange { id: source, n });
244 }
245 if target >= n {
246 return Err(IgraphError::VertexOutOfRange { id: target, n });
247 }
248 validate_weights(graph, weights)?;
249
250 if source == target {
251 return Ok(Some((vec![source], vec![])));
252 }
253
254 let n_usize = n as usize;
255 let mut dist: Vec<f64> = vec![f64::INFINITY; n_usize];
256 dist[source as usize] = 0.0;
257
258 let mut parent_edge: Vec<Option<EdgeId>> = vec![None; n_usize];
260
261 let mut queue: VecDeque<VertexId> = VecDeque::with_capacity(n_usize);
262 let mut in_queue: Vec<bool> = vec![true; n_usize];
263 let mut num_queued: Vec<u32> = vec![0; n_usize];
264 for v in 0..n {
265 queue.push_back(v);
266 }
267
268 while let Some(j) = queue.pop_front() {
269 let j_idx = j as usize;
270 in_queue[j_idx] = false;
271 num_queued[j_idx] = num_queued[j_idx]
272 .checked_add(1)
273 .ok_or(IgraphError::Internal("num_queued overflow"))?;
274 if num_queued[j_idx] > n {
275 return Err(IgraphError::InvalidArgument(
276 "negative cycle reachable from source while running Bellman-Ford".to_string(),
277 ));
278 }
279
280 if !dist[j_idx].is_finite() {
281 continue;
282 }
283
284 let incidents = incident_for_mode(graph, j, mode)?;
285 for eid in incidents {
286 let w = weights[eid as usize];
287 if w == f64::INFINITY {
288 continue;
289 }
290 let neighbor = graph.edge_other(eid, j)?;
291 let neighbor_idx = neighbor as usize;
292 let altdist = dist[j_idx] + w;
293 if altdist < dist[neighbor_idx] {
294 dist[neighbor_idx] = altdist;
295 parent_edge[neighbor_idx] = Some(eid);
296 if !in_queue[neighbor_idx] {
297 in_queue[neighbor_idx] = true;
298 queue.push_back(neighbor);
299 }
300 }
301 }
302 }
303
304 if !dist[target as usize].is_finite() {
306 return Ok(None);
307 }
308
309 let mut edges_rev: Vec<EdgeId> = Vec::new();
311 let mut vertices_rev: Vec<VertexId> = Vec::new();
312 let mut cur = target;
313 vertices_rev.push(cur);
314 while cur != source {
315 let eid = parent_edge[cur as usize].ok_or(IgraphError::Internal(
316 "bellman_ford_path_to: missing parent edge in path reconstruction",
317 ))?;
318 edges_rev.push(eid);
319 cur = graph.edge_other(eid, cur)?;
320 vertices_rev.push(cur);
321 }
322
323 vertices_rev.reverse();
324 edges_rev.reverse();
325 Ok(Some((vertices_rev, edges_rev)))
326}
327
328pub type BellmanFordPathEntry = Option<(Vec<VertexId>, Vec<EdgeId>)>;
331
332pub fn bellman_ford_paths(
371 graph: &Graph,
372 source: VertexId,
373 targets: &[VertexId],
374 weights: &[f64],
375) -> IgraphResult<Vec<BellmanFordPathEntry>> {
376 bellman_ford_paths_with_mode(graph, source, targets, weights, DijkstraMode::Out)
377}
378
379pub fn bellman_ford_paths_with_mode(
387 graph: &Graph,
388 source: VertexId,
389 targets: &[VertexId],
390 weights: &[f64],
391 mode: DijkstraMode,
392) -> IgraphResult<Vec<BellmanFordPathEntry>> {
393 let n = graph.vcount();
394 if source >= n {
395 return Err(IgraphError::VertexOutOfRange { id: source, n });
396 }
397 for &t in targets {
398 if t >= n {
399 return Err(IgraphError::VertexOutOfRange { id: t, n });
400 }
401 }
402 validate_weights(graph, weights)?;
403
404 let n_usize = n as usize;
405
406 let mut dist: Vec<f64> = vec![f64::INFINITY; n_usize];
408 dist[source as usize] = 0.0;
409
410 let mut parent_edge: Vec<Option<EdgeId>> = vec![None; n_usize];
411
412 let mut queue: VecDeque<VertexId> = VecDeque::with_capacity(n_usize);
413 let mut in_queue: Vec<bool> = vec![true; n_usize];
414 let mut num_queued: Vec<u32> = vec![0; n_usize];
415 for v in 0..n {
416 queue.push_back(v);
417 }
418
419 while let Some(j) = queue.pop_front() {
420 let j_idx = j as usize;
421 in_queue[j_idx] = false;
422 num_queued[j_idx] = num_queued[j_idx]
423 .checked_add(1)
424 .ok_or(IgraphError::Internal("num_queued overflow"))?;
425 if num_queued[j_idx] > n {
426 return Err(IgraphError::InvalidArgument(
427 "negative cycle reachable from source while running Bellman-Ford".to_string(),
428 ));
429 }
430
431 if !dist[j_idx].is_finite() {
432 continue;
433 }
434
435 let incidents = incident_for_mode(graph, j, mode)?;
436 for eid in incidents {
437 let w = weights[eid as usize];
438 if w == f64::INFINITY {
439 continue;
440 }
441 let neighbor = graph.edge_other(eid, j)?;
442 let neighbor_idx = neighbor as usize;
443 let altdist = dist[j_idx] + w;
444 if altdist < dist[neighbor_idx] {
445 dist[neighbor_idx] = altdist;
446 parent_edge[neighbor_idx] = Some(eid);
447 if !in_queue[neighbor_idx] {
448 in_queue[neighbor_idx] = true;
449 queue.push_back(neighbor);
450 }
451 }
452 }
453 }
454
455 let mut results: Vec<Option<(Vec<VertexId>, Vec<EdgeId>)>> = Vec::with_capacity(targets.len());
457 for &target in targets {
458 if target == source {
459 results.push(Some((vec![source], vec![])));
460 continue;
461 }
462 if !dist[target as usize].is_finite() {
463 results.push(None);
464 continue;
465 }
466 let mut edges_rev: Vec<EdgeId> = Vec::new();
467 let mut vertices_rev: Vec<VertexId> = Vec::new();
468 let mut cur = target;
469 vertices_rev.push(cur);
470 while cur != source {
471 let eid = parent_edge[cur as usize].ok_or(IgraphError::Internal(
472 "bellman_ford_paths: missing parent edge in path reconstruction",
473 ))?;
474 edges_rev.push(eid);
475 cur = graph.edge_other(eid, cur)?;
476 vertices_rev.push(cur);
477 }
478 vertices_rev.reverse();
479 edges_rev.reverse();
480 results.push(Some((vertices_rev, edges_rev)));
481 }
482
483 Ok(results)
484}
485
486#[cfg(test)]
487mod tests {
488 use super::*;
489
490 #[test]
491 fn positive_weights_match_dijkstra_triangle() {
492 let mut g = Graph::with_vertices(3);
495 g.add_edge(0, 1).unwrap();
496 g.add_edge(0, 2).unwrap();
497 g.add_edge(1, 2).unwrap();
498 let weights = [1.0, 4.0, 2.0];
499 let bf = bellman_ford_distances(&g, 0, &weights).unwrap();
500 assert_eq!(bf, vec![Some(0.0), Some(1.0), Some(3.0)]);
501 }
502
503 #[test]
504 fn negative_edge_directed_no_cycle() {
505 let mut g = Graph::new(3, true).unwrap();
507 g.add_edge(0, 1).unwrap();
508 g.add_edge(1, 2).unwrap();
509 let d = bellman_ford_distances(&g, 0, &[3.0, -1.0]).unwrap();
510 assert_eq!(d, vec![Some(0.0), Some(3.0), Some(2.0)]);
511 }
512
513 #[test]
514 fn unreachable_vertex_is_none() {
515 let mut g = Graph::with_vertices(4);
517 g.add_edge(0, 1).unwrap();
518 g.add_edge(2, 3).unwrap();
519 let d = bellman_ford_distances(&g, 0, &[1.0, 1.0]).unwrap();
520 assert_eq!(d, vec![Some(0.0), Some(1.0), None, None]);
521 }
522
523 #[test]
524 fn negative_cycle_directed_errors() {
525 let mut g = Graph::new(3, true).unwrap();
527 g.add_edge(0, 1).unwrap();
528 g.add_edge(1, 2).unwrap();
529 g.add_edge(2, 0).unwrap();
530 let err = bellman_ford_distances(&g, 0, &[1.0, 1.0, -3.0]).unwrap_err();
531 assert!(matches!(err, IgraphError::InvalidArgument(_)));
532 }
533
534 #[test]
535 fn negative_cycle_unreachable_does_not_error() {
536 let mut g = Graph::new(4, true).unwrap();
542 g.add_edge(0, 1).unwrap();
543 g.add_edge(2, 3).unwrap();
544 g.add_edge(3, 2).unwrap();
545 let d = bellman_ford_distances(&g, 0, &[1.0, -1.0, -1.0]).unwrap();
546 assert_eq!(d, vec![Some(0.0), Some(1.0), None, None]);
547 }
548
549 #[test]
550 fn zero_weights_ok() {
551 let mut g = Graph::with_vertices(3);
552 g.add_edge(0, 1).unwrap();
553 g.add_edge(1, 2).unwrap();
554 let d = bellman_ford_distances(&g, 0, &[0.0, 0.0]).unwrap();
555 assert_eq!(d, vec![Some(0.0), Some(0.0), Some(0.0)]);
556 }
557
558 #[test]
559 fn source_out_of_range_errors() {
560 let g = Graph::with_vertices(3);
561 let err = bellman_ford_distances(&g, 99, &[]).unwrap_err();
562 assert!(matches!(
563 err,
564 IgraphError::VertexOutOfRange { id: 99, n: 3 }
565 ));
566 }
567
568 #[test]
569 fn weights_size_mismatch_errors() {
570 let mut g = Graph::with_vertices(2);
571 g.add_edge(0, 1).unwrap();
572 let err = bellman_ford_distances(&g, 0, &[1.0, 2.0]).unwrap_err();
573 assert!(matches!(err, IgraphError::InvalidArgument(_)));
574 }
575
576 #[test]
577 fn nan_weight_errors() {
578 let mut g = Graph::with_vertices(2);
579 g.add_edge(0, 1).unwrap();
580 let err = bellman_ford_distances(&g, 0, &[f64::NAN]).unwrap_err();
581 assert!(matches!(err, IgraphError::InvalidArgument(_)));
582 }
583
584 #[test]
585 fn in_mode_walks_reverse_edges() {
586 let mut g = Graph::new(3, true).unwrap();
588 g.add_edge(0, 1).unwrap();
589 g.add_edge(1, 2).unwrap();
590 let d = bellman_ford_distances_with_mode(&g, 2, &[3.0, -1.0], DijkstraMode::In).unwrap();
591 assert_eq!(d, vec![Some(2.0), Some(-1.0), Some(0.0)]);
593 }
594
595 #[test]
596 fn all_mode_ignores_direction() {
597 let mut g = Graph::new(2, true).unwrap();
599 g.add_edge(0, 1).unwrap();
600 let d = bellman_ford_distances_with_mode(&g, 1, &[5.0], DijkstraMode::All).unwrap();
601 assert_eq!(d, vec![Some(5.0), Some(0.0)]);
602 }
603
604 #[test]
605 fn infinity_weight_ignored() {
606 let mut g = Graph::with_vertices(3);
608 g.add_edge(0, 1).unwrap();
609 g.add_edge(0, 2).unwrap();
610 let d = bellman_ford_distances(&g, 0, &[1.0, f64::INFINITY]).unwrap();
611 assert_eq!(d, vec![Some(0.0), Some(1.0), None]);
612 }
613
614 #[test]
617 fn path_to_simple_directed() {
618 let mut g = Graph::new(4, true).unwrap();
619 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];
624 let (vs, es) = bellman_ford_path_to(&g, 0, 3, &w).unwrap().unwrap();
625 assert_eq!(vs, vec![0, 1, 2, 3]);
626 assert_eq!(es, vec![0, 1, 3]);
627 }
628
629 #[test]
630 fn path_to_source_equals_target() {
631 let mut g = Graph::with_vertices(3);
632 g.add_edge(0, 1).unwrap();
633 let (vs, es) = bellman_ford_path_to(&g, 0, 0, &[1.0]).unwrap().unwrap();
634 assert_eq!(vs, vec![0]);
635 assert!(es.is_empty());
636 }
637
638 #[test]
639 fn path_to_unreachable() {
640 let mut g = Graph::new(3, true).unwrap();
641 g.add_edge(0, 1).unwrap();
642 let result = bellman_ford_path_to(&g, 0, 2, &[1.0]).unwrap();
643 assert!(result.is_none());
644 }
645
646 #[test]
647 fn path_to_negative_cycle_errors() {
648 let mut g = Graph::new(3, true).unwrap();
649 g.add_edge(0, 1).unwrap();
650 g.add_edge(1, 2).unwrap();
651 g.add_edge(2, 0).unwrap();
652 let err = bellman_ford_path_to(&g, 0, 2, &[1.0, 1.0, -3.0]).unwrap_err();
653 assert!(matches!(err, IgraphError::InvalidArgument(_)));
654 }
655
656 #[test]
657 fn path_to_prefers_negative_shortcut() {
658 let mut g = Graph::new(3, true).unwrap();
660 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])
664 .unwrap()
665 .unwrap();
666 assert_eq!(vs, vec![0, 1, 2]);
667 assert_eq!(es, vec![0, 1]);
668 }
669
670 #[test]
671 fn path_to_with_in_mode() {
672 let mut g = Graph::new(3, true).unwrap();
674 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)
677 .unwrap()
678 .unwrap();
679 assert_eq!(vs, vec![2, 1, 0]);
680 assert_eq!(es, vec![1, 0]);
681 }
682
683 #[test]
684 fn path_to_undirected_negative_cycle() {
685 let mut g = Graph::with_vertices(3);
688 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();
692 assert!(matches!(err, IgraphError::InvalidArgument(_)));
693 }
694
695 #[test]
696 fn path_to_multiple_hops() {
697 let mut g = Graph::new(4, true).unwrap();
699 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])
704 .unwrap()
705 .unwrap();
706 assert_eq!(vs, vec![0, 1, 2, 3]);
707 assert_eq!(es, vec![0, 1, 2]);
708 }
709
710 #[test]
713 fn paths_multi_target_directed() {
714 let mut g = Graph::new(4, true).unwrap();
715 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];
720 let results = bellman_ford_paths(&g, 0, &[1, 2, 3], &w).unwrap();
721 assert_eq!(results.len(), 3);
722 let (vs, es) = results[0].as_ref().unwrap();
724 assert_eq!(*vs, vec![0, 1]);
725 assert_eq!(*es, vec![0]);
726 let (vs, es) = results[1].as_ref().unwrap();
728 assert_eq!(*vs, vec![0, 1, 2]);
729 assert_eq!(*es, vec![0, 1]);
730 let (vs, es) = results[2].as_ref().unwrap();
732 assert_eq!(*vs, vec![0, 1, 2, 3]);
733 assert_eq!(*es, vec![0, 1, 3]);
734 }
735
736 #[test]
737 fn paths_source_in_targets() {
738 let mut g = Graph::new(3, true).unwrap();
739 g.add_edge(0, 1).unwrap();
740 g.add_edge(1, 2).unwrap();
741 let results = bellman_ford_paths(&g, 0, &[0, 2], &[1.0, 1.0]).unwrap();
742 let (vs, es) = results[0].as_ref().unwrap();
744 assert_eq!(*vs, vec![0]);
745 assert!(es.is_empty());
746 let (vs, es) = results[1].as_ref().unwrap();
748 assert_eq!(*vs, vec![0, 1, 2]);
749 assert_eq!(*es, vec![0, 1]);
750 }
751
752 #[test]
753 fn paths_unreachable_target() {
754 let mut g = Graph::new(4, true).unwrap();
755 g.add_edge(0, 1).unwrap();
756 let results = bellman_ford_paths(&g, 0, &[1, 2, 3], &[1.0]).unwrap();
758 assert!(results[0].is_some());
759 assert!(results[1].is_none());
760 assert!(results[2].is_none());
761 }
762
763 #[test]
764 fn paths_empty_targets() {
765 let g = Graph::with_vertices(3);
766 let results = bellman_ford_paths(&g, 0, &[], &[]).unwrap();
767 assert!(results.is_empty());
768 }
769
770 #[test]
771 fn paths_negative_cycle_errors() {
772 let mut g = Graph::new(3, true).unwrap();
773 g.add_edge(0, 1).unwrap();
774 g.add_edge(1, 2).unwrap();
775 g.add_edge(2, 0).unwrap();
776 let err = bellman_ford_paths(&g, 0, &[2], &[1.0, 1.0, -3.0]).unwrap_err();
777 assert!(matches!(err, IgraphError::InvalidArgument(_)));
778 }
779
780 #[test]
781 fn paths_duplicate_target() {
782 let mut g = Graph::new(3, true).unwrap();
783 g.add_edge(0, 1).unwrap();
784 g.add_edge(1, 2).unwrap();
785 let results = bellman_ford_paths(&g, 0, &[2, 2], &[1.0, 1.0]).unwrap();
786 assert_eq!(results.len(), 2);
787 assert_eq!(results[0], results[1]);
788 }
789
790 #[test]
791 fn paths_with_in_mode() {
792 let mut g = Graph::new(3, true).unwrap();
793 g.add_edge(0, 1).unwrap(); g.add_edge(1, 2).unwrap(); let results =
796 bellman_ford_paths_with_mode(&g, 2, &[0, 1], &[3.0, -1.0], DijkstraMode::In).unwrap();
797 let (vs, es) = results[1].as_ref().unwrap();
799 assert_eq!(*vs, vec![2, 1]);
800 assert_eq!(*es, vec![1]);
801 let (vs, es) = results[0].as_ref().unwrap();
803 assert_eq!(*vs, vec![2, 1, 0]);
804 assert_eq!(*es, vec![1, 0]);
805 }
806
807 #[test]
808 fn paths_agrees_with_path_to() {
809 let mut g = Graph::new(5, true).unwrap();
810 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];
816 let multi = bellman_ford_paths(&g, 0, &[1, 2, 3, 4], &w).unwrap();
817 for (i, &target) in [1u32, 2, 3, 4].iter().enumerate() {
818 let single = bellman_ford_path_to(&g, 0, target, &w).unwrap();
819 assert_eq!(multi[i], single, "mismatch for target {target}");
820 }
821 }
822
823 #[test]
824 fn paths_target_out_of_range() {
825 let g = Graph::with_vertices(3);
826 let err = bellman_ford_paths(&g, 0, &[99], &[]).unwrap_err();
827 assert!(matches!(
828 err,
829 IgraphError::VertexOutOfRange { id: 99, n: 3 }
830 ));
831 }
832}