1use crate::graph::{Edge, GraphArena, ScopedVar};
2
3#[derive(Debug, Clone, PartialEq, Eq)]
4pub enum EvalResult {
5 Success(Vec<(ScopedVar, i32)>),
6 NegativeCycle,
7 NumericOverflow,
8}
9
10pub fn evaluate_clause(edges: &[Edge]) -> EvalResult {
11 let mut vertices = Vec::new();
12 for edge in edges {
13 if !vertices.contains(&edge.source) {
14 vertices.push(edge.source.clone());
15 }
16 if !vertices.contains(&edge.target) {
17 vertices.push(edge.target.clone());
18 }
19 }
20
21 let v_count = vertices.len();
22 if v_count == 0 {
23 return EvalResult::Success(Vec::new());
24 }
25
26 let mut dist: Vec<i64> = vec![0; v_count];
27 let get_idx = |v: &ScopedVar| vertices.iter().position(|x| x == v).unwrap();
28
29 let indexed_edges: Vec<(usize, usize, i64)> = edges
30 .iter()
31 .map(|e| (get_idx(&e.source), get_idx(&e.target), e.weight as i64))
32 .collect();
33
34 for _ in 0..(v_count - 1) {
35 let mut updated = false;
36 for &(u, v, weight) in &indexed_edges {
37 if let Some(new_dist) = dist[u].checked_add(weight) {
38 if new_dist < dist[v] {
39 dist[v] = new_dist;
40 updated = true;
41 }
42 } else {
43 return EvalResult::NumericOverflow;
44 }
45 }
46 if !updated {
47 break;
48 }
49 }
50
51 for &(u, v, weight) in &indexed_edges {
52 if let Some(new_dist) = dist[u].checked_add(weight) {
53 if new_dist < dist[v] {
54 return EvalResult::NegativeCycle;
55 }
56 } else {
57 return EvalResult::NumericOverflow;
58 }
59 }
60
61 let final_dist = vertices.into_iter().zip(dist.into_iter().map(|d| d as i32)).collect();
62 EvalResult::Success(final_dist)
63}
64
65#[derive(Debug, Clone)]
66pub struct ExecutionLimits {
67 pub max_k_iterations: usize,
68 pub mcm: f64,
69}
70
71impl ExecutionLimits {
72 pub fn compute_for_graph(graph: &GraphArena) -> Option<Self> {
73 let n = graph.vars.len();
74 if n == 0 {
75 return None;
76 }
77
78 const INF: i64 = i64::MAX / 2;
79
80 let mut dp = vec![vec![INF; n]; n + 1];
83 for v in 0..n {
84 dp[0][v] = 0;
85 }
86
87 for k in 1..=n {
88 for &(u, v, w, _) in &graph.edges {
89 if dp[k - 1][u] == INF {
90 continue;
91 }
92 if let Some(new_dist) = dp[k - 1][u].checked_add(w as i64) {
93 if new_dist < dp[k][v] {
94 dp[k][v] = new_dist;
95 }
96 } else {
97 return None; }
99 }
100 }
101
102 let mut mcm: f64 = f64::INFINITY;
103 let mut has_cycle = false;
104
105 for v in 0..n {
106 if dp[n][v] == INF {
107 continue;
108 }
109 let mut min_val: f64 = f64::NEG_INFINITY;
110 for k in 0..n {
111 if dp[k][v] == INF {
112 continue;
113 }
114 let val = (dp[n][v] - dp[k][v]) as f64 / (n - k) as f64;
115 if val > min_val {
116 min_val = val;
117 }
118 }
119 if min_val < mcm {
120 mcm = min_val;
121 has_cycle = true;
122 }
123 }
124
125 if !has_cycle {
126 mcm = 0.0;
127 }
128
129 let max_iterations = if mcm < 0.0 {
131 0
133 } else {
134 n * 2
136 };
137
138 Some(ExecutionLimits {
139 max_k_iterations: max_iterations,
140 mcm,
141 })
142 }
143}