use std::convert::TryFrom;
use z3::ast::{Algebraic, Ast, AstKind, Bool, Real};
use z3::{SatResult, Solver, Sort};
fn alg(num: i64, den: i64) -> Algebraic {
Algebraic::try_from(Real::from_rational(num, den))
.expect("rational numeral is a valid algebraic value")
}
#[test]
fn is_value_true_for_rational_numerals() {
assert!(Algebraic::is_value(&Real::from_rational(1, 1)));
assert!(Algebraic::is_value(&Real::from_rational(2, 1)));
assert!(Algebraic::is_value(&Real::from_rational(1, 3)));
assert!(Algebraic::is_value(&Real::from_rational(-7, 4)));
}
#[test]
fn is_value_false_for_symbolic_real() {
assert!(!Algebraic::is_value(&Real::new_const("x")));
}
#[test]
fn try_from_rational_real_succeeds() {
assert!(Algebraic::try_from(Real::from_rational(2, 1)).is_ok());
}
#[test]
fn try_from_symbolic_real_fails() {
assert!(Algebraic::try_from(Real::new_const("x")).is_err());
}
#[test]
fn from_algebraic_widens_to_concrete_real() {
let r: Real = Real::from(alg(3, 2));
assert!(r.as_rational().is_some());
}
#[test]
fn roundtrip_rational_through_real() {
let a = alg(5, 4);
let back = Algebraic::try_from(Real::from(a.clone())).unwrap();
assert!(Algebraic::eq_algebraic(&a, &back));
}
#[test]
fn sort_of_rational_algebraic_is_real() {
assert_eq!(alg(2, 1).get_sort(), Sort::real());
}
#[test]
fn sort_of_computed_algebraic_is_real() {
assert_eq!(alg(2, 1).root(2).get_sort(), Sort::real());
}
#[test]
fn sort_after_arithmetic_is_real() {
let result = Algebraic::add(&alg(1, 1), &alg(2, 1));
assert_eq!(result.get_sort(), Sort::real());
}
#[test]
fn rational_algebraic_has_numeral_kind() {
assert_eq!(alg(2, 1).kind(), AstKind::Numeral);
}
#[test]
fn all_algebraic_values_satisfy_is_app() {
assert!(alg(2, 1).is_app());
assert!(alg(2, 1).root(2).is_app());
}
#[test]
fn display_and_debug_produce_nonempty_strings() {
let a = alg(2, 1);
assert!(!format!("{a}").is_empty());
assert!(!format!("{a:?}").is_empty());
let sqrt2 = alg(2, 1).root(2);
assert!(!format!("{sqrt2}").is_empty());
}
#[test]
fn clone_produces_algebraically_equal_value() {
let a = alg(7, 3);
assert!(Algebraic::eq_algebraic(&a, &a.clone()));
}
#[test]
fn sign_of_positive() {
let a = alg(3, 2);
assert!(a.is_positive());
assert!(!a.is_negative());
assert!(!a.is_zero());
assert_eq!(a.sign(), 1);
}
#[test]
fn sign_of_negative() {
let a = alg(-1, 1);
assert!(!a.is_positive());
assert!(a.is_negative());
assert!(!a.is_zero());
assert_eq!(a.sign(), -1);
}
#[test]
fn sign_of_zero() {
let zero = Algebraic::sub(&alg(1, 1), &alg(1, 1));
assert!(!zero.is_positive());
assert!(!zero.is_negative());
assert!(zero.is_zero());
assert_eq!(zero.sign(), 0);
}
#[test]
fn add() {
let sum = Algebraic::add(&alg(1, 1), &alg(2, 1));
assert!(Algebraic::eq_algebraic(&sum, &alg(3, 1)));
}
#[test]
fn sub() {
let diff = Algebraic::sub(&alg(5, 1), &alg(3, 1));
assert!(Algebraic::eq_algebraic(&diff, &alg(2, 1)));
}
#[test]
fn mul() {
let product = Algebraic::mul(&alg(3, 1), &alg(4, 1));
assert!(Algebraic::eq_algebraic(&product, &alg(12, 1)));
}
#[test]
fn div() {
let quotient = Algebraic::div(&alg(6, 1), &alg(2, 1));
assert!(Algebraic::eq_algebraic("ient, &alg(3, 1)));
}
#[test]
fn root_and_power_are_inverses() {
let two = alg(2, 1);
let back = two.root(2).power(2);
assert!(Algebraic::eq_algebraic(&back, &two));
}
#[test]
fn cube_root_of_8_is_2() {
assert!(Algebraic::eq_algebraic(&alg(8, 1).root(3), &alg(2, 1)));
}
#[test]
fn sqrt2_is_irrational() {
let sqrt2 = alg(2, 1).root(2);
assert!(!Algebraic::eq_algebraic(&sqrt2, &alg(1, 1)));
assert!(!Algebraic::eq_algebraic(&sqrt2, &alg(7, 5))); assert!(!Algebraic::eq_algebraic(&sqrt2, &alg(2, 1)));
}
#[test]
fn lt_and_gt() {
let one = alg(1, 1);
let two = alg(2, 1);
assert!(Algebraic::lt(&one, &two));
assert!(!Algebraic::lt(&two, &one));
assert!(!Algebraic::lt(&one, &one));
assert!(Algebraic::gt(&two, &one));
assert!(!Algebraic::gt(&one, &two));
assert!(!Algebraic::gt(&two, &two));
}
#[test]
fn sqrt2_lies_between_one_and_two() {
let sqrt2 = alg(2, 1).root(2);
assert!(Algebraic::gt(&sqrt2, &alg(1, 1)));
assert!(Algebraic::lt(&sqrt2, &alg(2, 1)));
}
#[test]
fn eq_algebraic_reflexive_and_discriminating() {
let a = alg(3, 2);
let b = alg(3, 2);
let c = alg(5, 2);
assert!(Algebraic::eq_algebraic(&a, &b));
assert!(!Algebraic::eq_algebraic(&a, &c));
}
#[test]
fn ast_eq_returns_satisfiable_bool_for_equal_values() {
let constraint: Bool = alg(2, 1).eq(alg(2, 1));
let solver = Solver::new();
solver.assert(constraint);
assert_eq!(solver.check(), SatResult::Sat);
}
#[test]
fn ast_ne_returns_satisfiable_bool_for_distinct_values() {
let constraint: Bool = alg(1, 1).ne(alg(2, 1));
let solver = Solver::new();
solver.assert(constraint);
assert_eq!(solver.check(), SatResult::Sat);
}
#[test]
fn widened_algebraic_used_as_symbolic_bound() {
let sqrt2: Real = Real::from(alg(2, 1).root(2));
let x = Real::new_const("x");
let solver = Solver::new();
solver.assert(x.gt(&sqrt2));
assert_eq!(solver.check(), SatResult::Sat);
}
#[ignore = "Fails when run with z3 4.8; likely an outdated upstream bug"]
#[test]
fn widened_algebraic_makes_constraint_unsat() {
let sqrt2: Real = Real::from(alg(2, 1).root(2));
let x = Real::new_const("x");
let solver = Solver::new();
solver.assert(x.lt(&sqrt2));
solver.assert(x.ge(&sqrt2));
assert_eq!(solver.check(), SatResult::Unsat);
}
#[test]
fn solver_returns_algebraic_value_convertible_via_try_from() {
let x = Real::new_const("x");
let solver = Solver::new();
solver.assert(Real::mul(&[&x, &x]).eq(Real::from_rational(2, 1)));
solver.assert(x.gt(Real::from_rational(0, 1)));
assert_eq!(solver.check(), SatResult::Sat);
let model = solver.get_model().unwrap();
let x_val = model.eval(&x, true).unwrap();
let x_alg = Algebraic::try_from(x_val).unwrap();
assert!(Algebraic::eq_algebraic(&x_alg, &alg(2, 1).root(2)));
}