use super::{CalcValue, CalcVertex, Value};
use crate::calc_engine::connector::Connector;
use logisheets_parser::ast;
fn calc<C, F>(args: Vec<CalcVertex>, fetcher: &mut C, f: &F) -> CalcVertex
where
C: Connector,
F: Fn(f64, i32) -> f64,
{
assert_or_return!(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!(num, first);
let second = fetcher.get_calc_value(args_iter.next().unwrap());
assert_f64_from_calc_value!(digits, second);
let r = f(num, digits.trunc() as i32);
CalcVertex::from_number(r)
}
pub fn calc_mround<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
C: Connector,
{
assert_or_return!(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!(num, first);
let second = fetcher.get_calc_value(args_iter.next().unwrap());
assert_f64_from_calc_value!(multiple, second);
assert_or_return!(multiple * num >= 0., ast::Error::Num);
if num == 0. {
return CalcVertex::from_number(0.);
}
let n = (num / multiple).round();
CalcVertex::from_number(n * multiple)
}
pub fn calc_round<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
C: Connector,
{
let f = |num: f64, digits: i32| -> f64 {
let shift_factor = 10_f64.powi(digits);
(num * shift_factor).round() / shift_factor
};
calc(args, fetcher, &f)
}
pub fn calc_rounddown<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
C: Connector,
{
let f = |num: f64, digits: i32| -> f64 {
let shift_factor = 10_f64.powi(digits);
(num * shift_factor).trunc() / shift_factor
};
calc(args, fetcher, &f)
}
pub fn calc_roundup<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
C: Connector,
{
let f = |num: f64, digits: i32| -> f64 {
let shift_factor = 10_f64.powi(digits);
if num > 0. {
(num * shift_factor).ceil() / shift_factor
} else if num == 0. {
0.
} else {
(num * shift_factor).floor() / shift_factor
}
};
calc(args, fetcher, &f)
}
pub fn calc_trunc<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!(num, first);
let digits = if let Some(arg) = args_iter.next() {
let v = fetcher.get_calc_value(arg);
assert_f64_from_calc_value!(d, v);
d.trunc() as i32
} else {
0
};
let shift = 10_f64.powi(digits);
CalcVertex::from_number((num * shift).trunc() / shift)
}
pub fn calc_ceiling<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
C: Connector,
{
calc_to_multiple(args, fetcher, true)
}
pub fn calc_floor<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
C: Connector,
{
calc_to_multiple(args, fetcher, false)
}
fn calc_to_multiple<C>(args: Vec<CalcVertex>, fetcher: &mut C, up: bool) -> CalcVertex
where
C: Connector,
{
assert_or_return!(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!(num, first);
let second = fetcher.get_calc_value(args_iter.next().unwrap());
assert_f64_from_calc_value!(sig, second);
if sig == 0. || num == 0. {
return CalcVertex::from_number(0.);
}
assert_or_return!((num > 0.) == (sig > 0.), ast::Error::Num);
let q = num / sig;
let rounded = if up { q.ceil() } else { q.floor() };
CalcVertex::from_number(rounded * sig)
}