1use hyperon_atom::*;
2use crate::metta::*;
3use crate::metta::text::Tokenizer;
4use super::{grounded_op, regex};
5use hyperon_atom::gnd::number::*;
6use hyperon_atom::gnd::bool::*;
7
8use std::convert::TryInto;
9
10#[derive(Clone, Debug)]
11pub struct PowMathOp {}
12grounded_op!(PowMathOp, "pow-math");
13impl Grounded for PowMathOp {
14 fn type_(&self) -> Atom {
15 Atom::expr([ARROW_SYMBOL, ATOM_TYPE_NUMBER, ATOM_TYPE_NUMBER, ATOM_TYPE_NUMBER])
16 }
17 fn as_execute(&self) -> Option<&dyn CustomExecute> {
18 Some(self)
19 }
20}
21impl CustomExecute for PowMathOp {
22 fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
23 let arg_error = || ExecError::from("pow-math expects two arguments: number (base) and number (power)");
24 let base: f64 = args.get(0).and_then(Number::from_atom).ok_or_else(arg_error)?.into();
25 let pow = args.get(1).and_then(Number::from_atom).ok_or_else(arg_error)?;
26 let res = match pow {
27 Number::Integer(n) => {
28 match TryInto::<i32>::try_into(n) {
29 Ok(n) => base.powi(n),
30 Err(_) => return Err(ExecError::from("power argument is too big, try using float value")),
31 }
32 },
33 Number::Float(f) => base.powf(f),
34 };
35 Ok(vec![Atom::gnd(Number::Float(res))])
36 }
37}
38
39#[derive(Clone, Debug)]
40pub struct SqrtMathOp {}
41
42grounded_op!(SqrtMathOp, "sqrt-math");
43
44impl Grounded for SqrtMathOp {
45 fn type_(&self) -> Atom {
46 Atom::expr([ARROW_SYMBOL, ATOM_TYPE_NUMBER, ATOM_TYPE_NUMBER])
47 }
48
49 fn as_execute(&self) -> Option<&dyn CustomExecute> {
50 Some(self)
51 }
52}
53
54impl CustomExecute for SqrtMathOp {
55 fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
56 let arg_error = || ExecError::from("sqrt-math expects one argument: number");
57 let input: f64 = args.get(0).and_then(Number::from_atom).ok_or_else(arg_error)?.into();
58 Ok(vec![Atom::gnd(Number::Float(input.sqrt()))])
59 }
60}
61
62#[derive(Clone, Debug)]
63pub struct AbsMathOp {}
64
65grounded_op!(AbsMathOp, "abs-math");
66
67impl Grounded for AbsMathOp {
68 fn type_(&self) -> Atom {
69 Atom::expr([ARROW_SYMBOL, ATOM_TYPE_NUMBER, ATOM_TYPE_NUMBER])
70 }
71
72 fn as_execute(&self) -> Option<&dyn CustomExecute> {
73 Some(self)
74 }
75}
76
77impl CustomExecute for AbsMathOp {
78 fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
79 let arg_error = || ExecError::from("abs-math expects one argument: number");
80 let input = args.get(0).and_then(Number::from_atom).ok_or_else(arg_error)?;
81 match input {
82 Number::Integer(n) => Ok(vec![Atom::gnd(Number::Integer(n.abs()))]),
83 Number::Float(f) => Ok(vec![Atom::gnd(Number::Float(f.abs()))])
84 }
85 }
86}
87
88#[derive(Clone, Debug)]
89pub struct LogMathOp {}
90
91grounded_op!(LogMathOp, "log-math");
92
93impl Grounded for LogMathOp {
94 fn type_(&self) -> Atom {
95 Atom::expr([ARROW_SYMBOL, ATOM_TYPE_NUMBER, ATOM_TYPE_NUMBER, ATOM_TYPE_NUMBER])
96 }
97
98 fn as_execute(&self) -> Option<&dyn CustomExecute> {
99 Some(self)
100 }
101}
102
103impl CustomExecute for LogMathOp {
104 fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
105 let arg_error = || ExecError::from("log-math expects two arguments: base (number) and input value (number)");
106 let base: f64 = args.get(0).and_then(Number::from_atom).ok_or_else(arg_error)?.into();
107 let input: f64 = args.get(1).and_then(Number::from_atom).ok_or_else(arg_error)?.into();
108 Ok(vec![Atom::gnd(Number::Float(input.log(base)))])
109 }
110}
111
112#[derive(Clone, Debug)]
113pub struct TruncMathOp {}
114
115grounded_op!(TruncMathOp, "trunc-math");
116
117impl Grounded for TruncMathOp {
118 fn type_(&self) -> Atom {
119 Atom::expr([ARROW_SYMBOL, ATOM_TYPE_NUMBER, ATOM_TYPE_NUMBER])
120 }
121
122 fn as_execute(&self) -> Option<&dyn CustomExecute> {
123 Some(self)
124 }
125}
126
127impl CustomExecute for TruncMathOp {
128 fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
129 let arg_error = || ExecError::from("trunc-math expects one argument: input number");
130 let input = args.get(0).and_then(Number::from_atom).ok_or_else(arg_error)?;
131 match input {
132 Number::Integer(n) => Ok(vec![Atom::gnd(Number::Integer(n))]),
133 Number::Float(f) => Ok(vec![Atom::gnd(Number::Float(f.trunc()))])
134 }
135 }
136}
137
138#[derive(Clone, Debug)]
139pub struct CeilMathOp {}
140
141grounded_op!(CeilMathOp, "ceil-math");
142
143impl Grounded for CeilMathOp {
144 fn type_(&self) -> Atom {
145 Atom::expr([ARROW_SYMBOL, ATOM_TYPE_NUMBER, ATOM_TYPE_NUMBER])
146 }
147
148 fn as_execute(&self) -> Option<&dyn CustomExecute> {
149 Some(self)
150 }
151}
152
153impl CustomExecute for CeilMathOp {
154 fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
155 let arg_error = || ExecError::from("ceil-math expects one argument: input number");
156 let input = args.get(0).and_then(Number::from_atom).ok_or_else(arg_error)?;
157 match input {
158 Number::Integer(n) => Ok(vec![Atom::gnd(Number::Integer(n))]),
159 Number::Float(f) => Ok(vec![Atom::gnd(Number::Float(f.ceil()))])
160 }
161 }
162}
163
164#[derive(Clone, Debug)]
165pub struct FloorMathOp {}
166
167grounded_op!(FloorMathOp, "floor-math");
168
169impl Grounded for FloorMathOp {
170 fn type_(&self) -> Atom {
171 Atom::expr([ARROW_SYMBOL, ATOM_TYPE_NUMBER, ATOM_TYPE_NUMBER])
172 }
173
174 fn as_execute(&self) -> Option<&dyn CustomExecute> {
175 Some(self)
176 }
177}
178
179impl CustomExecute for FloorMathOp {
180 fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
181 let arg_error = || ExecError::from("floor-math expects one argument: input number");
182 let input = args.get(0).and_then(Number::from_atom).ok_or_else(arg_error)?;
183 match input {
184 Number::Integer(n) => Ok(vec![Atom::gnd(Number::Integer(n))]),
185 Number::Float(f) => Ok(vec![Atom::gnd(Number::Float(f.floor()))])
186 }
187 }
188}
189
190#[derive(Clone, Debug)]
191pub struct RoundMathOp {}
192
193grounded_op!(RoundMathOp, "round-math");
194
195impl Grounded for RoundMathOp {
196 fn type_(&self) -> Atom {
197 Atom::expr([ARROW_SYMBOL, ATOM_TYPE_NUMBER, ATOM_TYPE_NUMBER])
198 }
199
200 fn as_execute(&self) -> Option<&dyn CustomExecute> {
201 Some(self)
202 }
203}
204
205impl CustomExecute for RoundMathOp {
206 fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
207 let arg_error = || ExecError::from("round-math expects one argument: input number");
208 let input = args.get(0).and_then(Number::from_atom).ok_or_else(arg_error)?;
209 match input {
210 Number::Integer(n) => Ok(vec![Atom::gnd(Number::Integer(n))]),
211 Number::Float(f) => Ok(vec![Atom::gnd(Number::Float(f.round()))])
212 }
213 }
214}
215
216#[derive(Clone, Debug)]
217pub struct SinMathOp {}
218
219grounded_op!(SinMathOp, "sin-math");
220
221impl Grounded for SinMathOp {
222 fn type_(&self) -> Atom {
223 Atom::expr([ARROW_SYMBOL, ATOM_TYPE_NUMBER, ATOM_TYPE_NUMBER])
224 }
225
226 fn as_execute(&self) -> Option<&dyn CustomExecute> {
227 Some(self)
228 }
229}
230
231impl CustomExecute for SinMathOp {
232 fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
233 let arg_error = || ExecError::from("sin-math expects one argument: input number");
234 let input: f64 = args.get(0).and_then(Number::from_atom).ok_or_else(arg_error)?.into();
235 Ok(vec![Atom::gnd(Number::Float(input.sin()))])
236 }
237}
238
239#[derive(Clone, Debug)]
240pub struct AsinMathOp {}
241
242grounded_op!(AsinMathOp, "asin-math");
243
244impl Grounded for AsinMathOp {
245 fn type_(&self) -> Atom {
246 Atom::expr([ARROW_SYMBOL, ATOM_TYPE_NUMBER, ATOM_TYPE_NUMBER])
247 }
248
249 fn as_execute(&self) -> Option<&dyn CustomExecute> {
250 Some(self)
251 }
252}
253
254impl CustomExecute for AsinMathOp {
255 fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
256 let arg_error = || ExecError::from("asin-math expects one argument: input number");
257 let input: f64 = args.get(0).and_then(Number::from_atom).ok_or_else(arg_error)?.into();
258 Ok(vec![Atom::gnd(Number::Float(input.asin()))])
259 }
260}
261
262#[derive(Clone, Debug)]
263pub struct CosMathOp {}
264
265grounded_op!(CosMathOp, "cos-math");
266
267impl Grounded for CosMathOp {
268 fn type_(&self) -> Atom {
269 Atom::expr([ARROW_SYMBOL, ATOM_TYPE_NUMBER, ATOM_TYPE_NUMBER])
270 }
271
272 fn as_execute(&self) -> Option<&dyn CustomExecute> {
273 Some(self)
274 }
275}
276
277impl CustomExecute for CosMathOp {
278 fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
279 let arg_error = || ExecError::from("cos-math expects one argument: input number");
280 let input: f64 = args.get(0).and_then(Number::from_atom).ok_or_else(arg_error)?.into();
281 Ok(vec![Atom::gnd(Number::Float(input.cos()))])
282 }
283}
284
285#[derive(Clone, Debug)]
286pub struct AcosMathOp {}
287
288grounded_op!(AcosMathOp, "acos-math");
289
290impl Grounded for AcosMathOp {
291 fn type_(&self) -> Atom {
292 Atom::expr([ARROW_SYMBOL, ATOM_TYPE_NUMBER, ATOM_TYPE_NUMBER])
293 }
294
295 fn as_execute(&self) -> Option<&dyn CustomExecute> {
296 Some(self)
297 }
298}
299
300impl CustomExecute for AcosMathOp {
301 fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
302 let arg_error = || ExecError::from("acos-math expects one argument: input number");
303 let input: f64 = args.get(0).and_then(Number::from_atom).ok_or_else(arg_error)?.into();
304 Ok(vec![Atom::gnd(Number::Float(input.acos()))])
305 }
306}
307
308#[derive(Clone, Debug)]
309pub struct TanMathOp {}
310
311grounded_op!(TanMathOp, "tan-math");
312
313impl Grounded for TanMathOp {
314 fn type_(&self) -> Atom {
315 Atom::expr([ARROW_SYMBOL, ATOM_TYPE_NUMBER, ATOM_TYPE_NUMBER])
316 }
317
318 fn as_execute(&self) -> Option<&dyn CustomExecute> {
319 Some(self)
320 }
321}
322
323impl CustomExecute for TanMathOp {
324 fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
325 let arg_error = || ExecError::from("tan-math expects one argument: input number");
326 let input: f64 = args.get(0).and_then(Number::from_atom).ok_or_else(arg_error)?.into();
327 Ok(vec![Atom::gnd(Number::Float(input.tan()))])
328 }
329}
330
331#[derive(Clone, Debug)]
332pub struct AtanMathOp {}
333
334grounded_op!(AtanMathOp, "atan-math");
335
336impl Grounded for AtanMathOp {
337 fn type_(&self) -> Atom {
338 Atom::expr([ARROW_SYMBOL, ATOM_TYPE_NUMBER, ATOM_TYPE_NUMBER])
339 }
340
341 fn as_execute(&self) -> Option<&dyn CustomExecute> {
342 Some(self)
343 }
344}
345
346impl CustomExecute for AtanMathOp {
347 fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
348 let arg_error = || ExecError::from("atan-math expects one argument: input number");
349 let input: f64 = args.get(0).and_then(Number::from_atom).ok_or_else(arg_error)?.into();
350 Ok(vec![Atom::gnd(Number::Float(input.atan()))])
351 }
352}
353
354#[derive(Clone, Debug)]
355pub struct IsNanMathOp {}
356
357grounded_op!(IsNanMathOp, "isnan-math");
358
359impl Grounded for IsNanMathOp {
360 fn type_(&self) -> Atom {
361 Atom::expr([ARROW_SYMBOL, ATOM_TYPE_NUMBER, ATOM_TYPE_BOOL])
362 }
363
364 fn as_execute(&self) -> Option<&dyn CustomExecute> {
365 Some(self)
366 }
367}
368
369impl CustomExecute for IsNanMathOp {
370 fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
371 let arg_error = || ExecError::from("isnan-math expects one argument: input number");
372 let input = args.get(0).and_then(Number::from_atom).ok_or_else(arg_error)?;
373 let res = match input {
374 Number::Integer(_) => false,
375 Number::Float(f) => f.is_nan(),
376 };
377 Ok(vec![Atom::gnd(Bool(res))])
378 }
379}
380
381#[derive(Clone, Debug)]
382pub struct IsInfMathOp {}
383
384grounded_op!(IsInfMathOp, "isinf-math");
385
386impl Grounded for IsInfMathOp {
387 fn type_(&self) -> Atom {
388 Atom::expr([ARROW_SYMBOL, ATOM_TYPE_NUMBER, ATOM_TYPE_BOOL])
389 }
390
391 fn as_execute(&self) -> Option<&dyn CustomExecute> {
392 Some(self)
393 }
394}
395
396impl CustomExecute for IsInfMathOp {
397 fn execute(&self, args: &[Atom]) -> Result<Vec<Atom>, ExecError> {
398 let arg_error = || ExecError::from("isinf-math expects one argument: input number");
399 let input = args.get(0).and_then(Number::from_atom).ok_or_else(arg_error)?;
400 let res = match input {
401 Number::Integer(_) => false,
402 Number::Float(f) => f.is_infinite(),
403 };
404 Ok(vec![Atom::gnd(Bool(res))])
405 }
406}
407
408pub(super) fn register_context_independent_tokens(tref: &mut Tokenizer) {
409 let pow_math_op = Atom::gnd(PowMathOp {});
410 tref.register_token(regex(r"pow-math"), move |_| { pow_math_op.clone() });
411 let sqrt_math_op = Atom::gnd(SqrtMathOp {});
412 tref.register_token(regex(r"sqrt-math"), move |_| { sqrt_math_op.clone() });
413 let abs_math_op = Atom::gnd(AbsMathOp {});
414 tref.register_token(regex(r"abs-math"), move |_| { abs_math_op.clone() });
415 let log_math_op = Atom::gnd(LogMathOp {});
416 tref.register_token(regex(r"log-math"), move |_| { log_math_op.clone() });
417 let trunc_math_op = Atom::gnd(TruncMathOp {});
418 tref.register_token(regex(r"trunc-math"), move |_| { trunc_math_op.clone() });
419 let ceil_math_op = Atom::gnd(CeilMathOp {});
420 tref.register_token(regex(r"ceil-math"), move |_| { ceil_math_op.clone() });
421 let floor_math_op = Atom::gnd(FloorMathOp{});
422 tref.register_token(regex(r"floor-math"), move |_| { floor_math_op.clone() });
423 let round_math_op = Atom::gnd(RoundMathOp{});
424 tref.register_token(regex(r"round-math"), move |_| { round_math_op.clone() });
425 let sin_math_op = Atom::gnd(SinMathOp{});
426 tref.register_token(regex(r"sin-math"), move |_| { sin_math_op.clone() });
427 let asin_math_op = Atom::gnd(AsinMathOp{});
428 tref.register_token(regex(r"asin-math"), move |_| { asin_math_op.clone() });
429 let cos_math_op = Atom::gnd(CosMathOp{});
430 tref.register_token(regex(r"cos-math"), move |_| { cos_math_op.clone() });
431 let acos_math_op = Atom::gnd(AcosMathOp{});
432 tref.register_token(regex(r"acos-math"), move |_| { acos_math_op.clone() });
433 let tan_math_op = Atom::gnd(TanMathOp{});
434 tref.register_token(regex(r"tan-math"), move |_| { tan_math_op.clone() });
435 let atan_math_op = Atom::gnd(AtanMathOp{});
436 tref.register_token(regex(r"atan-math"), move |_| { atan_math_op.clone() });
437 let isnan_math_op = Atom::gnd(IsNanMathOp{});
438 tref.register_token(regex(r"isnan-math"), move |_| { isnan_math_op.clone() });
439 let isinf_math_op = Atom::gnd(IsInfMathOp{});
440 tref.register_token(regex(r"isinf-math"), move |_| { isinf_math_op.clone() });
441 tref.register_token(regex(r"PI"),
442 |_| { Atom::gnd(Number::Float(std::f64::consts::PI)) });
443 tref.register_token(regex(r"EXP"),
444 |_| { Atom::gnd(Number::Float(std::f64::consts::E)) });
445}
446
447#[cfg(test)]
448mod tests {
449 use super::*;
450 use crate::metta::runner::run_program;
451
452 #[test]
453 fn metta_pow_math() {
454 assert_eq!(run_program(&format!("!(pow-math 5 2)")), Ok(vec![vec![expr!({Number::Integer(5_i64.pow(2))})]]));
455 assert_eq!(run_program(&format!("!(pow-math 5 200000000000000)")), Ok(vec![vec![expr!("Error" ({ PowMathOp{} } {Number::Integer(5)} {Number::Integer(200000000000000)}) "power argument is too big, try using float value")]]));
456 assert_eq!(run_program(&format!("!(pow-math 5.5 2.3)")), Ok(vec![vec![expr!({Number::Float(5.5_f64.powf(2.3))})]]));
457 assert_eq!(run_program(&format!("!(pow-math A 2)")), Ok(vec![vec![expr!("Error" ({ PowMathOp{} } "A" {Number::Integer(2)}) "pow-math expects two arguments: number (base) and number (power)")]]));
458 }
459
460 #[test]
461 fn metta_sqrt_math() {
462 assert_eq!(run_program(&format!("!(sqrt-math 4)")), Ok(vec![vec![expr!({Number::Integer(2)})]]));
463 assert_eq!(run_program(&format!("!(let $sqrt (sqrt-math -4) (isnan-math $sqrt))")), Ok(vec![vec![expr!({Bool(true)})]]));
464 assert_eq!(run_program(&format!("!(sqrt-math A)")), Ok(vec![vec![expr!("Error" ({ SqrtMathOp{} } "A") "sqrt-math expects one argument: number")]]));
465 }
466
467 #[test]
468 fn metta_abs_math() {
469 assert_eq!(run_program(&format!("!(abs-math 4)")), Ok(vec![vec![expr!({Number::Integer(4)})]]));
470 assert_eq!(run_program(&format!("!(abs-math -5)")), Ok(vec![vec![expr!({Number::Integer(5)})]]));
471 assert_eq!(run_program(&format!("!(abs-math A)")), Ok(vec![vec![expr!("Error" ({ AbsMathOp{} } "A") "abs-math expects one argument: number")]]));
472 }
473
474 #[test]
475 fn metta_log_math() {
476 assert_eq!(run_program(&format!("!(log-math 2 4)")), Ok(vec![vec![expr!({Number::Integer(2)})]]));
477 assert_eq!(run_program(&format!("!(let $log (log-math 0 0) (isnan-math $log))")), Ok(vec![vec![expr!({Bool(true)})]]));
478 assert_eq!(run_program(&format!("!(let $log (log-math 5 0) (isinf-math $log))")), Ok(vec![vec![expr!({Bool(true)})]]));
479 }
480
481 #[test]
482 fn metta_trunc_math() {
483 assert_eq!(run_program(&format!("!(trunc-math 2.4)")), Ok(vec![vec![expr!({Number::Integer(2)})]]));
484 assert_eq!(run_program(&format!("!(trunc-math A)")), Ok(vec![vec![expr!("Error" ({ TruncMathOp{} } "A") "trunc-math expects one argument: input number")]]));
485 }
486
487 #[test]
488 fn metta_ceil_math() {
489 assert_eq!(run_program(&format!("!(ceil-math 2.4)")), Ok(vec![vec![expr!({Number::Integer(3)})]]));
490 assert_eq!(run_program(&format!("!(ceil-math -2.4)")), Ok(vec![vec![expr!({Number::Integer(-2)})]]));
491 assert_eq!(run_program(&format!("!(ceil-math A)")), Ok(vec![vec![expr!("Error" ({ CeilMathOp{} } "A") "ceil-math expects one argument: input number")]]));
492 }
493
494 #[test]
495 fn metta_floor_math() {
496 assert_eq!(run_program(&format!("!(floor-math 2.4)")), Ok(vec![vec![expr!({Number::Integer(2)})]]));
497 assert_eq!(run_program(&format!("!(floor-math -2.4)")), Ok(vec![vec![expr!({Number::Integer(-3)})]]));
498 assert_eq!(run_program(&format!("!(floor-math A)")), Ok(vec![vec![expr!("Error" ({ FloorMathOp{} } "A") "floor-math expects one argument: input number")]]));
499 }
500
501 #[test]
502 fn metta_round_math() {
503 assert_eq!(run_program(&format!("!(round-math 2.4)")), Ok(vec![vec![expr!({Number::Integer(2)})]]));
504 assert_eq!(run_program(&format!("!(round-math -2.7)")), Ok(vec![vec![expr!({Number::Integer(-3)})]]));
505 assert_eq!(run_program(&format!("!(round-math A)")), Ok(vec![vec![expr!("Error" ({ RoundMathOp{} } "A") "round-math expects one argument: input number")]]));
506 }
507
508 #[test]
509 fn metta_sin_math() {
510 assert_eq!(run_program(&format!("!(sin-math 0)")), Ok(vec![vec![expr!({Number::Integer(0)})]]));
511 assert_eq!(run_program(&format!("!(let $sin (sin-math 1.570796327) (< (- $sin 1.0) 1e-10))")), Ok(vec![vec![expr!({Bool(true)})]]));
512 assert_eq!(run_program(&format!("!(sin-math A)")), Ok(vec![vec![expr!("Error" ({ SinMathOp{} } "A") "sin-math expects one argument: input number")]]));
513 }
514
515 #[test]
516 fn metta_asin_math() {
517 assert_eq!(run_program(&format!("!(asin-math 0)")), Ok(vec![vec![expr!({Number::Integer(0)})]]));
518 assert_eq!(run_program(&format!("!(let $sin (sin-math 1) (< (- (asin-math $sin) 1.0) 1e-10))")), Ok(vec![vec![expr!({Bool(true)})]]));
519 assert_eq!(run_program(&format!("!(asin-math A)")), Ok(vec![vec![expr!("Error" ({ AsinMathOp{} } "A") "asin-math expects one argument: input number")]]));
520 }
521
522 #[test]
523 fn metta_cos_math() {
524 assert_eq!(run_program(&format!("!(cos-math 0)")), Ok(vec![vec![expr!({Number::Integer(1)})]]));
525 assert_eq!(run_program(&format!("!(let $cos (cos-math 1.570796327) (< (- $cos 0.0) 1e-10))")), Ok(vec![vec![expr!({Bool(true)})]]));
526 assert_eq!(run_program(&format!("!(cos-math A)")), Ok(vec![vec![expr!("Error" ({ CosMathOp{} } "A") "cos-math expects one argument: input number")]]));
527 }
528
529 #[test]
530 fn metta_acos_math() {
531 assert_eq!(run_program(&format!("!(acos-math 1)")), Ok(vec![vec![expr!({Number::Integer(0)})]]));
532 assert_eq!(run_program(&format!("!(let $cos (cos-math 1) (< (- (acos-math $cos) 1.0) 1e-10))")), Ok(vec![vec![expr!({Bool(true)})]]));
533 assert_eq!(run_program(&format!("!(acos-math A)")), Ok(vec![vec![expr!("Error" ({ AcosMathOp{} } "A") "acos-math expects one argument: input number")]]));
534 }
535
536 #[test]
537 fn metta_tan_math() {
538 assert_eq!(run_program(&format!("!(tan-math 0)")), Ok(vec![vec![expr!({Number::Integer(0)})]]));
539 assert_eq!(run_program(&format!("!(let $tan (tan-math 0.78539816339) (< (- $tan 1.0) 1e-10))")), Ok(vec![vec![expr!({Bool(true)})]]));
540 assert_eq!(run_program(&format!("!(tan-math A)")), Ok(vec![vec![expr!("Error" ({ TanMathOp{} } "A") "tan-math expects one argument: input number")]]));
541 }
542
543 #[test]
544 fn metta_atan_math() {
545 assert_eq!(run_program(&format!("!(atan-math 0)")), Ok(vec![vec![expr!({Number::Integer(0)})]]));
546 assert_eq!(run_program(&format!("!(let $atan (atan-math 1) (< (- $atan 0.78539816339) 1e-10))")), Ok(vec![vec![expr!({Bool(true)})]]));
547 assert_eq!(run_program(&format!("!(atan-math A)")), Ok(vec![vec![expr!("Error" ({ AtanMathOp{} } "A") "atan-math expects one argument: input number")]]));
548 }
549
550 #[test]
551 fn metta_isnan_math() {
552 assert_eq!(run_program(&format!("!(isnan-math 0)")), Ok(vec![vec![expr!({Bool(false)})]]));
553 assert_eq!(run_program(&format!("!(let $log (log-math 0 0) (isnan-math $log))")), Ok(vec![vec![expr!({Bool(true)})]]));
554 assert_eq!(run_program(&format!("!(isnan-math A)")), Ok(vec![vec![expr!("Error" ({ IsNanMathOp{} } "A") "isnan-math expects one argument: input number")]]));
555 }
556
557 #[test]
558 fn metta_isinf_math() {
559 assert_eq!(run_program(&format!("!(isinf-math 0)")), Ok(vec![vec![expr!({Bool(false)})]]));
560 assert_eq!(run_program(&format!("!(let $log (log-math 5 0) (isinf-math $log))")), Ok(vec![vec![expr!({Bool(true)})]]));
561 assert_eq!(run_program(&format!("!(isinf-math A)")), Ok(vec![vec![expr!("Error" ({ IsInfMathOp{} } "A") "isinf-math expects one argument: input number")]]));
562 }
563
564 #[test]
565 fn pow_math_op() {
566 let res = PowMathOp {}.execute(&mut vec![expr!({Number::Integer(5)}), expr!({Number::Integer(2)})]).expect("No result returned");
567 assert_eq!(res, vec![expr!({Number::Integer(5_i64.pow(2))})]);
568 let res = PowMathOp {}.execute(&mut vec![expr!({Number::Integer(2)}), expr!({Number::Integer(200000000000000)})]);
569 assert_eq!(res, Err(ExecError::from("power argument is too big, try using float value")));
570 let res = PowMathOp {}.execute(&mut vec![expr!({Number::Float(5.5)}), expr!({Number::Float(2.3)})]).expect("No result returned");
571 assert_eq!(res, vec![expr!({Number::Float(5.5_f64.powf(2.3))})]);
572 let res = PowMathOp {}.execute(&mut vec![expr!("A"), expr!({Number::Integer(2)})]);
573 assert_eq!(res, Err(ExecError::from("pow-math expects two arguments: number (base) and number (power)")));
574 }
575
576 #[test]
577 fn sqrt_math_op() {
578 let res = SqrtMathOp {}.execute(&mut vec![expr!({Number::Integer(4)})]).expect("No result returned");
579 assert_eq!(res, vec![expr!({Number::Integer(2)})]);
580 let res = SqrtMathOp {}.execute(&mut vec![expr!({Number::Integer(-4)})]);
581 let res_f64: f64 = res.unwrap().get(0).and_then(Number::from_atom).unwrap().into();
582 assert!(res_f64.is_nan());
583 let res = SqrtMathOp {}.execute(&mut vec![expr!("A")]);
584 assert_eq!(res, Err(ExecError::from("sqrt-math expects one argument: number")));
585 }
586
587 #[test]
588 fn abs_math_op() {
589 let res = AbsMathOp {}.execute(&mut vec![expr!({Number::Integer(4)})]).expect("No result returned");
590 assert_eq!(res, vec![expr!({Number::Integer(4)})]);
591 let res = AbsMathOp {}.execute(&mut vec![expr!({Number::Integer(-4)})]).expect("No result returned");
592 assert_eq!(res, vec![expr!({Number::Integer(4)})]);
593 let res = AbsMathOp {}.execute(&mut vec![expr!("A")]);
594 assert_eq!(res, Err(ExecError::from("abs-math expects one argument: number")));
595 }
596
597 #[test]
598 fn log_math_op() {
599 let res = LogMathOp {}.execute(&mut vec![expr!({Number::Integer(2)}), expr!({Number::Integer(4)})]).expect("No result returned");
600 assert_eq!(res, vec![expr!({Number::Integer(2)})]);
601 let res = LogMathOp {}.execute(&mut vec![expr!({Number::Integer(0)}), expr!({Number::Integer(0)})]);
602 let res_f64: f64 = res.unwrap().get(0).and_then(Number::from_atom).unwrap().into();
603 assert!(res_f64.is_nan());
604 let res = LogMathOp {}.execute(&mut vec![expr!({Number::Integer(5)}), expr!({Number::Integer(0)})]);
605 let res_f64: f64 = res.unwrap().get(0).and_then(Number::from_atom).unwrap().into();
606 assert!(res_f64.is_infinite());
607 let res = LogMathOp {}.execute(&mut vec![expr!({Number::Integer(2)}), expr!("A")]);
608 assert_eq!(res, Err(ExecError::from("log-math expects two arguments: base (number) and input value (number)")));
609 }
610
611 #[test]
612 fn trunc_math_op() {
613 let res = TruncMathOp {}.execute(&mut vec![expr!({Number::Float(2.4)})]).expect("No result returned");
614 assert_eq!(res, vec![expr!({Number::Integer(2)})]);
615 let res = TruncMathOp {}.execute(&mut vec![expr!("A")]);
616 assert_eq!(res, Err(ExecError::from("trunc-math expects one argument: input number")));
617 }
618
619 #[test]
620 fn ceil_math_op() {
621 let res = CeilMathOp {}.execute(&mut vec![expr!({Number::Float(2.4)})]).expect("No result returned");
622 assert_eq!(res, vec![expr!({Number::Integer(3)})]);
623 let res = CeilMathOp {}.execute(&mut vec![expr!({Number::Float(-2.4)})]).expect("No result returned");
624 assert_eq!(res, vec![expr!({Number::Integer(-2)})]);
625 let res = CeilMathOp {}.execute(&mut vec![expr!("A")]);
626 assert_eq!(res, Err(ExecError::from("ceil-math expects one argument: input number")));
627 }
628
629 #[test]
630 fn floor_math_op() {
631 let res = FloorMathOp {}.execute(&mut vec![expr!({Number::Float(2.4)})]).expect("No result returned");
632 assert_eq!(res, vec![expr!({Number::Integer(2)})]);
633 let res = FloorMathOp {}.execute(&mut vec![expr!({Number::Float(-2.4)})]).expect("No result returned");
634 assert_eq!(res, vec![expr!({Number::Integer(-3)})]);
635 let res = FloorMathOp {}.execute(&mut vec![expr!("A")]);
636 assert_eq!(res, Err(ExecError::from("floor-math expects one argument: input number")));
637 }
638
639 #[test]
640 fn round_math_op() {
641 let res = RoundMathOp {}.execute(&mut vec![expr!({Number::Float(2.4)})]).expect("No result returned");
642 assert_eq!(res, vec![expr!({Number::Integer(2)})]);
643 let res = RoundMathOp {}.execute(&mut vec![expr!({Number::Float(-2.7)})]).expect("No result returned");
644 assert_eq!(res, vec![expr!({Number::Integer(-3)})]);
645 let res = RoundMathOp {}.execute(&mut vec![expr!("A")]);
646 assert_eq!(res, Err(ExecError::from("round-math expects one argument: input number")));
647 }
648
649 #[test]
650 fn sin_math_op() {
651 let res = SinMathOp {}.execute(&mut vec![expr!({Number::Integer(0)})]).expect("No result returned");
652 assert_eq!(res, vec![expr!({Number::Integer(0)})]);
653 let res = SinMathOp {}.execute(&mut vec![expr!({Number::Float(std::f64::consts::FRAC_PI_2)})]);
654 let res_f64: f64 = res.unwrap().get(0).and_then(Number::from_atom).unwrap().into();
655 let abs_difference = (res_f64 - 1.0).abs();
656 assert!(abs_difference < 1e-10);
657 let res = SinMathOp {}.execute(&mut vec![expr!("A")]);
658 assert_eq!(res, Err(ExecError::from("sin-math expects one argument: input number")));
659 }
660
661 #[test]
662 fn asin_math_op() {
663 let res = AsinMathOp {}.execute(&mut vec![expr!({Number::Integer(0)})]).expect("No result returned");
664 assert_eq!(res, vec![expr!({Number::Integer(0)})]);
665 let res = AsinMathOp {}.execute(&mut vec![expr!({Number::Float(1.0)})]);
666 let res_f64: f64 = res.unwrap().get(0).and_then(Number::from_atom).unwrap().into();
667 let abs_difference = (res_f64 - std::f64::consts::FRAC_PI_2).abs();
668 assert!(abs_difference < 1e-10);
669 let res = AsinMathOp {}.execute(&mut vec![expr!("A")]);
670 assert_eq!(res, Err(ExecError::from("asin-math expects one argument: input number")));
671 }
672
673 #[test]
674 fn cos_math_op() {
675 let res = CosMathOp {}.execute(&mut vec![expr!({Number::Integer(0)})]).expect("No result returned");
676 assert_eq!(res, vec![expr!({Number::Integer(1)})]);
677 let res = CosMathOp {}.execute(&mut vec![expr!({Number::Float(std::f64::consts::FRAC_PI_2)})]);
678 let res_f64: f64 = res.unwrap().get(0).and_then(Number::from_atom).unwrap().into();
679 let abs_difference = (res_f64 - 0.0).abs();
680 assert!(abs_difference < 1e-10);
681 let res = CosMathOp {}.execute(&mut vec![expr!("A")]);
682 assert_eq!(res, Err(ExecError::from("cos-math expects one argument: input number")));
683 }
684
685 #[test]
686 fn acos_math_op() {
687 let res = AcosMathOp {}.execute(&mut vec![expr!({Number::Integer(1)})]).expect("No result returned");
688 assert_eq!(res, vec![expr!({Number::Integer(0)})]);
689 let res = AcosMathOp {}.execute(&mut vec![expr!({Number::Integer(0)})]);
690 let res_f64: f64 = res.unwrap().get(0).and_then(Number::from_atom).unwrap().into();
691 let abs_difference = (res_f64 - std::f64::consts::FRAC_PI_2).abs();
692 assert!(abs_difference < 1e-10);
693 let res = AcosMathOp {}.execute(&mut vec![expr!("A")]);
694 assert_eq!(res, Err(ExecError::from("acos-math expects one argument: input number")));
695 }
696
697 #[test]
698 fn tan_math_op() {
699 let res = TanMathOp {}.execute(&mut vec![expr!({Number::Integer(0)})]).expect("No result returned");
700 assert_eq!(res, vec![expr!({Number::Integer(0)})]);
701 let res = TanMathOp {}.execute(&mut vec![expr!({Number::Float(std::f64::consts::FRAC_PI_4)})]);
702 let res_f64: f64 = res.unwrap().get(0).and_then(Number::from_atom).unwrap().into();
703 let abs_difference = (res_f64 - 1.0).abs();
704 assert!(abs_difference < 1e-10);
705 let res = TanMathOp {}.execute(&mut vec![expr!("A")]);
706 assert_eq!(res, Err(ExecError::from("tan-math expects one argument: input number")));
707 }
708
709 #[test]
710 fn atan_math_op() {
711 let res = AtanMathOp {}.execute(&mut vec![expr!({Number::Integer(0)})]).expect("No result returned");
712 assert_eq!(res, vec![expr!({Number::Integer(0)})]);
713 let res = AtanMathOp {}.execute(&mut vec![expr!({Number::Integer(1)})]);
714 let res_f64: f64 = res.unwrap().get(0).and_then(Number::from_atom).unwrap().into();
715 let abs_difference = (res_f64 - std::f64::consts::FRAC_PI_4).abs();
716 assert!(abs_difference < 1e-10);
717 let res = AtanMathOp {}.execute(&mut vec![expr!("A")]);
718 assert_eq!(res, Err(ExecError::from("atan-math expects one argument: input number")));
719 }
720
721 #[test]
722 fn isnan_math_op() {
723 let res = IsNanMathOp {}.execute(&mut vec![expr!({Number::Integer(0)})]).expect("No result returned");
724 assert_eq!(res, vec![expr!({Bool(false)})]);
725 let res = IsNanMathOp {}.execute(&mut vec![expr!({Number::Float(f64::NAN)})]).expect("No result returned");
726 assert_eq!(res, vec![expr!({Bool(true)})]);
727 let res = IsNanMathOp {}.execute(&mut vec![expr!("A")]);
728 assert_eq!(res, Err(ExecError::from("isnan-math expects one argument: input number")));
729 }
730
731 #[test]
732 fn isinf_math_op() {
733 let res = IsInfMathOp {}.execute(&mut vec![expr!({Number::Integer(0)})]).expect("No result returned");
734 assert_eq!(res, vec![expr!({Bool(false)})]);
735 let res = IsInfMathOp {}.execute(&mut vec![expr!({Number::Float(f64::INFINITY)})]).expect("No result returned");
736 assert_eq!(res, vec![expr!({Bool(true)})]);
737 let res = IsInfMathOp {}.execute(&mut vec![expr!("A")]);
738 assert_eq!(res, Err(ExecError::from("isinf-math expects one argument: input number")));
739 }
740}