1use super::JsValue;
4use super::coerce::to_number;
5
6pub fn call(method: &str, args: &[JsValue]) -> Option<JsValue> {
8 let r = match method {
9 "abs" => to_number(arg(args, 0)?).abs(),
11 "ceil" => to_number(arg(args, 0)?).ceil(),
12 "floor" => to_number(arg(args, 0)?).floor(),
13 "round" => js_round(to_number(arg(args, 0)?)),
14 "trunc" => to_number(arg(args, 0)?).trunc(),
15 "sign" => js_sign(to_number(arg(args, 0)?)),
16 "sqrt" => to_number(arg(args, 0)?).sqrt(),
17 "cbrt" => to_number(arg(args, 0)?).cbrt(),
18 "log" => to_number(arg(args, 0)?).ln(),
19 "log2" => to_number(arg(args, 0)?).log2(),
20 "log10" => to_number(arg(args, 0)?).log10(),
21 "log1p" => to_number(arg(args, 0)?).ln_1p(),
22 "exp" => to_number(arg(args, 0)?).exp(),
23 "expm1" => to_number(arg(args, 0)?).exp_m1(),
24 "sin" => to_number(arg(args, 0)?).sin(),
25 "cos" => to_number(arg(args, 0)?).cos(),
26 "tan" => to_number(arg(args, 0)?).tan(),
27 "asin" => to_number(arg(args, 0)?).asin(),
28 "acos" => to_number(arg(args, 0)?).acos(),
29 "atan" => to_number(arg(args, 0)?).atan(),
30 "sinh" => to_number(arg(args, 0)?).sinh(),
31 "cosh" => to_number(arg(args, 0)?).cosh(),
32 "tanh" => to_number(arg(args, 0)?).tanh(),
33 "asinh" => to_number(arg(args, 0)?).asinh(),
34 "acosh" => to_number(arg(args, 0)?).acosh(),
35 "atanh" => to_number(arg(args, 0)?).atanh(),
36 "fround" => (to_number(arg(args, 0)?) as f32) as f64,
37 "clz32" => f64::from(super::coerce::to_uint32(arg(args, 0)?).leading_zeros()),
38
39 "pow" => to_number(arg(args, 0)?).powf(to_number(arg(args, 1)?)),
41 "atan2" => to_number(arg(args, 0)?).atan2(to_number(arg(args, 1)?)),
42 "hypot" => {
43 let vals: Vec<f64> = args.iter().map(to_number).collect();
44 return Some(JsValue::Number(vals.iter().map(|v| v * v).sum::<f64>().sqrt()));
45 }
46 "imul" => {
47 let a = super::coerce::to_int32(arg(args, 0)?);
48 let b = super::coerce::to_int32(arg(args, 1)?);
49 return Some(JsValue::Number(f64::from(a.wrapping_mul(b))));
50 }
51
52 "max" => {
54 if args.is_empty() { return Some(JsValue::Number(f64::NEG_INFINITY)); }
55 let mut m = f64::NEG_INFINITY;
56 for a in args {
57 let n = to_number(a);
58 if n.is_nan() { return Some(JsValue::Number(f64::NAN)); }
59 if n > m { m = n; }
60 }
61 return Some(JsValue::Number(m));
62 }
63 "min" => {
64 if args.is_empty() { return Some(JsValue::Number(f64::INFINITY)); }
65 let mut m = f64::INFINITY;
66 for a in args {
67 let n = to_number(a);
68 if n.is_nan() { return Some(JsValue::Number(f64::NAN)); }
69 if n < m { m = n; }
70 }
71 return Some(JsValue::Number(m));
72 }
73
74 _ => return None,
75 };
76 Some(JsValue::Number(r))
77}
78
79pub fn constant(name: &str) -> Option<JsValue> {
81 let v = match name {
82 "PI" => std::f64::consts::PI,
83 "E" => std::f64::consts::E,
84 "LN2" => std::f64::consts::LN_2,
85 "LN10" => std::f64::consts::LN_10,
86 "LOG2E" => std::f64::consts::LOG2_E,
87 "LOG10E" => std::f64::consts::LOG10_E,
88 "SQRT2" => std::f64::consts::SQRT_2,
89 "SQRT1_2" => std::f64::consts::FRAC_1_SQRT_2,
90 _ => return None,
91 };
92 Some(JsValue::Number(v))
93}
94
95fn arg(args: &[JsValue], i: usize) -> Option<&JsValue> {
96 args.get(i)
97}
98
99fn js_round(n: f64) -> f64 {
100 if n.is_nan() || n.is_infinite() || n == 0.0 { return n; }
101 (n + 0.5).floor()
103}
104
105fn js_sign(n: f64) -> f64 {
106 if n.is_nan() { return f64::NAN; }
107 if n == 0.0 { return n; } if n > 0.0 { 1.0 } else { -1.0 }
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114 fn n(v: f64) -> JsValue { JsValue::Number(v) }
115
116 #[test]
117 fn test_basic_math() {
118 assert_eq!(call("abs", &[n(-5.0)]), Some(n(5.0)));
119 assert_eq!(call("floor", &[n(1.7)]), Some(n(1.0)));
120 assert_eq!(call("ceil", &[n(1.1)]), Some(n(2.0)));
121 assert_eq!(call("round", &[n(1.5)]), Some(n(2.0)));
122 assert_eq!(call("round", &[n(1.4)]), Some(n(1.0)));
123 assert_eq!(call("trunc", &[n(1.9)]), Some(n(1.0)));
124 assert_eq!(call("sqrt", &[n(9.0)]), Some(n(3.0)));
125 }
126
127 #[test]
128 fn test_pow_minmax() {
129 assert_eq!(call("pow", &[n(2.0), n(10.0)]), Some(n(1024.0)));
130 assert_eq!(call("max", &[n(1.0), n(3.0), n(2.0)]), Some(n(3.0)));
131 assert_eq!(call("min", &[n(1.0), n(3.0), n(2.0)]), Some(n(1.0)));
132 assert_eq!(call("max", &[]), Some(n(f64::NEG_INFINITY)));
133 assert_eq!(call("min", &[]), Some(n(f64::INFINITY)));
134 }
135
136 #[test]
137 fn test_constants() {
138 assert_eq!(constant("PI"), Some(n(std::f64::consts::PI)));
139 assert_eq!(constant("E"), Some(n(std::f64::consts::E)));
140 assert_eq!(constant("unknown"), None);
141 }
142
143 #[test]
144 fn test_imul() {
145 assert_eq!(call("imul", &[n(2.0), n(4.0)]), Some(n(8.0)));
146 assert_eq!(call("imul", &[n(0xFFFF_FFFF_u32 as f64), n(5.0)]), Some(n(-5.0)));
148 }
149
150 #[test]
151 fn test_clz32() {
152 assert_eq!(call("clz32", &[n(1.0)]), Some(n(31.0)));
153 assert_eq!(call("clz32", &[n(0.0)]), Some(n(32.0)));
154 }
155}