1use crate::error::StdlibError;
19use crate::module::StdlibModule;
20use crate::value::Value;
21
22pub struct MathModule;
24
25impl MathModule {
26 pub fn new() -> Self {
27 Self
28 }
29}
30
31impl Default for MathModule {
32 fn default() -> Self {
33 Self::new()
34 }
35}
36
37impl StdlibModule for MathModule {
38 fn name(&self) -> &'static str {
39 "math"
40 }
41
42 fn has_function(&self, function: &str) -> bool {
43 matches!(
44 function,
45 "abs"
46 | "min"
47 | "max"
48 | "floor"
49 | "ceil"
50 | "round"
51 | "round_to"
52 | "pow"
53 | "clamp"
54 | "sqrt"
55 | "PI"
56 | "E"
57 )
58 }
59
60 fn call(&self, function: &str, args: Vec<Value>) -> Result<Value, StdlibError> {
61 match function {
62 "abs" => self.abs(args),
63 "min" => self.min(args),
64 "max" => self.max(args),
65 "floor" => self.floor(args),
66 "ceil" => self.ceil(args),
67 "round" => self.round(args),
68 "round_to" => self.round_to(args),
69 "pow" => self.pow(args),
70 "clamp" => self.clamp(args),
71 "sqrt" => self.sqrt(args),
72 "PI" => self.pi(args),
74 "E" => self.e(args),
75 _ => Err(StdlibError::unknown_function("math", function)),
76 }
77 }
78}
79
80fn expect_one_number(fn_name: &str, args: &[Value]) -> Result<f64, StdlibError> {
84 if args.len() != 1 {
85 return Err(StdlibError::wrong_args(fn_name, 1, args.len()));
86 }
87 match &args[0] {
88 Value::Number(n) => Ok(*n),
89 other => Err(StdlibError::type_mismatch(
90 fn_name,
91 1,
92 "number",
93 other.type_name(),
94 )),
95 }
96}
97
98fn expect_two_numbers(fn_name: &str, args: &[Value]) -> Result<(f64, f64), StdlibError> {
100 if args.len() != 2 {
101 return Err(StdlibError::wrong_args(fn_name, 2, args.len()));
102 }
103 let a = match &args[0] {
104 Value::Number(n) => *n,
105 other => {
106 return Err(StdlibError::type_mismatch(
107 fn_name,
108 1,
109 "number",
110 other.type_name(),
111 ));
112 }
113 };
114 let b = match &args[1] {
115 Value::Number(n) => *n,
116 other => {
117 return Err(StdlibError::type_mismatch(
118 fn_name,
119 2,
120 "number",
121 other.type_name(),
122 ));
123 }
124 };
125 Ok((a, b))
126}
127
128fn nan_guard(fn_name: &str, result: f64) -> Result<Value, StdlibError> {
131 if result.is_nan() {
132 Err(StdlibError::RuntimeError(format!(
133 "{fn_name}: operation would produce NaN"
134 )))
135 } else if result.is_infinite() {
136 Err(StdlibError::RuntimeError(format!(
137 "{fn_name}: operation would produce infinity"
138 )))
139 } else {
140 Ok(Value::Number(result))
141 }
142}
143
144impl MathModule {
147 fn abs(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
151 let a = expect_one_number("math.abs", &args)?;
152 Ok(Value::Number(a.abs()))
153 }
154
155 fn min(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
159 let (a, b) = expect_two_numbers("math.min", &args)?;
160 Ok(Value::Number(a.min(b)))
162 }
163
164 fn max(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
168 let (a, b) = expect_two_numbers("math.max", &args)?;
169 Ok(Value::Number(a.max(b)))
170 }
171
172 fn floor(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
176 let a = expect_one_number("math.floor", &args)?;
177 Ok(Value::Number(a.floor()))
178 }
179
180 fn ceil(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
184 let a = expect_one_number("math.ceil", &args)?;
185 Ok(Value::Number(a.ceil()))
186 }
187
188 fn round(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
194 let a = expect_one_number("math.round", &args)?;
195 Ok(Value::Number((a + 0.5).floor()))
200 }
201
202 fn round_to(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
206 let (a, decimals) = expect_two_numbers("math.round_to", &args)?;
207
208 if decimals < 0.0 || decimals.fract() != 0.0 {
210 return Err(StdlibError::RuntimeError(
211 "math.round_to: decimals must be a non-negative integer".to_string(),
212 ));
213 }
214
215 let factor = 10_f64.powi(decimals as i32);
216 let scaled = a * factor;
217 let rounded = (scaled + 0.5).floor();
218 let result = rounded / factor;
219
220 nan_guard("math.round_to", result)
221 }
222
223 fn pow(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
227 let (base, exp) = expect_two_numbers("math.pow", &args)?;
228 let result = base.powf(exp);
229 nan_guard("math.pow", result)
230 }
231
232 fn clamp(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
236 if args.len() != 3 {
237 return Err(StdlibError::wrong_args("math.clamp", 3, args.len()));
238 }
239 let value = match &args[0] {
240 Value::Number(n) => *n,
241 other => {
242 return Err(StdlibError::type_mismatch(
243 "math.clamp",
244 1,
245 "number",
246 other.type_name(),
247 ));
248 }
249 };
250 let min = match &args[1] {
251 Value::Number(n) => *n,
252 other => {
253 return Err(StdlibError::type_mismatch(
254 "math.clamp",
255 2,
256 "number",
257 other.type_name(),
258 ));
259 }
260 };
261 let max = match &args[2] {
262 Value::Number(n) => *n,
263 other => {
264 return Err(StdlibError::type_mismatch(
265 "math.clamp",
266 3,
267 "number",
268 other.type_name(),
269 ));
270 }
271 };
272
273 if min > max {
274 return Err(StdlibError::RuntimeError(
275 "math.clamp: min must be <= max".to_string(),
276 ));
277 }
278
279 Ok(Value::Number(value.clamp(min, max)))
280 }
281
282 fn sqrt(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
286 let a = expect_one_number("math.sqrt", &args)?;
287 if a < 0.0 {
288 return Err(StdlibError::RuntimeError(
289 "math.sqrt: cannot take square root of negative number".to_string(),
290 ));
291 }
292 Ok(Value::Number(a.sqrt()))
293 }
294
295 fn pi(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
297 if !args.is_empty() {
298 return Err(StdlibError::wrong_args("math.PI", 0, args.len()));
299 }
300 Ok(Value::Number(std::f64::consts::PI))
301 }
302
303 fn e(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
305 if !args.is_empty() {
306 return Err(StdlibError::wrong_args("math.E", 0, args.len()));
307 }
308 Ok(Value::Number(std::f64::consts::E))
309 }
310}