2022-12-20 16:11:37 +01:00

200 lines
4.5 KiB
Rust

use std::cmp::Ordering;
use crate::prelude::*;
/// # Absolute
/// The absolute value of a numeric value.
///
/// ## Example
/// ```
/// #abs(-5) \
/// #abs(5pt - 2cm) \
/// #abs(2fr)
/// ```
///
/// ## Parameters
/// - value: ToAbs (positional, required)
/// The value whose absolute value to calculate.
///
/// ## Category
/// calculate
#[func]
pub fn abs(args: &mut Args) -> SourceResult<Value> {
Ok(args.expect::<ToAbs>("value")?.0)
}
/// A value of which the absolute value can be taken.
struct ToAbs(Value);
castable! {
ToAbs,
v: i64 => Self(Value::Int(v.abs())),
v: f64 => Self(Value::Float(v.abs())),
v: Length => Self(Value::Length(v.try_abs()
.ok_or_else(|| "cannot take absolute value of this length")?)),
v: Angle => Self(Value::Angle(v.abs())),
v: Ratio => Self(Value::Ratio(v.abs())),
v: Fr => Self(Value::Fraction(v.abs())),
}
/// # Minimum
/// The minimum of a sequence of values.
///
/// ## Example
/// ```
/// #min(1, -3, -5, 20, 3, 6) \
/// #min("Typst", "in", "beta")
/// ```
///
/// ## Parameters
/// - values: Value (positional, variadic)
/// The sequence of values from which to extract the minimum.
/// Must not be empty.
///
/// ## Category
/// calculate
#[func]
pub fn min(args: &mut Args) -> SourceResult<Value> {
minmax(args, Ordering::Less)
}
/// # Maximum
/// The maximum of a sequence of values.
///
/// ## Example
/// ```
/// #max(1, -3, -5, 20, 3, 6) \
/// #max("Typst", "in", "beta")
/// ```
///
/// ## Parameters
/// - values: Value (positional, variadic)
/// The sequence of values from which to extract the maximum.
/// Must not be empty.
///
/// ## Category
/// calculate
#[func]
pub fn max(args: &mut Args) -> SourceResult<Value> {
minmax(args, Ordering::Greater)
}
/// Find the minimum or maximum of a sequence of values.
fn minmax(args: &mut Args, goal: Ordering) -> SourceResult<Value> {
let mut extremum = args.expect::<Value>("value")?;
for Spanned { v, span } in args.all::<Spanned<Value>>()? {
match v.partial_cmp(&extremum) {
Some(ordering) => {
if ordering == goal {
extremum = v;
}
}
None => bail!(
span,
"cannot compare {} and {}",
extremum.type_name(),
v.type_name(),
),
}
}
Ok(extremum)
}
/// # Even
/// Whether an integer is even.
///
/// ## Example
/// ```
/// #even(4) \
/// #even(5) \
/// { range(10).filter(even) }
/// ```
///
/// ## Parameters
/// - value: i64 (positional, required)
/// The number to check for evenness.
///
/// ## Category
/// calculate
#[func]
pub fn even(args: &mut Args) -> SourceResult<Value> {
Ok(Value::Bool(args.expect::<i64>("value")? % 2 == 0))
}
/// # Odd
/// Whether an integer is odd.
///
/// ## Example
/// ```
/// #odd(4) \
/// #odd(5) \
/// { range(10).filter(odd) }
/// ```
///
///
/// ## Parameters
/// - value: i64 (positional, required)
/// The number to check for oddness.
///
/// ## Category
/// calculate
#[func]
pub fn odd(args: &mut Args) -> SourceResult<Value> {
Ok(Value::Bool(args.expect::<i64>("value")? % 2 != 0))
}
/// # Modulus
/// The modulus of two numbers.
///
/// ## Example
/// ```
/// #mod(20, 6) \
/// #mod(1.75, 0.5)
/// ```
///
/// ## Parameters
/// - dividend: ToMod (positional, required)
/// The dividend of the modulus.
///
/// - divisor: ToMod (positional, required)
/// The divisor of the modulus.
///
/// ## Category
/// calculate
#[func]
pub fn mod_(args: &mut Args) -> SourceResult<Value> {
let Spanned { v: v1, span: span1 } = args.expect("dividend")?;
let Spanned { v: v2, span: span2 } = args.expect("divisor")?;
let (a, b) = match (v1, v2) {
(Value::Int(a), Value::Int(b)) => match a.checked_rem(b) {
Some(res) => return Ok(Value::Int(res)),
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(Value::Float(a % b))
}
/// A value which can be passed to the `mod` function.
struct ToMod;
castable! {
ToMod,
_: i64 => Self,
_: f64 => Self,
}