From 43f90b21592f5ab4885d63a351e7caa3b5bdb225 Mon Sep 17 00:00:00 2001 From: frozolotl <44589151+frozolotl@users.noreply.github.com> Date: Fri, 17 Nov 2023 19:55:57 +0100 Subject: [PATCH] Implement euclidean division and remainder (#2678) --- crates/typst-library/src/compute/calc.rs | 58 +++++++++++++++++++++++- tests/typ/compute/calc.typ | 32 +++++++++++++ 2 files changed, 89 insertions(+), 1 deletion(-) diff --git a/crates/typst-library/src/compute/calc.rs b/crates/typst-library/src/compute/calc.rs index 83bf924ed..6276905c5 100644 --- a/crates/typst-library/src/compute/calc.rs +++ b/crates/typst-library/src/compute/calc.rs @@ -50,6 +50,8 @@ fn module() -> Module { scope.define_func::(); scope.define_func::(); scope.define_func::(); + scope.define_func::(); + scope.define_func::(); scope.define_func::(); scope.define("inf", f64::INFINITY); scope.define("nan", f64::NAN); @@ -790,7 +792,10 @@ pub fn odd( /// in magnitude than `y`. /// /// ```example -/// #calc.rem(20, 6) \ +/// #calc.rem(7, 3) \ +/// #calc.rem(7, -3) \ +/// #calc.rem(-7, 3) \ +/// #calc.rem(-7, -3) \ /// #calc.rem(1.75, 0.5) /// ``` #[func(title = "Remainder")] @@ -806,6 +811,57 @@ pub fn rem( Ok(dividend.apply2(divisor.v, Rem::rem, Rem::rem)) } +/// Performs euclidean division of two numbers. +/// +/// The result of this computation is that of a division rounded to the integer +/// `{n}` such that the dividend is greater than or equal to `{n}` times the divisor. +/// +/// ```example +/// #calc.div-euclid(7, 3) \ +/// #calc.div-euclid(7, -3) \ +/// #calc.div-euclid(-7, 3) \ +/// #calc.div-euclid(-7, -3) \ +/// #calc.div-euclid(1.75, 0.5) +/// ``` +#[func(title = "Euclidean Division")] +pub fn div_euclid( + /// The dividend of the division. + dividend: Num, + /// The divisor of the division. + divisor: Spanned, +) -> SourceResult { + if divisor.v.float() == 0.0 { + bail!(divisor.span, "divisor must not be zero"); + } + Ok(dividend.apply2(divisor.v, i64::div_euclid, f64::div_euclid)) +} + +/// This calculates the least nonnegative remainder of a division. +/// +/// Warning: Due to a floating point round-off error, the remainder may equal the absolute +/// value of the divisor if the dividend is much smaller in magnitude than the divisor +/// and the dividend is negative. This only applies for floating point inputs. +/// +/// ```example +/// #calc.rem-euclid(7, 3) \ +/// #calc.rem-euclid(7, -3) \ +/// #calc.rem-euclid(-7, 3) \ +/// #calc.rem-euclid(-7, -3) \ +/// #calc.rem(1.75, 0.5) +/// ``` +#[func(title = "Euclidean Remainder")] +pub fn rem_euclid( + /// The dividend of the remainder. + dividend: Num, + /// The divisor of the remainder. + divisor: Spanned, +) -> SourceResult { + if divisor.v.float() == 0.0 { + bail!(divisor.span, "divisor must not be zero"); + } + Ok(dividend.apply2(divisor.v, i64::rem_euclid, f64::rem_euclid)) +} + /// Calculates the quotient (floored division) of two numbers. /// /// ```example diff --git a/tests/typ/compute/calc.typ b/tests/typ/compute/calc.typ index 24411ecfa..86dcafcdb 100644 --- a/tests/typ/compute/calc.typ +++ b/tests/typ/compute/calc.typ @@ -76,6 +76,38 @@ // Error: 16-19 divisor must not be zero #calc.rem(3.0, 0.0) +--- +// Test the `div-euclid` function. +#test(calc.div-euclid(7, 3), 2) +#test(calc.div-euclid(7, -3), -2) +#test(calc.div-euclid(-7, 3), -3) +#test(calc.div-euclid(-7, -3), 3) +#test(calc.div-euclid(2.5, 2), 1) + +--- +// Error: 21-22 divisor must not be zero +#calc.div-euclid(5, 0) + +--- +// Error: 23-26 divisor must not be zero +#calc.div-euclid(3.0, 0.0) + +--- +// Test the `rem-euclid` function. +#test(calc.rem-euclid(7, 3), 1) +#test(calc.rem-euclid(7, -3), 1) +#test(calc.rem-euclid(-7, 3), 2) +#test(calc.rem-euclid(-7, -3), 2) +#test(calc.rem-euclid(2.5, 2), 0.5) + +--- +// Error: 21-22 divisor must not be zero +#calc.rem-euclid(5, 0) + +--- +// Error: 23-26 divisor must not be zero +#calc.rem-euclid(3.0, 0.0) + --- // Test the `quo` function. #test(calc.quo(1, 1), 1)