use alloc::string::{String, ToString};
use alloc::vec::Vec;
use crate::cmd_context::Session;
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum SatResult {
Sat,
Unsat,
Unknown,
}
#[derive(Default)]
pub struct Solver {
session: Session,
}
impl Solver {
pub fn new() -> Solver {
Solver {
session: Session::new(),
}
}
pub fn eval(&mut self, script: &str) -> Result<Vec<String>, String> {
self.session.eval(script)
}
pub fn assert(&mut self, script: &str) -> Result<(), String> {
if let Some(line) = self.eval(script)?.into_iter().next() {
return Err(alloc::format!("unexpected response from assert: {line}"));
}
Ok(())
}
pub fn check(&mut self) -> Result<SatResult, String> {
match self.eval("(check-sat)")?.first().map(String::as_str) {
Some("sat") => Ok(SatResult::Sat),
Some("unsat") => Ok(SatResult::Unsat),
Some("unknown") => Ok(SatResult::Unknown),
other => Err(alloc::format!("unexpected check-sat response: {other:?}")),
}
}
pub fn get_value(&mut self, term: &str) -> Result<String, String> {
let out = self.eval(&alloc::format!("(get-value ({term}))"))?;
let line = out
.first()
.ok_or_else(|| "get-value produced no response".to_string())?;
let peel = |s: &str| -> String {
s.trim()
.strip_prefix('(')
.and_then(|s| s.strip_suffix(')'))
.unwrap_or(s)
.trim()
.to_string()
};
let inner = peel(&peel(line));
Ok(inner
.strip_prefix(term)
.map(str::trim)
.unwrap_or(&inner)
.to_string())
}
pub fn push(&mut self) -> Result<(), String> {
self.assert("(push)")
}
pub fn pop(&mut self) -> Result<(), String> {
self.assert("(pop)")
}
pub fn reset(&mut self) -> Result<(), String> {
self.assert("(reset)")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn solver_incremental() {
let mut s = Solver::new();
s.assert("(declare-const x Int)(assert (> x 0))").unwrap();
assert_eq!(s.check().unwrap(), SatResult::Sat);
s.push().unwrap();
s.assert("(assert (< x 0))").unwrap();
assert_eq!(s.check().unwrap(), SatResult::Unsat);
s.pop().unwrap();
assert_eq!(s.check().unwrap(), SatResult::Sat);
}
#[test]
fn solver_get_value() {
let mut s = Solver::new();
s.assert("(declare-const b (_ BitVec 8))(assert (= (bvadd b #x01) #x10))")
.unwrap();
assert_eq!(s.check().unwrap(), SatResult::Sat);
assert_eq!(s.get_value("b").unwrap(), "#x0f");
}
#[test]
fn solver_optimization() {
let mut s = Solver::new();
s.assert("(declare-const x Int)(assert (<= x 10))(assert (>= x 0))(maximize x)")
.unwrap();
assert_eq!(s.check().unwrap(), SatResult::Sat);
assert_eq!(s.get_value("x").unwrap(), "10");
}
}