From eda59774ba32edf6598dec696a90500c3f68a14a Mon Sep 17 00:00:00 2001 From: frectonz Date: Fri, 16 Feb 2024 01:43:23 +0300 Subject: [PATCH] Implement `floor`, `ceil` and `round` --- core/src/ast.rs | 3 +++ core/src/num/bigrat.rs | 15 ++++++++++++ core/src/num/complex.rs | 12 ++++++++++ core/src/num/real.rs | 12 ++++++++++ core/src/num/unit.rs | 36 +++++++++++++++++++++++++++++ core/src/value.rs | 3 +++ core/src/value/built_in_function.rs | 6 +++++ core/tests/integration_tests.rs | 27 ++++++++++++++++++++++ 8 files changed, 114 insertions(+) diff --git a/core/src/ast.rs b/core/src/ast.rs index 7bbbd8fd..22c4ac56 100644 --- a/core/src/ast.rs +++ b/core/src/ast.rs @@ -632,6 +632,9 @@ pub(crate) fn resolve_identifier( "unitless" => Value::Num(Box::new(Number::from(1))), "arg" => Value::BuiltInFunction(BuiltInFunction::Arg), "abs" => Value::BuiltInFunction(BuiltInFunction::Abs), + "floor" => Value::BuiltInFunction(BuiltInFunction::Floor), + "ceil" => Value::BuiltInFunction(BuiltInFunction::Ceil), + "round" => Value::BuiltInFunction(BuiltInFunction::Round), "sin" => Value::BuiltInFunction(BuiltInFunction::Sin), "cos" => Value::BuiltInFunction(BuiltInFunction::Cos), "tan" => Value::BuiltInFunction(BuiltInFunction::Tan), diff --git a/core/src/num/bigrat.rs b/core/src/num/bigrat.rs index beb6424b..9bd46562 100644 --- a/core/src/num/bigrat.rs +++ b/core/src/num/bigrat.rs @@ -344,6 +344,21 @@ impl BigRat { Ok(self.apply_uint_op(BigUint::factorial, int)?.into()) } + pub(crate) fn floor(self, int: &I) -> FResult { + let float = self.into_f64(int)?.floor(); + Self::from_f64(float, int) + } + + pub(crate) fn ceil(self, int: &I) -> FResult { + let float = self.into_f64(int)?.ceil(); + Self::from_f64(float, int) + } + + pub(crate) fn round(self, int: &I) -> FResult { + let float = self.into_f64(int)?.round(); + Self::from_f64(float, int) + } + pub(crate) fn bitwise( self, rhs: Self, diff --git a/core/src/num/complex.rs b/core/src/num/complex.rs index f91d3953..a6bd720a 100644 --- a/core/src/num/complex.rs +++ b/core/src/num/complex.rs @@ -213,6 +213,18 @@ impl Complex { }) } + pub(crate) fn floor(self, int: &I) -> FResult> { + Ok(Exact::new(self.expect_real()?.floor(int)?, true)) + } + + pub(crate) fn ceil(self, int: &I) -> FResult> { + Ok(Exact::new(self.expect_real()?.ceil(int)?, true)) + } + + pub(crate) fn round(self, int: &I) -> FResult> { + Ok(Exact::new(self.expect_real()?.round(int)?, true)) + } + pub(crate) fn arg(self, int: &I) -> FResult> { Ok(Exact::new(self.imag.atan2(self.real, int)?, false)) } diff --git a/core/src/num/real.rs b/core/src/num/real.rs index 7c0afdf8..cdc42024 100644 --- a/core/src/num/real.rs +++ b/core/src/num/real.rs @@ -246,6 +246,18 @@ impl Real { Ok(Self::from(self.approximate(int)?.factorial(int)?)) } + pub(crate) fn floor(self, int: &I) -> FResult { + Ok(Self::from(self.approximate(int)?.floor(int)?)) + } + + pub(crate) fn ceil(self, int: &I) -> FResult { + Ok(Self::from(self.approximate(int)?.ceil(int)?)) + } + + pub(crate) fn round(self, int: &I) -> FResult { + Ok(Self::from(self.approximate(int)?.round(int)?)) + } + pub(crate) fn format( &self, base: Base, diff --git a/core/src/num/unit.rs b/core/src/num/unit.rs index 8a4313ab..5785886b 100644 --- a/core/src/num/unit.rs +++ b/core/src/num/unit.rs @@ -538,6 +538,42 @@ impl Value { } } + pub(crate) fn floor(self, int: &I) -> FResult { + let value = self.value.one_point()?.floor(int)?; + Ok(Self { + value: Complex::from(value.value).into(), + unit: self.unit, + exact: self.exact && value.exact, + base: self.base, + format: self.format, + simplifiable: self.simplifiable, + }) + } + + pub(crate) fn ceil(self, int: &I) -> FResult { + let value = self.value.one_point()?.ceil(int)?; + Ok(Self { + value: Complex::from(value.value).into(), + unit: self.unit, + exact: self.exact && value.exact, + base: self.base, + format: self.format, + simplifiable: self.simplifiable, + }) + } + + pub(crate) fn round(self, int: &I) -> FResult { + let value = self.value.one_point()?.round(int)?; + Ok(Self { + value: Complex::from(value.value).into(), + unit: self.unit, + exact: self.exact && value.exact, + base: self.base, + format: self.format, + simplifiable: self.simplifiable, + }) + } + pub(crate) fn real(self) -> FResult { Ok(Self { value: Complex::from(self.value.one_point()?.real()).into(), diff --git a/core/src/value.rs b/core/src/value.rs index e5e56ac1..ecd663d0 100644 --- a/core/src/value.rs +++ b/core/src/value.rs @@ -309,6 +309,9 @@ impl Value { BuiltInFunction::Real => arg.expect_num()?.real()?, BuiltInFunction::Imag => arg.expect_num()?.imag()?, BuiltInFunction::Arg => arg.expect_num()?.arg(int)?, + BuiltInFunction::Floor => arg.expect_num()?.floor(int)?, + BuiltInFunction::Ceil => arg.expect_num()?.ceil(int)?, + BuiltInFunction::Round => arg.expect_num()?.round(int)?, }))) } diff --git a/core/src/value/built_in_function.rs b/core/src/value/built_in_function.rs index 96e2b216..16427b8f 100644 --- a/core/src/value/built_in_function.rs +++ b/core/src/value/built_in_function.rs @@ -36,6 +36,9 @@ pub(crate) enum BuiltInFunction { Real, Imag, Arg, + Floor, + Ceil, + Round, } impl BuiltInFunction { @@ -98,6 +101,9 @@ impl BuiltInFunction { Self::Real => "real", Self::Imag => "imag", Self::Arg => "arg", + Self::Floor => "floor", + Self::Ceil => "ceil", + Self::Round => "round", } } diff --git a/core/tests/integration_tests.rs b/core/tests/integration_tests.rs index e0d3f1e3..03a8b349 100644 --- a/core/tests/integration_tests.rs +++ b/core/tests/integration_tests.rs @@ -2675,6 +2675,33 @@ fn factorial_of_three_kg() { expect_error("(3 kg)!", None); } +#[test] +fn test_floor() { + test_eval("floor(3)", "3"); + test_eval("floor(3.9)", "3"); + test_eval("floor(-3)", "-3"); + test_eval("floor(-3.1)", "-4"); +} + +#[test] +fn test_ceil() { + test_eval("ceil(3)", "3"); + test_eval("ceil(3.3)", "4"); + test_eval("ceil(-3)", "-3"); + test_eval("ceil(-3.3)", "-3"); +} + +#[test] +fn test_round() { + test_eval("round(3)", "3"); + + test_eval("round(3.3)", "3"); + test_eval("round(3.7)", "4"); + + test_eval("round(-3.3)", "-3"); + test_eval("round(-3.7)", "-4"); +} + #[test] fn recurring_digits_1() { test_eval_simple("9/11 to float", "0.(81)");