hyperon/metta/runner/stdlib/
math.rs

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}