extern crate env_logger;
#[macro_use]
extern crate log;
extern crate z3;
use std::convert::TryInto;
use std::ops::Add;
use std::time::Duration;
use z3::ast::{Array, Ast, Bool, Int, BV};
use z3::*;
#[cfg(feature = "arbitrary-size-numeral")]
extern crate num;
#[cfg(feature = "arbitrary-size-numeral")]
use num::bigint::BigInt;
#[cfg(feature = "arbitrary-size-numeral")]
use num::rational::BigRational;
#[cfg(feature = "arbitrary-size-numeral")]
use std::str::FromStr;
#[test]
fn test_config() {
let _ = env_logger::try_init();
let mut c = Config::new();
c.set_proof_generation(true);
}
#[test]
fn test_context() {
let _ = env_logger::try_init();
let mut cfg = Config::new();
cfg.set_proof_generation(true);
let _ = Context::new(&cfg);
}
#[test]
fn test_sorts_and_symbols() {
let _ = env_logger::try_init();
let cfg = Config::new();
let ctx = Context::new(&cfg);
let _ = ast::Int::new_const(&ctx, "x");
let _ = ast::Int::new_const(&ctx, "y");
}
#[test]
fn test_solving() {
let _ = env_logger::try_init();
let cfg = Config::new();
let ctx = Context::new(&cfg);
let x = ast::Int::new_const(&ctx, "x");
let y = ast::Int::new_const(&ctx, "y");
let solver = Solver::new(&ctx);
solver.assert(&x.gt(&y));
assert_eq!(solver.check(), SatResult::Sat);
}
#[test]
fn test_solving_for_model() {
let _ = env_logger::try_init();
let cfg = Config::new();
let ctx = Context::new(&cfg);
let x = ast::Int::new_const(&ctx, "x");
let y = ast::Int::new_const(&ctx, "y");
let zero = ast::Int::from_i64(&ctx, 0);
let two = ast::Int::from_i64(&ctx, 2);
let seven = ast::Int::from_i64(&ctx, 7);
let solver = Solver::new(&ctx);
solver.assert(&x.gt(&y));
solver.assert(&y.gt(&zero));
solver.assert(&y.rem(&seven)._eq(&two));
let x_plus_two = ast::Int::add(&ctx, &[&x, &two]);
solver.assert(&x_plus_two.gt(&seven));
assert_eq!(solver.check(), SatResult::Sat);
let model = solver.get_model().unwrap();
let xv = model.eval(&x, true).unwrap().as_i64().unwrap();
let yv = model.eval(&y, true).unwrap().as_i64().unwrap();
info!("x: {}", xv);
info!("y: {}", yv);
assert!(xv > yv);
assert!(yv % 7 == 2);
assert!(xv + 2 > 7);
}
#[test]
fn test_solving_for_model_cloned() {
let _ = env_logger::try_init();
let cfg = Config::new();
let ctx = Context::new(&cfg);
let x = ast::Int::new_const(&ctx, "x");
let y = ast::Int::new_const(&ctx, "y");
let zero = ast::Int::from_i64(&ctx, 0);
let two = ast::Int::from_i64(&ctx, 2);
let seven = ast::Int::from_i64(&ctx, 7);
let solver = Solver::new(&ctx);
solver.assert(&x.gt(&y));
solver.assert(&y.gt(&zero));
solver.assert(&y.rem(&seven)._eq(&two));
let x_plus_two = ast::Int::add(&ctx, &[&x, &two]);
solver.assert(&x_plus_two.gt(&seven));
let cloned = solver.clone();
assert_eq!(cloned.check(), SatResult::Sat);
let model = cloned.get_model().unwrap();
let xv = model.eval(&x, true).unwrap().as_i64().unwrap();
let yv = model.eval(&y, true).unwrap().as_i64().unwrap();
info!("x: {}", xv);
info!("y: {}", yv);
assert!(xv > yv);
assert!(yv % 7 == 2);
assert!(xv + 2 > 7);
}
#[test]
fn test_cloning_ast() {
let _ = env_logger::try_init();
let cfg = Config::new();
let ctx = Context::new(&cfg);
let x = ast::Int::new_const(&ctx, "x");
let y = x.clone();
let zero = ast::Int::from_i64(&ctx, 0);
let solver = Solver::new(&ctx);
solver.assert(&x._eq(&zero));
assert_eq!(solver.check(), SatResult::Sat);
let model = solver.get_model().unwrap();
let xv = model.eval(&x, true).unwrap().as_i64().unwrap();
let yv = model.eval(&y, true).unwrap().as_i64().unwrap();
assert_eq!(xv, 0);
assert_eq!(yv, 0);
}
#[test]
fn test_format() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let ast = ast::Int::new_const(&ctx, "x");
assert_eq!("x", format!("{}", ast));
let int = Sort::int(&ctx);
assert_eq!("Int", format!("{}", int));
}
#[test]
fn test_bitvectors() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let a = ast::BV::new_const(&ctx, "a", 64);
let b = ast::BV::new_const(&ctx, "b", 64);
let two = ast::BV::from_i64(&ctx, 2, 64);
let solver = Solver::new(&ctx);
solver.assert(&a.bvsgt(&b));
solver.assert(&b.bvsgt(&two));
let b_plus_two = b.bvadd(&two);
solver.assert(&b_plus_two.bvsgt(&a));
assert_eq!(solver.check(), SatResult::Sat);
let model = solver.get_model().unwrap();
let av = model.eval(&a, true).unwrap().as_i64().unwrap();
let bv = model.eval(&b, true).unwrap().as_i64().unwrap();
assert!(av > bv);
assert!(bv > 2);
assert!(bv + 2 > av);
}
#[test]
fn test_bitvector_from_str() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let a = ast::BV::new_const(&ctx, "a", 129);
let b = ast::BV::from_str(&ctx, 129, "340282366920938463463374607431768211456").unwrap();
let solver = Solver::new(&ctx);
solver.assert(&a._eq(&b));
assert_eq!(solver.check(), SatResult::Sat);
let model = solver.get_model().unwrap();
let av = model.eval(&a, true).unwrap().to_string();
assert_eq!(av, "#b100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".to_string());
}
#[test]
fn test_floating_point_bits() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let float32 = ast::Float::new_const_float32(&ctx, "float32");
let float64 = ast::Float::new_const_double(&ctx, "float64");
let float128 = ast::Float::new_const(&ctx, "float128", 15, 113);
let i = ast::Int::new_const(&ctx, "int");
let exp32 = Sort::float_exponent_size(&float32.get_sort());
let sig32 = Sort::float_significand_size(&float32.get_sort());
let exp64 = Sort::float_exponent_size(&float64.get_sort());
let sig64 = Sort::float_significand_size(&float64.get_sort());
let exp128 = Sort::float_exponent_size(&float128.get_sort());
let sig128 = Sort::float_significand_size(&float128.get_sort());
let expi = Sort::float_exponent_size(&i.get_sort());
let sigi = Sort::float_significand_size(&i.get_sort());
assert!(exp32 == Some(8));
assert!(sig32 == Some(24));
assert!(exp64 == Some(11));
assert!(sig64 == Some(53));
assert!(exp128 == Some(15));
assert!(sig128 == Some(113));
assert!(expi.is_none());
assert!(sigi.is_none());
}
#[test]
fn test_ast_translate() {
let cfg = Config::new();
let source = Context::new(&cfg);
let a = ast::Int::new_const(&source, "a");
let destination = Context::new(&cfg);
let translated_a = a.translate(&destination);
let slv = Solver::new(&destination);
slv.assert(&translated_a._eq(&ast::Int::from_u64(&destination, 2)));
assert_eq!(slv.check(), SatResult::Sat);
slv.assert(&translated_a._eq(&ast::Int::from_u64(&destination, 3)));
assert_eq!(slv.check(), SatResult::Unsat);
}
#[test]
fn test_solver_new_from_smtlib2() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let problem = r#"
(declare-const x Real)
(declare-const y Real)
(declare-const z Real)
(assert (=( -(+(* 3 x) (* 2 y)) z) 1))
(assert (=(+( -(* 2 x) (* 2 y)) (* 4 z)) -2))
"#;
let solver = Solver::new(&ctx);
solver.from_string(problem);
assert_eq!(solver.check(), SatResult::Sat);
}
#[test]
fn test_solver_translate() {
let cfg = Config::new();
let source = Context::new(&cfg);
let a = ast::Int::new_const(&source, "a");
let destination = Context::new(&cfg);
let translated_a = a.translate(&destination);
let slv = Solver::new(&destination);
slv.assert(&translated_a._eq(&ast::Int::from_u64(&destination, 2)));
assert_eq!(slv.check(), SatResult::Sat);
let translated_slv = slv.translate(&source);
slv.assert(&translated_a._eq(&ast::Int::from_u64(&destination, 3)));
assert_eq!(slv.check(), SatResult::Unsat);
assert_eq!(translated_slv.check(), SatResult::Sat);
}
#[test]
fn test_model_translate() {
let cfg = Config::new();
let source = Context::new(&cfg);
let a = ast::Int::new_const(&source, "a");
let destination = Context::new(&cfg);
let translated_a = a.translate(&destination);
let slv = Solver::new(&source);
slv.assert(&a._eq(&ast::Int::from_u64(&source, 2)));
assert_eq!(slv.check(), SatResult::Sat);
let model = slv.get_model().unwrap();
assert_eq!(2, model.eval(&a, true).unwrap().as_i64().unwrap());
let translated_model = model.translate(&destination);
assert_eq!(
2,
translated_model
.eval(&translated_a, true)
.unwrap()
.as_i64()
.unwrap()
);
}
#[test]
fn test_pb_ops_model() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let x = ast::Bool::new_const(&ctx, "x");
let y = ast::Bool::new_const(&ctx, "y");
let solver = Solver::new(&ctx);
solver.push();
solver.assert(&ast::Bool::pb_eq(&ctx, &[(&x, 1), (&y, 1)], 1));
assert_eq!(solver.check(), SatResult::Sat);
let model = solver.get_model().unwrap();
let xv = model.eval(&x, true).unwrap().as_bool().unwrap();
let yv = model.eval(&y, true).unwrap().as_bool().unwrap();
info!("x: {}", xv);
info!("y: {}", yv);
assert!((xv && !yv) || (!xv && yv));
solver.pop(1);
solver.push();
solver.assert(&ast::Bool::pb_ge(&ctx, &[(&x, 1), (&y, 1)], 2));
assert_eq!(solver.check(), SatResult::Sat);
let model = solver.get_model().unwrap();
let xv = model.eval(&x, true).unwrap().as_bool().unwrap();
let yv = model.eval(&y, true).unwrap().as_bool().unwrap();
info!("x: {}", xv);
info!("y: {}", yv);
assert!(xv && yv);
solver.pop(1);
solver.assert(&ast::Bool::pb_le(&ctx, &[(&x, 1), (&y, 1)], 0));
assert_eq!(solver.check(), SatResult::Sat);
let model = solver.get_model().unwrap();
let xv = model.eval(&x, true).unwrap().as_bool().unwrap();
let yv = model.eval(&y, true).unwrap().as_bool().unwrap();
info!("x: {}", xv);
info!("y: {}", yv);
assert!(!xv && !yv);
}
#[test]
fn function_ref_count() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let solver = Solver::new(&ctx);
let int_sort = Sort::int(&ctx);
let _f = FuncDecl::new(&ctx, "f", &[&int_sort], &int_sort);
let _g = FuncDecl::new(&ctx, "g", &[&int_sort], &int_sort);
assert_eq!(solver.check(), SatResult::Sat);
}
#[test]
fn test_params() {
let _ = env_logger::try_init();
let cfg = Config::new();
let ctx = Context::new(&cfg);
let x = ast::Int::new_const(&ctx, "x");
let y = ast::Int::new_const(&ctx, "y");
let mut params = Params::new(&ctx);
params.set_bool("smt.mbqi", false);
params.set_f64("smt.qi.eager_threshold", 5.0);
params.set_u32("smt.qi.max_instances", 999);
let solver = Solver::new(&ctx);
solver.set_params(¶ms);
solver.assert(&x.gt(&y));
assert_eq!(solver.check(), SatResult::Sat);
}
#[test]
fn test_global_params() {
let _ = env_logger::try_init();
reset_all_global_params();
let val = get_global_param("iDontExist");
assert_eq!(val, None);
let val = get_global_param("verbose");
assert_eq!(val, Some("0".into()));
set_global_param("verbose", "1");
let val = get_global_param("verbose");
assert_eq!(val, Some("1".into()));
reset_all_global_params();
let val = get_global_param("verbose");
assert_eq!(val, Some("0".into()));
}
#[test]
fn test_substitution() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let x = ast::Real::new_const(&ctx, "x");
let y = ast::Real::new_const(&ctx, "y");
let z = ast::Real::new_const(&ctx, "z");
let x_plus_y = ast::Real::add(&ctx, &[&x, &y]);
let x_plus_z = ast::Real::add(&ctx, &[&x, &z]);
let substitutions = &[(&y, &z)];
assert!(x_plus_y.substitute(substitutions) == x_plus_z);
}
#[test]
fn test_real_cmp() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let solver = Solver::new(&ctx);
let x = ast::Real::new_const(&ctx, "x");
let x_plus_1 = ast::Real::add(&ctx, &[&x, &ast::Real::from_real(&ctx, 1, 1)]);
let forall = ast::forall_const(&ctx, &[&x], &[], &x.lt(&x_plus_1));
solver.assert(&forall);
assert_eq!(solver.check(), SatResult::Sat);
}
#[test]
fn test_float_add() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let solver = Solver::new(&ctx);
let x = ast::Float::new_const_float32(&ctx, "x");
let x_plus_one = ast::Float::round_towards_zero(&ctx).add(&x, &ast::Float::from_f32(&ctx, 1.0));
let y = ast::Float::from_f32(&ctx, std::f32::consts::PI);
solver.assert(&x_plus_one._eq(&y));
assert_eq!(solver.check(), SatResult::Sat);
}
#[test]
fn test_arbitrary_size_real() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let solver = Solver::new(&ctx);
let x = ast::Real::from_real_str(&ctx, "99999999999999999999998", "99999999999999999999999")
.unwrap();
let y = ast::Real::from_real(&ctx, 1, 1);
solver.assert(&x.lt(&y));
assert_eq!(solver.check(), SatResult::Sat);
}
#[test]
fn test_arbitrary_size_int() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let solver = Solver::new(&ctx);
let x = ast::Int::from_str(&ctx, "99999999999999999999998").unwrap();
let one = ast::Int::from_i64(&ctx, 1);
let y = ast::Int::from_str(&ctx, "99999999999999999999999").unwrap();
solver.assert(&ast::Int::add(&ctx, &[&x, &one])._eq(&y));
assert_eq!(solver.check(), SatResult::Sat);
}
#[cfg(feature = "arbitrary-size-numeral")]
#[test]
fn test_arbitrary_size_real_from_bigrational() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let solver = Solver::new(&ctx);
let x = ast::Real::from_real_str(&ctx, "99999999999999999999998", "99999999999999999999999")
.unwrap();
let num = BigInt::from_str("99999999999999999999998").unwrap();
let den = BigInt::from_str("99999999999999999999999").unwrap();
let ratio = BigRational::new(num, den);
let y = ast::Real::from_big_rational(&ctx, &ratio);
solver.assert(&x._eq(&y));
assert_eq!(solver.check(), SatResult::Sat);
}
#[cfg(feature = "arbitrary-size-numeral")]
#[test]
fn test_arbitrary_size_int_from_bigint() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let solver = Solver::new(&ctx);
let num1 = BigInt::from_str("99999999999999999999998").unwrap();
let x = ast::Int::from_big_int(&ctx, &num1);
let y = ast::Int::from_i64(&ctx, 1);
let num2 = BigInt::from_str("99999999999999999999999").unwrap();
let z = ast::Int::from_big_int(&ctx, &num2);
solver.assert(&ast::Int::add(&ctx, &[&x, &y])._eq(&z));
assert_eq!(solver.check(), SatResult::Sat);
}
#[test]
fn test_string_eq() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let solver = Solver::new(&ctx);
let x = ast::String::from_str(&ctx, "foo").unwrap();
let y = ast::String::from_str(&ctx, "foo").unwrap();
let z = ast::String::from_str(&ctx, "bar").unwrap();
let h = ast::String::new_const(&ctx, "h");
solver.assert(&x._eq(&y));
solver.assert(&x._eq(&z).not());
solver.assert(&h._eq(&x));
assert_eq!(solver.check(), SatResult::Sat);
solver.assert(&h._eq(&z));
assert_eq!(solver.check(), SatResult::Unsat)
}
#[test]
fn test_string_concat() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let solver = Solver::new(&ctx);
let x = ast::String::from_str(&ctx, "foo").unwrap();
let y = ast::String::from_str(&ctx, "bar").unwrap();
let z = ast::String::from_str(&ctx, "foobar").unwrap();
solver.assert(&ast::String::concat(&ctx, &[&x, &y])._eq(&z));
assert_eq!(solver.check(), SatResult::Sat)
}
#[test]
fn test_string_prefix() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let solver = Solver::new(&ctx);
let x = ast::String::from_str(&ctx, "foo").unwrap();
let y = ast::String::from_str(&ctx, "foobar").unwrap();
solver.assert(&x.prefix(&y));
assert_eq!(solver.check(), SatResult::Sat)
}
#[test]
fn test_string_suffix() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let solver = Solver::new(&ctx);
let x = ast::String::from_str(&ctx, "bar").unwrap();
let y = ast::String::from_str(&ctx, "foobar").unwrap();
solver.assert(&x.suffix(&y));
assert_eq!(solver.check(), SatResult::Sat)
}
fn assert_string_roundtrip(source: &str) {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let expr = ast::String::from_str(&ctx, source).unwrap();
assert_eq!(&expr.as_string().unwrap(), source);
}
#[test]
fn test_string_as_string() {
assert_string_roundtrip("x");
assert_string_roundtrip("'x'");
assert_string_roundtrip(r#""x""#);
assert_string_roundtrip(r#"\\"x\\""#);
}
#[test]
fn test_rec_func_def() {
let _ = env_logger::try_init();
let cfg = Config::new();
let ctx = Context::new(&cfg);
let fac = RecFuncDecl::new(&ctx, "fac", &[&Sort::int(&ctx)], &Sort::int(&ctx));
let n = ast::Int::new_const(&ctx, "n");
let n_minus_1 = ast::Int::sub(&ctx, &[&n, &ast::Int::from_i64(&ctx, 1)]);
let fac_of_n_minus_1 = fac.apply(&[&n_minus_1]);
let cond: ast::Bool = n.le(&ast::Int::from_i64(&ctx, 0));
let body = cond.ite(
&ast::Int::from_i64(&ctx, 1),
&ast::Int::mul(&ctx, &[&n, &fac_of_n_minus_1.as_int().unwrap()]),
);
fac.add_def(&[&n], &body);
let x = ast::Int::new_const(&ctx, "x");
let y = ast::Int::new_const(&ctx, "y");
let solver = Solver::new(&ctx);
solver.assert(&x._eq(&fac.apply(&[&ast::Int::from_i64(&ctx, 4)]).as_int().unwrap()));
solver.assert(&y._eq(&ast::Int::mul(&ctx, &[&ast::Int::from_i64(&ctx, 5), &x])));
solver.assert(&y._eq(&fac.apply(&[&ast::Int::from_i64(&ctx, 5)]).as_int().unwrap()));
solver.assert(&y._eq(&ast::Int::from_i64(&ctx, 120)));
assert_eq!(solver.check(), SatResult::Sat)
}
#[test]
fn test_rec_func_def_unsat() {
let _ = env_logger::try_init();
let cfg = Config::new();
let ctx = Context::new(&cfg);
let fac = RecFuncDecl::new(&ctx, "fac", &[&Sort::int(&ctx)], &Sort::int(&ctx));
let n = ast::Int::new_const(&ctx, "n");
let n_minus_1 = ast::Int::sub(&ctx, &[&n, &ast::Int::from_i64(&ctx, 1)]);
let fac_of_n_minus_1 = fac.apply(&[&n_minus_1]);
let cond: ast::Bool = n.le(&ast::Int::from_i64(&ctx, 0));
let body = cond.ite(
&ast::Int::from_i64(&ctx, 1),
&ast::Int::mul(&ctx, &[&n, &fac_of_n_minus_1.as_int().unwrap()]),
);
fac.add_def(&[&n], &body);
let x = ast::Int::new_const(&ctx, "x");
let y = ast::Int::new_const(&ctx, "y");
let solver = Solver::new(&ctx);
solver.assert(&x._eq(&fac.apply(&[&ast::Int::from_i64(&ctx, 4)]).as_int().unwrap()));
solver.assert(&y._eq(&ast::Int::mul(&ctx, &[&ast::Int::from_i64(&ctx, 5), &x])));
solver.assert(&y._eq(&fac.apply(&[&ast::Int::from_i64(&ctx, 5)]).as_int().unwrap()));
solver.assert(&y._eq(&ast::Int::from_i64(&ctx, 25)));
assert_eq!(solver.check(), SatResult::Unsat)
}
#[test]
fn test_solver_unknown() {
let _ = env_logger::try_init();
let mut cfg = Config::new();
cfg.set_timeout_msec(1);
let ctx = Context::new(&cfg);
let x = ast::Int::new_const(&ctx, "x");
let y = ast::Int::new_const(&ctx, "y");
let z = ast::Int::new_const(&ctx, "z");
let x_cube = ast::Int::mul(&ctx, &[&x, &x, &x]);
let y_cube = ast::Int::mul(&ctx, &[&y, &y, &y]);
let z_cube = ast::Int::mul(&ctx, &[&z, &z, &z]);
let sum_of_cubes = &ast::Int::add(&ctx, &[&x_cube, &y_cube, &z_cube]);
let sum_of_cubes_is_42 = sum_of_cubes._eq(&ast::Int::from_i64(&ctx, 42));
let solver = Solver::new(&ctx);
solver.assert(&sum_of_cubes_is_42);
assert_eq!(solver.check(), SatResult::Unknown);
assert!(solver.get_reason_unknown().is_some());
}
#[test]
fn test_optimize_unknown() {
let _ = env_logger::try_init();
let mut cfg = Config::new();
cfg.set_timeout_msec(1);
let ctx = Context::new(&cfg);
let x = ast::Int::new_const(&ctx, "x");
let y = ast::Int::new_const(&ctx, "y");
let z = ast::Int::new_const(&ctx, "z");
let x_cube = ast::Int::mul(&ctx, &[&x, &x, &x]);
let y_cube = ast::Int::mul(&ctx, &[&y, &y, &y]);
let z_cube = ast::Int::mul(&ctx, &[&z, &z, &z]);
let sum_of_cubes = &ast::Int::add(&ctx, &[&x_cube, &y_cube, &z_cube]);
let sum_of_cubes_is_42 = sum_of_cubes._eq(&ast::Int::from_i64(&ctx, 42));
let optimize = Optimize::new(&ctx);
optimize.assert(&sum_of_cubes_is_42);
assert_eq!(optimize.check(&[]), SatResult::Unknown);
assert!(optimize.get_reason_unknown().is_some());
}
#[test]
fn test_optimize_new_from_smtlib2() {
let _ = env_logger::try_init();
let cfg = Config::new();
let ctx = Context::new(&cfg);
let problem = r#"
(declare-const x Real)
(declare-const y Real)
(declare-const z Real)
(assert (=( -(+(* 3 x) (* 2 y)) z) 1))
(assert (=(+( -(* 2 x) (* 2 y)) (* 4 z)) -2))
"#;
let optimize = Optimize::new(&ctx);
optimize.from_string(problem);
assert_eq!(optimize.check(&[]), SatResult::Sat);
}
#[test]
fn test_get_unsat_core() {
let _ = env_logger::try_init();
let cfg = Config::new();
let ctx = Context::new(&cfg);
let solver = Solver::new(&ctx);
assert!(
solver.get_unsat_core().is_empty(),
"no unsat core before assertions"
);
let x = ast::Int::new_const(&ctx, "x");
let x_is_three = ast::Bool::new_const(&ctx, "x-is-three");
solver.assert_and_track(&x._eq(&ast::Int::from_i64(&ctx, 3)), &x_is_three);
let x_is_five = ast::Bool::new_const(&ctx, "x-is-five");
solver.assert_and_track(&x._eq(&ast::Int::from_i64(&ctx, 5)), &x_is_five);
assert!(
solver.get_unsat_core().is_empty(),
"no unsat core before checking"
);
let result = solver.check();
assert_eq!(result, SatResult::Unsat);
let unsat_core = solver.get_unsat_core();
assert_eq!(unsat_core.len(), 2);
assert!(unsat_core.contains(&x_is_three));
assert!(unsat_core.contains(&x_is_five));
}
#[test]
fn test_datatype_builder() {
let _ = env_logger::try_init();
let cfg = Config::new();
let ctx = Context::new(&cfg);
let solver = Solver::new(&ctx);
let maybe_int = DatatypeBuilder::new(&ctx, "MaybeInt")
.variant("Nothing", vec![])
.variant(
"Just",
vec![("int", DatatypeAccessor::Sort(Sort::int(&ctx)))],
)
.finish();
let nothing = maybe_int.variants[0].constructor.apply(&[]);
let five = ast::Int::from_i64(&ctx, 5);
let just_five = maybe_int.variants[1].constructor.apply(&[&five]);
let nothing_is_nothing = maybe_int.variants[0]
.tester
.apply(&[¬hing])
.as_bool()
.unwrap();
solver.assert(¬hing_is_nothing);
let nothing_is_just = maybe_int.variants[1]
.tester
.apply(&[¬hing])
.as_bool()
.unwrap();
solver.assert(¬hing_is_just.not());
let just_five_is_nothing = maybe_int.variants[0]
.tester
.apply(&[&just_five])
.as_bool()
.unwrap();
solver.assert(&just_five_is_nothing.not());
let just_five_is_just = maybe_int.variants[1]
.tester
.apply(&[&just_five])
.as_bool()
.unwrap();
solver.assert(&just_five_is_just);
let five_two = maybe_int.variants[1].accessors[0]
.apply(&[&just_five])
.as_int()
.unwrap();
solver.assert(&five._eq(&five_two));
assert_eq!(solver.check(), SatResult::Sat);
}
#[test]
fn test_recursive_datatype() {
let _ = env_logger::try_init();
let cfg = Config::new();
let ctx = Context::new(&cfg);
let solver = Solver::new(&ctx);
let list_sort = DatatypeBuilder::new(&ctx, "List")
.variant("nil", vec![])
.variant(
"cons",
vec![
("car", DatatypeAccessor::Sort(Sort::int(&ctx))),
("cdr", DatatypeAccessor::Datatype("List".into())),
],
)
.finish();
assert_eq!(list_sort.variants.len(), 2);
let nil = list_sort.variants[0].constructor.apply(&[]);
let five = ast::Int::from_i64(&ctx, 5);
let cons_five_nil = list_sort.variants[1].constructor.apply(&[&five, &nil]);
let nil_is_nil = list_sort.variants[0]
.tester
.apply(&[&nil])
.as_bool()
.unwrap();
solver.assert(&nil_is_nil);
let nil_is_cons = list_sort.variants[1]
.tester
.apply(&[&nil])
.as_bool()
.unwrap();
solver.assert(&nil_is_cons.not());
let cons_five_nil_is_nil = list_sort.variants[0]
.tester
.apply(&[&cons_five_nil])
.as_bool()
.unwrap();
solver.assert(&cons_five_nil_is_nil.not());
let cons_five_nil_is_cons = list_sort.variants[1]
.tester
.apply(&[&cons_five_nil])
.as_bool()
.unwrap();
solver.assert(&cons_five_nil_is_cons);
let car_cons_five_is_five = list_sort.variants[1].accessors[0]
.apply(&[&cons_five_nil])
.as_int()
.unwrap();
solver.assert(&car_cons_five_is_five._eq(&five));
assert_eq!(solver.check(), SatResult::Sat);
let cdr_cons_five_is_nil = list_sort.variants[1].accessors[1]
.apply(&[&cons_five_nil])
.as_datatype()
.unwrap();
solver.assert(&cdr_cons_five_is_nil._eq(&nil.as_datatype().unwrap()));
assert_eq!(solver.check(), SatResult::Sat);
}
#[test]
fn test_mutually_recursive_datatype() {
let _ = env_logger::try_init();
let cfg = Config::new();
let ctx = Context::new(&cfg);
let solver = Solver::new(&ctx);
let tree_builder = DatatypeBuilder::new(&ctx, "Tree")
.variant(
"leaf",
vec![("val", DatatypeAccessor::Sort(Sort::int(&ctx)))],
)
.variant(
"node",
vec![("children", DatatypeAccessor::Datatype("TreeList".into()))],
);
let tree_list_builder = DatatypeBuilder::new(&ctx, "TreeList")
.variant("nil", vec![])
.variant(
"cons",
vec![
("car", DatatypeAccessor::Datatype("Tree".into())),
("cdr", DatatypeAccessor::Datatype("TreeList".into())),
],
);
let sorts = z3::datatype_builder::create_datatypes(vec![tree_builder, tree_list_builder]);
assert_eq!(sorts.len(), 2);
let tree_sort = &sorts[0];
assert_eq!(tree_sort.variants.len(), 2);
assert_eq!(tree_sort.variants[0].accessors.len(), 1);
assert_eq!(tree_sort.variants[1].accessors.len(), 1);
let tree_list_sort = &sorts[1];
assert_eq!(tree_list_sort.variants.len(), 2);
assert_eq!(tree_list_sort.variants[0].accessors.len(), 0);
assert_eq!(tree_list_sort.variants[1].accessors.len(), 2);
let ten = ast::Int::from_i64(&ctx, 10);
let leaf_ten = tree_sort.variants[0].constructor.apply(&[&ten]);
let leaf_ten_val_is_ten = tree_sort.variants[0].accessors[0]
.apply(&[&leaf_ten])
.as_int()
.unwrap();
solver.assert(&leaf_ten_val_is_ten._eq(&ten.clone()));
assert_eq!(solver.check(), SatResult::Sat);
let nil = tree_list_sort.variants[0].constructor.apply(&[]);
let twenty = ast::Int::from_i64(&ctx, 20);
let leaf_twenty = tree_sort.variants[0].constructor.apply(&[&twenty]);
let cons_leaf_twenty_nil = tree_list_sort.variants[1]
.constructor
.apply(&[&leaf_twenty, &nil]);
let cons_leaf_ten_cons_leaf_twenty_nil = tree_list_sort.variants[1]
.constructor
.apply(&[&leaf_ten, &cons_leaf_twenty_nil]);
let n1 = tree_sort.variants[1]
.constructor
.apply(&[&cons_leaf_ten_cons_leaf_twenty_nil]);
let n1_cons_nil = tree_list_sort.variants[1].constructor.apply(&[&n1, &nil]);
let n2 = tree_sort.variants[1].constructor.apply(&[&n1_cons_nil]);
solver.assert(&n2._eq(&n1).not());
solver.assert(
&tree_list_sort.variants[1].accessors[0]
.apply(&[&tree_sort.variants[1].accessors[0].apply(&[&n2])])
._eq(&n1),
);
assert_eq!(solver.check(), SatResult::Sat);
}
#[test]
fn get_model_without_check_does_not_exit() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let solver = Solver::new(&ctx);
solver.get_model();
}
#[test]
fn check_application_of_tactic_to_goal() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let params = Params::new(&ctx);
let tactic = Tactic::new(&ctx, "simplify");
let repeat_tactic = Tactic::repeat(&ctx, &tactic, 100);
let goal = Goal::new(&ctx, false, false, false);
let x = ast::Bool::new_const(&ctx, "x");
goal.assert(&x);
let one = ast::Int::from_i64(&ctx, 1);
let two = ast::Int::from_i64(&ctx, 2);
let y = ast::Int::new_const(&ctx, "y");
let y_plus_one_greater_than_or_equal_to_two = y.clone().add(&one).ge(&two);
goal.assert(&y_plus_one_greater_than_or_equal_to_two);
let y_greater_than_or_equal_to_one = y.ge(&one);
goal.assert(&y_greater_than_or_equal_to_one);
assert_eq!(
format!("{}", goal),
"(goal\n x\n (>= (+ y 1) 2)\n (>= y 1))"
);
let apply_results = repeat_tactic.apply(&goal, Some(¶ms));
let goal_results = apply_results
.unwrap()
.list_subgoals()
.collect::<Vec<Goal>>();
let goal_result = goal_results.first().unwrap();
assert_eq!(format!("{}", goal_result), "(goal\n x\n (>= y 1))");
}
#[test]
fn test_goal_depth() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let goal = Goal::new(&ctx, false, false, false);
let a = ast::Bool::new_const(&ctx, "a");
let b = ast::Bool::new_const(&ctx, "b");
goal.assert(&a);
goal.assert(&b);
assert_eq!(goal.get_depth(), 0);
}
#[test]
fn test_goal_size() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let goal = Goal::new(&ctx, false, false, false);
let a = ast::Bool::new_const(&ctx, "a");
let b = ast::Bool::new_const(&ctx, "b");
goal.assert(&a);
goal.assert(&b);
assert_eq!(goal.get_size(), 2);
}
#[test]
fn test_goal_num_expr() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let goal = Goal::new(&ctx, false, false, false);
let a = ast::Bool::new_const(&ctx, "a");
goal.assert(&a);
assert_eq!(goal.get_num_expr(), 1);
let goal = Goal::new(&ctx, false, false, false);
let a = ast::Bool::new_const(&ctx, "a");
let b = ast::Bool::new_const(&ctx, "b");
goal.assert(&a);
goal.assert(&b);
assert_eq!(goal.get_num_expr(), 2);
}
#[test]
fn test_goal_get_precision() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let false_bool = ast::Bool::from_bool(&ctx, false);
let goal = Goal::new(&ctx, false, false, false);
goal.assert(&false_bool);
assert_eq!(goal.get_precision(), z3::GoalPrec::Precise);
}
#[test]
fn test_goal_is_inconsistent() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let false_bool = ast::Bool::from_bool(&ctx, false);
let goal = Goal::new(&ctx, false, false, false);
goal.assert(&false_bool);
assert!(goal.is_inconsistent());
let true_bool = ast::Bool::from_bool(&ctx, true);
let goal = Goal::new(&ctx, false, false, false);
goal.assert(&true_bool);
assert!(!goal.is_inconsistent());
}
#[test]
fn test_goal_is_sat() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let false_bool = ast::Bool::from_bool(&ctx, false);
let goal = Goal::new(&ctx, false, false, false);
goal.assert(&false_bool);
assert!(!goal.is_decided_sat());
assert!(goal.is_decided_unsat());
let true_bool = ast::Bool::from_bool(&ctx, true);
let goal = Goal::new(&ctx, false, false, false);
goal.assert(&true_bool);
assert!(!goal.is_decided_unsat());
assert!(goal.is_decided_sat());
}
#[test]
fn test_goal_reset() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let a = ast::Bool::new_const(&ctx, "a");
let goal = Goal::new(&ctx, false, false, false);
goal.assert(&a);
assert_eq!(format!("{}", goal), "(goal\n a)");
goal.reset();
assert_eq!(format!("{}", goal), "(goal)");
}
#[test]
fn test_set_membership() {
let _ = env_logger::try_init();
let cfg = Config::new();
let ctx = Context::new(&cfg);
let solver = Solver::new(&ctx);
let set = ast::Set::new_const(&ctx, "integer_set", &Sort::int(&ctx));
let one = ast::Int::from_u64(&ctx, 1);
solver.push();
solver.assert(&set._eq(&ast::Set::empty(&ctx, &Sort::int(&ctx))));
solver.push();
solver.assert(&set.member(&one));
assert_eq!(solver.check(), SatResult::Unsat);
solver.pop(1);
solver.push();
let set_with_one = set.add(&one);
solver.assert(&set_with_one.member(&one));
assert_eq!(solver.check(), SatResult::Sat);
let set_without_one = set_with_one.del(&one);
solver.assert(&set_without_one.member(&one));
assert_eq!(solver.check(), SatResult::Unsat);
solver.pop(1);
solver.push();
let x = ast::Int::new_const(&ctx, "x");
let forall: ast::Bool = ast::forall_const(&ctx, &[&x], &[], &set.member(&x).not());
solver.assert(&forall);
assert_eq!(solver.check(), SatResult::Sat);
solver.pop(1);
solver.pop(1);
solver.push();
solver.assert(&set._eq(&ast::Set::empty(&ctx, &Sort::int(&ctx)).add(&one)));
solver.assert(&set.member(&one));
assert_eq!(solver.check(), SatResult::Sat);
solver.pop(1);
}
#[test]
fn test_dynamic_as_set() {
let _ = env_logger::try_init();
let cfg = Config::new();
let ctx = Context::new(&cfg);
let set_sort = Sort::set(&ctx, &Sort::int(&ctx));
let array_sort = Sort::array(&ctx, &Sort::int(&ctx), &Sort::int(&ctx));
let array_of_sets = ast::Array::new_const(&ctx, "array_of_sets", &Sort::int(&ctx), &set_sort);
let array_of_arrays =
ast::Array::new_const(&ctx, "array_of_arrays", &Sort::int(&ctx), &array_sort);
assert!(array_of_sets
.select(&ast::Int::from_u64(&ctx, 0))
.as_set()
.is_some());
assert!(array_of_arrays
.select(&ast::Int::from_u64(&ctx, 0))
.as_set()
.is_none());
}
#[test]
fn test_array_store_select() {
let _ = env_logger::try_init();
let cfg = Config::new();
let ctx = Context::new(&cfg);
let solver = Solver::new(&ctx);
let zero = ast::Int::from_u64(&ctx, 0);
let one = ast::Int::from_u64(&ctx, 1);
let set = ast::Array::new_const(&ctx, "integer_array", &Sort::int(&ctx), &Sort::int(&ctx))
.store(&zero, &one);
solver.assert(&set.select(&zero)._eq(&one.into()).not());
assert_eq!(solver.check(), SatResult::Unsat);
}
#[test]
fn test_goal_get_formulas() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let goal = Goal::new(&ctx, false, false, false);
let a = ast::Bool::new_const(&ctx, "a");
let b = ast::Bool::new_const(&ctx, "b");
let c = ast::Bool::new_const(&ctx, "c");
goal.assert(&a);
goal.assert(&b);
goal.assert(&c);
assert_eq!(goal.get_formulas::<Bool>(), vec![a, b, c],);
}
#[test]
fn test_tactic_skip() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let params = Params::new(&ctx);
let a = ast::Bool::new_const(&ctx, "a");
let b = ast::Bool::new_const(&ctx, "b");
let bools = [&a, &b, &a];
let a_and_b_and_a = Bool::and(&ctx, &bools);
let goal = Goal::new(&ctx, false, false, false);
goal.assert(&a_and_b_and_a);
let tactic = Tactic::create_skip(&ctx);
let apply_results = tactic.apply(&goal, Some(¶ms));
let goal_results = apply_results
.unwrap()
.list_subgoals()
.collect::<Vec<Goal>>();
let goal_result = goal_results.first().unwrap();
assert_eq!(goal_result.get_formulas::<Bool>(), vec![a.clone(), b, a],);
}
#[test]
fn test_tactic_fail() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let params = Params::new(&ctx);
let a = ast::Bool::new_const(&ctx, "a");
let goal = Goal::new(&ctx, false, false, false);
goal.assert(&a);
let tactic = Tactic::new(&ctx, "fail");
let apply_results = tactic.apply(&goal, Some(¶ms));
assert!(matches!(apply_results, Err(_)));
}
#[test]
fn test_tactic_try_for() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let params = Params::new(&ctx);
let one = ast::Int::from_i64(&ctx, 1);
let two = ast::Int::from_i64(&ctx, 2);
let x = ast::Int::new_const(&ctx, "x");
let goal = Goal::new(&ctx, false, false, false);
goal.assert(&x.ge(&one.add(&two)));
let tactic = Tactic::new(&ctx, "simplify");
let try_for_tactic = tactic.try_for(Duration::from_secs(u64::MAX));
let apply_results = try_for_tactic.apply(&goal, Some(¶ms));
let goal_results = apply_results
.unwrap()
.list_subgoals()
.collect::<Vec<Goal>>();
let goal_result = goal_results.first().unwrap();
assert_eq!(format!("{}", goal_result), "(goal\n (>= x 3))");
}
#[test]
fn test_tactic_and_then() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let params = Params::new(&ctx);
let a = ast::Bool::new_const(&ctx, "a");
let b = ast::Bool::new_const(&ctx, "b");
let bools = [&a, &b, &a];
let a_and_b_and_a = Bool::and(&ctx, &bools);
let goal = Goal::new(&ctx, false, false, false);
goal.assert(&a_and_b_and_a);
let tactic = Tactic::new(&ctx, "sat-preprocess");
let and_then_tactic = tactic.and_then(&Tactic::new(&ctx, "simplify"));
let apply_results = and_then_tactic.apply(&goal, Some(¶ms));
let goal_results = apply_results
.unwrap()
.list_subgoals()
.collect::<Vec<Goal>>();
let goal_result = goal_results.first().unwrap();
assert_eq!(goal_result.get_formulas::<Bool>(), vec![a, b]);
}
#[test]
fn test_tactic_or_else() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let params = Params::new(&ctx);
let a = ast::Bool::new_const(&ctx, "a");
let b = ast::Bool::new_const(&ctx, "b");
let bools = [&a, &b, &a];
let a_and_b_and_a = Bool::and(&ctx, &bools);
let goal = Goal::new(&ctx, false, false, false);
goal.assert(&a_and_b_and_a);
let tactic = Tactic::new(&ctx, "sat-preprocess");
let simplify = Tactic::new(&ctx, "simplify");
let or_else_tactic = tactic.or_else(&simplify);
let apply_results = or_else_tactic.apply(&goal, Some(¶ms));
let goal_results = apply_results
.unwrap()
.list_subgoals()
.collect::<Vec<Goal>>();
let goal_result = goal_results.first().unwrap();
assert_eq!(goal_result.get_formulas::<Bool>(), vec![a, b]);
}
#[test]
fn test_goal_apply_tactic() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
pub fn test_apply_tactic(
ctx: &Context,
goal: Goal,
before_formulas: Vec<Bool>,
after_formulas: Vec<Bool>,
) {
assert_eq!(goal.get_formulas::<Bool>(), before_formulas);
let params = Params::new(ctx);
let tactic = Tactic::new(ctx, "sat-preprocess");
let repeat_tactic = Tactic::repeat(ctx, &tactic, 100);
let apply_results = repeat_tactic.apply(&goal, Some(¶ms));
let goal_results = apply_results
.unwrap()
.list_subgoals()
.collect::<Vec<Goal>>();
let goal_result = goal_results.first().unwrap();
assert_eq!(
goal_result.get_formulas::<Bool>(),
after_formulas,
"Before: {:?}",
before_formulas
);
}
let a = ast::Bool::new_const(&ctx, "a");
let b = ast::Bool::new_const(&ctx, "b");
let bools = [&a, &b, &a];
let a_and_b_and_a = Bool::and(&ctx, &bools);
let goal = Goal::new(&ctx, false, false, false);
goal.assert(&a_and_b_and_a);
test_apply_tactic(
&ctx,
goal,
vec![a.clone(), b.clone(), a.clone()],
vec![a.clone(), b.clone()],
);
let a_implies_b = ast::Bool::implies(&a, &b).simplify();
let a_and_a_implies_b = Bool::and(&ctx, &[&a, &a_implies_b]);
let goal = Goal::new(&ctx, false, false, false);
goal.assert(&a_and_a_implies_b);
test_apply_tactic(
&ctx,
goal,
vec![a.clone(), a_implies_b.clone()],
vec![a.clone(), b.clone()],
);
let goal = Goal::new(&ctx, false, false, false);
goal.assert(&a);
goal.assert(&a_implies_b.clone());
test_apply_tactic(
&ctx,
goal,
vec![a.clone(), a_implies_b.clone()],
vec![a.clone(), b.clone()],
);
let true_bool = ast::Bool::from_bool(&ctx, true);
let false_bool = ast::Bool::from_bool(&ctx, false);
let goal = Goal::new(&ctx, false, false, false);
let true_and_false_and_true = ast::Bool::and(&ctx, &[&true_bool, &false_bool, &true_bool]);
goal.assert(&true_and_false_and_true);
test_apply_tactic(
&ctx,
goal,
vec![false_bool.clone()],
vec![false_bool.clone()],
);
}
#[test]
fn test_tactic_cond() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let t1 = Tactic::new(&ctx, "qfnra");
let t2 = Tactic::new(&ctx, "smt");
let p = Probe::new(&ctx, "is-qfnra");
let _t = Tactic::cond(&ctx, &p, &t1, &t2);
}
#[test]
fn test_tactic_conditions() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let t1 = Tactic::new(&ctx, "qfnra");
let t2 = Tactic::new(&ctx, "smt");
let p = Probe::new(&ctx, "is-qfnra");
t1.probe_or_else(&p, &t2);
t1.when(&p);
Tactic::cond(&ctx, &p, &t1, &t2);
Tactic::fail_if(&ctx, &p);
}
#[test]
fn test_probe_debug() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let _v: Vec<&str> = Probe::list_all(&ctx).map(|x| x.unwrap()).collect();
assert_eq!(
"A probe to give an upper bound of Ackermann congruence lemmas that a formula might generate.",
Probe::describe(&ctx, "ackr-bound-probe").unwrap(),
);
}
#[test]
fn test_probe_names() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let x = ast::Int::fresh_const(&ctx, "x");
let zero = ast::Int::from_u64(&ctx, 0);
let ten = ast::Int::from_u64(&ctx, 10);
let twenty = ast::Int::from_u64(&ctx, 20);
let g = Goal::new(&ctx, false, false, false);
g.assert(&x.gt(&zero));
g.assert(&x.lt(&ten));
g.assert(&x.lt(&twenty));
let num_consts_probe = Probe::new(&ctx, "num-consts");
assert_eq!(1.0, num_consts_probe.apply(&g));
let is_prop_probe = Probe::new(&ctx, "is-propositional");
assert_eq!(0.0, is_prop_probe.apply(&g));
let is_qflia_probe = Probe::new(&ctx, "is-qflia");
assert_eq!(1.0, is_qflia_probe.apply(&g));
}
#[test]
fn test_probe_eq() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let zero = ast::Int::from_u64(&ctx, 0);
let ten = ast::Int::from_u64(&ctx, 10);
let two_probe = Probe::constant(&ctx, 2.0);
let size_probe = Probe::new(&ctx, "size");
let equals_two_probe = &size_probe.eq(&two_probe);
let x = ast::Int::fresh_const(&ctx, "x");
let g = Goal::new(&ctx, false, false, false);
g.assert(&x.gt(&zero));
g.assert(&x.lt(&ten));
assert_eq!(1.0, equals_two_probe.apply(&g));
}
#[test]
fn test_probe_gt() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let ten_probe = Probe::constant(&ctx, 10.0);
let size_probe = Probe::new(&ctx, "size");
let gt_ten_probe = &size_probe.gt(&ten_probe);
let x = ast::Int::fresh_const(&ctx, "x");
let zero = ast::Int::from_u64(&ctx, 0);
let ten = ast::Int::from_u64(&ctx, 10);
let g = Goal::new(&ctx, false, false, false);
g.assert(&x.gt(&zero));
g.assert(&x.lt(&ten));
assert_eq!(0.0, gt_ten_probe.apply(&g));
}
#[test]
fn test_probe_gte() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let two_probe = Probe::constant(&ctx, 2.0);
let size_probe = Probe::new(&ctx, "size");
let ge_two_probe = &size_probe.ge(&two_probe);
let x = ast::Int::fresh_const(&ctx, "x");
let zero = ast::Int::from_u64(&ctx, 0);
let ten = ast::Int::from_u64(&ctx, 10);
let g = Goal::new(&ctx, false, false, false);
g.assert(&x.gt(&zero));
g.assert(&x.lt(&ten));
assert_eq!(1.0, ge_two_probe.apply(&g));
}
#[test]
fn test_probe_le() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let two_probe = Probe::constant(&ctx, 2.0);
let size_probe = Probe::new(&ctx, "size");
let le_two_probe = &size_probe.le(&two_probe);
let x = ast::Int::fresh_const(&ctx, "x");
let zero = ast::Int::from_u64(&ctx, 0);
let ten = ast::Int::from_u64(&ctx, 10);
let g = Goal::new(&ctx, false, false, false);
g.assert(&x.gt(&zero));
g.assert(&x.lt(&ten));
assert_eq!(1.0, le_two_probe.apply(&g));
}
#[test]
fn test_probe_lt() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let ten_probe = Probe::constant(&ctx, 10.0);
let size_probe = Probe::new(&ctx, "size");
let le_ten_probe = &size_probe.le(&ten_probe);
let x = ast::Int::fresh_const(&ctx, "x");
let zero = ast::Int::from_u64(&ctx, 0);
let ten = ast::Int::from_u64(&ctx, 10);
let g = Goal::new(&ctx, false, false, false);
g.assert(&x.gt(&zero));
g.assert(&x.lt(&ten));
assert_eq!(1.0, le_ten_probe.apply(&g));
}
#[test]
fn test_probe_ne() {
let cfg = Config::new();
let ctx = Context::new(&cfg);
let two_probe = Probe::constant(&ctx, 2.0);
let size_probe = Probe::new(&ctx, "size");
let ne_two_probe = &size_probe.ne(&two_probe);
let x = ast::Int::fresh_const(&ctx, "x");
let zero = ast::Int::from_u64(&ctx, 0);
let ten = ast::Int::from_u64(&ctx, 10);
let g = Goal::new(&ctx, false, false, false);
g.assert(&x.gt(&zero));
g.assert(&x.lt(&ten));
assert_eq!(0.0, ne_two_probe.apply(&g));
}
#[test]
#[should_panic]
fn test_issue_94() {
let cfg = Config::new();
let ctx0 = Context::new(&cfg);
let ctx1 = Context::new(&cfg);
let i0 = ast::Int::fresh_const(&ctx0, "a");
let i1 = ast::Int::fresh_const(&ctx1, "b");
ast::Int::add(&ctx0, &[&i0, &i1]);
}
#[test]
fn test_ast_safe_eq() {
let cfg = Config::new();
let ctx = &Context::new(&cfg);
let x: ast::Dynamic = ast::Bool::new_const(ctx, "a").into();
let y: ast::Dynamic = ast::String::from_str(ctx, "b").unwrap().into();
let other_bool: ast::Dynamic = ast::Bool::new_const(ctx, "c").into();
let other_string: ast::Dynamic = ast::String::from_str(ctx, "d").unwrap().into();
let sd: SortDiffers<'_> = SortDiffers::new(other_bool.get_sort(), other_string.get_sort());
let result = x._safe_eq(&y);
assert!(result.is_err());
let err = result.err().unwrap();
assert_eq!(err.left(), sd.left());
assert_eq!(err.right(), sd.right());
}
#[test]
fn test_ast_safe_decl() {
let cfg = Config::new();
let ctx = &Context::new(&cfg);
let x: ast::Bool = ast::Bool::new_const(ctx, "x");
let x_not = x.not();
assert_eq!(x_not.safe_decl().unwrap().kind(), DeclKind::NOT);
let f = FuncDecl::new(ctx, "f", &[&Sort::int(ctx)], &Sort::int(ctx));
let x = ast::Int::new_const(ctx, "x");
let f_x: ast::Int = f.apply(&[&x]).try_into().unwrap();
let f_x_pattern: Pattern = Pattern::new(ctx, &[&f_x]);
let forall = ast::forall_const(ctx, &[&x], &[&f_x_pattern], &x._eq(&f_x));
assert!(forall.safe_decl().is_err());
assert_eq!(
format!("{}", forall.safe_decl().err().unwrap()),
"ast node is not a function application, has kind Quantifier"
);
}
#[test]
fn test_regex_capital_foobar_intersect_az_plus_is_unsat() {
let cfg = Config::new();
let ctx = &Context::new(&cfg);
let solver = Solver::new(ctx);
let s = ast::String::new_const(ctx, "s");
let re = ast::Regexp::intersect(
ctx,
&[
&ast::Regexp::concat(
ctx,
&[
&ast::Regexp::literal(ctx, "FOO"),
&ast::Regexp::literal(ctx, "bar"),
],
),
&ast::Regexp::plus(&ast::Regexp::range(ctx, &'a', &'z')),
],
);
solver.assert(&s.regex_matches(&re));
assert!(solver.check() == SatResult::Unsat);
}
#[test]
fn test_array_example1() {
let cfg = Config::new();
let ctx = &Context::new(&cfg);
let g = Goal::new(ctx, true, false, false);
let aex = Array::new_const(ctx, "MyArray", &Sort::int(ctx), &Sort::bitvector(ctx, 32));
let sel = aex.select(&Int::from_u64(ctx, 0));
g.assert(&sel._eq(&BV::from_u64(ctx, 42, 32).into()));
let xc = Int::new_const(ctx, "x");
let fd = FuncDecl::new(ctx, "f", &[&Sort::int(ctx)], &Sort::int(ctx));
let fapp = fd.apply(&[&xc as &dyn Ast]);
g.assert(&Int::from_u64(ctx, 123)._eq(&xc.clone().add(&fapp.as_int().unwrap())));
let s = &Solver::new(ctx);
for a in g.get_formulas() {
s.assert(&a);
}
println!("Solver: {}", s);
let q = s.check();
println!("Status: {:?}", q);
if q != SatResult::Sat {
panic!("Solver did not return sat");
}
let model = s.get_model().unwrap();
assert!(
model.to_string()
== "MyArray -> ((as const (Array Int (_ BitVec 32))) #x0000002a)\nx -> 0\nf -> {\n 123\n}\n"
);
assert!(
model.get_const_interp(&aex).unwrap().to_string()
== "((as const (Array Int (_ BitVec 32))) #x0000002a)"
);
assert!(model.get_const_interp(&xc).unwrap().to_string() == "0");
assert!(model.get_func_interp(&fd).unwrap().to_string() == "[else -> 123]");
}
#[test]
fn return_number_args_in_given_entry() {
let cfg = Config::new();
let ctx = &Context::new(&cfg);
let f = FuncDecl::new(
ctx,
"f",
&[&Sort::int(ctx), &Sort::int(ctx)],
&Sort::int(ctx),
);
let solver = Solver::new(ctx);
solver.assert(
&f.apply(&[&Int::from_u64(ctx, 0), &Int::from_u64(ctx, 1)])
._eq(&Int::from_u64(ctx, 10).into()),
);
solver.assert(
&f.apply(&[&Int::from_u64(ctx, 1), &Int::from_u64(ctx, 2)])
._eq(&Int::from_u64(ctx, 20).into()),
);
solver.assert(
&f.apply(&[&Int::from_u64(ctx, 1), &Int::from_u64(ctx, 0)])
._eq(&Int::from_u64(ctx, 10).into()),
);
assert!(solver.check() == SatResult::Sat);
let model = solver.get_model().unwrap();
let f_i = model.get_func_interp(&f).unwrap();
assert!(f_i.get_num_entries() == 1);
let e = &f_i.get_entries()[0];
assert!(e.to_string() == "[1, 2, 20]");
assert!(e.get_num_args() == 2);
assert!(e.get_args()[0].to_string() == "1");
assert!(e.get_args()[1].to_string() == "2");
assert!(e.get_args().get(2).is_none());
assert!(model.to_string() == "f -> {\n 1 2 -> 20\n else -> 10\n}\n");
}
#[test]
fn iterate_all_solutions() {
let cfg = Config::new();
let ctx = &Context::new(&cfg);
let solver = Solver::new(ctx);
let a = &Int::new_const(ctx, "a");
let b = &Int::new_const(ctx, "b");
let one = Int::from_u64(ctx, 1);
let two = Int::from_u64(ctx, 2);
let five = &Int::from_u64(ctx, 5);
solver.assert(&one.le(a));
solver.assert(&a.le(five));
solver.assert(&one.le(b));
solver.assert(&b.le(five));
solver.assert(&a.ge(&(two * b)));
let mut solutions = std::collections::HashSet::new();
while solver.check() == SatResult::Sat {
let model = solver.get_model().unwrap();
let mut modifications = Vec::new();
let this_solution = model
.iter()
.map(|fd| {
modifications.push(
fd.apply(&[])
._eq(&model.get_const_interp(&fd.apply(&[])).unwrap())
.not(),
);
format!(
"{} = {}",
fd.name(),
model.get_const_interp(&fd.apply(&[])).unwrap()
)
})
.collect::<Vec<_>>()
.join(", ");
solutions.insert(format!("[{this_solution}]"));
solver.assert(&Bool::or(ctx, &modifications.iter().collect::<Vec<_>>()));
}
assert!(
solutions
== vec![
"[b = 1, a = 2]".to_string(),
"[b = 2, a = 4]".to_string(),
"[b = 1, a = 3]".to_string(),
"[b = 2, a = 5]".to_string(),
"[b = 1, a = 4]".to_string(),
"[b = 1, a = 5]".to_string()
]
.into_iter()
.collect()
)
}