use symplex::prelude::*;
fn fresh() -> Context {
Context::new()
}
fn q_fresh(build: fn(&Context) -> Ex, prop: Props) -> Option<bool> {
let ctx = fresh();
let e = build(&ctx);
ctx.query(&e, prop)
}
fn q_primed(build: fn(&Context) -> Ex, first: Props, prop: Props) -> Option<bool> {
let ctx = fresh();
let e = build(&ctx);
let _ = ctx.query(&e, first);
ctx.query(&e, prop)
}
const ALL: [Props; 24] = [
Props::COMMUTATIVE,
Props::COMPLEX,
Props::REAL,
Props::RATIONAL,
Props::INTEGER,
Props::ALGEBRAIC,
Props::TRANSCENDENTAL,
Props::IRRATIONAL,
Props::IMAGINARY,
Props::POSITIVE,
Props::NEGATIVE,
Props::NONNEGATIVE,
Props::NONPOSITIVE,
Props::ZERO,
Props::NONZERO,
Props::EVEN,
Props::ODD,
Props::PRIME,
Props::COMPOSITE,
Props::FINITE,
Props::INFINITE,
Props::HERMITIAN,
Props::ANTIHERMITIAN,
Props::EXTENDED_REAL,
];
fn assert_order_independent(build: fn(&Context) -> Ex, label: &str) {
for &prop in &ALL {
let base = q_fresh(build, prop);
for &first in &ALL {
let got = q_primed(build, first, prop);
assert_eq!(
got, base,
"{label}: query({prop}) = {base:?} fresh but {got:?} after query({first})"
);
}
}
}
#[test]
fn oo_is_positive_infinite_extended_real_not_finite_not_real() {
let ctx = fresh();
let oo = ctx.infinity();
assert_eq!(ctx.query(&oo, Props::POSITIVE), Some(true));
assert_eq!(ctx.query(&oo, Props::NONNEGATIVE), Some(true));
assert_eq!(ctx.query(&oo, Props::NONZERO), Some(true));
assert_eq!(ctx.query(&oo, Props::EXTENDED_REAL), Some(true));
assert_eq!(ctx.query(&oo, Props::INFINITE), Some(true));
assert_eq!(ctx.query(&oo, Props::FINITE), Some(false));
assert_eq!(ctx.query(&oo, Props::REAL), Some(false));
assert_eq!(ctx.query(&oo, Props::COMPLEX), Some(false));
assert_eq!(ctx.query(&oo, Props::ZERO), Some(false));
assert_eq!(ctx.query(&oo, Props::NEGATIVE), Some(false));
assert_eq!(ctx.query(&oo, Props::INTEGER), Some(false));
assert_eq!(ctx.query(&oo, Props::IMAGINARY), Some(false));
}
#[test]
fn neg_oo_is_negative_infinite_extended_real_not_finite_not_real() {
let ctx = fresh();
let m = ctx.neg_infinity();
assert_eq!(ctx.query(&m, Props::NEGATIVE), Some(true));
assert_eq!(ctx.query(&m, Props::NONPOSITIVE), Some(true));
assert_eq!(ctx.query(&m, Props::NONZERO), Some(true));
assert_eq!(ctx.query(&m, Props::EXTENDED_REAL), Some(true));
assert_eq!(ctx.query(&m, Props::INFINITE), Some(true));
assert_eq!(ctx.query(&m, Props::FINITE), Some(false));
assert_eq!(ctx.query(&m, Props::REAL), Some(false));
assert_eq!(ctx.query(&m, Props::COMPLEX), Some(false));
assert_eq!(ctx.query(&m, Props::POSITIVE), Some(false));
}
#[test]
fn zoo_is_infinite_and_nothing_real() {
let ctx = fresh();
let z = ctx.complex_infinity();
assert_eq!(ctx.query(&z, Props::INFINITE), Some(true));
assert_eq!(ctx.query(&z, Props::NONZERO), Some(true));
assert_eq!(ctx.query(&z, Props::FINITE), Some(false));
assert_eq!(ctx.query(&z, Props::REAL), Some(false));
assert_eq!(ctx.query(&z, Props::EXTENDED_REAL), Some(false));
assert_eq!(ctx.query(&z, Props::COMPLEX), Some(false));
assert_eq!(ctx.query(&z, Props::POSITIVE), Some(false));
assert_eq!(ctx.query(&z, Props::NEGATIVE), Some(false));
assert_eq!(ctx.query(&z, Props::ZERO), Some(false));
}
#[test]
fn nan_knows_nothing_numeric() {
let ctx = fresh();
let n = ctx.nan();
for &p in &ALL {
if p == Props::COMMUTATIVE {
assert_eq!(ctx.query(&n, p), Some(true));
} else {
assert_eq!(ctx.query(&n, p), None, "nan should not decide {p}");
}
}
}
#[test]
fn imaginary_unit_is_imaginary_finite_not_real() {
let ctx = fresh();
let i = ctx.i_unit();
assert_eq!(ctx.query(&i, Props::IMAGINARY), Some(true));
assert_eq!(ctx.query(&i, Props::COMPLEX), Some(true));
assert_eq!(ctx.query(&i, Props::FINITE), Some(true));
assert_eq!(ctx.query(&i, Props::ALGEBRAIC), Some(true));
assert_eq!(ctx.query(&i, Props::NONZERO), Some(true));
assert_eq!(ctx.query(&i, Props::REAL), Some(false));
assert_eq!(ctx.query(&i, Props::EXTENDED_REAL), Some(false));
assert_eq!(ctx.query(&i, Props::POSITIVE), Some(false));
assert_eq!(ctx.query(&i, Props::NEGATIVE), Some(false));
assert_eq!(ctx.query(&i, Props::ZERO), Some(false));
assert_eq!(ctx.query(&i, Props::INFINITE), Some(false));
}
#[test]
fn constants_are_query_order_independent() {
assert_order_independent(|c| c.infinity(), "oo");
assert_order_independent(|c| c.neg_infinity(), "-oo");
assert_order_independent(|c| c.complex_infinity(), "zoo");
assert_order_independent(|c| c.nan(), "nan");
assert_order_independent(|c| c.i_unit(), "I");
assert_order_independent(|c| c.pi(), "pi");
assert_order_independent(|c| c.e(), "E");
}
#[test]
fn no_constant_is_contradictory_after_chaining() {
let ctx = fresh();
let pairs = [
(Props::FINITE, Props::INFINITE),
(Props::ZERO, Props::NONZERO),
(Props::POSITIVE, Props::NONPOSITIVE),
(Props::NEGATIVE, Props::NONNEGATIVE),
];
for e in [
ctx.infinity(),
ctx.neg_infinity(),
ctx.complex_infinity(),
ctx.nan(),
ctx.i_unit(),
] {
for &(a, b) in &pairs {
let qa = ctx.query(&e, a);
let qb = ctx.query(&e, b);
assert!(
!(qa == Some(true) && qb == Some(true)),
"{e}: {a} and {b} both true"
);
}
}
}
#[test]
fn declared_positive_symbol_is_finite_and_real() {
let ctx = fresh();
let x = ctx.symbol_with("x", &[Assumption::Positive]);
assert_eq!(ctx.query(&x, Props::POSITIVE), Some(true));
assert_eq!(ctx.query(&x, Props::NONNEGATIVE), Some(true));
assert_eq!(ctx.query(&x, Props::NONZERO), Some(true));
assert_eq!(ctx.query(&x, Props::EXTENDED_REAL), Some(true));
assert_eq!(ctx.query(&x, Props::REAL), Some(true));
assert_eq!(ctx.query(&x, Props::FINITE), Some(true));
assert_eq!(ctx.query(&x, Props::COMPLEX), Some(true));
assert_eq!(ctx.query(&x, Props::NEGATIVE), Some(false));
assert_eq!(ctx.query(&x, Props::ZERO), Some(false));
}
#[test]
fn positive_and_infinite_is_consistent_in_the_lattice() {
let mut a = Assumptions::default();
a.assert_true(Props::POSITIVE);
a.assert_true(Props::INFINITE);
assert!(!a.is_contradictory(), "{a}");
assert_eq!(a.query(Props::EXTENDED_REAL), Some(true));
assert_eq!(a.query(Props::NONNEGATIVE), Some(true));
assert_eq!(a.query(Props::NONZERO), Some(true));
assert_eq!(a.query(Props::FINITE), Some(false));
assert_eq!(a.query(Props::REAL), Some(false));
let mut b = Assumptions::default();
b.assert_true(Props::POSITIVE);
b.assert_true(Props::FINITE);
assert!(!b.is_contradictory(), "{b}");
assert_eq!(b.query(Props::REAL), Some(true));
assert_eq!(b.query(Props::COMPLEX), Some(true));
}
#[test]
fn declared_extended_real_symbol_stays_agnostic_about_finiteness() {
let ctx = fresh();
let e = ctx.symbol_with("e", &[Assumption::ExtendedReal]);
assert_eq!(ctx.query(&e, Props::EXTENDED_REAL), Some(true));
assert_eq!(ctx.query(&e, Props::REAL), None);
assert_eq!(ctx.query(&e, Props::FINITE), None);
let p = ctx.symbol_with("p", &[Assumption::Positive, Assumption::Infinite]);
assert_eq!(ctx.query(&p, Props::POSITIVE), Some(true));
assert_eq!(ctx.query(&p, Props::FINITE), Some(false));
assert_eq!(ctx.query(&p, Props::REAL), Some(false));
let z = ctx.symbol_with("z", &[Assumption::NotReal]);
assert_eq!(ctx.query(&z, Props::POSITIVE), Some(false));
assert_eq!(ctx.query(&z, Props::FINITE), Some(true));
}
#[test]
fn assume_builder_and_refine_with_follow_the_same_convention() {
let ctx = fresh();
let t = ctx.symbol("t").assume(Assumption::Positive);
assert_eq!(t.is_real(), Some(true));
assert_eq!(t.is_finite(), Some(true));
let x = ctx.symbol("x");
let r = x.powi(2).sqrt().refine_with(&[(&x, Assumption::Positive)]);
assert_eq!(r, x);
}
#[test]
fn contradictory_symbol_assumptions_are_rejected() {
let ctx = fresh();
let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
ctx.symbol_with("bad", &[Assumption::Positive, Assumption::Negative])
}));
assert!(r.is_err(), "positive ∧ negative must be rejected");
let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
ctx.symbol_with("bad2", &[Assumption::Integer, Assumption::Irrational])
}));
assert!(r.is_err(), "integer ∧ irrational must be rejected");
}