1use anyhow::Result;
4
5use crate::{
6 errors::{KclError, KclErrorDetails},
7 execution::{
8 types::{ArrayLen, NumericType, RuntimeType},
9 ExecState, KclValue,
10 },
11 std::args::{Args, TyF64},
12 CompilationError,
13};
14
15pub 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
33pub 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
40pub 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
47pub 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
54pub 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
58 if input.n < 0.0 {
59 return Err(KclError::new_semantic(KclErrorDetails::new(
60 format!(
61 "Attempt to take square root (`sqrt`) of a number less than zero ({})",
62 input.n
63 ),
64 vec![args.source_range],
65 )));
66 }
67
68 let result = input.n.sqrt();
69
70 Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units())))
71}
72
73pub async fn abs(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
75 let input: TyF64 = args.get_unlabeled_kw_arg_typed("input", &RuntimeType::num_any(), exec_state)?;
76 let result = input.n.abs();
77
78 Ok(args.make_user_val_from_f64_with_type(input.map_value(result)))
79}
80
81pub async fn round(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
83 let input: TyF64 = args.get_unlabeled_kw_arg_typed("input", &RuntimeType::num_any(), exec_state)?;
84 let result = input.n.round();
85
86 Ok(args.make_user_val_from_f64_with_type(input.map_value(result)))
87}
88
89pub async fn floor(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
91 let input: TyF64 = args.get_unlabeled_kw_arg_typed("input", &RuntimeType::num_any(), exec_state)?;
92 let result = input.n.floor();
93
94 Ok(args.make_user_val_from_f64_with_type(input.map_value(result)))
95}
96
97pub async fn ceil(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
99 let input: TyF64 = args.get_unlabeled_kw_arg_typed("input", &RuntimeType::num_any(), exec_state)?;
100 let result = input.n.ceil();
101
102 Ok(args.make_user_val_from_f64_with_type(input.map_value(result)))
103}
104
105pub async fn min(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
107 let nums: Vec<TyF64> = args.get_unlabeled_kw_arg_typed(
108 "input",
109 &RuntimeType::Array(Box::new(RuntimeType::num_any()), ArrayLen::Minimum(1)),
110 exec_state,
111 )?;
112 let (nums, ty) = NumericType::combine_eq_array(&nums);
113 if ty == NumericType::Unknown {
114 exec_state.warn(CompilationError::err(
115 args.source_range,
116 "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})`",
117 ));
118 }
119
120 let mut result = f64::MAX;
121 for num in nums {
122 if num < result {
123 result = num;
124 }
125 }
126
127 Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, ty)))
128}
129
130pub async fn max(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
132 let nums: Vec<TyF64> = args.get_unlabeled_kw_arg_typed(
133 "input",
134 &RuntimeType::Array(Box::new(RuntimeType::num_any()), ArrayLen::Minimum(1)),
135 exec_state,
136 )?;
137 let (nums, ty) = NumericType::combine_eq_array(&nums);
138 if ty == NumericType::Unknown {
139 exec_state.warn(CompilationError::err(
140 args.source_range,
141 "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})`",
142 ));
143 }
144
145 let mut result = f64::MIN;
146 for num in nums {
147 if num > result {
148 result = num;
149 }
150 }
151
152 Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, ty)))
153}
154
155pub async fn pow(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
157 let input: TyF64 = args.get_unlabeled_kw_arg_typed("input", &RuntimeType::num_any(), exec_state)?;
158 let exp: TyF64 = args.get_kw_arg_typed("exp", &RuntimeType::count(), exec_state)?;
159 let result = input.n.powf(exp.n);
160
161 Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units())))
162}
163
164pub async fn acos(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
166 let input: TyF64 = args.get_unlabeled_kw_arg_typed("input", &RuntimeType::count(), exec_state)?;
167 let result = input.n.acos();
168
169 Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::radians())))
170}
171
172pub async fn asin(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
174 let input: TyF64 = args.get_unlabeled_kw_arg_typed("input", &RuntimeType::count(), exec_state)?;
175 let result = input.n.asin();
176
177 Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::radians())))
178}
179
180pub async fn atan(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
182 let input: TyF64 = args.get_unlabeled_kw_arg_typed("input", &RuntimeType::count(), exec_state)?;
183 let result = input.n.atan();
184
185 Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::radians())))
186}
187
188pub async fn atan2(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
190 let y = args.get_kw_arg_typed("y", &RuntimeType::length(), exec_state)?;
191 let x = args.get_kw_arg_typed("x", &RuntimeType::length(), exec_state)?;
192 let (y, x, _) = NumericType::combine_eq_coerce(y, x);
193 let result = y.atan2(x);
194
195 Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, NumericType::radians())))
196}
197
198pub async fn log(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
204 let input: TyF64 = args.get_unlabeled_kw_arg_typed("input", &RuntimeType::num_any(), exec_state)?;
205 let base: TyF64 = args.get_kw_arg_typed("base", &RuntimeType::count(), exec_state)?;
206 let result = input.n.log(base.n);
207
208 Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units())))
209}
210
211pub async fn log2(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
213 let input: TyF64 = args.get_unlabeled_kw_arg_typed("input", &RuntimeType::num_any(), exec_state)?;
214 let result = input.n.log2();
215
216 Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units())))
217}
218
219pub async fn log10(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
221 let input: TyF64 = args.get_unlabeled_kw_arg_typed("input", &RuntimeType::num_any(), exec_state)?;
222 let result = input.n.log10();
223
224 Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units())))
225}
226
227pub async fn ln(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
229 let input: TyF64 = args.get_unlabeled_kw_arg_typed("input", &RuntimeType::num_any(), exec_state)?;
230 let result = input.n.ln();
231
232 Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units())))
233}
234
235pub async fn leg_length(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
237 let hypotenuse: TyF64 = args.get_kw_arg_typed("hypotenuse", &RuntimeType::length(), exec_state)?;
238 let leg: TyF64 = args.get_kw_arg_typed("leg", &RuntimeType::length(), exec_state)?;
239 let (hypotenuse, leg, ty) = NumericType::combine_eq_coerce(hypotenuse, leg);
240 let result = (hypotenuse.powi(2) - f64::min(hypotenuse.abs(), leg.abs()).powi(2)).sqrt();
241 Ok(KclValue::from_number_with_type(result, ty, vec![args.into()]))
242}
243
244pub async fn leg_angle_x(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
246 let hypotenuse: TyF64 = args.get_kw_arg_typed("hypotenuse", &RuntimeType::length(), exec_state)?;
247 let leg: TyF64 = args.get_kw_arg_typed("leg", &RuntimeType::length(), exec_state)?;
248 let (hypotenuse, leg, _ty) = NumericType::combine_eq_coerce(hypotenuse, leg);
249 let result = (leg.min(hypotenuse) / hypotenuse).acos().to_degrees();
250 Ok(KclValue::from_number_with_type(
251 result,
252 NumericType::degrees(),
253 vec![args.into()],
254 ))
255}
256
257pub async fn leg_angle_y(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
259 let hypotenuse: TyF64 = args.get_kw_arg_typed("hypotenuse", &RuntimeType::length(), exec_state)?;
260 let leg: TyF64 = args.get_kw_arg_typed("leg", &RuntimeType::length(), exec_state)?;
261 let (hypotenuse, leg, _ty) = NumericType::combine_eq_coerce(hypotenuse, leg);
262 let result = (leg.min(hypotenuse) / hypotenuse).asin().to_degrees();
263 Ok(KclValue::from_number_with_type(
264 result,
265 NumericType::degrees(),
266 vec![args.into()],
267 ))
268}