use bitflags::bitflags;
use num_bigint::BigInt;
use num_traits::{Signed, Zero};
use rustc_hash::FxHashMap;
use crate::base::arena::Arena;
use crate::base::node::{ExprId, ExprNode, SymbolId};
bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Props: u32 {
const COMMUTATIVE = 1 << 0;
const COMPLEX = 1 << 1;
const REAL = 1 << 2;
const RATIONAL = 1 << 3;
const INTEGER = 1 << 4;
const ALGEBRAIC = 1 << 5;
const TRANSCENDENTAL = 1 << 6;
const IRRATIONAL = 1 << 7;
const IMAGINARY = 1 << 8;
const POSITIVE = 1 << 9;
const NEGATIVE = 1 << 10;
const NONNEGATIVE = 1 << 11;
const NONPOSITIVE = 1 << 12;
const ZERO = 1 << 13;
const NONZERO = 1 << 14;
const EVEN = 1 << 15;
const ODD = 1 << 16;
const PRIME = 1 << 17;
const COMPOSITE = 1 << 18;
const FINITE = 1 << 19;
const INFINITE = 1 << 20;
const HERMITIAN = 1 << 21;
const ANTIHERMITIAN = 1 << 22;
const EXTENDED_REAL = 1 << 23;
}
}
const PROP_NAMES: [(Props, &str); 24] = [
(Props::COMMUTATIVE, "commutative"),
(Props::COMPLEX, "complex"),
(Props::REAL, "real"),
(Props::RATIONAL, "rational"),
(Props::INTEGER, "integer"),
(Props::ALGEBRAIC, "algebraic"),
(Props::TRANSCENDENTAL, "transcendental"),
(Props::IRRATIONAL, "irrational"),
(Props::IMAGINARY, "imaginary"),
(Props::POSITIVE, "positive"),
(Props::NEGATIVE, "negative"),
(Props::NONNEGATIVE, "nonnegative"),
(Props::NONPOSITIVE, "nonpositive"),
(Props::ZERO, "zero"),
(Props::NONZERO, "nonzero"),
(Props::EVEN, "even"),
(Props::ODD, "odd"),
(Props::PRIME, "prime"),
(Props::COMPOSITE, "composite"),
(Props::FINITE, "finite"),
(Props::INFINITE, "infinite"),
(Props::HERMITIAN, "hermitian"),
(Props::ANTIHERMITIAN, "antihermitian"),
(Props::EXTENDED_REAL, "extended_real"),
];
impl std::fmt::Display for Props {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut first = true;
for (flag, name) in PROP_NAMES {
if self.contains(flag) {
if !first {
f.write_str(", ")?;
}
f.write_str(name)?;
first = false;
}
}
if first {
f.write_str("none")?;
}
Ok(())
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Assumption {
Commutative,
Complex,
Real,
Rational,
Integer,
Algebraic,
Transcendental,
Irrational,
Imaginary,
Positive,
Negative,
NonNegative,
NonPositive,
Zero,
NonZero,
Even,
Odd,
Prime,
Composite,
Finite,
Infinite,
Hermitian,
AntiHermitian,
ExtendedReal,
NotReal,
NotComplex,
NotInteger,
NotRational,
NotPositive,
NotNegative,
NotZero,
NotFinite,
NotCommutative,
NotAlgebraic,
NotTranscendental,
NotIrrational,
NotImaginary,
NotNonNegative,
NotNonPositive,
NotNonZero,
NotEven,
NotOdd,
NotPrime,
NotComposite,
NotInfinite,
NotHermitian,
NotAntiHermitian,
NotExtendedReal,
}
impl Assumption {
pub fn to_prop_value(self) -> (Props, bool) {
match self {
Assumption::Commutative => (Props::COMMUTATIVE, true),
Assumption::Complex => (Props::COMPLEX, true),
Assumption::Real => (Props::REAL, true),
Assumption::Rational => (Props::RATIONAL, true),
Assumption::Integer => (Props::INTEGER, true),
Assumption::Algebraic => (Props::ALGEBRAIC, true),
Assumption::Transcendental => (Props::TRANSCENDENTAL, true),
Assumption::Irrational => (Props::IRRATIONAL, true),
Assumption::Imaginary => (Props::IMAGINARY, true),
Assumption::Positive => (Props::POSITIVE, true),
Assumption::Negative => (Props::NEGATIVE, true),
Assumption::NonNegative => (Props::NONNEGATIVE, true),
Assumption::NonPositive => (Props::NONPOSITIVE, true),
Assumption::Zero => (Props::ZERO, true),
Assumption::NonZero => (Props::NONZERO, true),
Assumption::Even => (Props::EVEN, true),
Assumption::Odd => (Props::ODD, true),
Assumption::Prime => (Props::PRIME, true),
Assumption::Composite => (Props::COMPOSITE, true),
Assumption::Finite => (Props::FINITE, true),
Assumption::Infinite => (Props::INFINITE, true),
Assumption::Hermitian => (Props::HERMITIAN, true),
Assumption::AntiHermitian => (Props::ANTIHERMITIAN, true),
Assumption::ExtendedReal => (Props::EXTENDED_REAL, true),
Assumption::NotReal => (Props::REAL, false),
Assumption::NotComplex => (Props::COMPLEX, false),
Assumption::NotInteger => (Props::INTEGER, false),
Assumption::NotRational => (Props::RATIONAL, false),
Assumption::NotPositive => (Props::POSITIVE, false),
Assumption::NotNegative => (Props::NEGATIVE, false),
Assumption::NotZero => (Props::ZERO, false),
Assumption::NotFinite => (Props::FINITE, false),
Assumption::NotCommutative => (Props::COMMUTATIVE, false),
Assumption::NotAlgebraic => (Props::ALGEBRAIC, false),
Assumption::NotTranscendental => (Props::TRANSCENDENTAL, false),
Assumption::NotIrrational => (Props::IRRATIONAL, false),
Assumption::NotImaginary => (Props::IMAGINARY, false),
Assumption::NotNonNegative => (Props::NONNEGATIVE, false),
Assumption::NotNonPositive => (Props::NONPOSITIVE, false),
Assumption::NotNonZero => (Props::NONZERO, false),
Assumption::NotEven => (Props::EVEN, false),
Assumption::NotOdd => (Props::ODD, false),
Assumption::NotPrime => (Props::PRIME, false),
Assumption::NotComposite => (Props::COMPOSITE, false),
Assumption::NotInfinite => (Props::INFINITE, false),
Assumption::NotHermitian => (Props::HERMITIAN, false),
Assumption::NotAntiHermitian => (Props::ANTIHERMITIAN, false),
Assumption::NotExtendedReal => (Props::EXTENDED_REAL, false),
}
}
pub fn negate(self) -> Assumption {
match self {
Assumption::Commutative => Assumption::NotCommutative,
Assumption::Complex => Assumption::NotComplex,
Assumption::Real => Assumption::NotReal,
Assumption::Rational => Assumption::NotRational,
Assumption::Integer => Assumption::NotInteger,
Assumption::Algebraic => Assumption::NotAlgebraic,
Assumption::Transcendental => Assumption::NotTranscendental,
Assumption::Irrational => Assumption::NotIrrational,
Assumption::Imaginary => Assumption::NotImaginary,
Assumption::Positive => Assumption::NotPositive,
Assumption::Negative => Assumption::NotNegative,
Assumption::NonNegative => Assumption::NotNonNegative,
Assumption::NonPositive => Assumption::NotNonPositive,
Assumption::Zero => Assumption::NotZero,
Assumption::NonZero => Assumption::NotNonZero,
Assumption::Even => Assumption::NotEven,
Assumption::Odd => Assumption::NotOdd,
Assumption::Prime => Assumption::NotPrime,
Assumption::Composite => Assumption::NotComposite,
Assumption::Finite => Assumption::NotFinite,
Assumption::Infinite => Assumption::NotInfinite,
Assumption::Hermitian => Assumption::NotHermitian,
Assumption::AntiHermitian => Assumption::NotAntiHermitian,
Assumption::ExtendedReal => Assumption::NotExtendedReal,
Assumption::NotReal => Assumption::Real,
Assumption::NotComplex => Assumption::Complex,
Assumption::NotInteger => Assumption::Integer,
Assumption::NotRational => Assumption::Rational,
Assumption::NotPositive => Assumption::Positive,
Assumption::NotNegative => Assumption::Negative,
Assumption::NotZero => Assumption::Zero,
Assumption::NotFinite => Assumption::Finite,
Assumption::NotCommutative => Assumption::Commutative,
Assumption::NotAlgebraic => Assumption::Algebraic,
Assumption::NotTranscendental => Assumption::Transcendental,
Assumption::NotIrrational => Assumption::Irrational,
Assumption::NotImaginary => Assumption::Imaginary,
Assumption::NotNonNegative => Assumption::NonNegative,
Assumption::NotNonPositive => Assumption::NonPositive,
Assumption::NotNonZero => Assumption::NonZero,
Assumption::NotEven => Assumption::Even,
Assumption::NotOdd => Assumption::Odd,
Assumption::NotPrime => Assumption::Prime,
Assumption::NotComposite => Assumption::Composite,
Assumption::NotInfinite => Assumption::Infinite,
Assumption::NotHermitian => Assumption::Hermitian,
Assumption::NotAntiHermitian => Assumption::AntiHermitian,
Assumption::NotExtendedReal => Assumption::ExtendedReal,
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct Assumptions {
pub known_true: Props,
pub known_false: Props,
}
impl Assumptions {
#[inline]
pub fn query(&self, prop: Props) -> Option<bool> {
if self.known_true.contains(prop) {
Some(true)
} else if self.known_false.contains(prop) {
Some(false)
} else {
None
}
}
pub fn assert_true(&mut self, prop: Props) {
self.known_true.insert(prop);
self.forward_chain();
}
pub fn assert_false(&mut self, prop: Props) {
self.known_false.insert(prop);
self.forward_chain();
}
pub fn is_contradictory(&self) -> bool {
self.known_true.intersects(self.known_false)
}
pub fn normalize_declared(&mut self) {
self.forward_chain();
if self.query(Props::FINITE).is_some() {
return;
}
let signed = self.known_true.intersects(
Props::POSITIVE | Props::NEGATIVE | Props::NONNEGATIVE | Props::NONPOSITIVE,
);
let not_real = self.known_false.contains(Props::REAL);
if signed || not_real {
self.assert_true(Props::FINITE);
}
}
pub fn implies(&self, other: &Assumptions) -> bool {
let mut me = *self;
me.forward_chain();
if me.is_contradictory() {
return true;
}
me.known_true.contains(other.known_true) && me.known_false.contains(other.known_false)
}
}
impl std::fmt::Display for Assumptions {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut first = true;
for (flag, name) in PROP_NAMES {
if self.known_true.contains(flag) {
if !first {
f.write_str(", ")?;
}
f.write_str(name)?;
first = false;
}
}
for (flag, name) in PROP_NAMES {
if self.known_false.contains(flag) {
if !first {
f.write_str(", ")?;
}
f.write_str("!")?;
f.write_str(name)?;
first = false;
}
}
if first {
f.write_str("unknown")?;
}
Ok(())
}
}
impl Assumptions {
pub fn merge(&mut self, other: &Assumptions) -> bool {
let old_true = self.known_true;
let old_false = self.known_false;
self.known_true |= other.known_true;
self.known_false |= other.known_false;
if self.known_true != old_true || self.known_false != old_false {
self.forward_chain();
true
} else {
false
}
}
pub fn forward_chain(&mut self) {
loop {
let old_true = self.known_true;
let old_false = self.known_false;
if self.known_true.contains(Props::INTEGER) {
self.known_true |= Props::RATIONAL
| Props::ALGEBRAIC
| Props::REAL
| Props::COMPLEX
| Props::FINITE
| Props::COMMUTATIVE
| Props::HERMITIAN;
self.known_false |= Props::INFINITE | Props::IMAGINARY | Props::TRANSCENDENTAL;
}
if self.known_true.contains(Props::RATIONAL) {
self.known_true |= Props::ALGEBRAIC
| Props::REAL
| Props::COMPLEX
| Props::FINITE
| Props::COMMUTATIVE
| Props::HERMITIAN;
self.known_false |=
Props::INFINITE | Props::IMAGINARY | Props::TRANSCENDENTAL | Props::IRRATIONAL;
}
if self.known_true.contains(Props::IRRATIONAL) {
self.known_true |= Props::REAL
| Props::COMPLEX
| Props::FINITE
| Props::COMMUTATIVE
| Props::HERMITIAN
| Props::NONZERO;
self.known_false |= Props::RATIONAL
| Props::INTEGER
| Props::INFINITE
| Props::IMAGINARY
| Props::ZERO
| Props::EVEN
| Props::ODD
| Props::PRIME
| Props::COMPOSITE;
}
if self.known_true.contains(Props::ALGEBRAIC) {
self.known_true |= Props::COMPLEX | Props::FINITE | Props::COMMUTATIVE;
self.known_false |= Props::INFINITE | Props::TRANSCENDENTAL;
}
if self.known_true.contains(Props::TRANSCENDENTAL) {
self.known_true |= Props::COMPLEX | Props::FINITE | Props::COMMUTATIVE;
self.known_false |=
Props::ALGEBRAIC | Props::RATIONAL | Props::INTEGER | Props::INFINITE;
}
if self.known_true.contains(Props::REAL) {
self.known_true |= Props::EXTENDED_REAL
| Props::COMPLEX
| Props::FINITE
| Props::COMMUTATIVE
| Props::HERMITIAN;
self.known_false |= Props::IMAGINARY | Props::INFINITE;
}
if self.known_true.contains(Props::IMAGINARY) {
self.known_true |= Props::COMPLEX
| Props::FINITE
| Props::NONZERO
| Props::COMMUTATIVE
| Props::ANTIHERMITIAN;
self.known_false |= Props::REAL
| Props::EXTENDED_REAL
| Props::INFINITE
| Props::ZERO
| Props::POSITIVE
| Props::NEGATIVE
| Props::NONNEGATIVE
| Props::NONPOSITIVE
| Props::INTEGER
| Props::RATIONAL
| Props::IRRATIONAL
| Props::EVEN
| Props::ODD
| Props::PRIME
| Props::COMPOSITE;
}
if self.known_true.contains(Props::COMPLEX) {
self.known_true |= Props::COMMUTATIVE | Props::FINITE;
self.known_false |= Props::INFINITE;
}
if self.known_true.contains(Props::EXTENDED_REAL) {
self.known_true |= Props::COMMUTATIVE;
self.known_false |= Props::IMAGINARY;
}
if self.known_true.contains(Props::POSITIVE) {
self.known_true |=
Props::NONNEGATIVE | Props::NONZERO | Props::EXTENDED_REAL | Props::COMMUTATIVE;
self.known_false |=
Props::NEGATIVE | Props::ZERO | Props::NONPOSITIVE | Props::IMAGINARY;
}
if self.known_true.contains(Props::NEGATIVE) {
self.known_true |=
Props::NONPOSITIVE | Props::NONZERO | Props::EXTENDED_REAL | Props::COMMUTATIVE;
self.known_false |=
Props::POSITIVE | Props::ZERO | Props::NONNEGATIVE | Props::IMAGINARY;
}
if self.known_true.contains(Props::NONNEGATIVE) {
self.known_true |= Props::EXTENDED_REAL | Props::COMMUTATIVE;
self.known_false |= Props::NEGATIVE | Props::IMAGINARY;
}
if self.known_true.contains(Props::NONPOSITIVE) {
self.known_true |= Props::EXTENDED_REAL | Props::COMMUTATIVE;
self.known_false |= Props::POSITIVE | Props::IMAGINARY;
}
if self.known_true.contains(Props::ZERO) {
self.known_true |= Props::EVEN
| Props::FINITE
| Props::NONNEGATIVE
| Props::NONPOSITIVE
| Props::REAL
| Props::INTEGER
| Props::RATIONAL
| Props::ALGEBRAIC
| Props::COMPLEX
| Props::COMMUTATIVE
| Props::HERMITIAN;
self.known_false |= Props::NONZERO
| Props::POSITIVE
| Props::NEGATIVE
| Props::ODD
| Props::PRIME
| Props::COMPOSITE
| Props::INFINITE
| Props::IMAGINARY
| Props::IRRATIONAL
| Props::TRANSCENDENTAL;
}
if self.known_true.contains(Props::NONZERO) {
self.known_false |= Props::ZERO;
}
if self.known_true.contains(Props::EVEN) {
self.known_true |= Props::INTEGER
| Props::RATIONAL
| Props::ALGEBRAIC
| Props::REAL
| Props::COMPLEX
| Props::FINITE
| Props::COMMUTATIVE
| Props::HERMITIAN;
self.known_false |= Props::ODD
| Props::IMAGINARY
| Props::INFINITE
| Props::IRRATIONAL
| Props::TRANSCENDENTAL;
}
if self.known_true.contains(Props::ODD) {
self.known_true |= Props::INTEGER
| Props::NONZERO
| Props::RATIONAL
| Props::ALGEBRAIC
| Props::REAL
| Props::COMPLEX
| Props::FINITE
| Props::COMMUTATIVE
| Props::HERMITIAN;
self.known_false |= Props::EVEN
| Props::ZERO
| Props::IMAGINARY
| Props::INFINITE
| Props::IRRATIONAL
| Props::TRANSCENDENTAL;
}
if self.known_true.contains(Props::PRIME) {
self.known_true |= Props::INTEGER
| Props::POSITIVE
| Props::NONNEGATIVE
| Props::NONZERO
| Props::RATIONAL
| Props::ALGEBRAIC
| Props::REAL
| Props::COMPLEX
| Props::FINITE
| Props::COMMUTATIVE
| Props::HERMITIAN;
self.known_false |= Props::COMPOSITE
| Props::ZERO
| Props::NEGATIVE
| Props::NONPOSITIVE
| Props::IMAGINARY
| Props::INFINITE
| Props::IRRATIONAL
| Props::TRANSCENDENTAL;
}
if self.known_true.contains(Props::COMPOSITE) {
self.known_true |= Props::INTEGER
| Props::POSITIVE
| Props::NONNEGATIVE
| Props::NONZERO
| Props::RATIONAL
| Props::ALGEBRAIC
| Props::REAL
| Props::COMPLEX
| Props::FINITE
| Props::COMMUTATIVE
| Props::HERMITIAN;
self.known_false |= Props::PRIME
| Props::ZERO
| Props::NEGATIVE
| Props::NONPOSITIVE
| Props::IMAGINARY
| Props::INFINITE
| Props::IRRATIONAL
| Props::TRANSCENDENTAL;
}
if self.known_true.contains(Props::FINITE) {
self.known_false |= Props::INFINITE;
}
if self.known_true.contains(Props::INFINITE) {
self.known_true |= Props::NONZERO;
self.known_false |= Props::FINITE
| Props::ZERO
| Props::COMPLEX
| Props::REAL
| Props::IMAGINARY
| Props::INTEGER
| Props::RATIONAL
| Props::IRRATIONAL
| Props::ALGEBRAIC
| Props::TRANSCENDENTAL
| Props::EVEN
| Props::ODD
| Props::PRIME
| Props::COMPOSITE;
}
if self.known_false.contains(Props::COMPLEX) {
self.known_false |= Props::REAL
| Props::RATIONAL
| Props::INTEGER
| Props::ALGEBRAIC
| Props::TRANSCENDENTAL
| Props::IMAGINARY
| Props::IRRATIONAL
| Props::ZERO
| Props::EVEN
| Props::ODD
| Props::PRIME
| Props::COMPOSITE
| Props::HERMITIAN
| Props::ANTIHERMITIAN;
}
if self.known_false.contains(Props::REAL) {
self.known_false |= Props::RATIONAL
| Props::INTEGER
| Props::IRRATIONAL
| Props::EVEN
| Props::ODD
| Props::PRIME
| Props::COMPOSITE
| Props::ZERO;
}
if self.known_false.contains(Props::EXTENDED_REAL) {
self.known_false |= Props::POSITIVE
| Props::NEGATIVE
| Props::NONNEGATIVE
| Props::NONPOSITIVE
| Props::ZERO;
}
if self.known_false.contains(Props::RATIONAL) {
self.known_false |= Props::INTEGER
| Props::EVEN
| Props::ODD
| Props::PRIME
| Props::COMPOSITE
| Props::ZERO;
}
if self.known_false.contains(Props::INTEGER) {
self.known_false |= Props::EVEN | Props::ODD | Props::PRIME | Props::COMPOSITE;
}
if self.known_false.contains(Props::FINITE) {
self.known_true |= Props::INFINITE;
self.known_false |= Props::COMPLEX;
}
if self.known_false.contains(Props::INFINITE) {
self.known_true |= Props::FINITE;
}
if self.known_false.contains(Props::EXTENDED_REAL) {
self.known_false |= Props::REAL;
}
if self
.known_true
.contains(Props::NONNEGATIVE | Props::NONZERO)
{
self.known_true.insert(Props::POSITIVE);
}
if self
.known_true
.contains(Props::NONPOSITIVE | Props::NONZERO)
{
self.known_true.insert(Props::NEGATIVE);
}
if self
.known_true
.contains(Props::NONNEGATIVE | Props::NONPOSITIVE)
{
self.known_true.insert(Props::ZERO);
}
if self.known_true.contains(Props::REAL) && self.known_false.contains(Props::RATIONAL) {
self.known_true.insert(Props::IRRATIONAL);
}
if self.known_true.contains(Props::COMPLEX)
&& self.known_false.contains(Props::ALGEBRAIC)
&& (self.known_true.contains(Props::REAL)
|| self.known_true.contains(Props::IMAGINARY))
{
self.known_true.insert(Props::TRANSCENDENTAL);
}
if self.known_true.contains(Props::INTEGER) && self.known_false.contains(Props::EVEN) {
self.known_true.insert(Props::ODD);
}
if self.known_true.contains(Props::INTEGER) && self.known_false.contains(Props::ODD) {
self.known_true.insert(Props::EVEN);
}
if self
.known_true
.contains(Props::TRANSCENDENTAL | Props::REAL)
{
self.known_true.insert(Props::IRRATIONAL);
}
if self
.known_true
.contains(Props::EXTENDED_REAL | Props::FINITE)
{
self.known_true.insert(Props::REAL);
}
if self.known_false.contains(Props::REAL) && self.known_true.contains(Props::FINITE) {
self.known_false.insert(Props::EXTENDED_REAL);
}
if self.known_true.contains(Props::EXTENDED_REAL)
&& self.known_false.contains(Props::REAL)
{
self.known_true.insert(Props::INFINITE);
}
if self.known_true == old_true && self.known_false == old_false {
break;
}
}
}
}
#[derive(Debug, Default)]
pub struct AssumptionCache {
cache: FxHashMap<ExprId, Assumptions>,
}
impl AssumptionCache {
pub fn new() -> Self {
Self::default()
}
pub fn query(&mut self, arena: &Arena, id: ExprId, prop: Props) -> Option<bool> {
if let Some(cached) = self.cache.get(&id)
&& let Some(val) = cached.query(prop)
{
return Some(val);
}
let assumptions = self.compute(arena, id);
let entry = self.cache.entry(id).or_default();
entry.merge(&assumptions);
entry.query(prop)
}
fn compute(&mut self, arena: &Arena, id: ExprId) -> Assumptions {
if let Some(cached) = self.cache.get(&id) {
return *cached;
}
let node = arena.node(id).clone();
let result = match node {
ExprNode::Num(nid) => self.compute_num(arena, nid),
ExprNode::Symbol(sid) => compute_symbol(arena, sid),
ExprNode::Pi => compute_pi(),
ExprNode::E => compute_e(),
ExprNode::ImaginaryUnit => compute_imaginary_unit(),
ExprNode::Infinity => compute_infinity(),
ExprNode::NegInfinity => compute_neg_infinity(),
ExprNode::ComplexInfinity => compute_complex_infinity(),
ExprNode::NaN => compute_nan(),
ExprNode::Add(ref args) => self.compute_add(arena, args),
ExprNode::Mul(ref args) => self.compute_mul(arena, args),
ExprNode::Pow(base, exp) => self.compute_pow(arena, base, exp),
ExprNode::Neg(inner) => self.compute_neg(arena, inner),
ExprNode::Sin(inner) | ExprNode::Cos(inner) | ExprNode::Tan(inner) => {
self.compute_trig(arena, inner)
}
ExprNode::Exp(inner) => self.compute_exp(arena, inner),
ExprNode::Ln(inner) => self.compute_ln(arena, inner),
ExprNode::Abs(inner) => self.compute_abs(arena, inner),
ExprNode::Sinh(inner)
| ExprNode::Tanh(inner)
| ExprNode::Asinh(inner)
| ExprNode::Atanh(inner) => self.compute_hyp_odd(arena, inner),
ExprNode::Cosh(inner) => self.compute_cosh(arena, inner),
ExprNode::Asin(inner) | ExprNode::Acos(inner) | ExprNode::Atan(inner) => {
self.compute_inverse_trig(arena, inner)
}
ExprNode::Acosh(inner) => self.compute_acosh(arena, inner),
ExprNode::EulerGamma | ExprNode::Catalan => compute_positive_real_constant(),
ExprNode::GoldenRatio => compute_golden_ratio(),
ExprNode::Re(_) | ExprNode::Im(_) | ExprNode::Arg(_) => compute_real_valued(),
ExprNode::Conjugate(inner) => self.compute_conjugate(arena, inner),
ExprNode::KroneckerDelta(..) => compute_kronecker_delta(),
ExprNode::Floor(inner) | ExprNode::Ceiling(inner) | ExprNode::Sign(inner) => {
self.compute_real_to_integer(arena, inner)
}
ExprNode::Gamma(inner)
| ExprNode::Digamma(inner)
| ExprNode::Erf(inner)
| ExprNode::Erfc(inner)
| ExprNode::Heaviside(inner)
| ExprNode::Si(inner)
| ExprNode::Ei(inner)
| ExprNode::Zeta(inner) => self.compute_real_to_real(arena, inner),
ExprNode::LogGamma(inner) | ExprNode::Ci(inner) | ExprNode::Li(inner) => {
self.compute_positive_to_real(arena, inner)
}
ExprNode::Polygamma(_, x) => self.compute_positive_to_real(arena, x),
ExprNode::Atan2(a, b) => self.compute_all_real_to_real(arena, &[a, b]),
ExprNode::Min(ref args) | ExprNode::Max(ref args) => {
self.compute_all_real_to_real(arena, args)
}
ExprNode::DefiniteIntegral(body, _, lo, hi) => {
self.compute_all_real_to_real(arena, &[body, lo, hi])
}
_ => Assumptions::default(), };
let mut result = result;
result.forward_chain();
if matches!(node, ExprNode::Add(_))
&& result.query(Props::POSITIVE).is_none()
&& result.query(Props::NEGATIVE).is_none()
{
let extra = polynomial_sign_facts(arena, id);
if extra != Assumptions::default() {
result.merge(&extra);
result.forward_chain();
}
}
if result.is_contradictory() {
tracing::debug!(
node = ?arena.node(id),
assumptions = %result,
"assumptions: handler produced a contradictory set"
);
}
self.cache.insert(id, result);
result
}
fn compute_num(&self, arena: &Arena, nid: crate::base::node::NumId) -> Assumptions {
let val = arena.num(nid);
let mut a = Assumptions::default();
a.known_true |= Props::RATIONAL
| Props::ALGEBRAIC
| Props::REAL
| Props::COMPLEX
| Props::FINITE
| Props::COMMUTATIVE
| Props::HERMITIAN;
a.known_false |=
Props::INFINITE | Props::IMAGINARY | Props::TRANSCENDENTAL | Props::IRRATIONAL;
if val.is_integer() {
a.known_true |= Props::INTEGER;
} else {
a.known_false |=
Props::INTEGER | Props::EVEN | Props::ODD | Props::PRIME | Props::COMPOSITE;
}
if val.is_zero() {
a.known_true |= Props::ZERO
| Props::EVEN
| Props::NONNEGATIVE
| Props::NONPOSITIVE
| Props::INTEGER;
a.known_false |= Props::NONZERO
| Props::POSITIVE
| Props::NEGATIVE
| Props::ODD
| Props::PRIME
| Props::COMPOSITE;
} else {
a.known_true |= Props::NONZERO;
a.known_false |= Props::ZERO;
if val.is_positive() {
a.known_true |= Props::POSITIVE | Props::NONNEGATIVE;
a.known_false |= Props::NEGATIVE | Props::NONPOSITIVE;
} else {
a.known_true |= Props::NEGATIVE | Props::NONPOSITIVE;
a.known_false |= Props::POSITIVE | Props::NONNEGATIVE;
}
if val.is_integer() {
let int_val = val.to_integer();
if (&int_val % BigInt::from(2)).is_zero() {
a.known_true.insert(Props::EVEN);
a.known_false.insert(Props::ODD);
} else {
a.known_true.insert(Props::ODD);
a.known_false.insert(Props::EVEN);
}
if val.is_positive() {
let n: Option<u64> = int_val.try_into().ok();
if let Some(n) = n {
if is_small_prime(n) {
a.known_true.insert(Props::PRIME);
a.known_false.insert(Props::COMPOSITE);
} else if n > 1 {
a.known_false.insert(Props::PRIME);
a.known_true.insert(Props::COMPOSITE);
}
}
}
}
}
a.forward_chain();
a
}
fn compute_add(
&mut self,
arena: &Arena,
args: &smallvec::SmallVec<[ExprId; 6]>,
) -> Assumptions {
let mut a = Assumptions::default();
if args.is_empty() {
return a;
}
let mut all_integer = true;
let mut all_rational = true;
let mut all_real = true;
let mut all_complex = true;
let mut all_finite = true;
let mut all_commutative = true;
let mut all_nonneg = true;
let mut any_positive = false;
let mut all_nonpos = true;
let mut any_negative = false;
for &child in args.iter() {
let child_a = self.compute(arena, child);
if child_a.query(Props::INTEGER) != Some(true) {
all_integer = false;
}
if child_a.query(Props::RATIONAL) != Some(true) {
all_rational = false;
}
if child_a.query(Props::REAL) != Some(true) {
all_real = false;
}
if child_a.query(Props::COMPLEX) != Some(true) {
all_complex = false;
}
if child_a.query(Props::FINITE) != Some(true) {
all_finite = false;
}
if child_a.query(Props::COMMUTATIVE) != Some(true) {
all_commutative = false;
}
if child_a.query(Props::POSITIVE) == Some(true) {
any_positive = true;
}
if child_a.query(Props::NONNEGATIVE) != Some(true) {
all_nonneg = false;
}
if child_a.query(Props::NEGATIVE) == Some(true) {
any_negative = true;
}
if child_a.query(Props::NONPOSITIVE) != Some(true) {
all_nonpos = false;
}
}
if all_integer {
a.known_true |= Props::INTEGER;
}
if all_rational {
a.known_true |= Props::RATIONAL;
}
if all_real {
a.known_true |= Props::REAL;
}
if all_complex {
a.known_true |= Props::COMPLEX;
}
if all_finite {
a.known_true |= Props::FINITE;
}
if all_commutative {
a.known_true |= Props::COMMUTATIVE;
}
if all_nonneg && any_positive {
a.known_true |= Props::POSITIVE;
} else if all_nonneg {
a.known_true |= Props::NONNEGATIVE;
}
if all_nonpos && any_negative {
a.known_true |= Props::NEGATIVE;
} else if all_nonpos {
a.known_true |= Props::NONPOSITIVE;
}
a.forward_chain();
a
}
fn compute_mul(
&mut self,
arena: &Arena,
args: &smallvec::SmallVec<[ExprId; 6]>,
) -> Assumptions {
let mut a = Assumptions::default();
if args.is_empty() {
return a;
}
let mut all_integer = true;
let mut all_rational = true;
let mut all_real = true;
let mut all_complex = true;
let mut all_finite = true;
let mut all_commutative = true;
let mut any_zero = false;
let mut negative_count = 0u32;
let mut all_nonzero = true;
let mut sign_known = true;
let mut imaginary_count: usize = 0;
let mut real_count: usize = 0;
let mut any_even: bool = false;
let mut all_odd: bool = true;
for &child in args.iter() {
let child_a = self.compute(arena, child);
if child_a.query(Props::INTEGER) != Some(true) {
all_integer = false;
}
if child_a.query(Props::RATIONAL) != Some(true) {
all_rational = false;
}
if child_a.query(Props::REAL) != Some(true) {
all_real = false;
}
if child_a.query(Props::COMPLEX) != Some(true) {
all_complex = false;
}
if child_a.query(Props::FINITE) != Some(true) {
all_finite = false;
}
if child_a.query(Props::COMMUTATIVE) != Some(true) {
all_commutative = false;
}
if child_a.query(Props::ZERO) == Some(true) {
any_zero = true;
}
if child_a.query(Props::NONZERO) != Some(true) {
all_nonzero = false;
}
match child_a.query(Props::NEGATIVE) {
Some(true) => {
negative_count += 1;
}
Some(false) => { }
None => {
sign_known = false;
}
}
if child_a.query(Props::EVEN) == Some(true) {
any_even = true;
all_odd = false;
} else if child_a.query(Props::ODD) == Some(true) {
} else {
all_odd = false;
}
if child_a.query(Props::IMAGINARY) == Some(true) {
imaginary_count += 1;
}
if child_a.query(Props::REAL) == Some(true) {
real_count += 1;
}
}
if all_integer {
a.known_true |= Props::INTEGER;
if any_even {
a.known_true |= Props::EVEN;
a.known_false |= Props::ODD;
} else if all_odd && !args.is_empty() {
a.known_true |= Props::ODD;
a.known_false |= Props::EVEN;
}
}
if all_rational {
a.known_true |= Props::RATIONAL;
}
if all_real {
a.known_true |= Props::REAL;
}
if imaginary_count > 0 && real_count + imaginary_count == args.len() {
if imaginary_count.is_multiple_of(2) {
a.known_true |= Props::REAL;
} else {
a.known_true |= Props::IMAGINARY;
}
}
if all_complex {
a.known_true |= Props::COMPLEX;
}
if all_finite {
a.known_true |= Props::FINITE;
}
if all_commutative {
a.known_true |= Props::COMMUTATIVE;
}
if any_zero && all_finite {
a.known_true |= Props::ZERO;
}
if all_nonzero {
a.known_true |= Props::NONZERO;
}
if all_real && sign_known && all_nonzero {
if negative_count.is_multiple_of(2) {
a.known_true |= Props::POSITIVE;
} else {
a.known_true |= Props::NEGATIVE;
}
}
a.forward_chain();
a
}
fn compute_pow(&mut self, arena: &Arena, base: ExprId, exp: ExprId) -> Assumptions {
let mut a = Assumptions::default();
let base_a = self.compute(arena, base);
let exp_a = self.compute(arena, exp);
if base_a.query(Props::COMPLEX) == Some(true) && exp_a.query(Props::COMPLEX) == Some(true) {
a.known_true |= Props::COMPLEX;
}
if base_a.query(Props::REAL) == Some(true) && exp_a.query(Props::REAL) == Some(true) {
a.known_true |= Props::COMPLEX;
}
if base_a.query(Props::POSITIVE) == Some(true) && exp_a.query(Props::REAL) == Some(true) {
a.known_true |= Props::POSITIVE | Props::REAL;
}
if base_a.query(Props::NONNEGATIVE) == Some(true)
&& exp_a.query(Props::POSITIVE) == Some(true)
{
a.known_true |= Props::NONNEGATIVE | Props::REAL;
}
if base_a.query(Props::REAL) == Some(true) && exp_a.query(Props::EVEN) == Some(true) {
a.known_true |= Props::NONNEGATIVE | Props::REAL;
}
if base_a.query(Props::REAL) == Some(true) && exp_a.query(Props::INTEGER) == Some(true) {
a.known_true |= Props::REAL;
}
if base_a.query(Props::INTEGER) == Some(true)
&& exp_a.query(Props::INTEGER) == Some(true)
&& exp_a.query(Props::NONNEGATIVE) == Some(true)
{
a.known_true |= Props::INTEGER;
}
if base_a.query(Props::RATIONAL) == Some(true) && exp_a.query(Props::INTEGER) == Some(true)
{
a.known_true |= Props::RATIONAL;
}
if base_a.query(Props::FINITE) == Some(true) && exp_a.query(Props::FINITE) == Some(true) {
a.known_true |= Props::FINITE;
}
a.known_true |= Props::COMMUTATIVE;
a.forward_chain();
a
}
fn compute_neg(&mut self, arena: &Arena, inner: ExprId) -> Assumptions {
let mut a = self.compute(arena, inner);
let was_positive = a.query(Props::POSITIVE);
let was_negative = a.query(Props::NEGATIVE);
let was_nonneg = a.query(Props::NONNEGATIVE);
let was_nonpos = a.query(Props::NONPOSITIVE);
a.known_true
.remove(Props::POSITIVE | Props::NEGATIVE | Props::NONNEGATIVE | Props::NONPOSITIVE);
a.known_false
.remove(Props::POSITIVE | Props::NEGATIVE | Props::NONNEGATIVE | Props::NONPOSITIVE);
if was_positive == Some(true) {
a.known_true |= Props::NEGATIVE;
a.known_false |= Props::POSITIVE;
}
if was_positive == Some(false) {
a.known_false |= Props::NEGATIVE;
}
if was_negative == Some(true) {
a.known_true |= Props::POSITIVE;
a.known_false |= Props::NEGATIVE;
}
if was_negative == Some(false) {
a.known_false |= Props::POSITIVE;
}
if was_nonneg == Some(true) {
a.known_true |= Props::NONPOSITIVE;
}
if was_nonpos == Some(true) {
a.known_true |= Props::NONNEGATIVE;
}
a.forward_chain();
a
}
fn compute_trig(&mut self, arena: &Arena, inner: ExprId) -> Assumptions {
let mut a = Assumptions::default();
let inner_a = self.compute(arena, inner);
if inner_a.query(Props::REAL) == Some(true) {
a.known_true |= Props::REAL;
}
if inner_a.query(Props::COMPLEX) == Some(true) {
a.known_true |= Props::COMPLEX;
}
if inner_a.query(Props::FINITE) == Some(true) {
a.known_true |= Props::FINITE;
}
a.known_true |= Props::COMMUTATIVE;
a.forward_chain();
a
}
fn compute_exp(&mut self, arena: &Arena, inner: ExprId) -> Assumptions {
let mut a = Assumptions::default();
let inner_a = self.compute(arena, inner);
if inner_a.query(Props::REAL) == Some(true) {
a.known_true |= Props::POSITIVE | Props::REAL | Props::NONZERO;
}
if inner_a.query(Props::COMPLEX) == Some(true) {
a.known_true |= Props::COMPLEX | Props::NONZERO;
}
if inner_a.query(Props::FINITE) == Some(true) {
a.known_true |= Props::FINITE | Props::NONZERO;
}
a.known_true |= Props::COMMUTATIVE;
a.forward_chain();
a
}
fn compute_ln(&mut self, arena: &Arena, inner: ExprId) -> Assumptions {
let mut a = Assumptions::default();
let inner_a = self.compute(arena, inner);
if inner_a.query(Props::POSITIVE) == Some(true) {
a.known_true |= Props::REAL;
}
if inner_a.query(Props::COMPLEX) == Some(true) {
a.known_true |= Props::COMPLEX;
}
if inner_a.query(Props::FINITE) == Some(true) && inner_a.query(Props::NONZERO) == Some(true)
{
a.known_true |= Props::FINITE;
}
a.known_true |= Props::COMMUTATIVE;
a.forward_chain();
a
}
fn compute_abs(&mut self, arena: &Arena, inner: ExprId) -> Assumptions {
let mut a = Assumptions::default();
let inner_a = self.compute(arena, inner);
a.known_true |= Props::NONNEGATIVE | Props::REAL;
if inner_a.query(Props::ZERO) == Some(true) {
a.known_true |= Props::ZERO;
}
if inner_a.query(Props::NONZERO) == Some(true) {
a.known_true |= Props::POSITIVE;
}
if inner_a.query(Props::FINITE) == Some(true) {
a.known_true |= Props::FINITE;
}
if inner_a.query(Props::INTEGER) == Some(true) {
a.known_true |= Props::INTEGER;
}
a.known_true |= Props::COMMUTATIVE;
a.forward_chain();
a
}
fn compute_hyp_odd(&mut self, arena: &Arena, inner: ExprId) -> Assumptions {
let inner_a = self.compute(arena, inner);
let mut a = Assumptions::default();
a.known_true |= Props::COMMUTATIVE | Props::FINITE;
if inner_a.query(Props::REAL) == Some(true) {
a.known_true |= Props::REAL;
}
if inner_a.query(Props::COMPLEX) == Some(true) {
a.known_true |= Props::COMPLEX;
}
a.forward_chain();
a
}
fn compute_cosh(&mut self, arena: &Arena, inner: ExprId) -> Assumptions {
let inner_a = self.compute(arena, inner);
let mut a = Assumptions::default();
a.known_true |= Props::COMMUTATIVE | Props::FINITE;
if inner_a.query(Props::REAL) == Some(true) {
a.known_true |= Props::REAL | Props::POSITIVE;
}
if inner_a.query(Props::COMPLEX) == Some(true) {
a.known_true |= Props::COMPLEX;
}
a.forward_chain();
a
}
fn compute_inverse_trig(&mut self, arena: &Arena, inner: ExprId) -> Assumptions {
let inner_a = self.compute(arena, inner);
let mut a = Assumptions::default();
a.known_true |= Props::COMMUTATIVE | Props::FINITE;
if inner_a.query(Props::REAL) == Some(true) {
a.known_true |= Props::REAL; }
if inner_a.query(Props::COMPLEX) == Some(true) {
a.known_true |= Props::COMPLEX;
}
a.forward_chain();
a
}
fn compute_acosh(&mut self, arena: &Arena, inner: ExprId) -> Assumptions {
let inner_a = self.compute(arena, inner);
let mut a = Assumptions::default();
a.known_true |= Props::COMMUTATIVE | Props::FINITE;
if inner_a.query(Props::REAL) == Some(true) {
a.known_true |= Props::COMPLEX;
if inner_a.query(Props::POSITIVE) == Some(true) {
a.known_true |= Props::REAL | Props::NONNEGATIVE;
}
}
if inner_a.query(Props::COMPLEX) == Some(true) {
a.known_true |= Props::COMPLEX;
}
a.forward_chain();
a
}
fn compute_conjugate(&mut self, arena: &Arena, inner: ExprId) -> Assumptions {
let inner_a = self.compute(arena, inner);
let mut a = Assumptions::default();
a.known_true |= Props::COMMUTATIVE;
for p in [
Props::REAL,
Props::COMPLEX,
Props::IMAGINARY,
Props::FINITE,
Props::INFINITE,
Props::ZERO,
Props::NONZERO,
Props::RATIONAL,
Props::INTEGER,
Props::ALGEBRAIC,
Props::POSITIVE,
Props::NEGATIVE,
Props::NONNEGATIVE,
Props::NONPOSITIVE,
] {
match inner_a.query(p) {
Some(true) => a.known_true |= p,
Some(false) => a.known_false |= p,
None => {}
}
}
a.forward_chain();
a
}
fn compute_real_to_real(&mut self, arena: &Arena, inner: ExprId) -> Assumptions {
let inner_a = self.compute(arena, inner);
let mut a = Assumptions::default();
a.known_true |= Props::COMMUTATIVE;
if inner_a.query(Props::REAL) == Some(true) {
a.known_true |= Props::REAL;
}
if inner_a.query(Props::COMPLEX) == Some(true) {
a.known_true |= Props::COMPLEX;
}
a.forward_chain();
a
}
fn compute_positive_to_real(&mut self, arena: &Arena, inner: ExprId) -> Assumptions {
let inner_a = self.compute(arena, inner);
let mut a = Assumptions::default();
a.known_true |= Props::COMMUTATIVE;
if inner_a.query(Props::POSITIVE) == Some(true) {
a.known_true |= Props::REAL;
}
if inner_a.query(Props::COMPLEX) == Some(true) {
a.known_true |= Props::COMPLEX;
}
a.forward_chain();
a
}
fn compute_real_to_integer(&mut self, arena: &Arena, inner: ExprId) -> Assumptions {
let inner_a = self.compute(arena, inner);
let mut a = Assumptions::default();
a.known_true |= Props::COMMUTATIVE;
if inner_a.query(Props::REAL) == Some(true) {
a.known_true |= Props::INTEGER | Props::REAL | Props::FINITE;
}
if inner_a.query(Props::COMPLEX) == Some(true) {
a.known_true |= Props::COMPLEX;
}
a.forward_chain();
a
}
fn compute_all_real_to_real(&mut self, arena: &Arena, args: &[ExprId]) -> Assumptions {
let mut all_real = true;
let mut all_complex = true;
for &c in args {
let ca = self.compute(arena, c);
if ca.query(Props::REAL) != Some(true) {
all_real = false;
}
if ca.query(Props::COMPLEX) != Some(true) {
all_complex = false;
}
}
let mut a = Assumptions::default();
a.known_true |= Props::COMMUTATIVE;
if all_real {
a.known_true |= Props::REAL;
}
if all_complex {
a.known_true |= Props::COMPLEX;
}
a.forward_chain();
a
}
pub fn set_symbol_assumptions(&mut self, id: ExprId, assumptions: Assumptions) {
let mut assumptions = assumptions;
assumptions.normalize_declared();
assert!(
!assumptions.is_contradictory(),
"contradictory assumptions declared on symbol {id:?}: {assumptions} \
(properties {} are both asserted and denied)",
assumptions.known_true & assumptions.known_false
);
self.cache.insert(id, assumptions);
}
}
fn compute_positive_real_constant() -> Assumptions {
let mut a = Assumptions::default();
a.known_true |= Props::POSITIVE
| Props::NONNEGATIVE
| Props::NONZERO
| Props::REAL
| Props::COMPLEX
| Props::FINITE
| Props::COMMUTATIVE
| Props::HERMITIAN;
a.known_false |= Props::NEGATIVE
| Props::NONPOSITIVE
| Props::ZERO
| Props::INTEGER
| Props::IMAGINARY
| Props::INFINITE
| Props::EVEN
| Props::ODD
| Props::PRIME
| Props::COMPOSITE;
a
}
fn compute_golden_ratio() -> Assumptions {
let mut a = Assumptions::default();
a.known_true |= Props::POSITIVE
| Props::NONNEGATIVE
| Props::NONZERO
| Props::REAL
| Props::COMPLEX
| Props::FINITE
| Props::COMMUTATIVE
| Props::ALGEBRAIC
| Props::IRRATIONAL
| Props::HERMITIAN;
a.known_false |= Props::NEGATIVE
| Props::NONPOSITIVE
| Props::ZERO
| Props::INTEGER
| Props::RATIONAL
| Props::TRANSCENDENTAL
| Props::IMAGINARY
| Props::INFINITE
| Props::EVEN
| Props::ODD
| Props::PRIME
| Props::COMPOSITE;
a
}
fn compute_real_valued() -> Assumptions {
let mut a = Assumptions::default();
a.known_true |= Props::REAL | Props::COMPLEX | Props::COMMUTATIVE | Props::HERMITIAN;
a.known_false |= Props::IMAGINARY;
a
}
fn compute_kronecker_delta() -> Assumptions {
let mut a = Assumptions::default();
a.known_true |= Props::INTEGER | Props::NONNEGATIVE | Props::FINITE | Props::COMMUTATIVE;
a.known_false |= Props::NEGATIVE;
a.forward_chain();
a
}
fn compute_symbol(arena: &Arena, sid: SymbolId) -> Assumptions {
let mut a = arena.symbol_assumptions(sid);
if a.query(Props::COMMUTATIVE).is_none() {
a.assert_true(Props::COMMUTATIVE);
}
a
}
fn compute_pi() -> Assumptions {
let mut a = Assumptions::default();
a.known_true |= Props::POSITIVE
| Props::NONNEGATIVE
| Props::NONZERO
| Props::REAL
| Props::EXTENDED_REAL
| Props::COMPLEX
| Props::FINITE
| Props::COMMUTATIVE
| Props::TRANSCENDENTAL
| Props::IRRATIONAL
| Props::HERMITIAN;
a.known_false |= Props::NEGATIVE
| Props::NONPOSITIVE
| Props::ZERO
| Props::INTEGER
| Props::RATIONAL
| Props::ALGEBRAIC
| Props::IMAGINARY
| Props::INFINITE
| Props::EVEN
| Props::ODD
| Props::PRIME
| Props::COMPOSITE;
a
}
fn compute_e() -> Assumptions {
let mut a = Assumptions::default();
a.known_true |= Props::POSITIVE
| Props::NONNEGATIVE
| Props::NONZERO
| Props::REAL
| Props::EXTENDED_REAL
| Props::COMPLEX
| Props::FINITE
| Props::COMMUTATIVE
| Props::TRANSCENDENTAL
| Props::IRRATIONAL
| Props::HERMITIAN;
a.known_false |= Props::NEGATIVE
| Props::NONPOSITIVE
| Props::ZERO
| Props::INTEGER
| Props::RATIONAL
| Props::ALGEBRAIC
| Props::IMAGINARY
| Props::INFINITE
| Props::EVEN
| Props::ODD
| Props::PRIME
| Props::COMPOSITE;
a
}
fn compute_imaginary_unit() -> Assumptions {
let mut a = Assumptions::default();
a.known_true |= Props::IMAGINARY
| Props::ALGEBRAIC
| Props::COMPLEX
| Props::FINITE
| Props::COMMUTATIVE
| Props::NONZERO
| Props::ANTIHERMITIAN;
a.known_false |= Props::REAL
| Props::EXTENDED_REAL
| Props::RATIONAL
| Props::INTEGER
| Props::POSITIVE
| Props::NEGATIVE
| Props::NONNEGATIVE
| Props::NONPOSITIVE
| Props::ZERO
| Props::INFINITE
| Props::TRANSCENDENTAL
| Props::IRRATIONAL
| Props::EVEN
| Props::ODD
| Props::PRIME
| Props::COMPOSITE
| Props::HERMITIAN;
a
}
fn compute_infinity() -> Assumptions {
let mut a = Assumptions::default();
a.known_true |= Props::INFINITE
| Props::EXTENDED_REAL
| Props::POSITIVE
| Props::NONNEGATIVE
| Props::NONZERO
| Props::COMMUTATIVE;
a.known_false |= Props::FINITE
| Props::REAL
| Props::COMPLEX
| Props::NEGATIVE
| Props::NONPOSITIVE
| Props::ZERO
| Props::INTEGER
| Props::RATIONAL
| Props::IRRATIONAL
| Props::ALGEBRAIC
| Props::TRANSCENDENTAL
| Props::IMAGINARY
| Props::EVEN
| Props::ODD
| Props::PRIME
| Props::COMPOSITE;
a.forward_chain();
debug_assert!(!a.is_contradictory(), "oo: {a}");
a
}
fn compute_neg_infinity() -> Assumptions {
let mut a = Assumptions::default();
a.known_true |= Props::INFINITE
| Props::EXTENDED_REAL
| Props::NEGATIVE
| Props::NONPOSITIVE
| Props::NONZERO
| Props::COMMUTATIVE;
a.known_false |= Props::FINITE
| Props::REAL
| Props::COMPLEX
| Props::POSITIVE
| Props::NONNEGATIVE
| Props::ZERO
| Props::INTEGER
| Props::RATIONAL
| Props::IRRATIONAL
| Props::ALGEBRAIC
| Props::TRANSCENDENTAL
| Props::IMAGINARY
| Props::EVEN
| Props::ODD
| Props::PRIME
| Props::COMPOSITE;
a.forward_chain();
debug_assert!(!a.is_contradictory(), "-oo: {a}");
a
}
fn compute_complex_infinity() -> Assumptions {
let mut a = Assumptions::default();
a.known_true |= Props::INFINITE | Props::NONZERO | Props::COMMUTATIVE;
a.known_false |= Props::FINITE
| Props::EXTENDED_REAL
| Props::REAL
| Props::COMPLEX
| Props::POSITIVE
| Props::NEGATIVE
| Props::NONNEGATIVE
| Props::NONPOSITIVE
| Props::ZERO
| Props::INTEGER
| Props::RATIONAL
| Props::IRRATIONAL
| Props::ALGEBRAIC
| Props::TRANSCENDENTAL
| Props::IMAGINARY
| Props::EVEN
| Props::ODD
| Props::PRIME
| Props::COMPOSITE;
a.forward_chain();
debug_assert!(!a.is_contradictory(), "zoo: {a}");
a
}
fn compute_nan() -> Assumptions {
let mut a = Assumptions::default();
a.known_true |= Props::COMMUTATIVE;
a
}
fn is_small_prime(n: u64) -> bool {
if n < 2 {
return false;
}
if n < 4 {
return true;
}
if n.is_multiple_of(2) || n.is_multiple_of(3) {
return false;
}
let mut i = 5u64;
while i * i <= n {
if n.is_multiple_of(i) || n.is_multiple_of(i + 2) {
return false;
}
i += 6;
}
true
}
const POLY_SIGN_MAX_DEGREE: usize = 24;
fn polynomial_sign_facts(arena: &Arena, id: ExprId) -> Assumptions {
use crate::api::expr_poly_ext::{Endpoint, poly_sign_on_interval};
use num_rational::Ratio;
let mut facts = Assumptions::default();
let syms = crate::base::walk::free_symbols(arena, id);
let [var] = syms.as_slice() else {
return facts;
};
let ExprNode::Symbol(sid) = arena.node(*var) else {
return facts;
};
let sym = compute_symbol(arena, *sid);
if sym.query(Props::REAL) != Some(true) {
return facts;
}
let Some(f) = crate::poly::polybridge::expr_to_poly(arena, id, *var) else {
return facts;
};
let deg = f.degree().unwrap_or(0);
if deg == 0 || deg > POLY_SIGN_MAX_DEGREE {
return facts;
}
let zero = Ratio::from_integer(BigInt::from(0));
let (lo, hi, open_at) = if sym.query(Props::POSITIVE) == Some(true) {
(
Endpoint::Finite(zero.clone()),
Endpoint::PosInf,
Some(zero.clone()),
)
} else if sym.query(Props::NONNEGATIVE) == Some(true) {
(Endpoint::Finite(zero.clone()), Endpoint::PosInf, None)
} else if sym.query(Props::NEGATIVE) == Some(true) {
(
Endpoint::NegInf,
Endpoint::Finite(zero.clone()),
Some(zero.clone()),
)
} else if sym.query(Props::NONPOSITIVE) == Some(true) {
(Endpoint::NegInf, Endpoint::Finite(zero.clone()), None)
} else {
(Endpoint::NegInf, Endpoint::PosInf, None)
};
let strict_on_domain = |g: &crate::poly::Poly| -> bool {
if poly_sign_on_interval(g, &lo, &hi, true) {
return true;
}
let Some(a) = &open_at else {
return false;
};
if !poly_sign_on_interval(g, &lo, &hi, false) || !g.eval(a).is_zero() {
return false;
}
let bound = crate::poly::sturm::cauchy_bound(g) + Ratio::from_integer(BigInt::from(1));
let chain = crate::poly::sturm::SturmChain::new(g);
let roots_closed = match (&lo, &hi) {
(Endpoint::Finite(l), Endpoint::PosInf) => chain.count_roots_in_closed(l, &bound),
(Endpoint::NegInf, Endpoint::Finite(h)) => chain.count_roots_in_closed(&(-bound), h),
_ => return false,
};
roots_closed == 1
};
let neg_f = f.neg();
if strict_on_domain(&f) {
facts.assert_true(Props::POSITIVE);
} else if poly_sign_on_interval(&f, &lo, &hi, false) {
facts.assert_true(Props::NONNEGATIVE);
} else if strict_on_domain(&neg_f) {
facts.assert_true(Props::NEGATIVE);
} else if poly_sign_on_interval(&neg_f, &lo, &hi, false) {
facts.assert_true(Props::NONPOSITIVE);
}
if facts != Assumptions::default() {
facts.assert_true(Props::REAL);
}
facts
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_assumptions_are_all_unknown() {
let a = Assumptions::default();
assert_eq!(a.query(Props::REAL), None);
assert_eq!(a.query(Props::POSITIVE), None);
assert_eq!(a.query(Props::INTEGER), None);
}
#[test]
fn assert_true_and_query() {
let mut a = Assumptions::default();
a.assert_true(Props::POSITIVE);
assert_eq!(a.query(Props::POSITIVE), Some(true));
}
#[test]
fn assert_false_and_query() {
let mut a = Assumptions::default();
a.assert_false(Props::REAL);
assert_eq!(a.query(Props::REAL), Some(false));
}
#[test]
fn contradiction_detection() {
let a = Assumptions {
known_true: Props::POSITIVE,
known_false: Props::POSITIVE,
};
assert!(a.is_contradictory());
}
#[test]
fn integer_implies_rational_real_complex() {
let mut a = Assumptions::default();
a.assert_true(Props::INTEGER);
assert_eq!(a.query(Props::RATIONAL), Some(true));
assert_eq!(a.query(Props::REAL), Some(true));
assert_eq!(a.query(Props::COMPLEX), Some(true));
assert_eq!(a.query(Props::FINITE), Some(true));
assert_eq!(a.query(Props::COMMUTATIVE), Some(true));
assert_eq!(a.query(Props::ALGEBRAIC), Some(true));
assert_eq!(a.query(Props::IMAGINARY), Some(false));
assert_eq!(a.query(Props::INFINITE), Some(false));
}
#[test]
fn positive_implies_extended_real_nonneg_nonzero() {
let mut a = Assumptions::default();
a.assert_true(Props::POSITIVE);
assert_eq!(a.query(Props::NONNEGATIVE), Some(true));
assert_eq!(a.query(Props::NONZERO), Some(true));
assert_eq!(a.query(Props::EXTENDED_REAL), Some(true));
assert_eq!(a.query(Props::REAL), None, "could be +oo");
assert_eq!(a.query(Props::FINITE), None, "could be +oo");
assert_eq!(a.query(Props::NEGATIVE), Some(false));
assert_eq!(a.query(Props::ZERO), Some(false));
a.assert_true(Props::FINITE);
assert_eq!(a.query(Props::REAL), Some(true));
assert_eq!(a.query(Props::COMPLEX), Some(true));
assert!(!a.is_contradictory());
}
#[test]
fn positive_and_infinite_is_consistent() {
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::REAL), Some(false));
assert_eq!(a.query(Props::COMPLEX), Some(false));
assert_eq!(a.query(Props::EXTENDED_REAL), Some(true));
let mut b = Assumptions::default();
b.assert_true(Props::EXTENDED_REAL);
b.assert_false(Props::REAL);
assert_eq!(b.query(Props::INFINITE), Some(true));
}
#[test]
fn normalize_declared_makes_signed_symbols_finite() {
let mut pos = Assumptions::default();
pos.assert_true(Props::POSITIVE);
pos.normalize_declared();
assert_eq!(pos.query(Props::FINITE), Some(true));
assert_eq!(pos.query(Props::REAL), Some(true));
pos.normalize_declared();
assert_eq!(pos.query(Props::REAL), Some(true), "idempotent");
let mut ext = Assumptions::default();
ext.assert_true(Props::EXTENDED_REAL);
ext.normalize_declared();
assert_eq!(ext.query(Props::FINITE), None);
let mut pos_inf = Assumptions::default();
pos_inf.assert_true(Props::POSITIVE);
pos_inf.assert_true(Props::INFINITE);
pos_inf.normalize_declared();
assert_eq!(pos_inf.query(Props::FINITE), Some(false));
assert!(!pos_inf.is_contradictory());
let mut not_real = Assumptions::default();
not_real.assert_false(Props::REAL);
not_real.normalize_declared();
assert_eq!(not_real.query(Props::POSITIVE), Some(false));
}
#[test]
fn zero_implies_even_integer_nonneg_nonpos() {
let mut a = Assumptions::default();
a.assert_true(Props::ZERO);
assert_eq!(a.query(Props::EVEN), Some(true));
assert_eq!(a.query(Props::INTEGER), Some(true));
assert_eq!(a.query(Props::NONNEGATIVE), Some(true));
assert_eq!(a.query(Props::NONPOSITIVE), Some(true));
assert_eq!(a.query(Props::NONZERO), Some(false));
assert_eq!(a.query(Props::POSITIVE), Some(false));
assert_eq!(a.query(Props::NEGATIVE), Some(false));
}
#[test]
fn not_complex_implies_not_real_not_integer() {
let mut a = Assumptions::default();
a.assert_false(Props::COMPLEX);
assert_eq!(a.query(Props::REAL), Some(false));
assert_eq!(a.query(Props::INTEGER), Some(false));
assert_eq!(a.query(Props::RATIONAL), Some(false));
}
#[test]
fn nonneg_and_nonzero_implies_positive() {
let mut a = Assumptions::default();
a.assert_true(Props::NONNEGATIVE);
a.assert_true(Props::NONZERO);
assert_eq!(a.query(Props::POSITIVE), Some(true));
}
#[test]
fn nonneg_and_nonpos_implies_zero() {
let mut a = Assumptions::default();
a.assert_true(Props::NONNEGATIVE);
a.assert_true(Props::NONPOSITIVE);
assert_eq!(a.query(Props::ZERO), Some(true));
}
#[test]
fn integer_and_not_even_implies_odd() {
let mut a = Assumptions::default();
a.assert_true(Props::INTEGER);
a.assert_false(Props::EVEN);
assert_eq!(a.query(Props::ODD), Some(true));
}
#[test]
fn prime_implies_integer_positive() {
let mut a = Assumptions::default();
a.assert_true(Props::PRIME);
assert_eq!(a.query(Props::INTEGER), Some(true));
assert_eq!(a.query(Props::POSITIVE), Some(true));
assert_eq!(a.query(Props::COMPOSITE), Some(false));
assert_eq!(a.query(Props::RATIONAL), Some(true));
assert_eq!(a.query(Props::NONZERO), Some(true));
}
#[test]
fn even_and_odd_imply_integer() {
let mut e = Assumptions::default();
e.assert_true(Props::EVEN);
assert_eq!(e.query(Props::INTEGER), Some(true));
assert_eq!(e.query(Props::RATIONAL), Some(true));
assert_eq!(e.query(Props::ODD), Some(false));
let mut o = Assumptions::default();
o.assert_true(Props::ODD);
assert_eq!(o.query(Props::INTEGER), Some(true));
assert_eq!(o.query(Props::EVEN), Some(false));
assert_eq!(o.query(Props::NONZERO), Some(true));
assert_eq!(o.query(Props::ZERO), Some(false));
}
#[test]
fn zero_full_chain() {
let mut a = Assumptions::default();
a.assert_true(Props::ZERO);
for p in [
Props::EVEN,
Props::NONNEGATIVE,
Props::NONPOSITIVE,
Props::FINITE,
Props::ALGEBRAIC,
Props::RATIONAL,
Props::INTEGER,
Props::REAL,
Props::COMPLEX,
Props::EXTENDED_REAL,
] {
assert_eq!(a.query(p), Some(true), "zero ⇒ {p}");
}
for p in [
Props::NONZERO,
Props::POSITIVE,
Props::NEGATIVE,
Props::ODD,
Props::PRIME,
Props::IRRATIONAL,
Props::TRANSCENDENTAL,
Props::IMAGINARY,
Props::INFINITE,
] {
assert_eq!(a.query(p), Some(false), "zero ⇒ ¬{p}");
}
}
#[test]
fn integer_rational_algebraic_complex_tower() {
let mut i = Assumptions::default();
i.assert_true(Props::INTEGER);
assert_eq!(i.query(Props::RATIONAL), Some(true));
let mut r = Assumptions::default();
r.assert_true(Props::RATIONAL);
assert_eq!(r.query(Props::ALGEBRAIC), Some(true));
assert_eq!(r.query(Props::IRRATIONAL), Some(false));
assert_eq!(r.query(Props::TRANSCENDENTAL), Some(false));
let mut al = Assumptions::default();
al.assert_true(Props::ALGEBRAIC);
assert_eq!(al.query(Props::COMPLEX), Some(true));
assert_eq!(al.query(Props::TRANSCENDENTAL), Some(false));
assert_eq!(al.query(Props::REAL), None);
}
#[test]
fn irrational_implies_real_nonzero_nonint() {
let mut a = Assumptions::default();
a.assert_true(Props::IRRATIONAL);
assert_eq!(a.query(Props::REAL), Some(true));
assert_eq!(a.query(Props::NONZERO), Some(true));
assert_eq!(a.query(Props::ZERO), Some(false));
assert_eq!(a.query(Props::INTEGER), Some(false));
assert_eq!(a.query(Props::RATIONAL), Some(false));
assert_eq!(a.query(Props::EVEN), Some(false));
assert_eq!(a.query(Props::PRIME), Some(false));
assert_eq!(a.query(Props::ALGEBRAIC), None);
assert_eq!(a.query(Props::TRANSCENDENTAL), None);
}
#[test]
fn transcendental_real_implies_irrational_but_complex_alone_does_not() {
let mut t = Assumptions::default();
t.assert_true(Props::TRANSCENDENTAL);
assert_eq!(
t.query(Props::IRRATIONAL),
None,
"transcendental alone: unknown"
);
assert_eq!(t.query(Props::RATIONAL), Some(false));
t.assert_true(Props::REAL);
assert_eq!(t.query(Props::IRRATIONAL), Some(true));
assert_eq!(t.query(Props::NONZERO), Some(true));
let mut t2 = Assumptions::default();
t2.assert_true(Props::REAL);
t2.assert_true(Props::TRANSCENDENTAL);
assert_eq!(t2.query(Props::IRRATIONAL), Some(true));
}
#[test]
fn positive_full_chain() {
let mut a = Assumptions::default();
a.assert_true(Props::POSITIVE);
assert_eq!(a.query(Props::NONNEGATIVE), Some(true));
assert_eq!(a.query(Props::NONZERO), Some(true));
assert_eq!(a.query(Props::EXTENDED_REAL), Some(true));
assert_eq!(a.query(Props::COMMUTATIVE), Some(true));
assert_eq!(a.query(Props::NONPOSITIVE), Some(false));
assert_eq!(a.query(Props::NEGATIVE), Some(false));
assert_eq!(a.query(Props::ZERO), Some(false));
assert_eq!(a.query(Props::IMAGINARY), Some(false));
}
#[test]
fn imaginary_implies_nonzero_complex_not_real() {
let mut a = Assumptions::default();
a.assert_true(Props::IMAGINARY);
assert_eq!(a.query(Props::NONZERO), Some(true));
assert_eq!(a.query(Props::ZERO), Some(false));
assert_eq!(a.query(Props::COMPLEX), Some(true));
assert_eq!(a.query(Props::REAL), Some(false));
assert_eq!(a.query(Props::EXTENDED_REAL), Some(false));
assert_eq!(a.query(Props::INTEGER), Some(false));
assert_eq!(a.query(Props::POSITIVE), Some(false));
assert_eq!(a.query(Props::EVEN), Some(false));
}
#[test]
fn extended_real_rules() {
let mut r = Assumptions::default();
r.assert_true(Props::REAL);
assert_eq!(r.query(Props::EXTENDED_REAL), Some(true));
let mut ef = Assumptions::default();
ef.assert_true(Props::EXTENDED_REAL);
assert_eq!(ef.query(Props::REAL), None, "could be ±∞");
ef.assert_true(Props::FINITE);
assert_eq!(ef.query(Props::REAL), Some(true));
let mut ne = Assumptions::default();
ne.assert_false(Props::EXTENDED_REAL);
assert_eq!(ne.query(Props::REAL), Some(false));
assert_eq!(ne.query(Props::POSITIVE), Some(false));
let mut nr = Assumptions::default();
nr.assert_false(Props::REAL);
assert_eq!(nr.query(Props::EXTENDED_REAL), None, "could be ±∞");
nr.assert_true(Props::FINITE);
assert_eq!(nr.query(Props::EXTENDED_REAL), Some(false));
let arena = Arena::new();
let mut cache = AssumptionCache::new();
assert_eq!(
cache.query(&arena, arena.infinity, Props::EXTENDED_REAL),
Some(true)
);
assert_eq!(
cache.query(&arena, arena.neg_infinity, Props::EXTENDED_REAL),
Some(true)
);
assert_eq!(
cache.query(&arena, arena.complex_infinity, Props::EXTENDED_REAL),
Some(false)
);
assert_eq!(
cache.query(&arena, arena.i_unit, Props::EXTENDED_REAL),
Some(false)
);
assert_eq!(
cache.query(&arena, arena.pi, Props::EXTENDED_REAL),
Some(true)
);
assert_eq!(
cache.query(&arena, arena.one, Props::EXTENDED_REAL),
Some(true)
);
}
#[test]
fn contradictions_from_new_chains() {
let mut a = Assumptions::default();
a.assert_true(Props::IRRATIONAL);
a.assert_true(Props::ZERO);
assert!(a.is_contradictory(), "irrational ∧ zero");
let mut b = Assumptions::default();
b.assert_true(Props::IMAGINARY);
b.assert_true(Props::ZERO);
assert!(b.is_contradictory(), "imaginary ∧ zero");
let mut c = Assumptions::default();
c.assert_true(Props::TRANSCENDENTAL);
c.assert_true(Props::REAL);
c.assert_true(Props::RATIONAL);
assert!(c.is_contradictory(), "transcendental ∧ real ∧ rational");
let mut d = Assumptions::default();
d.assert_true(Props::EXTENDED_REAL);
d.assert_true(Props::FINITE);
d.assert_false(Props::REAL);
assert!(d.is_contradictory(), "extended_real ∧ finite ∧ ¬real");
let mut e = Assumptions::default();
e.assert_true(Props::EVEN);
e.assert_false(Props::INTEGER);
assert!(e.is_contradictory(), "even ∧ ¬integer");
let mut f = Assumptions::default();
f.assert_true(Props::PRIME);
f.assert_true(Props::NEGATIVE);
assert!(f.is_contradictory(), "prime ∧ negative");
let mut ok = Assumptions::default();
ok.assert_true(Props::IRRATIONAL);
ok.assert_true(Props::POSITIVE);
assert!(!ok.is_contradictory());
}
#[test]
fn implies_uses_derived_facts() {
let mut pos = Assumptions::default();
pos.assert_true(Props::POSITIVE);
let real = Assumptions {
known_true: Props::REAL,
known_false: Props::empty(),
};
let ext_real = Assumptions {
known_true: Props::EXTENDED_REAL,
known_false: Props::empty(),
};
let not_neg = Assumptions {
known_true: Props::empty(),
known_false: Props::NEGATIVE,
};
assert!(pos.implies(&ext_real));
assert!(pos.implies(¬_neg));
assert!(!pos.implies(&real), "positive alone could be +oo");
assert!(!real.implies(&pos));
assert!(
pos.implies(&Assumptions::default()),
"everything implies nothing"
);
assert!(pos.implies(&pos));
let raw = Assumptions {
known_true: Props::INTEGER,
known_false: Props::empty(),
};
assert!(raw.implies(&real));
let bad = Assumptions {
known_true: Props::ZERO,
known_false: Props::ZERO,
};
assert!(bad.implies(&pos));
}
#[test]
fn negate_is_an_involution_and_flips_value() {
let all = [
Assumption::Commutative,
Assumption::Complex,
Assumption::Real,
Assumption::Rational,
Assumption::Integer,
Assumption::Algebraic,
Assumption::Transcendental,
Assumption::Irrational,
Assumption::Imaginary,
Assumption::Positive,
Assumption::Negative,
Assumption::NonNegative,
Assumption::NonPositive,
Assumption::Zero,
Assumption::NonZero,
Assumption::Even,
Assumption::Odd,
Assumption::Prime,
Assumption::Composite,
Assumption::Finite,
Assumption::Infinite,
Assumption::Hermitian,
Assumption::AntiHermitian,
Assumption::ExtendedReal,
];
for a in all {
let n = a.negate();
assert_ne!(a, n);
assert_eq!(n.negate(), a, "{a:?}");
let (p, v) = a.to_prop_value();
let (np, nv) = n.to_prop_value();
assert_eq!(p, np, "{a:?}");
assert_eq!(v, !nv, "{a:?}");
}
assert_eq!(Assumption::NotZero.negate(), Assumption::Zero);
assert_eq!(
Assumption::NotExtendedReal.to_prop_value(),
(Props::EXTENDED_REAL, false)
);
}
#[test]
fn display_lists_active_props() {
assert_eq!(Assumptions::default().to_string(), "unknown");
let a = Assumptions {
known_true: Props::POSITIVE | Props::REAL,
known_false: Props::ZERO,
};
assert_eq!(a.to_string(), "real, positive, !zero");
let mut chained = Assumptions::default();
chained.assert_true(Props::PRIME);
let s = chained.to_string();
assert!(
s.starts_with("commutative, complex, real, rational, integer"),
"{s}"
);
assert!(s.contains("prime") && s.contains("!composite"), "{s}");
assert_eq!(Props::empty().to_string(), "none");
assert_eq!((Props::EVEN | Props::INTEGER).to_string(), "integer, even");
}
#[test]
fn zero_value_assumptions() {
let arena = Arena::new();
let mut cache = AssumptionCache::new();
let zero = arena.zero;
assert_eq!(cache.query(&arena, zero, Props::ZERO), Some(true));
assert_eq!(cache.query(&arena, zero, Props::INTEGER), Some(true));
assert_eq!(cache.query(&arena, zero, Props::EVEN), Some(true));
assert_eq!(cache.query(&arena, zero, Props::POSITIVE), Some(false));
}
#[test]
fn one_value_assumptions() {
let arena = Arena::new();
let mut cache = AssumptionCache::new();
let one = arena.one;
assert_eq!(cache.query(&arena, one, Props::POSITIVE), Some(true));
assert_eq!(cache.query(&arena, one, Props::INTEGER), Some(true));
assert_eq!(cache.query(&arena, one, Props::ODD), Some(true));
assert_eq!(cache.query(&arena, one, Props::ZERO), Some(false));
}
#[test]
fn neg_one_value_assumptions() {
let arena = Arena::new();
let mut cache = AssumptionCache::new();
let neg_one = arena.neg_one;
assert_eq!(cache.query(&arena, neg_one, Props::NEGATIVE), Some(true));
assert_eq!(cache.query(&arena, neg_one, Props::INTEGER), Some(true));
assert_eq!(cache.query(&arena, neg_one, Props::ODD), Some(true));
}
#[test]
fn rational_value_assumptions() {
let mut arena = Arena::new();
let r = arena.rational(1, 3);
let mut cache = AssumptionCache::new();
assert_eq!(cache.query(&arena, r, Props::RATIONAL), Some(true));
assert_eq!(cache.query(&arena, r, Props::REAL), Some(true));
assert_eq!(cache.query(&arena, r, Props::POSITIVE), Some(true));
assert_eq!(cache.query(&arena, r, Props::INTEGER), Some(false));
}
#[test]
fn prime_detection() {
let mut arena = Arena::new();
let mut cache = AssumptionCache::new();
let seven = arena.int(7);
assert_eq!(cache.query(&arena, seven, Props::PRIME), Some(true));
let four = arena.int(4);
assert_eq!(cache.query(&arena, four, Props::PRIME), Some(false));
assert_eq!(cache.query(&arena, four, Props::COMPOSITE), Some(true));
}
#[test]
fn pi_assumptions() {
let arena = Arena::new();
let mut cache = AssumptionCache::new();
assert_eq!(cache.query(&arena, arena.pi, Props::POSITIVE), Some(true));
assert_eq!(cache.query(&arena, arena.pi, Props::REAL), Some(true));
assert_eq!(
cache.query(&arena, arena.pi, Props::TRANSCENDENTAL),
Some(true)
);
assert_eq!(cache.query(&arena, arena.pi, Props::IRRATIONAL), Some(true));
assert_eq!(cache.query(&arena, arena.pi, Props::RATIONAL), Some(false));
}
#[test]
fn e_assumptions() {
let arena = Arena::new();
let mut cache = AssumptionCache::new();
assert_eq!(
cache.query(&arena, arena.e_const, Props::POSITIVE),
Some(true)
);
assert_eq!(
cache.query(&arena, arena.e_const, Props::TRANSCENDENTAL),
Some(true)
);
}
#[test]
fn imaginary_unit_assumptions() {
let arena = Arena::new();
let mut cache = AssumptionCache::new();
assert_eq!(
cache.query(&arena, arena.i_unit, Props::IMAGINARY),
Some(true)
);
assert_eq!(cache.query(&arena, arena.i_unit, Props::REAL), Some(false));
assert_eq!(
cache.query(&arena, arena.i_unit, Props::COMPLEX),
Some(true)
);
assert_eq!(
cache.query(&arena, arena.i_unit, Props::ALGEBRAIC),
Some(true)
);
}
#[test]
fn infinity_assumptions() {
let arena = Arena::new();
let mut cache = AssumptionCache::new();
assert_eq!(
cache.query(&arena, arena.infinity, Props::INFINITE),
Some(true)
);
assert_eq!(
cache.query(&arena, arena.infinity, Props::POSITIVE),
Some(true)
);
assert_eq!(
cache.query(&arena, arena.infinity, Props::FINITE),
Some(false)
);
}
#[test]
fn neg_infinity_assumptions() {
let arena = Arena::new();
let mut cache = AssumptionCache::new();
assert_eq!(
cache.query(&arena, arena.neg_infinity, Props::INFINITE),
Some(true)
);
assert_eq!(
cache.query(&arena, arena.neg_infinity, Props::NEGATIVE),
Some(true)
);
}
#[test]
fn nan_assumptions() {
let arena = Arena::new();
let mut cache = AssumptionCache::new();
assert_eq!(cache.query(&arena, arena.nan, Props::REAL), None);
assert_eq!(
cache.query(&arena, arena.nan, Props::COMMUTATIVE),
Some(true)
);
}
#[test]
fn add_positive_positive_is_positive() {
let mut arena = Arena::new();
let mut cache = AssumptionCache::new();
let two = arena.add(&[arena.one, arena.one]);
assert_eq!(cache.query(&arena, two, Props::POSITIVE), Some(true));
assert_eq!(cache.query(&arena, two, Props::INTEGER), Some(true));
}
#[test]
fn mul_positive_negative_is_negative() {
let mut arena = Arena::new();
let mut cache = AssumptionCache::new();
let one = arena.one;
let neg_one = arena.neg_one;
let prod = arena.mul(&[one, neg_one]);
assert_eq!(cache.query(&arena, prod, Props::NEGATIVE), Some(true));
}
#[test]
fn pow_real_even_is_nonneg() {
let mut arena = Arena::new();
let mut cache = AssumptionCache::new();
let neg_one = arena.neg_one;
let two = arena.int(2);
let result = arena.pow(neg_one, two);
assert_eq!(cache.query(&arena, result, Props::NONNEGATIVE), Some(true));
}
#[test]
fn small_prime_test() {
assert!(!is_small_prime(0));
assert!(!is_small_prime(1));
assert!(is_small_prime(2));
assert!(is_small_prime(3));
assert!(!is_small_prime(4));
assert!(is_small_prime(5));
assert!(is_small_prime(7));
assert!(!is_small_prime(9));
assert!(is_small_prime(11));
assert!(is_small_prime(97));
assert!(!is_small_prime(100));
}
#[test]
fn sinh_of_real_is_real() {
let mut arena = Arena::new();
let mut cache = AssumptionCache::new();
let expr = arena.sinh(arena.pi);
assert_eq!(cache.query(&arena, expr, Props::REAL), Some(true));
}
#[test]
fn cosh_of_real_is_positive() {
let mut arena = Arena::new();
let mut cache = AssumptionCache::new();
let expr = arena.cosh(arena.pi);
assert_eq!(cache.query(&arena, expr, Props::POSITIVE), Some(true));
}
#[test]
fn tanh_of_real_is_real() {
let mut arena = Arena::new();
let mut cache = AssumptionCache::new();
let expr = arena.tanh(arena.pi);
assert_eq!(cache.query(&arena, expr, Props::REAL), Some(true));
}
#[test]
fn asin_of_real_is_real() {
let mut arena = Arena::new();
let mut cache = AssumptionCache::new();
let expr = arena.asin(arena.one);
assert_eq!(cache.query(&arena, expr, Props::REAL), Some(true));
}
#[test]
fn product_with_i_is_imaginary() {
let mut arena = Arena::new();
let mut cache = AssumptionCache::new();
let three = arena.int(3);
let prod = arena.mul(&[three, arena.i_unit]); assert_eq!(cache.query(&arena, prod, Props::IMAGINARY), Some(true));
}
#[test]
fn product_i_times_i_is_real() {
let mut arena = Arena::new();
let mut cache = AssumptionCache::new();
let prod = arena.mul(&[arena.i_unit, arena.i_unit]);
assert_eq!(cache.query(&arena, prod, Props::REAL), Some(true));
}
}