use crate::kernel::{ExprData, ExprId, ExprPool};
pub(crate) const PI_NAME: &str = "pi";
pub(crate) fn is_pi(expr: ExprId, pool: &ExprPool) -> bool {
matches!(pool.get(expr), ExprData::Symbol { name, .. } if name == PI_NAME)
}
pub(crate) fn pi_symbol(pool: &ExprPool) -> ExprId {
pool.symbol(PI_NAME, crate::kernel::Domain::Real)
}
pub(crate) fn is_named_constant(expr: ExprId, pool: &ExprPool) -> bool {
is_pi(expr, pool) || pool.is_imaginary_unit(expr)
}
pub(crate) fn walk_symbols(expr: ExprId, pool: &ExprPool, f: &mut impl FnMut(ExprId, &ExprPool)) {
match pool.get(expr) {
ExprData::Symbol { .. } => f(expr, pool),
ExprData::Add(args) | ExprData::Mul(args) | ExprData::Func { args, .. } => {
for a in args {
walk_symbols(a, pool, f);
}
}
ExprData::Pow { base, exp } => {
walk_symbols(base, pool, f);
walk_symbols(exp, pool, f);
}
_ => {}
}
}
pub(crate) fn collect_free_symbols(expr: ExprId, pool: &ExprPool, out: &mut Vec<ExprId>) {
walk_symbols(expr, pool, &mut |s, pool| {
if !is_named_constant(s, pool) && !out.contains(&s) {
out.push(s);
}
});
}
#[cfg(test)]
mod tests {
use super::*;
use crate::kernel::Domain;
#[test]
fn pi_and_i_are_constants_and_everything_else_is_a_parameter() {
let p = ExprPool::new();
let pi = p.symbol(PI_NAME, Domain::Real);
let i = p.imaginary_unit();
let k = p.symbol("k", Domain::Real);
assert!(is_named_constant(pi, &p));
assert!(is_named_constant(i, &p));
assert!(!is_named_constant(k, &p));
let two_pi = p.mul(vec![p.integer(2_i32), pi]);
let e = p.mul(vec![p.func("cos", vec![p.add(vec![k, two_pi])]), i]);
let mut free = Vec::new();
collect_free_symbols(e, &p, &mut free);
assert_eq!(free, vec![k]);
}
#[test]
fn every_real_evaluator_resolves_pi_and_an_explicit_binding_still_wins() {
use crate::ball::IntervalEval;
use std::collections::HashMap;
let p = ExprPool::new();
let pi = pi_symbol(&p);
let e = p.mul(vec![p.rational(1, 2), pi]);
let empty: HashMap<ExprId, f64> = HashMap::new();
let want = std::f64::consts::FRAC_PI_2;
assert!((crate::eval::eval_f64(e, &p, &empty).unwrap() - want).abs() < 1e-15);
assert!((crate::jit::eval_interp(e, &empty, &p).unwrap() - want).abs() < 1e-15);
let ball = crate::eval::eval_interval(e, &p, &IntervalEval::new(128)).unwrap();
assert!(ball.lo().to_f64() <= want && want <= ball.hi().to_f64());
assert!(ball.rad_f64() < 1e-30, "π enclosure is not tight: {ball}");
let bound: HashMap<ExprId, f64> = HashMap::from([(pi, 10.0)]);
assert_eq!(crate::eval::eval_f64(e, &p, &bound).unwrap(), 5.0);
assert_eq!(crate::jit::eval_interp(e, &bound, &p).unwrap(), 5.0);
let mut iv = IntervalEval::new(64);
iv.bind(pi, crate::ball::ArbBall::from_f64(10.0, 64));
assert_eq!(
crate::eval::eval_interval(e, &p, &iv).unwrap().mid_f64(),
5.0
);
}
}