1use crate::ast::Var;
2use crate::graph::{GraphArena, ScopedVar};
3
4pub fn export_smt_lib(
5 arena: &GraphArena,
6 formula_name: &str,
7 collision_trace: Option<&str>,
8 sc_actions: &[String],
9 success_depths: Option<&[i32]>,
10) -> String {
11 let mut out = String::new();
12
13 out.push_str("; === BEGIN STRATIFICATION WITNESS ===\n");
14 out.push_str(&format!("(set-info :formula-name \"{}\")\n", escape_smt_string(formula_name)));
15
16 if let Some(trace) = collision_trace {
17 out.push_str(&format!("(set-info :extensionality-collision-trace \"{}\")\n", escape_smt_string(trace)));
18 }
19
20 if !sc_actions.is_empty() {
21 let actions_str = sc_actions.join("\n");
22 out.push_str(&format!("(set-info :sc-daemon-actions \"{}\")\n", escape_smt_string(&actions_str)));
23 }
24
25 if let Some(depths) = success_depths {
26 let mut depth_str = String::new();
27 for (i, d) in depths.iter().enumerate() {
28 let var_name = format!("v{}", i);
29 depth_str.push_str(&format!("{} -> {}\n", var_name, d));
30 }
31 out.push_str(&format!("(set-info :stratification-success-depths \"{}\")\n", escape_smt_string(depth_str.trim_end())));
32 }
33
34 out.push_str("(set-logic QF_LIA)\n\n");
35
36 for (i, var) in arena.vars.iter().enumerate() {
38 let name = format!("v{}", i);
39 let ScopedVar(v, depth) = var;
40 let orig_name = match v {
41 Var::Free(s) => s.clone(),
42 Var::Bound(idx) => format!("b{}", idx),
43 };
44 out.push_str(&format!("; original variable: {} depth: {}\n", escape_smt_string(&orig_name), depth));
45 out.push_str(&format!("(declare-fun {} () Int)\n", name));
46 }
47 out.push_str("\n");
48
49 for &(u, v, w, _) in &arena.edges {
52 let u_name = format!("v{}", u);
53 let v_name = format!("v{}", v);
54 out.push_str(&format!("(assert (<= (- {} {}) {}))\n", v_name, u_name, w));
55 }
56
57 out.push_str("\n(check-sat)\n");
58 out.push_str("(get-model)\n");
59 out.push_str("; === END STRATIFICATION WITNESS ===\n");
60
61 out
62}
63
64fn escape_smt_string(s: &str) -> String {
65 s.replace("\"", "\"\"")
66}