mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Add quo
, trunc
and fract
calculation methods and rename mod
to rem
(#890)
This commit is contained in:
parent
c117e2dc27
commit
42b93b7b53
@ -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.mod(20, 6) \
|
||||
/// #calc.mod(1.75, 0.5)
|
||||
/// #calc.rem(20, 6) \
|
||||
/// #calc.rem(1.75, 0.5)
|
||||
/// ```
|
||||
///
|
||||
/// Display: Modulus
|
||||
/// Display: Remainder
|
||||
/// Category: calculate
|
||||
/// Returns: integer or float
|
||||
#[func]
|
||||
pub fn mod_(
|
||||
/// The dividend of the modulus.
|
||||
pub fn rem(
|
||||
/// The dividend of the remainder.
|
||||
dividend: Num,
|
||||
/// The divisor of the modulus.
|
||||
/// 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)
|
||||
dividend.apply2(divisor.v, Rem::rem, Rem::rem).value()
|
||||
}
|
||||
|
||||
/// Calculate the quotient of two numbers.
|
||||
///
|
||||
/// ## Example
|
||||
/// ```example
|
||||
/// #calc.quo(14, 5) \
|
||||
/// #calc.quo(3.46, 0.5)
|
||||
/// ```
|
||||
///
|
||||
/// Display: Quotient
|
||||
/// Category: calculate
|
||||
/// Returns: integer or float
|
||||
#[func]
|
||||
pub fn quo(
|
||||
/// The dividend of the quotient.
|
||||
dividend: Num,
|
||||
/// The divisor of the quotient.
|
||||
divisor: Spanned<Num>,
|
||||
) -> Value {
|
||||
if divisor.v.float() == 0.0 {
|
||||
bail!(divisor.span, "divisor must not be zero");
|
||||
}
|
||||
|
||||
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())),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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.
|
||||
|
@ -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],
|
||||
|
Loading…
x
Reference in New Issue
Block a user