use num::rational::Ratio;
use petgraph::graph::DiGraph;
use petgraph::prelude::*;
use crate::dijkstra::{dijkstra, dijkstra_path};
use crate::neg_cycle::NegCycleFinder;
use crate::parametric::{MaxParametricSolver, ParametricAPI};
use petgraph::graph::EdgeReference;
#[test]
fn test_integration_bellman_ford_with_neg_cycle() {
use petgraph::algo::{bellman_ford, find_negative_cycle};
let graph = Graph::<(), f64, Directed>::from_edges([
(0, 1, 2.0),
(1, 2, 3.0),
(2, 0, -6.0), ]);
let result = bellman_ford(&graph, NodeIndex::new(0));
assert!(result.is_err());
let cycle = find_negative_cycle(&graph, NodeIndex::new(0));
assert!(cycle.is_some());
}
#[test]
fn test_integration_neg_cycle_finder_with_parametric() {
struct TestAPI;
impl ParametricAPI<(), Ratio<i32>> for TestAPI {
fn distance(&self, ratio: &Ratio<i32>, edge: &EdgeReference<Ratio<i32>>) -> Ratio<i32> {
*edge.weight() - *ratio
}
fn zero_cancel(&self, cycle: &[EdgeReference<Ratio<i32>>]) -> Ratio<i32> {
let sum_a: Ratio<i32> = cycle.iter().map(|e| *e.weight()).sum();
let sum_b = Ratio::new(cycle.len() as i32, 1);
sum_a / sum_b
}
}
let digraph = DiGraph::<(), Ratio<i32>>::from_edges([
(0, 1, Ratio::new(1, 1)),
(1, 2, Ratio::new(1, 1)),
(2, 0, Ratio::new(-3, 1)),
]);
let mut solver = MaxParametricSolver::new(&digraph, TestAPI);
let mut dist = [Ratio::new(0, 1), Ratio::new(0, 1), Ratio::new(0, 1)];
let mut ratio = Ratio::new(0, 1);
let cycle = solver.run(&mut dist, &mut ratio);
assert!(!cycle.is_empty());
assert_eq!(ratio, Ratio::new(-1, 3));
}
#[test]
fn test_integration_dijkstra_neg_cycle_comparison() {
use petgraph::algo::bellman_ford;
let graph = Graph::<(), f64, Directed>::from_edges([
(0, 1, 4.0),
(0, 2, 2.0),
(1, 2, 1.0),
(1, 3, 5.0),
(2, 3, 8.0),
(2, 4, 10.0),
(3, 4, 2.0),
]);
let source = NodeIndex::new(0);
let dijkstra_result = dijkstra(&graph, source).unwrap();
let bellman_ford_result = bellman_ford(&graph, source).unwrap();
assert_eq!(dijkstra_result.distances, bellman_ford_result.distances);
}
#[test]
fn test_integration_complete_workflow() {
use petgraph::algo::bellman_ford;
let mut graph = Graph::new();
let nodes: Vec<NodeIndex> = (0..6).map(|_| graph.add_node(())).collect();
let edges = [
(0, 1, 2.0),
(0, 2, 5.0),
(1, 2, 2.0),
(1, 3, 4.0),
(2, 3, 1.0),
(2, 4, 7.0),
(3, 4, 3.0),
(3, 5, 6.0),
(4, 5, 1.0),
];
graph.extend_with_edges(edges.iter().cloned());
let source = nodes[0];
let dijkstra_result = dijkstra(&graph, source).unwrap();
let bellman_ford_result = bellman_ford(&graph, source).unwrap();
for (i, (d_dist, bf_dist)) in dijkstra_result
.distances
.iter()
.zip(bellman_ford_result.distances.iter())
.enumerate()
{
assert_eq!(*d_dist, *bf_dist, "Distance mismatch at node {}", i);
}
let target = nodes[5];
let path = dijkstra_path(&graph, source, target);
assert!(path.is_some());
let path = path.unwrap();
let mut path_cost = 0.0;
for i in 0..path.len() - 1 {
let u = path[i];
let v = path[i + 1];
let edge = graph.find_edge(u, v).expect("Edge not found");
path_cost += graph[edge];
}
assert_eq!(path_cost, dijkstra_result.distances[target.index()]);
}
#[test]
fn test_integration_large_graph_performance() {
use petgraph::Graph;
let mut graph = Graph::new();
let num_nodes = 100;
let nodes: Vec<NodeIndex> = (0..num_nodes).map(|_| graph.add_node(())).collect();
for i in 0..num_nodes {
for j in i + 1..std::cmp::min(i + 5, num_nodes) {
graph.add_edge(nodes[i], nodes[j], (j - i) as f64);
}
}
let source = nodes[0];
let result = dijkstra(&graph, source);
assert!(result.is_ok());
let paths = result.unwrap();
assert_eq!(paths.distances.len(), num_nodes);
assert_eq!(paths.distances[source.index()], 0.0);
for (i, &dist) in paths.distances.iter().enumerate() {
assert!(dist >= 0.0, "Negative distance at node {}", i);
}
}
#[test]
fn test_integration_parametric_with_neg_cycle_finder() {
struct LinearAPI;
impl ParametricAPI<(), Ratio<i32>> for LinearAPI {
fn distance(&self, ratio: &Ratio<i32>, edge: &EdgeReference<Ratio<i32>>) -> Ratio<i32> {
*edge.weight() - *ratio
}
fn zero_cancel(&self, cycle: &[EdgeReference<Ratio<i32>>]) -> Ratio<i32> {
let sum_a: Ratio<i32> = cycle.iter().map(|e| *e.weight()).sum();
let sum_b = Ratio::new(cycle.len() as i32, 1);
sum_a / sum_b
}
}
let digraph = DiGraph::<(), Ratio<i32>>::from_edges([
(0, 1, Ratio::new(2, 1)),
(1, 2, Ratio::new(3, 1)),
(2, 0, Ratio::new(-7, 1)),
(0, 3, Ratio::new(1, 1)),
(3, 4, Ratio::new(1, 1)),
(4, 5, Ratio::new(1, 1)),
(5, 3, Ratio::new(-4, 1)),
]);
let mut ncf = NegCycleFinder::new(&digraph);
let mut dist = [
Ratio::new(0, 1),
Ratio::new(0, 1),
Ratio::new(0, 1),
Ratio::new(0, 1),
Ratio::new(0, 1),
Ratio::new(0, 1),
];
let neg_cycle = ncf.howard(&mut dist, |e| *e.weight());
assert!(neg_cycle.is_some());
let mut solver = MaxParametricSolver::new(&digraph, LinearAPI);
let mut dist2 = [
Ratio::new(0, 1),
Ratio::new(0, 1),
Ratio::new(0, 1),
Ratio::new(0, 1),
Ratio::new(0, 1),
Ratio::new(0, 1),
];
let mut ratio = Ratio::new(0, 1);
let cycle = solver.run(&mut dist2, &mut ratio);
assert!(!cycle.is_empty());
}