kcl_lib/std/
math.rs

1//! Functions related to mathematics.
2
3use anyhow::Result;
4
5use crate::{
6    errors::KclError,
7    execution::{
8        types::{ArrayLen, NumericType, RuntimeType},
9        ExecState, KclValue,
10    },
11    std::args::{Args, TyF64},
12    CompilationError,
13};
14
15/// Compute the remainder after dividing `num` by `div`.
16/// If `num` is negative, the result will be too.
17pub async fn rem(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
18    let n: TyF64 = args.get_unlabeled_kw_arg_typed("number to divide", &RuntimeType::num_any(), exec_state)?;
19    let d: TyF64 = args.get_kw_arg_typed("divisor", &RuntimeType::num_any(), exec_state)?;
20
21    let (n, d, ty) = NumericType::combine_div(n, d);
22    if ty == NumericType::Unknown {
23        exec_state.err(CompilationError::err(
24            args.source_range,
25            "Calling `rem` on numbers which have unknown or incompatible units.\n\nYou may need to add information about the type of the argument, for example:\n  using a numeric suffix: `42{ty}`\n  or using type ascription: `foo(): number({ty})`"
26        ));
27    }
28    let remainder = n % d;
29
30    Ok(args.make_user_val_from_f64_with_type(TyF64::new(remainder, ty)))
31}
32
33/// Compute the cosine of a number (in radians).
34pub async fn cos(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
35    let num: TyF64 = args.get_unlabeled_kw_arg_typed("input", &RuntimeType::angle(), exec_state)?;
36    let num = num.to_radians();
37    Ok(args.make_user_val_from_f64_with_type(TyF64::count(num.cos())))
38}
39
40/// Compute the sine of a number (in radians).
41pub async fn sin(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
42    let num: TyF64 = args.get_unlabeled_kw_arg_typed("input", &RuntimeType::angle(), exec_state)?;
43    let num = num.to_radians();
44    Ok(args.make_user_val_from_f64_with_type(TyF64::count(num.sin())))
45}
46
47/// Compute the tangent of a number (in radians).
48pub async fn tan(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
49    let num: TyF64 = args.get_unlabeled_kw_arg_typed("input", &RuntimeType::angle(), exec_state)?;
50    let num = num.to_radians();
51    Ok(args.make_user_val_from_f64_with_type(TyF64::count(num.tan())))
52}
53
54/// Compute the square root of a number.
55pub async fn sqrt(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
56    let input: TyF64 = args.get_unlabeled_kw_arg_typed("input", &RuntimeType::num_any(), exec_state)?;
57    let result = input.n.sqrt();
58
59    Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units())))
60}
61
62/// Compute the absolute value of a number.
63pub async fn abs(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
64    let input: TyF64 = args.get_unlabeled_kw_arg_typed("input", &RuntimeType::num_any(), exec_state)?;
65    let result = input.n.abs();
66
67    Ok(args.make_user_val_from_f64_with_type(input.map_value(result)))
68}
69
70/// Round a number to the nearest integer.
71pub async fn round(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
72    let input: TyF64 = args.get_unlabeled_kw_arg_typed("input", &RuntimeType::num_any(), exec_state)?;
73    let result = input.n.round();
74
75    Ok(args.make_user_val_from_f64_with_type(input.map_value(result)))
76}
77
78/// Compute the largest integer less than or equal to a number.
79pub async fn floor(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
80    let input: TyF64 = args.get_unlabeled_kw_arg_typed("input", &RuntimeType::num_any(), exec_state)?;
81    let result = input.n.floor();
82
83    Ok(args.make_user_val_from_f64_with_type(input.map_value(result)))
84}
85
86/// Compute the smallest integer greater than or equal to a number.
87pub async fn ceil(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
88    let input: TyF64 = args.get_unlabeled_kw_arg_typed("input", &RuntimeType::num_any(), exec_state)?;
89    let result = input.n.ceil();
90
91    Ok(args.make_user_val_from_f64_with_type(input.map_value(result)))
92}
93
94/// Compute the minimum of the given arguments.
95pub async fn min(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
96    let nums: Vec<TyF64> = args.get_unlabeled_kw_arg_typed(
97        "input",
98        &RuntimeType::Array(Box::new(RuntimeType::num_any()), ArrayLen::NonEmpty),
99        exec_state,
100    )?;
101    let (nums, ty) = NumericType::combine_eq_array(&nums);
102    if ty == NumericType::Unknown {
103        exec_state.warn(CompilationError::err(
104            args.source_range,
105            "Calling `min` on numbers which have unknown or incompatible units.\n\nYou may need to add information about the type of the argument, for example:\n  using a numeric suffix: `42{ty}`\n  or using type ascription: `foo(): number({ty})`",
106        ));
107    }
108
109    let mut result = f64::MAX;
110    for num in nums {
111        if num < result {
112            result = num;
113        }
114    }
115
116    Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, ty)))
117}
118
119/// Compute the maximum of the given arguments.
120pub async fn max(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
121    let nums: Vec<TyF64> = args.get_unlabeled_kw_arg_typed(
122        "input",
123        &RuntimeType::Array(Box::new(RuntimeType::num_any()), ArrayLen::NonEmpty),
124        exec_state,
125    )?;
126    let (nums, ty) = NumericType::combine_eq_array(&nums);
127    if ty == NumericType::Unknown {
128        exec_state.warn(CompilationError::err(
129            args.source_range,
130            "Calling `max` on numbers which have unknown or incompatible units.\n\nYou may need to add information about the type of the argument, for example:\n  using a numeric suffix: `42{ty}`\n  or using type ascription: `foo(): number({ty})`",
131        ));
132    }
133
134    let mut result = f64::MIN;
135    for num in nums {
136        if num > result {
137            result = num;
138        }
139    }
140
141    Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, ty)))
142}
143
144/// Compute the number to a power.
145pub async fn pow(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
146    let input: TyF64 = args.get_unlabeled_kw_arg_typed("input", &RuntimeType::num_any(), exec_state)?;
147    let exp: TyF64 = args.get_kw_arg_typed("exp", &RuntimeType::count(), exec_state)?;
148    let result = input.n.powf(exp.n);
149
150    Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units())))
151}
152
153/// Compute the arccosine of a number (in radians).
154pub async fn acos(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
155    let input: TyF64 = args.get_unlabeled_kw_arg_typed("input", &RuntimeType::count(), exec_state)?;
156    let result = input.n.acos();
157
158    Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::radians())))
159}
160
161/// Compute the arcsine of a number (in radians).
162pub async fn asin(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
163    let input: TyF64 = args.get_unlabeled_kw_arg_typed("input", &RuntimeType::count(), exec_state)?;
164    let result = input.n.asin();
165
166    Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::radians())))
167}
168
169/// Compute the arctangent of a number (in radians).
170pub async fn atan(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
171    let input: TyF64 = args.get_unlabeled_kw_arg_typed("input", &RuntimeType::count(), exec_state)?;
172    let result = input.n.atan();
173
174    Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::radians())))
175}
176
177/// Compute the four quadrant arctangent of Y and X (in radians).
178pub async fn atan2(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
179    let y = args.get_kw_arg_typed("y", &RuntimeType::length(), exec_state)?;
180    let x = args.get_kw_arg_typed("x", &RuntimeType::length(), exec_state)?;
181    let (y, x, _) = NumericType::combine_eq_coerce(y, x);
182    let result = y.atan2(x);
183
184    Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::radians())))
185}
186
187/// Compute the logarithm of the number with respect to an arbitrary base.
188///
189/// The result might not be correctly rounded owing to implementation
190/// details; `log2()` can produce more accurate results for base 2,
191/// and `log10()` can produce more accurate results for base 10.
192pub async fn log(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
193    let input: TyF64 = args.get_unlabeled_kw_arg_typed("input", &RuntimeType::num_any(), exec_state)?;
194    let base: TyF64 = args.get_kw_arg_typed("base", &RuntimeType::count(), exec_state)?;
195    let result = input.n.log(base.n);
196
197    Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units())))
198}
199
200/// Compute the base 2 logarithm of the number.
201pub async fn log2(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
202    let input: TyF64 = args.get_unlabeled_kw_arg_typed("input", &RuntimeType::num_any(), exec_state)?;
203    let result = input.n.log2();
204
205    Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units())))
206}
207
208/// Compute the base 10 logarithm of the number.
209pub async fn log10(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
210    let input: TyF64 = args.get_unlabeled_kw_arg_typed("input", &RuntimeType::num_any(), exec_state)?;
211    let result = input.n.log10();
212
213    Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units())))
214}
215
216/// Compute the natural logarithm of the number.
217pub async fn ln(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
218    let input: TyF64 = args.get_unlabeled_kw_arg_typed("input", &RuntimeType::num_any(), exec_state)?;
219    let result = input.n.ln();
220
221    Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units())))
222}