use super::*;
use meval::{tokenizer::Token, ContextProvider, Expr};
fn ctx() -> meval::Context<'static> {
let mut x = meval::Context::new();
x.func("log", |x| x.log10());
x
}
#[derive(Clone)]
pub struct Eq {
vars: Vec<(String, usize)>,
params: Vec<String>,
expr: Expr,
estr: String,
}
impl Equation for Eq {
fn parse(expr: &str, columns: &Headers) -> Result<Self> {
let func = expr
.parse::<Expr>()
.into_diagnostic()
.wrap_err_with(|| format!("parsing '{expr}' failed"))?;
let ctx = ctx();
let mut vars = Vec::new();
let mut params = Vec::new();
for t in func.iter() {
if let Token::Var(n) = t {
if ctx.get_var(n).is_some() {
continue; }
match columns.find_ignore_case_and_ws(n) {
Some(i) => vars.push((n.to_string(), i)),
None => params.push(n.to_string()),
}
}
}
vars.sort_unstable();
vars.dedup();
params.sort_unstable();
params.dedup();
let x = Self {
vars,
params,
expr: func,
estr: expr.to_string(),
};
let v = x.build_inputs();
let _ = x
.expr
.clone()
.bindn_with_context(ctx, &v)
.into_diagnostic()
.wrap_err_with(|| format!("in expr: {}", x.estr))?;
Ok(x)
}
fn params_len(&self) -> usize {
self.params.len()
}
fn solve(&self, params: &[f64], row: DataRow) -> Option<f64> {
let vars = self.build_inputs();
let f = self
.expr
.clone()
.bindn_with_context(ctx(), &vars)
.map_err(|e| eprintln!("{e}"))
.ok()?;
let mut inputs = Vec::with_capacity(vars.len());
inputs.extend_from_slice(params); for (_, i) in &self.vars {
inputs.push(row.get_num(*i)?.map_err(|e| eprintln!("{e}")).ok()?); }
Some(f(&inputs)) }
fn expr(&self) -> Option<String> {
self.estr.to_string().into()
}
fn params(&self) -> Vec<String> {
self.params.clone()
}
fn vars(&self) -> Vec<String> {
self.vars.iter().map(|(s, _)| s.clone()).collect()
}
}
impl Eq {
fn build_inputs(&self) -> Vec<&str> {
self.params
.iter()
.map(String::as_str)
.chain(self.vars.iter().map(|(x, _)| x.as_str()))
.collect()
}
}