1use std::cmp;
4use std::cmp::Ordering;
5
6use az::SaturatingAs;
7use typst_syntax::{Span, Spanned};
8use typst_utils::{round_int_with_precision, round_with_precision};
9
10use crate::diag::{bail, At, HintedString, SourceResult, StrResult};
11use crate::foundations::{cast, func, ops, Decimal, IntoValue, Module, Scope, Value};
12use crate::layout::{Angle, Fr, Length, Ratio};
13
14pub fn module() -> Module {
16 let mut scope = Scope::new();
17 scope.define_func::<abs>();
18 scope.define_func::<pow>();
19 scope.define_func::<exp>();
20 scope.define_func::<sqrt>();
21 scope.define_func::<root>();
22 scope.define_func::<sin>();
23 scope.define_func::<cos>();
24 scope.define_func::<tan>();
25 scope.define_func::<asin>();
26 scope.define_func::<acos>();
27 scope.define_func::<atan>();
28 scope.define_func::<atan2>();
29 scope.define_func::<sinh>();
30 scope.define_func::<cosh>();
31 scope.define_func::<tanh>();
32 scope.define_func::<log>();
33 scope.define_func::<ln>();
34 scope.define_func::<fact>();
35 scope.define_func::<perm>();
36 scope.define_func::<binom>();
37 scope.define_func::<gcd>();
38 scope.define_func::<lcm>();
39 scope.define_func::<floor>();
40 scope.define_func::<ceil>();
41 scope.define_func::<trunc>();
42 scope.define_func::<fract>();
43 scope.define_func::<round>();
44 scope.define_func::<clamp>();
45 scope.define_func::<min>();
46 scope.define_func::<max>();
47 scope.define_func::<even>();
48 scope.define_func::<odd>();
49 scope.define_func::<rem>();
50 scope.define_func::<div_euclid>();
51 scope.define_func::<rem_euclid>();
52 scope.define_func::<quo>();
53 scope.define_func::<norm>();
54 scope.define("inf", f64::INFINITY);
55 scope.define("pi", std::f64::consts::PI);
56 scope.define("tau", std::f64::consts::TAU);
57 scope.define("e", std::f64::consts::E);
58 Module::new("calc", scope)
59}
60
61#[func(title = "Absolute")]
70pub fn abs(
71 value: ToAbs,
73) -> Value {
74 value.0
75}
76
77pub struct ToAbs(Value);
79
80cast! {
81 ToAbs,
82 v: i64 => Self(v.abs().into_value()),
83 v: f64 => Self(v.abs().into_value()),
84 v: Length => Self(Value::Length(v.try_abs()
85 .ok_or("cannot take absolute value of this length")?)),
86 v: Angle => Self(Value::Angle(v.abs())),
87 v: Ratio => Self(Value::Ratio(v.abs())),
88 v: Fr => Self(Value::Fraction(v.abs())),
89 v: Decimal => Self(Value::Decimal(v.abs()))
90}
91
92#[func(title = "Power")]
99pub fn pow(
100 span: Span,
101 base: DecNum,
105 exponent: Spanned<Num>,
107) -> SourceResult<DecNum> {
108 match exponent.v {
109 _ if exponent.v.float() == 0.0 && base.is_zero() => {
110 bail!(span, "zero to the power of zero is undefined")
111 }
112 Num::Int(i) if i32::try_from(i).is_err() => {
113 bail!(exponent.span, "exponent is too large")
114 }
115 Num::Float(f) if !f.is_normal() && f != 0.0 => {
116 bail!(exponent.span, "exponent may not be infinite, subnormal, or NaN")
117 }
118 _ => {}
119 };
120
121 match (base, exponent.v) {
122 (DecNum::Int(a), Num::Int(b)) if b >= 0 => a
123 .checked_pow(b as u32)
124 .map(DecNum::Int)
125 .ok_or_else(too_large)
126 .at(span),
127 (DecNum::Decimal(a), Num::Int(b)) => {
128 a.checked_powi(b).map(DecNum::Decimal).ok_or_else(too_large).at(span)
129 }
130 (a, b) => {
131 let Some(a) = a.float() else {
132 return Err(cant_apply_to_decimal_and_float()).at(span);
133 };
134
135 let result = if a == std::f64::consts::E {
136 b.float().exp()
137 } else if a == 2.0 {
138 b.float().exp2()
139 } else if let Num::Int(b) = b {
140 a.powi(b as i32)
141 } else {
142 a.powf(b.float())
143 };
144
145 if result.is_nan() {
146 bail!(span, "the result is not a real number")
147 }
148
149 Ok(DecNum::Float(result))
150 }
151 }
152}
153
154#[func(title = "Exponential")]
160pub fn exp(
161 span: Span,
162 exponent: Spanned<Num>,
164) -> SourceResult<f64> {
165 match exponent.v {
166 Num::Int(i) if i32::try_from(i).is_err() => {
167 bail!(exponent.span, "exponent is too large")
168 }
169 Num::Float(f) if !f.is_normal() && f != 0.0 => {
170 bail!(exponent.span, "exponent may not be infinite, subnormal, or NaN")
171 }
172 _ => {}
173 }
174
175 let result = exponent.v.float().exp();
176 if result.is_nan() {
177 bail!(span, "the result is not a real number")
178 }
179
180 Ok(result)
181}
182
183#[func(title = "Square Root")]
190pub fn sqrt(
191 value: Spanned<Num>,
193) -> SourceResult<f64> {
194 if value.v.float() < 0.0 {
195 bail!(value.span, "cannot take square root of negative number");
196 }
197 Ok(value.v.float().sqrt())
198}
199
200#[func]
209pub fn root(
210 radicand: f64,
212 index: Spanned<i64>,
214) -> SourceResult<f64> {
215 if index.v == 0 {
216 bail!(index.span, "cannot take the 0th root of a number");
217 } else if radicand < 0.0 {
218 if index.v % 2 == 0 {
219 bail!(
220 index.span,
221 "negative numbers do not have a real nth root when n is even"
222 );
223 } else {
224 Ok(-(-radicand).powf(1.0 / index.v as f64))
225 }
226 } else {
227 Ok(radicand.powf(1.0 / index.v as f64))
228 }
229}
230
231#[func(title = "Sine")]
241pub fn sin(
242 angle: AngleLike,
244) -> f64 {
245 match angle {
246 AngleLike::Angle(a) => a.sin(),
247 AngleLike::Int(n) => (n as f64).sin(),
248 AngleLike::Float(n) => n.sin(),
249 }
250}
251
252#[func(title = "Cosine")]
262pub fn cos(
263 angle: AngleLike,
265) -> f64 {
266 match angle {
267 AngleLike::Angle(a) => a.cos(),
268 AngleLike::Int(n) => (n as f64).cos(),
269 AngleLike::Float(n) => n.cos(),
270 }
271}
272
273#[func(title = "Tangent")]
283pub fn tan(
284 angle: AngleLike,
286) -> f64 {
287 match angle {
288 AngleLike::Angle(a) => a.tan(),
289 AngleLike::Int(n) => (n as f64).tan(),
290 AngleLike::Float(n) => n.tan(),
291 }
292}
293
294#[func(title = "Arcsine")]
301pub fn asin(
302 value: Spanned<Num>,
304) -> SourceResult<Angle> {
305 let val = value.v.float();
306 if val < -1.0 || val > 1.0 {
307 bail!(value.span, "value must be between -1 and 1");
308 }
309 Ok(Angle::rad(val.asin()))
310}
311
312#[func(title = "Arccosine")]
319pub fn acos(
320 value: Spanned<Num>,
322) -> SourceResult<Angle> {
323 let val = value.v.float();
324 if val < -1.0 || val > 1.0 {
325 bail!(value.span, "value must be between -1 and 1");
326 }
327 Ok(Angle::rad(val.acos()))
328}
329
330#[func(title = "Arctangent")]
337pub fn atan(
338 value: Num,
340) -> Angle {
341 Angle::rad(value.float().atan())
342}
343
344#[func(title = "Four-quadrant Arctangent")]
353pub fn atan2(
354 x: Num,
356 y: Num,
358) -> Angle {
359 Angle::rad(f64::atan2(y.float(), x.float()))
360}
361
362#[func(title = "Hyperbolic Sine")]
369pub fn sinh(
370 value: f64,
372) -> f64 {
373 value.sinh()
374}
375
376#[func(title = "Hyperbolic Cosine")]
383pub fn cosh(
384 value: f64,
386) -> f64 {
387 value.cosh()
388}
389
390#[func(title = "Hyperbolic Tangent")]
397pub fn tanh(
398 value: f64,
400) -> f64 {
401 value.tanh()
402}
403
404#[func(title = "Logarithm")]
412pub fn log(
413 span: Span,
414 value: Spanned<Num>,
416 #[named]
418 #[default(Spanned::new(10.0, Span::detached()))]
419 base: Spanned<f64>,
420) -> SourceResult<f64> {
421 let number = value.v.float();
422 if number <= 0.0 {
423 bail!(value.span, "value must be strictly positive")
424 }
425
426 if !base.v.is_normal() {
427 bail!(base.span, "base may not be zero, NaN, infinite, or subnormal")
428 }
429
430 let result = if base.v == std::f64::consts::E {
431 number.ln()
432 } else if base.v == 2.0 {
433 number.log2()
434 } else if base.v == 10.0 {
435 number.log10()
436 } else {
437 number.log(base.v)
438 };
439
440 if result.is_infinite() || result.is_nan() {
441 bail!(span, "the result is not a real number")
442 }
443
444 Ok(result)
445}
446
447#[func(title = "Natural Logarithm")]
453pub fn ln(
454 span: Span,
455 value: Spanned<Num>,
457) -> SourceResult<f64> {
458 let number = value.v.float();
459 if number <= 0.0 {
460 bail!(value.span, "value must be strictly positive")
461 }
462
463 let result = number.ln();
464 if result.is_infinite() {
465 bail!(span, "result close to -inf")
466 }
467
468 Ok(result)
469}
470
471#[func(title = "Factorial")]
477pub fn fact(
478 number: u64,
480) -> StrResult<i64> {
481 Ok(fact_impl(1, number).ok_or_else(too_large)?)
482}
483
484#[func(title = "Permutation")]
494pub fn perm(
495 base: u64,
497 numbers: u64,
499) -> StrResult<i64> {
500 if base < numbers {
502 return Ok(0);
503 }
504
505 Ok(fact_impl(base - numbers + 1, base).ok_or_else(too_large)?)
506}
507
508fn fact_impl(start: u64, end: u64) -> Option<i64> {
511 if end + 1 < start {
513 return Some(0);
514 }
515
516 let real_start: u64 = cmp::max(1, start);
517 let mut count: u64 = 1;
518 for i in real_start..=end {
519 count = count.checked_mul(i)?;
520 }
521
522 count.try_into().ok()
523}
524
525#[func(title = "Binomial")]
534pub fn binom(
535 n: u64,
537 k: u64,
539) -> StrResult<i64> {
540 Ok(binom_impl(n, k).ok_or_else(too_large)?)
541}
542
543fn binom_impl(n: u64, k: u64) -> Option<i64> {
547 if k > n {
548 return Some(0);
549 }
550
551 let real_k = cmp::min(n - k, k);
553 if real_k == 0 {
554 return Some(1);
555 }
556
557 let mut result: u64 = 1;
558 for i in 0..real_k {
559 result = result.checked_mul(n - i)?.checked_div(i + 1)?;
560 }
561
562 result.try_into().ok()
563}
564
565#[func(title = "Greatest Common Divisor")]
571pub fn gcd(
572 a: i64,
574 b: i64,
576) -> i64 {
577 let (mut a, mut b) = (a, b);
578 while b != 0 {
579 let temp = b;
580 b = a % b;
581 a = temp;
582 }
583
584 a.abs()
585}
586
587#[func(title = "Least Common Multiple")]
593pub fn lcm(
594 a: i64,
596 b: i64,
598) -> StrResult<i64> {
599 if a == b {
600 return Ok(a.abs());
601 }
602
603 Ok(a.checked_div(gcd(a, b))
604 .and_then(|gcd| gcd.checked_mul(b))
605 .map(|v| v.abs())
606 .ok_or_else(too_large)?)
607}
608
609#[func]
624pub fn floor(
625 value: DecNum,
627) -> StrResult<i64> {
628 match value {
629 DecNum::Int(n) => Ok(n),
630 DecNum::Float(n) => Ok(crate::foundations::convert_float_to_int(n.floor())
631 .map_err(|_| too_large())?),
632 DecNum::Decimal(n) => Ok(i64::try_from(n.floor()).map_err(|_| too_large())?),
633 }
634}
635
636#[func]
651pub fn ceil(
652 value: DecNum,
654) -> StrResult<i64> {
655 match value {
656 DecNum::Int(n) => Ok(n),
657 DecNum::Float(n) => Ok(crate::foundations::convert_float_to_int(n.ceil())
658 .map_err(|_| too_large())?),
659 DecNum::Decimal(n) => Ok(i64::try_from(n.ceil()).map_err(|_| too_large())?),
660 }
661}
662
663#[func(title = "Truncate")]
678pub fn trunc(
679 value: DecNum,
681) -> StrResult<i64> {
682 match value {
683 DecNum::Int(n) => Ok(n),
684 DecNum::Float(n) => Ok(crate::foundations::convert_float_to_int(n.trunc())
685 .map_err(|_| too_large())?),
686 DecNum::Decimal(n) => Ok(i64::try_from(n.trunc()).map_err(|_| too_large())?),
687 }
688}
689
690#[func(title = "Fractional")]
700pub fn fract(
701 value: DecNum,
703) -> DecNum {
704 match value {
705 DecNum::Int(_) => DecNum::Int(0),
706 DecNum::Float(n) => DecNum::Float(n.fract()),
707 DecNum::Decimal(n) => DecNum::Decimal(n.fract()),
708 }
709}
710
711#[func]
745pub fn round(
746 value: DecNum,
748 #[named]
753 #[default(0)]
754 digits: i64,
755) -> StrResult<DecNum> {
756 match value {
757 DecNum::Int(n) => Ok(DecNum::Int(
758 round_int_with_precision(n, digits.saturating_as::<i16>())
759 .ok_or_else(too_large)?,
760 )),
761 DecNum::Float(n) => {
762 Ok(DecNum::Float(round_with_precision(n, digits.saturating_as::<i16>())))
763 }
764 DecNum::Decimal(n) => Ok(DecNum::Decimal(
765 n.round(digits.saturating_as::<i32>()).ok_or_else(too_large)?,
766 )),
767 }
768}
769
770#[func]
780pub fn clamp(
781 span: Span,
782 value: DecNum,
784 min: DecNum,
786 max: Spanned<DecNum>,
788) -> SourceResult<DecNum> {
789 if min
793 .apply2(max.v, |min, max| max < min, |min, max| max < min, |min, max| max < min)
794 .unwrap_or(false)
795 {
796 bail!(max.span, "max must be greater than or equal to min")
797 }
798
799 value
800 .apply3(min, max.v, i64::clamp, f64::clamp, Decimal::clamp)
801 .ok_or_else(cant_apply_to_decimal_and_float)
802 .at(span)
803}
804
805#[func(title = "Minimum")]
812pub fn min(
813 span: Span,
814 #[variadic]
817 values: Vec<Spanned<Value>>,
818) -> SourceResult<Value> {
819 minmax(span, values, Ordering::Less)
820}
821
822#[func(title = "Maximum")]
829pub fn max(
830 span: Span,
831 #[variadic]
834 values: Vec<Spanned<Value>>,
835) -> SourceResult<Value> {
836 minmax(span, values, Ordering::Greater)
837}
838
839fn minmax(
841 span: Span,
842 values: Vec<Spanned<Value>>,
843 goal: Ordering,
844) -> SourceResult<Value> {
845 let mut iter = values.into_iter();
846 let Some(Spanned { v: mut extremum, .. }) = iter.next() else {
847 bail!(span, "expected at least one value");
848 };
849
850 for Spanned { v, span } in iter {
851 let ordering = ops::compare(&v, &extremum).at(span)?;
852 if ordering == goal {
853 extremum = v;
854 }
855 }
856
857 Ok(extremum)
858}
859
860#[func]
868pub fn even(
869 value: i64,
871) -> bool {
872 value % 2 == 0
873}
874
875#[func]
883pub fn odd(
884 value: i64,
886) -> bool {
887 value % 2 != 0
888}
889
890#[func(title = "Remainder")]
906pub fn rem(
907 span: Span,
908 dividend: DecNum,
910 divisor: Spanned<DecNum>,
912) -> SourceResult<DecNum> {
913 if divisor.v.is_zero() {
914 bail!(divisor.span, "divisor must not be zero");
915 }
916
917 dividend
918 .apply2(
919 divisor.v,
920 |a, b| Some(DecNum::Int(a % b)),
921 |a, b| Some(DecNum::Float(a % b)),
922 |a, b| a.checked_rem(b).map(DecNum::Decimal),
923 )
924 .ok_or_else(cant_apply_to_decimal_and_float)
925 .at(span)?
926 .ok_or("dividend too small compared to divisor")
927 .at(span)
928}
929
930#[func(title = "Euclidean Division")]
944pub fn div_euclid(
945 span: Span,
946 dividend: DecNum,
948 divisor: Spanned<DecNum>,
950) -> SourceResult<DecNum> {
951 if divisor.v.is_zero() {
952 bail!(divisor.span, "divisor must not be zero");
953 }
954
955 dividend
956 .apply2(
957 divisor.v,
958 |a, b| Some(DecNum::Int(a.div_euclid(b))),
959 |a, b| Some(DecNum::Float(a.div_euclid(b))),
960 |a, b| a.checked_div_euclid(b).map(DecNum::Decimal),
961 )
962 .ok_or_else(cant_apply_to_decimal_and_float)
963 .at(span)?
964 .ok_or_else(too_large)
965 .at(span)
966}
967
968#[func(title = "Euclidean Remainder", keywords = ["modulo", "modulus"])]
987pub fn rem_euclid(
988 span: Span,
989 dividend: DecNum,
991 divisor: Spanned<DecNum>,
993) -> SourceResult<DecNum> {
994 if divisor.v.is_zero() {
995 bail!(divisor.span, "divisor must not be zero");
996 }
997
998 dividend
999 .apply2(
1000 divisor.v,
1001 |a, b| Some(DecNum::Int(a.rem_euclid(b))),
1002 |a, b| Some(DecNum::Float(a.rem_euclid(b))),
1003 |a, b| a.checked_rem_euclid(b).map(DecNum::Decimal),
1004 )
1005 .ok_or_else(cant_apply_to_decimal_and_float)
1006 .at(span)?
1007 .ok_or("dividend too small compared to divisor")
1008 .at(span)
1009}
1010
1011#[func(title = "Quotient")]
1023pub fn quo(
1024 span: Span,
1025 dividend: DecNum,
1027 divisor: Spanned<DecNum>,
1029) -> SourceResult<i64> {
1030 if divisor.v.is_zero() {
1031 bail!(divisor.span, "divisor must not be zero");
1032 }
1033
1034 let divided = dividend
1035 .apply2(
1036 divisor.v,
1037 |a, b| Some(DecNum::Int(a / b)),
1038 |a, b| Some(DecNum::Float(a / b)),
1039 |a, b| a.checked_div(b).map(DecNum::Decimal),
1040 )
1041 .ok_or_else(cant_apply_to_decimal_and_float)
1042 .at(span)?
1043 .ok_or_else(too_large)
1044 .at(span)?;
1045
1046 floor(divided).at(span)
1047}
1048
1049#[func(title = "𝑝-Norm")]
1056pub fn norm(
1057 #[named]
1059 #[default(Spanned::new(2.0, Span::detached()))]
1060 p: Spanned<f64>,
1061 #[variadic]
1064 values: Vec<f64>,
1065) -> SourceResult<f64> {
1066 if p.v <= 0.0 {
1067 bail!(p.span, "p must be greater than zero");
1068 }
1069
1070 let abs = values.into_iter().map(f64::abs);
1072
1073 Ok(if p.v.is_infinite() {
1074 abs.max_by(|a, b| a.total_cmp(b)).unwrap_or(0.0)
1076 } else {
1077 abs.map(|v| v.powf(p.v)).sum::<f64>().powf(1.0 / p.v)
1078 })
1079}
1080
1081#[derive(Debug, Copy, Clone)]
1083pub enum Num {
1084 Int(i64),
1085 Float(f64),
1086}
1087
1088impl Num {
1089 fn float(self) -> f64 {
1090 match self {
1091 Self::Int(v) => v as f64,
1092 Self::Float(v) => v,
1093 }
1094 }
1095}
1096
1097cast! {
1098 Num,
1099 self => match self {
1100 Self::Int(v) => v.into_value(),
1101 Self::Float(v) => v.into_value(),
1102 },
1103 v: i64 => Self::Int(v),
1104 v: f64 => Self::Float(v),
1105}
1106
1107#[derive(Debug, Copy, Clone)]
1110pub enum DecNum {
1111 Int(i64),
1112 Float(f64),
1113 Decimal(Decimal),
1114}
1115
1116impl DecNum {
1117 fn is_zero(self) -> bool {
1119 match self {
1120 Self::Int(i) => i == 0,
1121 Self::Float(f) => f == 0.0,
1122 Self::Decimal(d) => d.is_zero(),
1123 }
1124 }
1125
1126 fn float(self) -> Option<f64> {
1129 match self {
1130 Self::Int(i) => Some(i as f64),
1131 Self::Float(f) => Some(f),
1132 Self::Decimal(_) => None,
1133 }
1134 }
1135
1136 fn decimal(self) -> Option<Decimal> {
1139 match self {
1140 Self::Int(i) => Some(Decimal::from(i)),
1141 Self::Float(_) => None,
1142 Self::Decimal(d) => Some(d),
1143 }
1144 }
1145
1146 fn apply2<T>(
1150 self,
1151 other: Self,
1152 int: impl FnOnce(i64, i64) -> T,
1153 float: impl FnOnce(f64, f64) -> T,
1154 decimal: impl FnOnce(Decimal, Decimal) -> T,
1155 ) -> Option<T> {
1156 match (self, other) {
1157 (Self::Int(a), Self::Int(b)) => Some(int(a, b)),
1158 (Self::Decimal(a), Self::Decimal(b)) => Some(decimal(a, b)),
1159 (Self::Decimal(a), Self::Int(b)) => Some(decimal(a, Decimal::from(b))),
1160 (Self::Int(a), Self::Decimal(b)) => Some(decimal(Decimal::from(a), b)),
1161 (a, b) => Some(float(a.float()?, b.float()?)),
1162 }
1163 }
1164
1165 fn apply3(
1169 self,
1170 other: Self,
1171 third: Self,
1172 int: impl FnOnce(i64, i64, i64) -> i64,
1173 float: impl FnOnce(f64, f64, f64) -> f64,
1174 decimal: impl FnOnce(Decimal, Decimal, Decimal) -> Decimal,
1175 ) -> Option<Self> {
1176 match (self, other, third) {
1177 (Self::Int(a), Self::Int(b), Self::Int(c)) => Some(Self::Int(int(a, b, c))),
1178 (Self::Decimal(a), b, c) => {
1179 Some(Self::Decimal(decimal(a, b.decimal()?, c.decimal()?)))
1180 }
1181 (a, Self::Decimal(b), c) => {
1182 Some(Self::Decimal(decimal(a.decimal()?, b, c.decimal()?)))
1183 }
1184 (a, b, Self::Decimal(c)) => {
1185 Some(Self::Decimal(decimal(a.decimal()?, b.decimal()?, c)))
1186 }
1187 (a, b, c) => Some(Self::Float(float(a.float()?, b.float()?, c.float()?))),
1188 }
1189 }
1190}
1191
1192cast! {
1193 DecNum,
1194 self => match self {
1195 Self::Int(v) => v.into_value(),
1196 Self::Float(v) => v.into_value(),
1197 Self::Decimal(v) => v.into_value(),
1198 },
1199 v: i64 => Self::Int(v),
1200 v: f64 => Self::Float(v),
1201 v: Decimal => Self::Decimal(v),
1202}
1203
1204pub enum AngleLike {
1206 Int(i64),
1207 Float(f64),
1208 Angle(Angle),
1209}
1210
1211cast! {
1212 AngleLike,
1213 v: i64 => Self::Int(v),
1214 v: f64 => Self::Float(v),
1215 v: Angle => Self::Angle(v),
1216}
1217
1218#[cold]
1220fn too_large() -> &'static str {
1221 "the result is too large"
1222}
1223
1224#[cold]
1227fn cant_apply_to_decimal_and_float() -> HintedString {
1228 HintedString::new("cannot apply this operation to a decimal and a float".into())
1229 .with_hint(
1230 "if loss of precision is acceptable, explicitly cast the \
1231 decimal to a float with `float(value)`",
1232 )
1233}