use miette::IntoDiagnostic;
use smtlib::{backend::z3_binary::Z3Binary, funs::Fun, sorts::Sort, terms, Bool, Storage};
fn main() -> miette::Result<()> {
miette::set_panic_hook();
let st = Storage::new();
let mut solver = smtlib::Solver::new(&st, Z3Binary::new("z3").into_diagnostic()?)?;
solver.set_logger((
|cmd: smtlib_lowlevel::ast::Command<'_>| println!("> {cmd}"),
|_cmd: smtlib_lowlevel::ast::Command<'_>, res: &str| println!(" => {}", res.trim_end()),
));
solver.set_logic(smtlib::Logic::Custom("UF".to_string()))?;
let s_sort = Sort::new(&st, "S");
let human = Fun::new(&st, "Human", vec![s_sort], Bool::sort());
let mortal = Fun::new(&st, "Mortal", vec![s_sort], Bool::sort());
solver.declare_fun(&human)?;
solver.declare_fun(&mortal)?;
let socrates = s_sort.new_const(&st, "Socrates");
let x = s_sort.new_const(&st, "x");
let human_x = human.call(&[x.into()])?;
let mortal_x = mortal.call(&[x.into()])?;
let human_implies_mortal = human_x.as_bool()?.implies(mortal_x.as_bool()?);
let all_humans_mortal = terms::forall(&st, vec![x], human_implies_mortal);
solver.assert(all_humans_mortal)?;
let human_socrates = human.call(&[socrates.into()])?;
solver.assert(human_socrates.as_bool()?)?;
let mortal_socrates = mortal.call(&[socrates.into()])?;
let not_mortal_socrates = !mortal_socrates.as_bool()?;
solver.assert(not_mortal_socrates)?;
let result = solver.check_sat()?;
println!(
r#"
Result: {result}
Explanation:
The result should be 'unsat' (unsatisfiable), which proves the syllogism:
1. All humans are mortal {all_humans_mortal}
2. Socrates is human {human_socrates}
3. Therefore, Socrates is mortal {mortal_socrates}
By asserting the negation of the conclusion {not_mortal_socrates}
and getting 'unsat', we've proven that the conclusion must be true."#
);
Ok(())
}