From 3f76aadb1ad8735ccd879a0b4797dabddc257896 Mon Sep 17 00:00:00 2001 From: Martin Haug Date: Wed, 2 Feb 2022 20:56:40 +0100 Subject: [PATCH] Add modulo --- src/library/mod.rs | 1 + src/library/utility.rs | 30 ++++++++++++++++++++++++++++++ tests/typ/utility/math.typ | 16 ++++++++++++++++ 3 files changed, 47 insertions(+) diff --git a/src/library/mod.rs b/src/library/mod.rs index 97d30e317..4ea248ce7 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -139,6 +139,7 @@ pub fn new() -> Scope { std.def_func("max", max); std.def_func("even", even); std.def_func("odd", odd); + std.def_func("mod", modulo); std.def_func("range", range); std.def_func("rgb", rgb); std.def_func("lower", lower); diff --git a/src/library/utility.rs b/src/library/utility.rs index 1909dff21..2935d458a 100644 --- a/src/library/utility.rs +++ b/src/library/utility.rs @@ -170,6 +170,36 @@ pub fn odd(_: &mut EvalContext, args: &mut Args) -> TypResult { Ok(Value::Bool(args.expect::("integer")? % 2 != 0)) } +/// The modulo of two numbers. +pub fn modulo(_: &mut EvalContext, args: &mut Args) -> TypResult { + let Spanned { v: v1, span: span1 } = args.expect("integer or float")?; + let Spanned { v: v2, span: span2 } = args.expect("integer or float")?; + + let (a, b) = match (v1, v2) { + (Value::Int(a), Value::Int(b)) => match a.checked_rem(b) { + Some(res) => return Ok(res.into()), + None => bail!(span2, "divisor must not be zero"), + }, + (Value::Int(a), Value::Float(b)) => (a as f64, b), + (Value::Float(a), Value::Int(b)) => (a, b as f64), + (Value::Float(a), Value::Float(b)) => (a, b), + (Value::Int(_), b) | (Value::Float(_), b) => bail!( + span2, + format!("expected integer or float, found {}", b.type_name()) + ), + (a, _) => bail!( + span1, + format!("expected integer or float, found {}", a.type_name()) + ), + }; + + if b == 0.0 { + bail!(span2, "divisor must not be zero"); + } + + Ok((a % b).into()) +} + /// Find the minimum or maximum of a sequence of values. fn minmax(args: &mut Args, goal: Ordering) -> TypResult { let mut extremum = args.expect::("value")?; diff --git a/tests/typ/utility/math.typ b/tests/typ/utility/math.typ index 705db9379..edbbd6826 100644 --- a/tests/typ/utility/math.typ +++ b/tests/typ/utility/math.typ @@ -18,6 +18,22 @@ #test(odd(-1), true) #test(even(-11), false) +--- +// Test the `mod` function. +#test(mod(1, 1), 0) +#test(mod(5, 3), 2) +#test(mod(5, -3), 2) +#test(mod(22.5, 10), 2.5) +#test(mod(9, 4.5), 0) + +--- +// Error: 9-10 divisor must not be zero +#mod(5, 0) + +--- +// Error: 11-14 divisor must not be zero +#mod(3.0, 0.0) + --- // Error: 6-16 cannot take absolute value of a linear #abs(10pt + 50%)