diff options
| author | HydroH <ixlesis@gmail.com> | 2024-10-31 20:41:50 +0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-10-31 12:41:50 +0000 |
| commit | 066e9349f96b7bc56536d48aaae29d98f0ee76fa (patch) | |
| tree | 1d2f4e6a6edb7a279dac3c75a1c6ebfbaa9bb1ca /crates | |
| parent | 30427ac842000d10666b602a2a313058bc32708e (diff) | |
Add `calc.norm()` function to compute euclidean norms (#4581)
Co-authored-by: +merlan #flirora <uruwi@protonmail.com>
Co-authored-by: Yip Coekjan <69834864+Coekjan@users.noreply.github.com>
Co-authored-by: Malo <57839069+MDLC01@users.noreply.github.com>
Co-authored-by: Laurenz <laurmaedje@gmail.com>
Diffstat (limited to 'crates')
| -rw-r--r-- | crates/typst-library/src/foundations/calc.rs | 33 |
1 files changed, 33 insertions, 0 deletions
diff --git a/crates/typst-library/src/foundations/calc.rs b/crates/typst-library/src/foundations/calc.rs index f12ca74c..e7eb1405 100644 --- a/crates/typst-library/src/foundations/calc.rs +++ b/crates/typst-library/src/foundations/calc.rs @@ -50,6 +50,7 @@ pub fn module() -> Module { scope.define_func::<div_euclid>(); scope.define_func::<rem_euclid>(); scope.define_func::<quo>(); + scope.define_func::<norm>(); scope.define("inf", f64::INFINITY); scope.define("pi", std::f64::consts::PI); scope.define("tau", std::f64::consts::TAU); @@ -1056,6 +1057,38 @@ pub fn quo( 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 { + bail!(p.span, "p must be greater than zero"); + } + + // Create an iterator over the absolute values. + let abs = values.into_iter().map(f64::abs); + + Ok(if p.v.is_infinite() { + // When p is infinity, the p-norm is the maximum of the absolute values. + abs.max_by(|a, b| a.total_cmp(b)).unwrap_or(0.0) + } else { + abs.map(|v| v.powf(p.v)).sum::<f64>().powf(1.0 / p.v) + }) +} + /// A value which can be passed to functions that work with integers and floats. #[derive(Debug, Copy, Clone)] pub enum Num { |
