summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHarmoGlace <23212967+HarmoGlace@users.noreply.github.com>2023-04-20 16:09:41 +0200
committerGitHub <noreply@github.com>2023-04-20 16:09:41 +0200
commit42b93b7b534557205a6dc3dfda8fe3cfccfcc458 (patch)
tree924171b6c1299fef0b628e0ac8c89c5775fea8be
parentc117e2dc276d19b022abccac0d252bd7fc1cd44e (diff)
Add `quo`, `trunc` and `fract` calculation methods and rename `mod` to `rem` (#890)
-rw-r--r--library/src/compute/calc.rs114
-rw-r--r--tests/typ/compiler/break-continue.typ4
-rw-r--r--tests/typ/compiler/string.typ2
-rw-r--r--tests/typ/compute/calc.typ32
-rw-r--r--tests/typ/layout/enum-numbering.typ2
5 files changed, 125 insertions, 29 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())),
}
}
diff --git a/tests/typ/compiler/break-continue.typ b/tests/typ/compiler/break-continue.typ
index f6245007..6f505f80 100644
--- a/tests/typ/compiler/break-continue.typ
+++ b/tests/typ/compiler/break-continue.typ
@@ -41,7 +41,7 @@
#while x < 8 {
i += 1
- if calc.mod(i, 3) == 0 {
+ if calc.rem(i, 3) == 0 {
continue
}
x += i
@@ -55,7 +55,7 @@
#let x = for i in range(5) {
"a"
- if calc.mod(i, 3) == 0 {
+ if calc.rem(i, 3) == 0 {
"_"
continue
}
diff --git a/tests/typ/compiler/string.typ b/tests/typ/compiler/string.typ
index 95e204c5..2f7ba9ec 100644
--- a/tests/typ/compiler/string.typ
+++ b/tests/typ/compiler/string.typ
@@ -103,7 +103,7 @@
let caps = match.captures
time += 60 * int(caps.at(0)) + int(caps.at(1))
}
- str(int(time / 60)) + ":" + str(calc.mod(time, 60))
+ str(int(time / 60)) + ":" + str(calc.rem(time, 60))
}
#test(timesum(""), "0:0")
diff --git a/tests/typ/compute/calc.typ b/tests/typ/compute/calc.typ
index 6af5189b..c9a37d1b 100644
--- a/tests/typ/compute/calc.typ
+++ b/tests/typ/compute/calc.typ
@@ -55,20 +55,36 @@
#test(calc.even(-11), false)
---
-// Test the `mod` function.
-#test(calc.mod(1, 1), 0)
-#test(calc.mod(5, 3), 2)
-#test(calc.mod(5, -3), 2)
-#test(calc.mod(22.5, 10), 2.5)
-#test(calc.mod(9, 4.5), 0)
+// Test the `rem` function.
+#test(calc.rem(1, 1), 0)
+#test(calc.rem(5, 3), 2)
+#test(calc.rem(5, -3), 2)
+#test(calc.rem(22.5, 10), 2.5)
+#test(calc.rem(9, 4.5), 0)
---
// Error: 14-15 divisor must not be zero
-#calc.mod(5, 0)
+#calc.rem(5, 0)
---
// Error: 16-19 divisor must not be zero
-#calc.mod(3.0, 0.0)
+#calc.rem(3.0, 0.0)
+
+---
+// Test the `quo` function.
+#test(calc.quo(1, 1), 1)
+#test(calc.quo(5, 3), 1)
+#test(calc.quo(5, -3), -1)
+#test(calc.quo(22.5, 10), 2)
+#test(calc.quo(9, 4.5), 2)
+
+---
+// Error: 14-15 divisor must not be zero
+#calc.quo(5, 0)
+
+---
+// Error: 16-19 divisor must not be zero
+#calc.quo(3.0, 0.0)
---
// Test the `min` and `max` functions.
diff --git a/tests/typ/layout/enum-numbering.typ b/tests/typ/layout/enum-numbering.typ
index 3eaf352e..7efe195f 100644
--- a/tests/typ/layout/enum-numbering.typ
+++ b/tests/typ/layout/enum-numbering.typ
@@ -22,7 +22,7 @@
spacing: 0.65em - 3pt,
tight: false,
numbering: n => text(
- fill: (red, green, blue).at(calc.mod(n, 3)),
+ fill: (red, green, blue).at(calc.rem(n, 3)),
numbering("A", n),
),
[Red], [Green], [Blue], [Red],