diff --git a/src/math/functions.rs b/src/math/functions.rs index ee0f59c..b69dc94 100644 --- a/src/math/functions.rs +++ b/src/math/functions.rs @@ -2,6 +2,8 @@ use crate::math::context::Context; use crate::math::types::Expression; use rust_decimal::Decimal; use rust_decimal::MathematicalOps; +use rust_decimal::RoundingStrategy; +use rust_decimal::dec; use rust_decimal::prelude::ToPrimitive; impl Context { @@ -30,17 +32,53 @@ impl Context { self.register_function("atan", |params: Vec| { map_decimal(¶ms[0], |d| map_f64(&d, |f| f.atan())) }); + self.register_function("round", |params: Vec| { + let decimal = if params.len() == 2 { + if let Some(d) = get_decimal(¶ms[1]) { + d + } else { + dec!(0) + } + } else { + dec!(0) + } + .to_u32() + .expect("unable to parse decimal number"); + + map_decimal(¶ms[0], |d| { + d.round_dp_with_strategy(decimal, RoundingStrategy::MidpointAwayFromZero) + }) + }); + self.register_function("floor", |params: Vec| { + map_decimal(¶ms[0], |d| d.floor()) + }); + self.register_function("ceil", |params: Vec| { + map_decimal(¶ms[0], |d| d.ceil()) + }); } } -fn map_decimal(expr: &Expression, f: fn(Decimal) -> Decimal) -> Expression { +fn get_decimal(expr: &Expression) -> Option { + match expr { + Expression::Number(d) => Some(*d), + _ => None, + } +} + +fn map_decimal(expr: &Expression, f: F) -> Expression +where + F: Fn(Decimal) -> Decimal, +{ match expr { Expression::Number(d) => Expression::Number(f(*d)), _ => panic!("expected decimal"), } } -fn map_f64(decimal: &Decimal, f: fn(f64) -> f64) -> Decimal { +fn map_f64(decimal: &Decimal, f: F) -> Decimal +where + F: Fn(f64) -> f64, +{ let num = decimal.to_f64().expect("unable to unwrap decimal"); let num = f(num); Decimal::from_f64_retain(num).expect("unable to wrap decimal") diff --git a/src/math/mod.rs b/src/math/mod.rs index 2ed2081..b9b8846 100644 --- a/src/math/mod.rs +++ b/src/math/mod.rs @@ -1,5 +1,3 @@ -use crate::math::types::Expression; - #[cfg(test)] mod tests;