diff options
| author | HarmoGlace <23212967+HarmoGlace@users.noreply.github.com> | 2023-04-20 16:09:41 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-04-20 16:09:41 +0200 |
| commit | 42b93b7b534557205a6dc3dfda8fe3cfccfcc458 (patch) | |
| tree | 924171b6c1299fef0b628e0ac8c89c5775fea8be /library/src/compute | |
| parent | c117e2dc276d19b022abccac0d252bd7fc1cd44e (diff) | |
Add `quo`, `trunc` and `fract` calculation methods and rename `mod` to `rem` (#890)
Diffstat (limited to 'library/src/compute')
| -rw-r--r-- | library/src/compute/calc.rs | 114 |
1 files changed, 97 insertions, 17 deletions
diff --git a/library/src/compute/calc.rs b/library/src/compute/calc.rs index bba0e3b9..b6b2442f 100644 --- a/library/src/compute/calc.rs +++ b/library/src/compute/calc.rs @@ -2,7 +2,7 @@ use std::cmp; use std::cmp::Ordering; -use std::ops::Rem; +use std::ops::{Div, Rem}; use typst::eval::{Module, Scope}; @@ -32,13 +32,16 @@ pub fn module() -> Module { scope.define("lcm", lcm); scope.define("floor", floor); scope.define("ceil", ceil); + scope.define("trunc", trunc); + scope.define("fract", fract); scope.define("round", round); scope.define("clamp", clamp); scope.define("min", min); scope.define("max", max); scope.define("even", even); scope.define("odd", odd); - scope.define("mod", mod_); + scope.define("rem", rem); + scope.define("quo", quo); scope.define("inf", Value::Float(f64::INFINITY)); scope.define("nan", Value::Float(f64::NAN)); scope.define("pi", Value::Float(std::f64::consts::PI)); @@ -664,6 +667,55 @@ pub fn ceil( } } +/// Returns the integer part of a number. +/// +/// If the number is already an integer, it is returned unchanged. +/// +/// ## Example +/// ```example +/// #assert(calc.trunc(3) == 3) +/// #assert(calc.trunc(-3.7) == -3) +/// #assert(calc.trunc(15.9) == 15) +/// ``` +/// +/// Display: Truncate +/// Category: calculate +/// Returns: integer +#[func] +pub fn trunc( + /// The number to truncate. + value: Num, +) -> Value { + Value::Int(match value { + Num::Int(n) => n, + Num::Float(n) => n.trunc() as i64, + }) +} + +/// Returns the fractional part of a number. +/// +/// If the number is an integer, it returns `0`. +/// +/// ## Example +/// ```example +/// #assert(calc.fract(3) == 0) +/// #calc.fract(-3.1) +/// ``` +/// +/// Display: Fractional +/// Category: calculate +/// Returns: integer or float +#[func] +pub fn fract( + /// The number to truncate. + value: Num, +) -> Value { + match value { + Num::Int(_) => Value::Int(0), + Num::Float(n) => Value::Float(n.fract()), + } +} + /// Round a number to the nearest integer. /// /// Optionally, a number of decimal places can be specified. @@ -721,7 +773,7 @@ pub fn clamp( if max.v.float() < min.float() { bail!(max.span, "max must be greater than or equal to min") } - value.apply3(min, max.v, i64::clamp, f64::clamp) + value.apply3(min, max.v, i64::clamp, f64::clamp).value() } /// Determine the minimum of a sequence of values. @@ -836,28 +888,56 @@ pub fn odd( Value::Bool(value % 2 != 0) } -/// Calculate the modulus of two numbers. +/// Calculate the remainder of two numbers. +/// +/// ## Example +/// ```example +/// #calc.rem(20, 6) \ +/// #calc.rem(1.75, 0.5) +/// ``` +/// +/// Display: Remainder +/// Category: calculate +/// Returns: integer or float +#[func] +pub fn rem( + /// The dividend of the remainder. + dividend: Num, + /// The divisor of the remainder. + divisor: Spanned<Num>, +) -> Value { + if divisor.v.float() == 0.0 { + bail!(divisor.span, "divisor must not be zero"); + } + dividend.apply2(divisor.v, Rem::rem, Rem::rem).value() +} + +/// Calculate the quotient of two numbers. /// /// ## Example /// ```example -/// #calc.mod(20, 6) \ -/// #calc.mod(1.75, 0.5) +/// #calc.quo(14, 5) \ +/// #calc.quo(3.46, 0.5) /// ``` /// -/// Display: Modulus +/// Display: Quotient /// Category: calculate /// Returns: integer or float #[func] -pub fn mod_( - /// The dividend of the modulus. +pub fn quo( + /// The dividend of the quotient. dividend: Num, - /// The divisor of the modulus. + /// The divisor of the quotient. divisor: Spanned<Num>, ) -> Value { if divisor.v.float() == 0.0 { bail!(divisor.span, "divisor must not be zero"); } - dividend.apply2(divisor.v, Rem::rem, Rem::rem) + + Value::Int(match dividend.apply2(divisor.v, Div::div, Div::div) { + Num::Int(i) => i, + Num::Float(f) => f.floor() as i64, // Note: the result should be an integer but floats doesn't have the same precision as i64. + }) } /// A value which can be passed to functions that work with integers and floats. @@ -873,10 +953,10 @@ impl Num { other: Self, int: impl FnOnce(i64, i64) -> i64, float: impl FnOnce(f64, f64) -> f64, - ) -> Value { + ) -> Num { match (self, other) { - (Self::Int(a), Self::Int(b)) => Value::Int(int(a, b)), - (a, b) => Value::Float(float(a.float(), b.float())), + (Self::Int(a), Self::Int(b)) => Num::Int(int(a, b)), + (a, b) => Num::Float(float(a.float(), b.float())), } } @@ -886,10 +966,10 @@ impl Num { third: Self, int: impl FnOnce(i64, i64, i64) -> i64, float: impl FnOnce(f64, f64, f64) -> f64, - ) -> Value { + ) -> Num { match (self, other, third) { - (Self::Int(a), Self::Int(b), Self::Int(c)) => Value::Int(int(a, b, c)), - (a, b, c) => Value::Float(float(a.float(), b.float(), c.float())), + (Self::Int(a), Self::Int(b), Self::Int(c)) => Num::Int(int(a, b, c)), + (a, b, c) => Num::Float(float(a.float(), b.float(), c.float())), } } |
