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 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);
}
});
}
pub(crate) fn collect_named_constants(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]);
let mut named = Vec::new();
collect_named_constants(e, &p, &mut named);
assert_eq!(named.len(), 2);
assert!(named.contains(&pi) && named.contains(&i));
}
}