Repository: typst/typst
File: crates/typst-library/src/foundations/calc.rs (Rust)

Code before:
    span: Span,
    /// The dividend of the quotient.
    dividend: DecNum,
    /// The divisor of the quotient.
    divisor: Spanned<DecNum>,
) -> SourceResult<i64> {
    if divisor.v.is_zero() {
        bail!(divisor.span, "divisor must not be zero");
    }

    let divided = dividend
        .apply2(
            divisor.v,
            |a, b| {
                let q = a.checked_div(b)?;
                // Round towards negative infinity if the fraction is negative.
                Some(DecNum::Int(if (a < 0) != (b < 0) && a % b != 0 {
                    q - 1
                } else {
                    q
                }))
            },
            |a, b| Some(DecNum::Float(a / b)),
            |a, b| a.checked_div(b).map(DecNum::Decimal),
        )
        .ok_or_else(cant_apply_to_decimal_and_float)
        .at(span)?
        .ok_or_else(too_large)
        .at(span)?;

    floor(divided).at(span)
}

/// Calculates the p-norm of a sequence of values.
///
/// ```example
/// #calc.norm(1, 2, -3, 0.5) \
/// #calc.norm(p: 3, 1, 2)
/// ```
#[func(title = "𝑝-Norm")]
pub fn norm(
    /// The p value to calculate the p-norm of.
    #[named]
    #[default(Spanned::new(2.0, Span::detached()))]
    p: Spanned<f64>,
    /// The sequence of values from which to calculate the p-norm.
    /// Returns `0.0` if empty.
    #[variadic]
    values: Vec<f64>,
) -> SourceResult<f64> {
    if p.v <= 0.0 {

Review comment (bug):
Turns out the division by zero check above wasn't enough. This currently crashes the compiler: 

```typ
#calc.quo(int("-9223372036854775808"), -1)
```

Surprisingly, the same happens for `div_euclid`:

```typ
#calc.div-euclid(int("-9223372036854775808"), -1)
```

So it would indeed be nice to apply this change (using `checked`) to both quo and div_euclid.

Code after:
/// Note that this function will always return an [integer]($int), and will
/// error if the resulting number is larger than the maximum 64-bit signed
/// integer or smaller than the minimum for that type.
///
/// ```example
/// $ "quo"(a, b) &= floor(a/b) \
///   "quo"(14, 5) &= #calc.quo(14, 5) \
///   "quo"(3.46, 0.5) &= #calc.quo(3.46, 0.5) $
/// ```
#[func(title = "Quotient")]
pub fn quo(
    span: Span,
    /// The dividend of the quotient.
    dividend: DecNum,
    /// The divisor of the quotient.
    divisor: Spanned<DecNum>,
) -> SourceResult<i64> {
    if divisor.v.is_zero() {
        bail!(divisor.span, "divisor must not be zero");
    }

    let divided = dividend
        .apply2(
            divisor.v,
            |a, b| {
                let q = a.checked_div(b)?;
                // Round towards negative infinity if the fraction is negative.
                Some(DecNum::Int(if (a < 0) != (b < 0) && a % b != 0 {
                    q - 1
                } else {
                    q
                }))
            },
            |a, b| Some(DecNum::Float(a / b)),
            |a, b| a.checked_div(b).map(DecNum::Decimal),
        )
        .ok_or_else(cant_apply_to_decimal_and_float)
        .at(span)?
        .ok_or_else(too_large)
        .at(span)?;

    floor(divided).at(span)
}

/// Calculates the p-norm of a sequence of values.
///
/// ```example
/// #calc.norm(1, 2, -3, 0.5) \
/// #calc.norm(p: 3, 1, 2)
/// ```
#[func(title = "𝑝-Norm")]