use crate::calc_engine::{
calculator::calc_vertex::{CalcValue, CalcVertex, Value},
connector::Connector,
};
use logisheets_parser::ast;
fn calc<C, F>(args: Vec<CalcVertex>, fetcher: &mut C, func: F) -> CalcVertex
where
C: Connector,
F: 'static + Fn(f64) -> f64,
{
if args.len() != 1 {
return CalcVertex::from_error(ast::Error::Unspecified);
}
let arg = args.into_iter().next().unwrap();
let value = fetcher.get_calc_value(arg);
let v = match value {
CalcValue::Scalar(s) => CalcValue::Scalar(call(&s, &func)),
CalcValue::Range(r) => {
let vec2d = r.map(move |v| call(v, &func));
CalcValue::Range(vec2d)
}
CalcValue::Cube(_) => CalcValue::Scalar(Value::Error(ast::Error::Ref)),
CalcValue::Union(u) => {
if u.len() == 1 {
let arg = *u.into_iter().next().unwrap();
let v = vec![CalcVertex::Value(arg)];
if let CalcVertex::Value(value) = calc(v, fetcher, func) {
value
} else {
CalcValue::Scalar(Value::Error(ast::Error::Value))
}
} else {
CalcValue::Scalar(Value::Error(ast::Error::Value))
}
}
};
CalcVertex::Value(v)
}
fn call<F>(value: &Value, func: &F) -> Value
where
F: Fn(f64) -> f64,
{
match value {
Value::Blank => Value::Number(func(0_f64)),
Value::Number(n) => Value::Number(func(n.clone())),
Value::Text(_) => Value::Error(ast::Error::Value),
Value::Boolean(b) => {
if *b {
Value::Number(func(1_f64))
} else {
Value::Number(func(0_f64))
}
}
Value::Error(e) => Value::Error(e.clone()),
}
}
fn calc_checked<C, F>(args: Vec<CalcVertex>, fetcher: &mut C, func: F) -> CalcVertex
where
C: Connector,
F: 'static + Fn(f64) -> Result<f64, ast::Error>,
{
if args.len() != 1 {
return CalcVertex::from_error(ast::Error::Unspecified);
}
let arg = args.into_iter().next().unwrap();
let value = fetcher.get_calc_value(arg);
let v = match value {
CalcValue::Scalar(s) => CalcValue::Scalar(call_checked(&s, &func)),
CalcValue::Range(r) => CalcValue::Range(r.map(move |v| call_checked(v, &func))),
CalcValue::Cube(_) => CalcValue::Scalar(Value::Error(ast::Error::Ref)),
CalcValue::Union(u) => {
if u.len() == 1 {
let arg = *u.into_iter().next().unwrap();
let v = vec![CalcVertex::Value(arg)];
if let CalcVertex::Value(value) = calc_checked(v, fetcher, func) {
value
} else {
CalcValue::Scalar(Value::Error(ast::Error::Value))
}
} else {
CalcValue::Scalar(Value::Error(ast::Error::Value))
}
}
};
CalcVertex::Value(v)
}
fn call_checked<F>(value: &Value, func: &F) -> Value
where
F: Fn(f64) -> Result<f64, ast::Error>,
{
let apply = |x: f64| match func(x) {
Ok(n) => Value::Number(n),
Err(e) => Value::Error(e),
};
match value {
Value::Blank => apply(0_f64),
Value::Number(n) => apply(n.clone()),
Value::Text(_) => Value::Error(ast::Error::Value),
Value::Boolean(b) => apply(if *b { 1_f64 } else { 0_f64 }),
Value::Error(e) => Value::Error(e.clone()),
}
}
pub fn calc_abs<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
C: Connector,
{
calc(args, fetcher, |a| a.abs())
}
pub fn calc_sin<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
C: Connector,
{
calc(args, fetcher, |a| a.sin())
}
pub fn calc_csc<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
C: Connector,
{
calc(args, fetcher, |a| 1. / a.sin())
}
pub fn calc_asin<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
C: Connector,
{
calc(args, fetcher, |a| a.asin())
}
pub fn calc_asinh<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
C: Connector,
{
calc(args, fetcher, |a| a.asinh())
}
pub fn calc_cos<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
C: Connector,
{
calc(args, fetcher, |a| a.cos())
}
pub fn calc_tan<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
C: Connector,
{
calc(args, fetcher, |a| a.tan())
}
pub fn calc_cot<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
C: Connector,
{
calc(args, fetcher, |a| 1. / a.tan())
}
pub fn calc_tanh<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
C: Connector,
{
calc(args, fetcher, |a| a.tanh())
}
pub fn calc_coth<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
C: Connector,
{
calc(args, fetcher, |a| 1. / a.tanh())
}
pub fn calc_sinh<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
C: Connector,
{
calc(args, fetcher, |a| a.sinh())
}
pub fn calc_cosh<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
C: Connector,
{
calc(args, fetcher, |a| a.cosh())
}
pub fn calc_sqrt<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
C: Connector,
{
calc_checked(args, fetcher, |a| {
if a < 0. {
Err(ast::Error::Num)
} else {
Ok(a.sqrt())
}
})
}
pub fn calc_sqrtpi<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
C: Connector,
{
calc_checked(args, fetcher, |a| {
if a < 0. {
Err(ast::Error::Num)
} else {
Ok((std::f64::consts::PI * a).sqrt())
}
})
}
pub fn calc_acos<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
C: Connector,
{
calc(args, fetcher, |a| a.acos())
}
pub fn calc_atan<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
C: Connector,
{
calc(args, fetcher, |a| a.atan())
}
pub fn calc_atanh<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
C: Connector,
{
calc(args, fetcher, |a| a.atanh())
}
pub fn calc_acosh<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
C: Connector,
{
calc(args, fetcher, |a| a.acosh())
}
pub fn calc_ln<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
C: Connector,
{
calc(args, fetcher, |a| a.ln())
}
pub fn calc_int<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
C: Connector,
{
calc(args, fetcher, |a| a.floor())
}
pub fn calc_log<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
C: Connector,
{
assert_or_return!(args.len() == 1 || args.len() == 2, ast::Error::Unspecified);
let mut args_iter = args.into_iter();
let first = fetcher.get_calc_value(args_iter.next().unwrap());
assert_f64_from_calc_value!(number, first);
let base = if let Some(arg) = args_iter.next() {
let v = fetcher.get_calc_value(arg);
assert_f64_from_calc_value!(b, v);
b
} else {
10.
};
assert_or_return!(number > 0., ast::Error::Num);
assert_or_return!(base > 0. && base != 1., ast::Error::Num);
CalcVertex::from_number(number.ln() / base.ln())
}
pub fn calc_log10<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
C: Connector,
{
calc(args, fetcher, |a| a.log10())
}
pub fn calc_exp<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
C: Connector,
{
calc(args, fetcher, |a| a.exp())
}
pub fn calc_factdouble<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
C: Connector,
{
use crate::calc_engine::calculator::math::fact::factdouble;
calc(args, fetcher, |a| factdouble(a.floor() as u64) as f64)
}
pub fn calc_odd<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
C: Connector,
{
let func = |n: f64| -> f64 {
let pos = n >= 0.;
let c = n.abs().ceil() as u32;
let u_c = if c % 2 == 0 { c + 1 } else { c };
if pos { u_c as f64 } else { -(u_c as f64) }
};
calc(args, fetcher, func)
}
pub fn calc_even<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
C: Connector,
{
let func = |n: f64| -> f64 {
let pos = n >= 0.;
let c = n.abs().ceil() as u32;
let u_c = if c % 2 == 0 { c } else { c + 1 };
if pos { u_c as f64 } else { -(u_c as f64) }
};
calc(args, fetcher, func)
}
pub fn calc_sign<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
C: Connector,
{
let func = |n: f64| -> f64 {
if n == 0. {
0.
} else if n > 0. {
1.
} else {
-1.
}
};
calc(args, fetcher, func)
}
pub fn calc_radians<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
C: Connector,
{
let func = |n: f64| -> f64 { n * std::f64::consts::PI / 180. };
calc(args, fetcher, func)
}
pub fn calc_degrees<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
C: Connector,
{
let func = |n: f64| -> f64 { n * 180. / std::f64::consts::PI };
calc(args, fetcher, func)
}
pub fn calc_gamma<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
C: Connector,
{
let func = |n: f64| -> f64 { statrs::function::gamma::gamma(n) };
calc(args, fetcher, func)
}
pub fn calc_normsdist<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
C: Connector,
{
use statrs::distribution::{ContinuousCDF, Normal};
let func = |x: f64| -> f64 {
let n = Normal::new(0.0, 1.0).unwrap();
n.cdf(x)
};
calc(args, fetcher, func)
}
pub fn calc_gammaln<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
C: Connector,
{
let func = |n: f64| -> f64 {
let g = statrs::function::gamma::gamma(n);
g.ln()
};
calc(args, fetcher, func)
}