rz3 0.1.3

Deterministic, exact-rational SMT solver in pure Rust (DPLL(T)/CDCL) with an exact linear-arithmetic core. Zero C dependencies, WASM-deployable, reproducible run-to-run.
Documentation
use num_bigint::BigInt;
use num_rational::BigRational;
use rz3::ast::{fp::FloatSort, Expr, ModelValue, Type};
use rz3::parser::{Command, Parser};
use rz3::{Rz3Solver, SolverResult};

fn fp32(sign: u64, exp: u64, sig: u64) -> Expr {
    Expr::App(
        "fp".to_string(),
        vec![
            Expr::BvConst(sign, 1),
            Expr::BvConst(exp, 8),
            Expr::BvConst(sig, 23),
        ],
    )
}

#[test]
fn parser_preserves_get_value_expressions() {
    let mut parser = Parser::new("(get-value (x #b00000101))");
    let exprs = match parser.parse_command() {
        Some(Command::GetValue(exprs)) => exprs,
        other => {
            assert!(
                matches!(other, Some(Command::GetValue(_))),
                "expected Command::GetValue"
            );
            return;
        }
    };

    assert_eq!(exprs.len(), 2);
    assert!(matches!(&exprs[0], Expr::Var(name, _) if name == "x"));
    assert!(matches!(exprs[1], Expr::BvConst(5, 8)));
}

#[test]
fn get_value_returns_exact_lra_rational_model_value() {
    let x = Expr::Var("x".to_string(), Type::Real);
    let mut solver = Rz3Solver::new();
    solver.declare_fun("x".to_string(), Type::Real);
    solver.assert(&Expr::Eq(Box::new(x.clone()), Box::new(Expr::Real(125, 2))));

    assert!(matches!(solver.check(), SolverResult::Sat));
    let value = match solver.get_value(&x) {
        Some(value) => value,
        None => {
            assert!(
                solver.get_value(&x).is_some(),
                "expected exact model value for x"
            );
            return;
        }
    };

    assert!(matches!(
        value,
        ModelValue::Real(r) if r == BigRational::new(BigInt::from(125), BigInt::from(100))
    ));
}

#[test]
fn get_value_handles_large_decimal_scale_without_i64_overflow() {
    let x = Expr::Var("tiny".to_string(), Type::Real);
    let mut solver = Rz3Solver::new();
    solver.declare_fun("tiny".to_string(), Type::Real);
    solver.assert(&Expr::Eq(Box::new(x.clone()), Box::new(Expr::Real(1, 30))));

    assert!(matches!(solver.check(), SolverResult::Sat));
    let value = match solver.get_value(&x) {
        Some(value) => value,
        None => {
            assert!(
                solver.get_value(&x).is_some(),
                "expected exact model value for tiny"
            );
            return;
        }
    };

    assert!(matches!(
        value,
        ModelValue::Real(r) if r == BigRational::new(BigInt::from(1), BigInt::from(10u8).pow(30))
    ));
}

#[test]
fn get_value_returns_exact_ground_fp_value() {
    let one = fp32(0, 0x7f, 0);
    let solver = Rz3Solver::new();
    let value = match solver.get_value(&one) {
        Some(value) => value,
        None => {
            assert!(solver.get_value(&one).is_some(), "expected ground FP value");
            return;
        }
    };

    assert!(matches!(
        value,
        ModelValue::Float(fp)
            if fp.sort == (FloatSort {
                exponent_bits: 8,
                significand_bits: 24
            })
    ));
}