//! Calculations and processing of numeric values. use std::cmp; use std::cmp::Ordering; use az::SaturatingAs; use typst_syntax::{Span, Spanned}; use typst_utils::{round_int_with_precision, round_with_precision}; use crate::diag::{bail, At, HintedString, SourceResult, StrResult}; use crate::foundations::{cast, func, ops, Decimal, IntoValue, Module, Scope, Value}; use crate::layout::{Angle, Fr, Length, Ratio}; /// A module with calculation definitions. pub fn module() -> Module { let mut scope = Scope::new(); scope.define_func::(); scope.define_func::(); scope.define_func::(); scope.define_func::(); scope.define_func::(); scope.define_func::(); scope.define_func::(); scope.define_func::(); scope.define_func::(); scope.define_func::(); scope.define_func::(); scope.define_func::(); scope.define_func::(); scope.define_func::(); scope.define_func::(); scope.define_func::(); scope.define_func::(); scope.define_func::(); scope.define_func::(); scope.define_func::(); scope.define_func::(); scope.define_func::(); scope.define_func::(); scope.define_func::(); scope.define_func::(); scope.define_func::(); scope.define_func::(); scope.define_func::(); scope.define_func::(); scope.define_func::(); scope.define_func::(); 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("pi", std::f64::consts::PI); scope.define("tau", std::f64::consts::TAU); scope.define("e", std::f64::consts::E); Module::new("calc", scope) } /// Calculates the absolute value of a numeric value. /// /// ```example /// #calc.abs(-5) \ /// #calc.abs(5pt - 2cm) \ /// #calc.abs(2fr) \ /// #calc.abs(decimal("-342.440")) /// ``` #[func(title = "Absolute")] pub fn abs( /// The value whose absolute value to calculate. value: ToAbs, ) -> Value { value.0 } /// A value of which the absolute value can be taken. pub struct ToAbs(Value); cast! { ToAbs, v: i64 => Self(v.abs().into_value()), v: f64 => Self(v.abs().into_value()), v: Length => Self(Value::Length(v.try_abs() .ok_or("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())), v: Decimal => Self(Value::Decimal(v.abs())) } /// Raises a value to some exponent. /// /// ```example /// #calc.pow(2, 3) \ /// #calc.pow(decimal("2.5"), 2) /// ``` #[func(title = "Power")] pub fn pow( span: Span, /// The base of the power. /// /// If this is a [`decimal`], the exponent can only be an [integer]($int). base: DecNum, /// The exponent of the power. exponent: Spanned, ) -> SourceResult { match exponent.v { _ if exponent.v.float() == 0.0 && base.is_zero() => { bail!(span, "zero to the power of zero is undefined") } Num::Int(i) if i32::try_from(i).is_err() => { bail!(exponent.span, "exponent is too large") } Num::Float(f) if !f.is_normal() && f != 0.0 => { bail!(exponent.span, "exponent may not be infinite, subnormal, or NaN") } _ => {} }; match (base, exponent.v) { (DecNum::Int(a), Num::Int(b)) if b >= 0 => a .checked_pow(b as u32) .map(DecNum::Int) .ok_or_else(too_large) .at(span), (DecNum::Decimal(a), Num::Int(b)) => { a.checked_powi(b).map(DecNum::Decimal).ok_or_else(too_large).at(span) } (a, b) => { let Some(a) = a.float() else { return Err(cant_apply_to_decimal_and_float()).at(span); }; let result = if a == std::f64::consts::E { b.float().exp() } else if a == 2.0 { b.float().exp2() } else if let Num::Int(b) = b { a.powi(b as i32) } else { a.powf(b.float()) }; if result.is_nan() { bail!(span, "the result is not a real number") } Ok(DecNum::Float(result)) } } } /// Raises a value to some exponent of e. /// /// ```example /// #calc.exp(1) /// ``` #[func(title = "Exponential")] pub fn exp( span: Span, /// The exponent of the power. exponent: Spanned, ) -> SourceResult { match exponent.v { Num::Int(i) if i32::try_from(i).is_err() => { bail!(exponent.span, "exponent is too large") } Num::Float(f) if !f.is_normal() && f != 0.0 => { bail!(exponent.span, "exponent may not be infinite, subnormal, or NaN") } _ => {} } let result = exponent.v.float().exp(); if result.is_nan() { bail!(span, "the result is not a real number") } Ok(result) } /// Calculates the square root of a number. /// /// ```example /// #calc.sqrt(16) \ /// #calc.sqrt(2.5) /// ``` #[func(title = "Square Root")] pub fn sqrt( /// The number whose square root to calculate. Must be non-negative. value: Spanned, ) -> SourceResult { if value.v.float() < 0.0 { bail!(value.span, "cannot take square root of negative number"); } Ok(value.v.float().sqrt()) } /// Calculates the real nth root of a number. /// /// If the number is negative, then n must be odd. /// /// ```example /// #calc.root(16.0, 4) \ /// #calc.root(27.0, 3) /// ``` #[func] pub fn root( /// The expression to take the root of. radicand: f64, /// Which root of the radicand to take. index: Spanned, ) -> SourceResult { if index.v == 0 { bail!(index.span, "cannot take the 0th root of a number"); } else if radicand < 0.0 { if index.v % 2 == 0 { bail!( index.span, "negative numbers do not have a real nth root when n is even" ); } else { Ok(-(-radicand).powf(1.0 / index.v as f64)) } } else { Ok(radicand.powf(1.0 / index.v as f64)) } } /// Calculates the sine of an angle. /// /// When called with an integer or a float, they will be interpreted as /// radians. /// /// ```example /// #calc.sin(1.5) \ /// #calc.sin(90deg) /// ``` #[func(title = "Sine")] pub fn sin( /// The angle whose sine to calculate. angle: AngleLike, ) -> f64 { match angle { AngleLike::Angle(a) => a.sin(), AngleLike::Int(n) => (n as f64).sin(), AngleLike::Float(n) => n.sin(), } } /// Calculates the cosine of an angle. /// /// When called with an integer or a float, they will be interpreted as /// radians. /// /// ```example /// #calc.cos(1.5) \ /// #calc.cos(90deg) /// ``` #[func(title = "Cosine")] pub fn cos( /// The angle whose cosine to calculate. angle: AngleLike, ) -> f64 { match angle { AngleLike::Angle(a) => a.cos(), AngleLike::Int(n) => (n as f64).cos(), AngleLike::Float(n) => n.cos(), } } /// Calculates the tangent of an angle. /// /// When called with an integer or a float, they will be interpreted as /// radians. /// /// ```example /// #calc.tan(1.5) \ /// #calc.tan(90deg) /// ``` #[func(title = "Tangent")] pub fn tan( /// The angle whose tangent to calculate. angle: AngleLike, ) -> f64 { match angle { AngleLike::Angle(a) => a.tan(), AngleLike::Int(n) => (n as f64).tan(), AngleLike::Float(n) => n.tan(), } } /// Calculates the arcsine of a number. /// /// ```example /// #calc.asin(0) \ /// #calc.asin(1) /// ``` #[func(title = "Arcsine")] pub fn asin( /// The number whose arcsine to calculate. Must be between -1 and 1. value: Spanned, ) -> SourceResult { let val = value.v.float(); if val < -1.0 || val > 1.0 { bail!(value.span, "value must be between -1 and 1"); } Ok(Angle::rad(val.asin())) } /// Calculates the arccosine of a number. /// /// ```example /// #calc.acos(0) \ /// #calc.acos(1) /// ``` #[func(title = "Arccosine")] pub fn acos( /// The number whose arccosine to calculate. Must be between -1 and 1. value: Spanned, ) -> SourceResult { let val = value.v.float(); if val < -1.0 || val > 1.0 { bail!(value.span, "value must be between -1 and 1"); } Ok(Angle::rad(val.acos())) } /// Calculates the arctangent of a number. /// /// ```example /// #calc.atan(0) \ /// #calc.atan(1) /// ``` #[func(title = "Arctangent")] pub fn atan( /// The number whose arctangent to calculate. value: Num, ) -> Angle { Angle::rad(value.float().atan()) } /// Calculates the four-quadrant arctangent of a coordinate. /// /// The arguments are `(x, y)`, not `(y, x)`. /// /// ```example /// #calc.atan2(1, 1) \ /// #calc.atan2(-2, -3) /// ``` #[func(title = "Four-quadrant Arctangent")] pub fn atan2( /// The X coordinate. x: Num, /// The Y coordinate. y: Num, ) -> Angle { Angle::rad(f64::atan2(y.float(), x.float())) } /// Calculates the hyperbolic sine of a hyperbolic angle. /// /// ```example /// #calc.sinh(0) \ /// #calc.sinh(1.5) /// ``` #[func(title = "Hyperbolic Sine")] pub fn sinh( /// The hyperbolic angle whose hyperbolic sine to calculate. value: f64, ) -> f64 { value.sinh() } /// Calculates the hyperbolic cosine of a hyperbolic angle. /// /// ```example /// #calc.cosh(0) \ /// #calc.cosh(1.5) /// ``` #[func(title = "Hyperbolic Cosine")] pub fn cosh( /// The hyperbolic angle whose hyperbolic cosine to calculate. value: f64, ) -> f64 { value.cosh() } /// Calculates the hyperbolic tangent of a hyperbolic angle. /// /// ```example /// #calc.tanh(0) \ /// #calc.tanh(1.5) /// ``` #[func(title = "Hyperbolic Tangent")] pub fn tanh( /// The hyperbolic angle whose hyperbolic tangent to calculate. value: f64, ) -> f64 { value.tanh() } /// Calculates the logarithm of a number. /// /// If the base is not specified, the logarithm is calculated in base 10. /// /// ```example /// #calc.log(100) /// ``` #[func(title = "Logarithm")] pub fn log( span: Span, /// The number whose logarithm to calculate. Must be strictly positive. value: Spanned, /// The base of the logarithm. May not be zero. #[named] #[default(Spanned::new(10.0, Span::detached()))] base: Spanned, ) -> SourceResult { let number = value.v.float(); if number <= 0.0 { bail!(value.span, "value must be strictly positive") } if !base.v.is_normal() { bail!(base.span, "base may not be zero, NaN, infinite, or subnormal") } let result = if base.v == std::f64::consts::E { number.ln() } else if base.v == 2.0 { number.log2() } else if base.v == 10.0 { number.log10() } else { number.log(base.v) }; if result.is_infinite() || result.is_nan() { bail!(span, "the result is not a real number") } Ok(result) } /// Calculates the natural logarithm of a number. /// /// ```example /// #calc.ln(calc.e) /// ``` #[func(title = "Natural Logarithm")] pub fn ln( span: Span, /// The number whose logarithm to calculate. Must be strictly positive. value: Spanned, ) -> SourceResult { let number = value.v.float(); if number <= 0.0 { bail!(value.span, "value must be strictly positive") } let result = number.ln(); if result.is_infinite() { bail!(span, "result close to -inf") } Ok(result) } /// Calculates the factorial of a number. /// /// ```example /// #calc.fact(5) /// ``` #[func(title = "Factorial")] pub fn fact( /// The number whose factorial to calculate. Must be non-negative. number: u64, ) -> StrResult { Ok(fact_impl(1, number).ok_or_else(too_large)?) } /// Calculates a permutation. /// /// Returns the `k`-permutation of `n`, or the number of ways to choose `k` /// items from a set of `n` with regard to order. /// /// ```example /// $ "perm"(n, k) &= n!/((n - k)!) \ /// "perm"(5, 3) &= #calc.perm(5, 3) $ /// ``` #[func(title = "Permutation")] pub fn perm( /// The base number. Must be non-negative. base: u64, /// The number of permutations. Must be non-negative. numbers: u64, ) -> StrResult { // By convention. if base < numbers { return Ok(0); } Ok(fact_impl(base - numbers + 1, base).ok_or_else(too_large)?) } /// Calculates the product of a range of numbers. Used to calculate /// permutations. Returns None if the result is larger than `i64::MAX` fn fact_impl(start: u64, end: u64) -> Option { // By convention if end + 1 < start { return Some(0); } let real_start: u64 = cmp::max(1, start); let mut count: u64 = 1; for i in real_start..=end { count = count.checked_mul(i)?; } count.try_into().ok() } /// Calculates a binomial coefficient. /// /// Returns the `k`-combination of `n`, or the number of ways to choose `k` /// items from a set of `n` without regard to order. /// /// ```example /// #calc.binom(10, 5) /// ``` #[func(title = "Binomial")] pub fn binom( /// The upper coefficient. Must be non-negative. n: u64, /// The lower coefficient. Must be non-negative. k: u64, ) -> StrResult { Ok(binom_impl(n, k).ok_or_else(too_large)?) } /// Calculates a binomial coefficient, with `n` the upper coefficient and `k` /// the lower coefficient. Returns `None` if the result is larger than /// `i64::MAX` fn binom_impl(n: u64, k: u64) -> Option { if k > n { return Some(0); } // By symmetry let real_k = cmp::min(n - k, k); if real_k == 0 { return Some(1); } let mut result: u64 = 1; for i in 0..real_k { result = result.checked_mul(n - i)?.checked_div(i + 1)?; } result.try_into().ok() } /// Calculates the greatest common divisor of two integers. /// /// ```example /// #calc.gcd(7, 42) /// ``` #[func(title = "Greatest Common Divisor")] pub fn gcd( /// The first integer. a: i64, /// The second integer. b: i64, ) -> i64 { let (mut a, mut b) = (a, b); while b != 0 { let temp = b; b = a % b; a = temp; } a.abs() } /// Calculates the least common multiple of two integers. /// /// ```example /// #calc.lcm(96, 13) /// ``` #[func(title = "Least Common Multiple")] pub fn lcm( /// The first integer. a: i64, /// The second integer. b: i64, ) -> StrResult { if a == b { return Ok(a.abs()); } Ok(a.checked_div(gcd(a, b)) .and_then(|gcd| gcd.checked_mul(b)) .map(|v| v.abs()) .ok_or_else(too_large)?) } /// Rounds a number down to the nearest integer. /// /// If the number is already an integer, it is returned unchanged. /// /// Note that this function will always return an [integer]($int), and will /// error if the resulting [`float`] or [`decimal`] is larger than the maximum /// 64-bit signed integer or smaller than the minimum for that type. /// /// ```example /// #calc.floor(500.1) /// #assert(calc.floor(3) == 3) /// #assert(calc.floor(3.14) == 3) /// #assert(calc.floor(decimal("-3.14")) == -4) /// ``` #[func] pub fn floor( /// The number to round down. value: DecNum, ) -> StrResult { match value { DecNum::Int(n) => Ok(n), DecNum::Float(n) => Ok(crate::foundations::convert_float_to_int(n.floor()) .map_err(|_| too_large())?), DecNum::Decimal(n) => Ok(i64::try_from(n.floor()).map_err(|_| too_large())?), } } /// Rounds a number up to the nearest integer. /// /// If the number is already an integer, it is returned unchanged. /// /// Note that this function will always return an [integer]($int), and will /// error if the resulting [`float`] or [`decimal`] is larger than the maximum /// 64-bit signed integer or smaller than the minimum for that type. /// /// ```example /// #calc.ceil(500.1) /// #assert(calc.ceil(3) == 3) /// #assert(calc.ceil(3.14) == 4) /// #assert(calc.ceil(decimal("-3.14")) == -3) /// ``` #[func] pub fn ceil( /// The number to round up. value: DecNum, ) -> StrResult { match value { DecNum::Int(n) => Ok(n), DecNum::Float(n) => Ok(crate::foundations::convert_float_to_int(n.ceil()) .map_err(|_| too_large())?), DecNum::Decimal(n) => Ok(i64::try_from(n.ceil()).map_err(|_| too_large())?), } } /// Returns the integer part of a number. /// /// If the number is already an integer, it is returned unchanged. /// /// Note that this function will always return an [integer]($int), and will /// error if the resulting [`float`] or [`decimal`] is larger than the maximum /// 64-bit signed integer or smaller than the minimum for that type. /// /// ```example /// #calc.trunc(15.9) /// #assert(calc.trunc(3) == 3) /// #assert(calc.trunc(-3.7) == -3) /// #assert(calc.trunc(decimal("8493.12949582390")) == 8493) /// ``` #[func(title = "Truncate")] pub fn trunc( /// The number to truncate. value: DecNum, ) -> StrResult { match value { DecNum::Int(n) => Ok(n), DecNum::Float(n) => Ok(crate::foundations::convert_float_to_int(n.trunc()) .map_err(|_| too_large())?), DecNum::Decimal(n) => Ok(i64::try_from(n.trunc()).map_err(|_| too_large())?), } } /// Returns the fractional part of a number. /// /// If the number is an integer, returns `0`. /// /// ```example /// #calc.fract(-3.1) /// #assert(calc.fract(3) == 0) /// #assert(calc.fract(decimal("234.23949211")) == decimal("0.23949211")) /// ``` #[func(title = "Fractional")] pub fn fract( /// The number to truncate. value: DecNum, ) -> DecNum { match value { DecNum::Int(_) => DecNum::Int(0), DecNum::Float(n) => DecNum::Float(n.fract()), DecNum::Decimal(n) => DecNum::Decimal(n.fract()), } } /// Rounds a number to the nearest integer. /// /// Half-integers are rounded away from zero. /// /// Optionally, a number of decimal places can be specified. If negative, its /// absolute value will indicate the amount of significant integer digits to /// remove before the decimal point. /// /// Note that this function will return the same type as the operand. That is, /// applying `round` to a [`float`] will return a `float`, and to a [`decimal`], /// another `decimal`. You may explicitly convert the output of this function to /// an integer with [`int`], but note that such a conversion will error if the /// `float` or `decimal` is larger than the maximum 64-bit signed integer or /// smaller than the minimum integer. /// /// In addition, this function can error if there is an attempt to round beyond /// the maximum or minimum integer or `decimal`. If the number is a `float`, /// such an attempt will cause `{float.inf}` or `{-float.inf}` to be returned /// for maximum and minimum respectively. /// /// ```example /// #calc.round(3.1415, digits: 2) /// #assert(calc.round(3) == 3) /// #assert(calc.round(3.14) == 3) /// #assert(calc.round(3.5) == 4.0) /// #assert(calc.round(3333.45, digits: -2) == 3300.0) /// #assert(calc.round(-48953.45, digits: -3) == -49000.0) /// #assert(calc.round(3333, digits: -2) == 3300) /// #assert(calc.round(-48953, digits: -3) == -49000) /// #assert(calc.round(decimal("-6.5")) == decimal("-7")) /// #assert(calc.round(decimal("7.123456789"), digits: 6) == decimal("7.123457")) /// #assert(calc.round(decimal("3333.45"), digits: -2) == decimal("3300")) /// #assert(calc.round(decimal("-48953.45"), digits: -3) == decimal("-49000")) /// ``` #[func] pub fn round( /// The number to round. value: DecNum, /// If positive, the number of decimal places. /// /// If negative, the number of significant integer digits that should be /// removed before the decimal point. #[named] #[default(0)] digits: i64, ) -> StrResult { match value { DecNum::Int(n) => Ok(DecNum::Int( round_int_with_precision(n, digits.saturating_as::()) .ok_or_else(too_large)?, )), DecNum::Float(n) => { Ok(DecNum::Float(round_with_precision(n, digits.saturating_as::()))) } DecNum::Decimal(n) => Ok(DecNum::Decimal( n.round(digits.saturating_as::()).ok_or_else(too_large)?, )), } } /// Clamps a number between a minimum and maximum value. /// /// ```example /// #calc.clamp(5, 0, 4) /// #assert(calc.clamp(5, 0, 10) == 5) /// #assert(calc.clamp(5, 6, 10) == 6) /// #assert(calc.clamp(decimal("5.45"), 2, decimal("45.9")) == decimal("5.45")) /// #assert(calc.clamp(decimal("5.45"), decimal("6.75"), 12) == decimal("6.75")) /// ``` #[func] pub fn clamp( span: Span, /// The number to clamp. value: DecNum, /// The inclusive minimum value. min: DecNum, /// The inclusive maximum value. max: Spanned, ) -> SourceResult { // Ignore if there are incompatible types (decimal and float) since that // will cause `apply3` below to error before calling clamp, avoiding a // panic. if min .apply2(max.v, |min, max| max < min, |min, max| max < min, |min, max| max < min) .unwrap_or(false) { bail!(max.span, "max must be greater than or equal to min") } value .apply3(min, max.v, i64::clamp, f64::clamp, Decimal::clamp) .ok_or_else(cant_apply_to_decimal_and_float) .at(span) } /// Determines the minimum of a sequence of values. /// /// ```example /// #calc.min(1, -3, -5, 20, 3, 6) \ /// #calc.min("typst", "is", "cool") /// ``` #[func(title = "Minimum")] pub fn min( span: Span, /// The sequence of values from which to extract the minimum. /// Must not be empty. #[variadic] values: Vec>, ) -> SourceResult { minmax(span, values, Ordering::Less) } /// Determines the maximum of a sequence of values. /// /// ```example /// #calc.max(1, -3, -5, 20, 3, 6) \ /// #calc.max("typst", "is", "cool") /// ``` #[func(title = "Maximum")] pub fn max( span: Span, /// The sequence of values from which to extract the maximum. /// Must not be empty. #[variadic] values: Vec>, ) -> SourceResult { minmax(span, values, Ordering::Greater) } /// Find the minimum or maximum of a sequence of values. fn minmax( span: Span, values: Vec>, goal: Ordering, ) -> SourceResult { let mut iter = values.into_iter(); let Some(Spanned { v: mut extremum, .. }) = iter.next() else { bail!(span, "expected at least one value"); }; for Spanned { v, span } in iter { let ordering = ops::compare(&v, &extremum).at(span)?; if ordering == goal { extremum = v; } } Ok(extremum) } /// Determines whether an integer is even. /// /// ```example /// #calc.even(4) \ /// #calc.even(5) \ /// #range(10).filter(calc.even) /// ``` #[func] pub fn even( /// The number to check for evenness. value: i64, ) -> bool { value % 2 == 0 } /// Determines whether an integer is odd. /// /// ```example /// #calc.odd(4) \ /// #calc.odd(5) \ /// #range(10).filter(calc.odd) /// ``` #[func] pub fn odd( /// The number to check for oddness. value: i64, ) -> bool { value % 2 != 0 } /// Calculates the remainder of two numbers. /// /// The value `calc.rem(x, y)` always has the same sign as `x`, and is smaller /// in magnitude than `y`. /// /// This can error if given a [`decimal`] input and the dividend is too small in /// magnitude compared to the divisor. /// /// ```example /// #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")] pub fn rem( span: Span, /// The dividend of the remainder. dividend: DecNum, /// The divisor of the remainder. divisor: Spanned, ) -> SourceResult { if divisor.v.is_zero() { bail!(divisor.span, "divisor must not be zero"); } dividend .apply2( divisor.v, |a, b| Some(DecNum::Int(a % b)), |a, b| Some(DecNum::Float(a % b)), |a, b| a.checked_rem(b).map(DecNum::Decimal), ) .ok_or_else(cant_apply_to_decimal_and_float) .at(span)? .ok_or("dividend too small compared to divisor") .at(span) } /// 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) \ /// #calc.div-euclid(decimal("1.75"), decimal("0.5")) /// ``` #[func(title = "Euclidean Division")] pub fn div_euclid( span: Span, /// The dividend of the division. dividend: DecNum, /// The divisor of the division. divisor: Spanned, ) -> SourceResult { if divisor.v.is_zero() { bail!(divisor.span, "divisor must not be zero"); } dividend .apply2( divisor.v, |a, b| Some(DecNum::Int(a.div_euclid(b))), |a, b| Some(DecNum::Float(a.div_euclid(b))), |a, b| a.checked_div_euclid(b).map(DecNum::Decimal), ) .ok_or_else(cant_apply_to_decimal_and_float) .at(span)? .ok_or_else(too_large) .at(span) } /// 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. /// /// In addition, this can error if given a [`decimal`] input and the dividend is /// too small in magnitude compared to the divisor. /// /// ```example /// #calc.rem-euclid(7, 3) \ /// #calc.rem-euclid(7, -3) \ /// #calc.rem-euclid(-7, 3) \ /// #calc.rem-euclid(-7, -3) \ /// #calc.rem-euclid(1.75, 0.5) \ /// #calc.rem-euclid(decimal("1.75"), decimal("0.5")) /// ``` #[func(title = "Euclidean Remainder", keywords = ["modulo", "modulus"])] pub fn rem_euclid( span: Span, /// The dividend of the remainder. dividend: DecNum, /// The divisor of the remainder. divisor: Spanned, ) -> SourceResult { if divisor.v.is_zero() { bail!(divisor.span, "divisor must not be zero"); } dividend .apply2( divisor.v, |a, b| Some(DecNum::Int(a.rem_euclid(b))), |a, b| Some(DecNum::Float(a.rem_euclid(b))), |a, b| a.checked_rem_euclid(b).map(DecNum::Decimal), ) .ok_or_else(cant_apply_to_decimal_and_float) .at(span)? .ok_or("dividend too small compared to divisor") .at(span) } /// Calculates the quotient (floored division) of two numbers. /// /// Note that this function will always return an [integer]($int), and will /// error if the resulting [`float`] or [`decimal`] 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, ) -> SourceResult { if divisor.v.is_zero() { bail!(divisor.span, "divisor must not be zero"); } let divided = dividend .apply2( divisor.v, |a, b| Some(DecNum::Int(a / b)), |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, /// The sequence of values from which to calculate the p-norm. /// Returns `0.0` if empty. #[variadic] values: Vec, ) -> SourceResult { 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::().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 { Int(i64), Float(f64), } impl Num { fn float(self) -> f64 { match self { Self::Int(v) => v as f64, Self::Float(v) => v, } } } cast! { Num, self => match self { Self::Int(v) => v.into_value(), Self::Float(v) => v.into_value(), }, v: i64 => Self::Int(v), v: f64 => Self::Float(v), } /// A value which can be passed to functions that work with integers, floats, /// and decimals. #[derive(Debug, Copy, Clone)] pub enum DecNum { Int(i64), Float(f64), Decimal(Decimal), } impl DecNum { /// Checks if this number is equivalent to zero. fn is_zero(self) -> bool { match self { Self::Int(i) => i == 0, Self::Float(f) => f == 0.0, Self::Decimal(d) => d.is_zero(), } } /// If this `DecNum` holds an integer or float, returns a float. /// Otherwise, returns `None`. fn float(self) -> Option { match self { Self::Int(i) => Some(i as f64), Self::Float(f) => Some(f), Self::Decimal(_) => None, } } /// If this `DecNum` holds an integer or decimal, returns a decimal. /// Otherwise, returns `None`. fn decimal(self) -> Option { match self { Self::Int(i) => Some(Decimal::from(i)), Self::Float(_) => None, Self::Decimal(d) => Some(d), } } /// Tries to apply a function to two decimal or numeric arguments. /// /// Fails with `None` if one is a float and the other is a decimal. fn apply2( self, other: Self, int: impl FnOnce(i64, i64) -> T, float: impl FnOnce(f64, f64) -> T, decimal: impl FnOnce(Decimal, Decimal) -> T, ) -> Option { match (self, other) { (Self::Int(a), Self::Int(b)) => Some(int(a, b)), (Self::Decimal(a), Self::Decimal(b)) => Some(decimal(a, b)), (Self::Decimal(a), Self::Int(b)) => Some(decimal(a, Decimal::from(b))), (Self::Int(a), Self::Decimal(b)) => Some(decimal(Decimal::from(a), b)), (a, b) => Some(float(a.float()?, b.float()?)), } } /// Tries to apply a function to three decimal or numeric arguments. /// /// Fails with `None` if one is a float and the other is a decimal. fn apply3( self, other: Self, third: Self, int: impl FnOnce(i64, i64, i64) -> i64, float: impl FnOnce(f64, f64, f64) -> f64, decimal: impl FnOnce(Decimal, Decimal, Decimal) -> Decimal, ) -> Option { match (self, other, third) { (Self::Int(a), Self::Int(b), Self::Int(c)) => Some(Self::Int(int(a, b, c))), (Self::Decimal(a), b, c) => { Some(Self::Decimal(decimal(a, b.decimal()?, c.decimal()?))) } (a, Self::Decimal(b), c) => { Some(Self::Decimal(decimal(a.decimal()?, b, c.decimal()?))) } (a, b, Self::Decimal(c)) => { Some(Self::Decimal(decimal(a.decimal()?, b.decimal()?, c))) } (a, b, c) => Some(Self::Float(float(a.float()?, b.float()?, c.float()?))), } } } cast! { DecNum, self => match self { Self::Int(v) => v.into_value(), Self::Float(v) => v.into_value(), Self::Decimal(v) => v.into_value(), }, v: i64 => Self::Int(v), v: f64 => Self::Float(v), v: Decimal => Self::Decimal(v), } /// A value that can be passed to a trigonometric function. pub enum AngleLike { Int(i64), Float(f64), Angle(Angle), } cast! { AngleLike, v: i64 => Self::Int(v), v: f64 => Self::Float(v), v: Angle => Self::Angle(v), } /// The error message when the result is too large to be represented. #[cold] fn too_large() -> &'static str { "the result is too large" } /// The hinted error message when trying to apply an operation to decimal and /// float operands. #[cold] fn cant_apply_to_decimal_and_float() -> HintedString { HintedString::new("cannot apply this operation to a decimal and a float".into()) .with_hint( "if loss of precision is acceptable, explicitly cast the \ decimal to a float with `float(value)`", ) }