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;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::ops::Rem;
|
use std::ops::{Div, Rem};
|
||||||
|
|
||||||
use typst::eval::{Module, Scope};
|
use typst::eval::{Module, Scope};
|
||||||
|
|
||||||
@ -32,13 +32,16 @@ pub fn module() -> Module {
|
|||||||
scope.define("lcm", lcm);
|
scope.define("lcm", lcm);
|
||||||
scope.define("floor", floor);
|
scope.define("floor", floor);
|
||||||
scope.define("ceil", ceil);
|
scope.define("ceil", ceil);
|
||||||
|
scope.define("trunc", trunc);
|
||||||
|
scope.define("fract", fract);
|
||||||
scope.define("round", round);
|
scope.define("round", round);
|
||||||
scope.define("clamp", clamp);
|
scope.define("clamp", clamp);
|
||||||
scope.define("min", min);
|
scope.define("min", min);
|
||||||
scope.define("max", max);
|
scope.define("max", max);
|
||||||
scope.define("even", even);
|
scope.define("even", even);
|
||||||
scope.define("odd", odd);
|
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("inf", Value::Float(f64::INFINITY));
|
||||||
scope.define("nan", Value::Float(f64::NAN));
|
scope.define("nan", Value::Float(f64::NAN));
|
||||||
scope.define("pi", Value::Float(std::f64::consts::PI));
|
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.
|
/// Round a number to the nearest integer.
|
||||||
///
|
///
|
||||||
/// Optionally, a number of decimal places can be specified.
|
/// Optionally, a number of decimal places can be specified.
|
||||||
@ -721,7 +773,7 @@ pub fn clamp(
|
|||||||
if max.v.float() < min.float() {
|
if max.v.float() < min.float() {
|
||||||
bail!(max.span, "max must be greater than or equal to min")
|
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.
|
/// Determine the minimum of a sequence of values.
|
||||||
@ -836,28 +888,56 @@ pub fn odd(
|
|||||||
Value::Bool(value % 2 != 0)
|
Value::Bool(value % 2 != 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculate the modulus of two numbers.
|
/// Calculate the remainder of two numbers.
|
||||||
///
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
/// ```example
|
/// ```example
|
||||||
/// #calc.mod(20, 6) \
|
/// #calc.rem(20, 6) \
|
||||||
/// #calc.mod(1.75, 0.5)
|
/// #calc.rem(1.75, 0.5)
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// Display: Modulus
|
/// Display: Remainder
|
||||||
/// Category: calculate
|
/// Category: calculate
|
||||||
/// Returns: integer or float
|
/// Returns: integer or float
|
||||||
#[func]
|
#[func]
|
||||||
pub fn mod_(
|
pub fn rem(
|
||||||
/// The dividend of the modulus.
|
/// The dividend of the remainder.
|
||||||
dividend: Num,
|
dividend: Num,
|
||||||
/// The divisor of the modulus.
|
/// The divisor of the remainder.
|
||||||
divisor: Spanned<Num>,
|
divisor: Spanned<Num>,
|
||||||
) -> Value {
|
) -> Value {
|
||||||
if divisor.v.float() == 0.0 {
|
if divisor.v.float() == 0.0 {
|
||||||
bail!(divisor.span, "divisor must not be zero");
|
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.
|
/// A value which can be passed to functions that work with integers and floats.
|
||||||
@ -873,10 +953,10 @@ impl Num {
|
|||||||
other: Self,
|
other: Self,
|
||||||
int: impl FnOnce(i64, i64) -> i64,
|
int: impl FnOnce(i64, i64) -> i64,
|
||||||
float: impl FnOnce(f64, f64) -> f64,
|
float: impl FnOnce(f64, f64) -> f64,
|
||||||
) -> Value {
|
) -> Num {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
(Self::Int(a), Self::Int(b)) => Value::Int(int(a, b)),
|
(Self::Int(a), Self::Int(b)) => Num::Int(int(a, b)),
|
||||||
(a, b) => Value::Float(float(a.float(), b.float())),
|
(a, b) => Num::Float(float(a.float(), b.float())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -886,10 +966,10 @@ impl Num {
|
|||||||
third: Self,
|
third: Self,
|
||||||
int: impl FnOnce(i64, i64, i64) -> i64,
|
int: impl FnOnce(i64, i64, i64) -> i64,
|
||||||
float: impl FnOnce(f64, f64, f64) -> f64,
|
float: impl FnOnce(f64, f64, f64) -> f64,
|
||||||
) -> Value {
|
) -> Num {
|
||||||
match (self, other, third) {
|
match (self, other, third) {
|
||||||
(Self::Int(a), Self::Int(b), Self::Int(c)) => Value::Int(int(a, b, c)),
|
(Self::Int(a), Self::Int(b), Self::Int(c)) => Num::Int(int(a, b, c)),
|
||||||
(a, b, c) => Value::Float(float(a.float(), b.float(), c.float())),
|
(a, b, c) => Num::Float(float(a.float(), b.float(), c.float())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@
|
|||||||
|
|
||||||
#while x < 8 {
|
#while x < 8 {
|
||||||
i += 1
|
i += 1
|
||||||
if calc.mod(i, 3) == 0 {
|
if calc.rem(i, 3) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
x += i
|
x += i
|
||||||
@ -55,7 +55,7 @@
|
|||||||
|
|
||||||
#let x = for i in range(5) {
|
#let x = for i in range(5) {
|
||||||
"a"
|
"a"
|
||||||
if calc.mod(i, 3) == 0 {
|
if calc.rem(i, 3) == 0 {
|
||||||
"_"
|
"_"
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,7 @@
|
|||||||
let caps = match.captures
|
let caps = match.captures
|
||||||
time += 60 * int(caps.at(0)) + int(caps.at(1))
|
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")
|
#test(timesum(""), "0:0")
|
||||||
|
@ -55,20 +55,36 @@
|
|||||||
#test(calc.even(-11), false)
|
#test(calc.even(-11), false)
|
||||||
|
|
||||||
---
|
---
|
||||||
// Test the `mod` function.
|
// Test the `rem` function.
|
||||||
#test(calc.mod(1, 1), 0)
|
#test(calc.rem(1, 1), 0)
|
||||||
#test(calc.mod(5, 3), 2)
|
#test(calc.rem(5, 3), 2)
|
||||||
#test(calc.mod(5, -3), 2)
|
#test(calc.rem(5, -3), 2)
|
||||||
#test(calc.mod(22.5, 10), 2.5)
|
#test(calc.rem(22.5, 10), 2.5)
|
||||||
#test(calc.mod(9, 4.5), 0)
|
#test(calc.rem(9, 4.5), 0)
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 14-15 divisor must not be zero
|
// Error: 14-15 divisor must not be zero
|
||||||
#calc.mod(5, 0)
|
#calc.rem(5, 0)
|
||||||
|
|
||||||
---
|
---
|
||||||
// Error: 16-19 divisor must not be zero
|
// 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.
|
// Test the `min` and `max` functions.
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
spacing: 0.65em - 3pt,
|
spacing: 0.65em - 3pt,
|
||||||
tight: false,
|
tight: false,
|
||||||
numbering: n => text(
|
numbering: n => text(
|
||||||
fill: (red, green, blue).at(calc.mod(n, 3)),
|
fill: (red, green, blue).at(calc.rem(n, 3)),
|
||||||
numbering("A", n),
|
numbering("A", n),
|
||||||
),
|
),
|
||||||
[Red], [Green], [Blue], [Red],
|
[Red], [Green], [Blue], [Red],
|
||||||
|
Loading…
x
Reference in New Issue
Block a user