boa/builtins/math/
mod.rs

1//! This module implements the global `Math` object.
2//!
3//! `Math` is a built-in object that has properties and methods for mathematical constants and functions. It’s not a function object.
4//!
5//! `Math` works with the `Number` type. It doesn't work with `BigInt`.
6//!
7//! More information:
8//!  - [ECMAScript reference][spec]
9//!  - [MDN documentation][mdn]
10//!
11//! [spec]: https://tc39.es/ecma262/#sec-math-object
12//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math
13
14use crate::{
15    builtins::BuiltIn, object::ObjectInitializer, property::Attribute, symbol::WellKnownSymbols,
16    BoaProfiler, Context, JsResult, JsValue,
17};
18
19use super::JsArgs;
20
21#[cfg(test)]
22mod tests;
23
24/// Javascript `Math` object.
25#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
26pub(crate) struct Math;
27
28impl BuiltIn for Math {
29    const NAME: &'static str = "Math";
30
31    fn attribute() -> Attribute {
32        Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE
33    }
34
35    fn init(context: &mut Context) -> (&'static str, JsValue, Attribute) {
36        let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
37
38        let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT;
39        let string_tag = WellKnownSymbols::to_string_tag();
40        let object = ObjectInitializer::new(context)
41            .property("E", std::f64::consts::E, attribute)
42            .property("LN10", std::f64::consts::LN_10, attribute)
43            .property("LN2", std::f64::consts::LN_2, attribute)
44            .property("LOG10E", std::f64::consts::LOG10_E, attribute)
45            .property("LOG2E", std::f64::consts::LOG2_E, attribute)
46            .property("PI", std::f64::consts::PI, attribute)
47            .property("SQRT1_2", std::f64::consts::FRAC_1_SQRT_2, attribute)
48            .property("SQRT2", std::f64::consts::SQRT_2, attribute)
49            .function(Self::abs, "abs", 1)
50            .function(Self::acos, "acos", 1)
51            .function(Self::acosh, "acosh", 1)
52            .function(Self::asin, "asin", 1)
53            .function(Self::asinh, "asinh", 1)
54            .function(Self::atan, "atan", 1)
55            .function(Self::atanh, "atanh", 1)
56            .function(Self::atan2, "atan2", 2)
57            .function(Self::cbrt, "cbrt", 1)
58            .function(Self::ceil, "ceil", 1)
59            .function(Self::clz32, "clz32", 1)
60            .function(Self::cos, "cos", 1)
61            .function(Self::cosh, "cosh", 1)
62            .function(Self::exp, "exp", 1)
63            .function(Self::expm1, "expm1", 1)
64            .function(Self::floor, "floor", 1)
65            .function(Self::fround, "fround", 1)
66            .function(Self::hypot, "hypot", 2)
67            .function(Self::imul, "imul", 1)
68            .function(Self::log, "log", 1)
69            .function(Self::log1p, "log1p", 1)
70            .function(Self::log10, "log10", 1)
71            .function(Self::log2, "log2", 1)
72            .function(Self::max, "max", 2)
73            .function(Self::min, "min", 2)
74            .function(Self::pow, "pow", 2)
75            .function(Self::random, "random", 0)
76            .function(Self::round, "round", 1)
77            .function(Self::sign, "sign", 1)
78            .function(Self::sin, "sin", 1)
79            .function(Self::sinh, "sinh", 1)
80            .function(Self::sqrt, "sqrt", 1)
81            .function(Self::tan, "tan", 1)
82            .function(Self::tanh, "tanh", 1)
83            .function(Self::trunc, "trunc", 1)
84            .property(
85                string_tag,
86                Math::NAME,
87                Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
88            )
89            .build();
90
91        (Self::NAME, object.into(), Self::attribute())
92    }
93}
94
95impl Math {
96    /// Get the absolute value of a number.
97    ///
98    /// More information:
99    ///  - [ECMAScript reference][spec]
100    ///  - [MDN documentation][mdn]
101    ///
102    /// [spec]: https://tc39.es/ecma262/#sec-math.abs
103    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/abs
104    pub(crate) fn abs(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
105        Ok(args
106            .get_or_undefined(0)
107            // 1. Let n be ? ToNumber(x).
108            .to_number(context)?
109            // 3. If n is -0𝔽, return +0𝔽.
110            // 2. If n is NaN, return NaN.
111            // 4. If n is -βˆžπ”½, return +βˆžπ”½.
112            // 5. If n < +0𝔽, return -n.
113            // 6. Return n.
114            .abs()
115            .into())
116    }
117
118    /// Get the arccos of a number.
119    ///
120    /// More information:
121    ///  - [ECMAScript reference][spec]
122    ///  - [MDN documentation][mdn]
123    ///
124    /// [spec]: https://tc39.es/ecma262/#sec-math.acos
125    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/acos
126    pub(crate) fn acos(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
127        Ok(args
128            .get_or_undefined(0)
129            // 1. Let n be ? ToNumber(x).
130            .to_number(context)?
131            // 2. If n is NaN, n > 1𝔽, or n < -1𝔽, return NaN.
132            // 3. If n is 1𝔽, return +0𝔽.
133            // 4. Return an implementation-approximated value representing the result of the inverse cosine of ℝ(n).
134            .acos()
135            .into())
136    }
137
138    /// Get the hyperbolic arccos of a number.
139    ///
140    /// More information:
141    ///  - [ECMAScript reference][spec]
142    ///  - [MDN documentation][mdn]
143    ///
144    /// [spec]: https://tc39.es/ecma262/#sec-math.acosh
145    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/acosh
146    pub(crate) fn acosh(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
147        Ok(args
148            .get_or_undefined(0)
149            // 1. Let n be ? ToNumber(x).
150            .to_number(context)?
151            // 4. If n < 1𝔽, return NaN.
152            // 2. If n is NaN or n is +βˆžπ”½, return n.
153            // 3. If n is 1𝔽, return +0𝔽.
154            // 5. Return an implementation-approximated value representing the result of the inverse hyperbolic cosine of ℝ(n).
155            .acosh()
156            .into())
157    }
158
159    /// Get the arcsine of a number.
160    ///
161    /// More information:
162    ///  - [ECMAScript reference][spec]
163    ///  - [MDN documentation][mdn]
164    ///
165    /// [spec]: https://tc39.es/ecma262/#sec-math.asin
166    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/asin
167    pub(crate) fn asin(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
168        Ok(args
169            .get_or_undefined(0)
170            // 1. Let n be ? ToNumber(x).
171            .to_number(context)?
172            // 2. If n is NaN, n is +0𝔽, or n is -0𝔽, return n.
173            // 3. If n > 1𝔽 or n < -1𝔽, return NaN.
174            // 4. Return an implementation-approximated value representing the result of the inverse sine of ℝ(n).
175            .asin()
176            .into())
177    }
178
179    /// Get the hyperbolic arcsine of a number.
180    ///
181    /// More information:
182    ///  - [ECMAScript reference][spec]
183    ///  - [MDN documentation][mdn]
184    ///
185    /// [spec]: https://tc39.es/ecma262/#sec-math.asinh
186    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/asinh
187    pub(crate) fn asinh(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
188        Ok(args
189            .get_or_undefined(0)
190            // 1. Let n be ? ToNumber(x).
191            .to_number(context)?
192            // 2. If n is NaN, n is +0𝔽, n is -0𝔽, n is +βˆžπ”½, or n is -βˆžπ”½, return n.
193            // 3. Return an implementation-approximated value representing the result of the inverse hyperbolic sine of ℝ(n).
194            .asinh()
195            .into())
196    }
197
198    /// Get the arctangent of a number.
199    ///
200    /// More information:
201    ///  - [ECMAScript reference][spec]
202    ///  - [MDN documentation][mdn]
203    ///
204    /// [spec]: https://tc39.es/ecma262/#sec-math.atan
205    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan
206    pub(crate) fn atan(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
207        Ok(args
208            .get_or_undefined(0)
209            // 1. Let n be ? ToNumber(x).
210            .to_number(context)?
211            // 2. If n is NaN, n is +0𝔽, or n is -0𝔽, return n.
212            // 3. If n is +βˆžπ”½, return an implementation-approximated value representing Ο€ / 2.
213            // 4. If n is -βˆžπ”½, return an implementation-approximated value representing -Ο€ / 2.
214            // 5. Return an implementation-approximated value representing the result of the inverse tangent of ℝ(n).
215            .atan()
216            .into())
217    }
218
219    /// Get the hyperbolic arctangent of a number.
220    ///
221    /// More information:
222    ///  - [ECMAScript reference][spec]
223    ///  - [MDN documentation][mdn]
224    ///
225    /// [spec]: https://tc39.es/ecma262/#sec-math.atanh
226    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atanh
227    pub(crate) fn atanh(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
228        Ok(args
229            .get_or_undefined(0)
230            // 1. Let n be ? ToNumber(x).
231            .to_number(context)?
232            // 2. If n is NaN, n is +0𝔽, or n is -0𝔽, return n.
233            // 3. If n > 1𝔽 or n < -1𝔽, return NaN.
234            // 4. If n is 1𝔽, return +βˆžπ”½.
235            // 5. If n is -1𝔽, return -βˆžπ”½.
236            // 6. Return an implementation-approximated value representing the result of the inverse hyperbolic tangent of ℝ(n).
237            .atanh()
238            .into())
239    }
240
241    /// Get the four quadrant arctangent of the quotient y / x.
242    ///
243    /// More information:
244    ///  - [ECMAScript reference][spec]
245    ///  - [MDN documentation][mdn]
246    ///
247    /// [spec]: https://tc39.es/ecma262/#sec-math.atan2
248    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan2
249    pub(crate) fn atan2(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
250        // 1. Let ny be ? ToNumber(y).
251        let y = args.get_or_undefined(0).to_number(context)?;
252
253        // 2. Let nx be ? ToNumber(x).
254        let x = args.get_or_undefined(1).to_number(context)?;
255
256        // 4. If ny is +βˆžπ”½, then
257        // a. If nx is +βˆžπ”½, return an implementation-approximated value representing Ο€ / 4.
258        // b. If nx is -βˆžπ”½, return an implementation-approximated value representing 3Ο€ / 4.
259        // c. Return an implementation-approximated value representing Ο€ / 2.
260        // 5. If ny is -βˆžπ”½, then
261        // a. If nx is +βˆžπ”½, return an implementation-approximated value representing -Ο€ / 4.
262        // b. If nx is -βˆžπ”½, return an implementation-approximated value representing -3Ο€ / 4.
263        // c. Return an implementation-approximated value representing -Ο€ / 2.
264        // 6. If ny is +0𝔽, then
265        // a. If nx > +0𝔽 or nx is +0𝔽, return +0𝔽.
266        // b. Return an implementation-approximated value representing Ο€.
267        // 7. If ny is -0𝔽, then
268        // a. If nx > +0𝔽 or nx is +0𝔽, return -0𝔽.
269        // b. Return an implementation-approximated value representing -Ο€.
270        // 8. Assert: ny is finite and is neither +0𝔽 nor -0𝔽.
271        // 9. If ny > +0𝔽, then
272        // a. If nx is +βˆžπ”½, return +0𝔽.
273        // b. If nx is -βˆžπ”½, return an implementation-approximated value representing Ο€.
274        // c. If nx is +0𝔽 or nx is -0𝔽, return an implementation-approximated value representing Ο€ / 2.
275        // 10. If ny < +0𝔽, then
276        // a. If nx is +βˆžπ”½, return -0𝔽.
277        // b. If nx is -βˆžπ”½, return an implementation-approximated value representing -Ο€.
278        // c. If nx is +0𝔽 or nx is -0𝔽, return an implementation-approximated value representing -Ο€ / 2.
279        // 11. Assert: nx is finite and is neither +0𝔽 nor -0𝔽.
280        // 12. Return an implementation-approximated value representing the result of the inverse tangent of the quotient ℝ(ny) / ℝ(nx).
281        Ok(y.atan2(x).into())
282    }
283
284    /// Get the cubic root of a number.
285    ///
286    /// More information:
287    ///  - [ECMAScript reference][spec]
288    ///  - [MDN documentation][mdn]
289    ///
290    /// [spec]: https://tc39.es/ecma262/#sec-math.cbrt
291    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cbrt
292    pub(crate) fn cbrt(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
293        Ok(args
294            .get_or_undefined(0)
295            // 1. Let n be ? ToNumber(x).
296            .to_number(context)?
297            // 2. If n is NaN, n is +0𝔽, n is -0𝔽, n is +βˆžπ”½, or n is -βˆžπ”½, return n.
298            // 3. Return an implementation-approximated value representing the result of the cube root of ℝ(n).
299            .cbrt()
300            .into())
301    }
302
303    /// Get lowest integer above a number.
304    ///
305    /// More information:
306    ///  - [ECMAScript reference][spec]
307    ///  - [MDN documentation][mdn]
308    ///
309    /// [spec]: https://tc39.es/ecma262/#sec-math.ceil
310    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/ceil
311    pub(crate) fn ceil(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
312        Ok(args
313            .get_or_undefined(0)
314            // 1. Let n be ? ToNumber(x).
315            .to_number(context)?
316            // 2. If n is NaN, n is +0𝔽, n is -0𝔽, n is +βˆžπ”½, or n is -βˆžπ”½, return n.
317            // 3. If n < +0𝔽 and n > -1𝔽, return -0𝔽.
318            // 4. If n is an integral Number, return n.
319            // 5. Return the smallest (closest to -∞) integral Number value that is not less than n.
320            .ceil()
321            .into())
322    }
323
324    /// Get the number of leading zeros in the 32 bit representation of a number
325    ///
326    /// More information:
327    ///  - [ECMAScript reference][spec]
328    ///  - [MDN documentation][mdn]
329    ///
330    /// [spec]: https://tc39.es/ecma262/#sec-math.clz32
331    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/clz32
332    pub(crate) fn clz32(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
333        Ok(args
334            .get_or_undefined(0)
335            // 1. Let n be ? ToUint32(x).
336            .to_u32(context)?
337            // 2. Let p be the number of leading zero bits in the unsigned 32-bit binary representation of n.
338            // 3. Return 𝔽(p).
339            .leading_zeros()
340            .into())
341    }
342
343    /// Get the cosine of a number.
344    ///
345    /// More information:
346    ///  - [ECMAScript reference][spec]
347    ///  - [MDN documentation][mdn]
348    ///
349    /// [spec]: https://tc39.es/ecma262/#sec-math.cos
350    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cos
351    pub(crate) fn cos(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
352        Ok(args
353            .get_or_undefined(0)
354            // 1. Let n be ? ToNumber(x).
355            .to_number(context)?
356            // 2. If n is NaN, n is +βˆžπ”½, or n is -βˆžπ”½, return NaN.
357            // 3. If n is +0𝔽 or n is -0𝔽, return 1𝔽.
358            // 4. Return an implementation-approximated value representing the result of the cosine of ℝ(n).
359            .cos()
360            .into())
361    }
362
363    /// Get the hyperbolic cosine of a number.
364    ///
365    /// More information:
366    ///  - [ECMAScript reference][spec]
367    ///  - [MDN documentation][mdn]
368    ///
369    /// [spec]: https://tc39.es/ecma262/#sec-math.cosh
370    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cosh
371    pub(crate) fn cosh(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
372        Ok(args
373            .get_or_undefined(0)
374            // 1. Let n be ? ToNumber(x).
375            .to_number(context)?
376            // 2. If n is NaN, return NaN.
377            // 3. If n is +βˆžπ”½ or n is -βˆžπ”½, return +βˆžπ”½.
378            // 4. If n is +0𝔽 or n is -0𝔽, return 1𝔽.
379            // 5. Return an implementation-approximated value representing the result of the hyperbolic cosine of ℝ(n).
380            .cosh()
381            .into())
382    }
383
384    /// Get the power to raise the natural logarithm to get the number.
385    ///
386    /// More information:
387    ///  - [ECMAScript reference][spec]
388    ///  - [MDN documentation][mdn]
389    ///
390    /// [spec]: https://tc39.es/ecma262/#sec-math.exp
391    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/exp
392    pub(crate) fn exp(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
393        Ok(args
394            .get_or_undefined(0)
395            // 1. Let n be ? ToNumber(x).
396            .to_number(context)?
397            // 2. If n is NaN or n is +βˆžπ”½, return n.
398            // 3. If n is +0𝔽 or n is -0𝔽, return 1𝔽.
399            // 4. If n is -βˆžπ”½, return +0𝔽.
400            // 5. Return an implementation-approximated value representing the result of the exponential function of ℝ(n).
401            .exp()
402            .into())
403    }
404
405    /// The Math.expm1() function returns e^x - 1, where x is the argument, and e the base of
406    /// the natural logarithms. The result is computed in a way that is accurate even when the
407    /// value of x is close 0
408    ///
409    /// More information:
410    ///  - [ECMAScript reference][spec]
411    ///  - [MDN documentation][mdn]
412    ///
413    /// [spec]: https://tc39.es/ecma262/#sec-math.expm1
414    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/expm1
415    pub(crate) fn expm1(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
416        Ok(args
417            .get_or_undefined(0)
418            // 1. Let n be ? ToNumber(x).
419            .to_number(context)?
420            // 2. If n is NaN, n is +0𝔽, n is -0𝔽, or n is +βˆžπ”½, return n.
421            // 3. If n is -βˆžπ”½, return -1𝔽.
422            // 4. Return an implementation-approximated value representing the result of subtracting 1 from the exponential function of ℝ(n).
423            .exp_m1()
424            .into())
425    }
426
427    /// Get the highest integer below a number.
428    ///
429    /// More information:
430    ///  - [ECMAScript reference][spec]
431    ///  - [MDN documentation][mdn]
432    ///
433    /// [spec]: https://tc39.es/ecma262/#sec-math.floor
434    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/floor
435    pub(crate) fn floor(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
436        Ok(args
437            .get_or_undefined(0)
438            // 1. Let n be ? ToNumber(x).
439            .to_number(context)?
440            // 2. If n is NaN, n is +0𝔽, n is -0𝔽, n is +βˆžπ”½, or n is -βˆžπ”½, return n.
441            // 3. If n < 1𝔽 and n > +0𝔽, return +0𝔽.
442            // 4. If n is an integral Number, return n.
443            // 5. Return the greatest (closest to +∞) integral Number value that is not greater than n.
444            .floor()
445            .into())
446    }
447
448    /// Get the nearest 32-bit single precision float representation of a number.
449    ///
450    /// More information:
451    ///  - [ECMAScript reference][spec]
452    ///  - [MDN documentation][mdn]
453    ///
454    /// [spec]: https://tc39.es/ecma262/#sec-math.fround
455    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/fround
456    pub(crate) fn fround(
457        _: &JsValue,
458        args: &[JsValue],
459        context: &mut Context,
460    ) -> JsResult<JsValue> {
461        // 1. Let n be ? ToNumber(x).
462        let x = args.get_or_undefined(0).to_number(context)?;
463
464        // 2. If n is NaN, return NaN.
465        // 3. If n is one of +0𝔽, -0𝔽, +βˆžπ”½, or -βˆžπ”½, return n.
466        // 4. Let n32 be the result of converting n to a value in IEEE 754-2019 binary32 format using roundTiesToEven mode.
467        // 5. Let n64 be the result of converting n32 to a value in IEEE 754-2019 binary64 format.
468        // 6. Return the ECMAScript Number value corresponding to n64.
469        Ok((x as f32 as f64).into())
470    }
471
472    /// Get an approximation of the square root of the sum of squares of all arguments.
473    ///
474    /// More information:
475    ///  - [ECMAScript reference][spec]
476    ///  - [MDN documentation][mdn]
477    ///
478    /// [spec]: https://tc39.es/ecma262/#sec-math.hypot
479    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/hypot
480    pub(crate) fn hypot(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
481        // 1. Let coerced be a new empty List.
482        // 2. For each element arg of args, do
483        // a. Let n be ? ToNumber(arg).
484        // b. Append n to coerced.
485        // 3. For each element number of coerced, do
486        // 5. For each element number of coerced, do
487        let mut result = 0f64;
488        for arg in args {
489            let num = arg.to_number(context)?;
490            // a. If number is +βˆžπ”½ or number is -βˆžπ”½, return +βˆžπ”½.
491            // 4. Let onlyZero be true.
492            // a. If number is NaN, return NaN.
493            // b. If number is neither +0𝔽 nor -0𝔽, set onlyZero to false.
494            // 6. If onlyZero is true, return +0𝔽.
495            // 7. Return an implementation-approximated value representing the square root of the sum of squares of the mathematical values of the elements of coerced.
496            result = result.hypot(num);
497        }
498
499        Ok(result.into())
500    }
501
502    /// Get the result of the C-like 32-bit multiplication of the two parameters.
503    ///
504    /// More information:
505    ///  - [ECMAScript reference][spec]
506    ///  - [MDN documentation][mdn]
507    ///
508    /// [spec]: https://tc39.es/ecma262/#sec-math.imul
509    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul
510    pub(crate) fn imul(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
511        // 1. Let a be ℝ(? ToUint32(x)).
512        let x = args.get_or_undefined(0).to_u32(context)?;
513
514        // 2. Let b be ℝ(? ToUint32(y)).
515        let y = args.get_or_undefined(1).to_u32(context)?;
516
517        // 3. Let product be (a Γ— b) modulo 2^32.
518        // 4. If product β‰₯ 2^31, return 𝔽(product - 2^32); otherwise return 𝔽(product).
519        Ok((x.wrapping_mul(y) as i32).into())
520    }
521
522    /// Get the natural logarithm of a number.
523    ///
524    /// More information:
525    ///  - [ECMAScript reference][spec]
526    ///  - [MDN documentation][mdn]
527    ///
528    /// [spec]: https://tc39.es/ecma262/#sec-math.log
529    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log
530    pub(crate) fn log(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
531        Ok(args
532            .get_or_undefined(0)
533            // 1. Let n be ? ToNumber(x).
534            .to_number(context)?
535            // 2. If n is NaN or n is +βˆžπ”½, return n.
536            // 3. If n is 1𝔽, return +0𝔽.
537            // 4. If n is +0𝔽 or n is -0𝔽, return -βˆžπ”½.
538            // 5. If n < +0𝔽, return NaN.
539            // 6. Return an implementation-approximated value representing the result of the natural logarithm of ℝ(n).
540            .ln()
541            .into())
542    }
543
544    /// Get approximation to the natural logarithm of 1 + x.
545    ///
546    /// More information:
547    ///  - [ECMAScript reference][spec]
548    ///  - [MDN documentation][mdn]
549    ///
550    /// [spec]: https://tc39.es/ecma262/#sec-math.log1p
551    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log1p
552    pub(crate) fn log1p(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
553        Ok(args
554            .get_or_undefined(0)
555            // 1. Let n be ? ToNumber(x).
556            .to_number(context)?
557            // 2. If n is NaN, n is +0𝔽, n is -0𝔽, or n is +βˆžπ”½, return n.
558            // 3. If n is -1𝔽, return -βˆžπ”½.
559            // 4. If n < -1𝔽, return NaN.
560            // 5. Return an implementation-approximated value representing the result of the natural logarithm of 1 + ℝ(n).
561            .ln_1p()
562            .into())
563    }
564
565    /// Get the base 10 logarithm of the number.
566    ///
567    /// More information:
568    ///  - [ECMAScript reference][spec]
569    ///  - [MDN documentation][mdn]
570    ///
571    /// [spec]: https://tc39.es/ecma262/#sec-math.log10
572    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log10
573    pub(crate) fn log10(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
574        Ok(args
575            .get_or_undefined(0)
576            // 1. Let n be ? ToNumber(x).
577            .to_number(context)?
578            // 2. If n is NaN or n is +βˆžπ”½, return n.
579            // 3. If n is 1𝔽, return +0𝔽.
580            // 4. If n is +0𝔽 or n is -0𝔽, return -βˆžπ”½.
581            // 5. If n < +0𝔽, return NaN.
582            // 6. Return an implementation-approximated value representing the result of the base 10 logarithm of ℝ(n).
583            .log10()
584            .into())
585    }
586
587    /// Get the base 2 logarithm of the number.
588    ///
589    /// More information:
590    ///  - [ECMAScript reference][spec]
591    ///  - [MDN documentation][mdn]
592    ///
593    /// [spec]: https://tc39.es/ecma262/#sec-math.log2
594    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log2
595    pub(crate) fn log2(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
596        Ok(args
597            .get_or_undefined(0)
598            // 1. Let n be ? ToNumber(x).
599            .to_number(context)?
600            // 2. If n is NaN or n is +βˆžπ”½, return n.
601            // 3. If n is 1𝔽, return +0𝔽.
602            // 4. If n is +0𝔽 or n is -0𝔽, return -βˆžπ”½.
603            // 5. If n < +0𝔽, return NaN.
604            // 6. Return an implementation-approximated value representing the result of the base 2 logarithm of ℝ(n).
605            .log2()
606            .into())
607    }
608
609    /// Get the maximum of several numbers.
610    ///
611    /// More information:
612    ///  - [ECMAScript reference][spec]
613    ///  - [MDN documentation][mdn]
614    ///
615    /// [spec]: https://tc39.es/ecma262/#sec-math.max
616    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/max
617    pub(crate) fn max(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
618        // 1. Let coerced be a new empty List.
619        // 2. For each element arg of args, do
620        // b. Append n to coerced.
621        // 3. Let highest be -βˆžπ”½.
622        let mut highest = f64::NEG_INFINITY;
623
624        // 4. For each element number of coerced, do
625        for arg in args {
626            // a. Let n be ? ToNumber(arg).
627            let num = arg.to_number(context)?;
628
629            highest = if highest.is_nan() {
630                continue;
631            } else if num.is_nan() {
632                // a. If number is NaN, return NaN.
633                f64::NAN
634            } else {
635                // b. If number is +0𝔽 and highest is -0𝔽, set highest to +0𝔽.
636                // c. If number > highest, set highest to number.
637                highest.max(num)
638            };
639        }
640        // 5. Return highest.
641        Ok(highest.into())
642    }
643
644    /// Get the minimum of several numbers.
645    ///
646    /// More information:
647    ///  - [ECMAScript reference][spec]
648    ///  - [MDN documentation][mdn]
649    ///
650    /// [spec]: https://tc39.es/ecma262/#sec-math.min
651    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/min
652    pub(crate) fn min(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
653        // 1. Let coerced be a new empty List.
654        // 2. For each element arg of args, do
655        // b. Append n to coerced.
656        // 3. Let lowest be +βˆžπ”½.
657        let mut lowest = f64::INFINITY;
658
659        // 4. For each element number of coerced, do
660        for arg in args {
661            // a. Let n be ? ToNumber(arg).
662            let num = arg.to_number(context)?;
663
664            lowest = if lowest.is_nan() {
665                continue;
666            } else if num.is_nan() {
667                // a. If number is NaN, return NaN.
668                f64::NAN
669            } else {
670                // b. If number is -0𝔽 and lowest is +0𝔽, set lowest to -0𝔽.
671                // c. If number < lowest, set lowest to number.
672                lowest.min(num)
673            };
674        }
675        // 5. Return lowest.
676        Ok(lowest.into())
677    }
678
679    /// Raise a number to a power.
680    ///
681    /// More information:
682    ///  - [ECMAScript reference][spec]
683    ///  - [MDN documentation][mdn]
684    ///
685    /// [spec]: https://tc39.es/ecma262/#sec-math.pow
686    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/pow
687    pub(crate) fn pow(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
688        // 1. Set base to ? ToNumber(base).
689        let x = args.get_or_undefined(0).to_number(context)?;
690
691        // 2. Set exponent to ? ToNumber(exponent).
692        let y = args.get_or_undefined(1).to_number(context)?;
693
694        // 3. Return ! Number::exponentiate(base, exponent).
695        Ok(x.powf(y).into())
696    }
697
698    /// Generate a random floating-point number between `0` and `1`.
699    ///
700    /// More information:
701    ///  - [ECMAScript reference][spec]
702    ///  - [MDN documentation][mdn]
703    ///
704    /// [spec]: https://tc39.es/ecma262/#sec-math.random
705    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random
706    pub(crate) fn random(_: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
707        // NOTE: Each Math.random function created for distinct realms must produce a distinct sequence of values from successive calls.
708        Ok(rand::random::<f64>().into())
709    }
710
711    /// Round a number to the nearest integer.
712    ///
713    /// More information:
714    ///  - [ECMAScript reference][spec]
715    ///  - [MDN documentation][mdn]
716    ///
717    /// [spec]: https://tc39.es/ecma262/#sec-math.round
718    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
719    pub(crate) fn round(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
720        Ok(args
721            .get_or_undefined(0)
722            //1. Let n be ? ToNumber(x).
723            .to_number(context)?
724            //2. If n is NaN, +βˆžπ”½, -βˆžπ”½, or an integral Number, return n.
725            //3. If n < 0.5𝔽 and n > +0𝔽, return +0𝔽.
726            //4. If n < +0𝔽 and n β‰₯ -0.5𝔽, return -0𝔽.
727            //5. Return the integral Number closest to n, preferring the Number closer to +∞ in the case of a tie.
728            .round()
729            .into())
730    }
731
732    /// Get the sign of a number.
733    ///
734    /// More information:
735    ///  - [ECMAScript reference][spec]
736    ///  - [MDN documentation][mdn]
737    ///
738    /// [spec]: https://tc39.es/ecma262/#sec-math.sign
739    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign
740    pub(crate) fn sign(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
741        // 1. Let n be ? ToNumber(x).
742        let n = args.get_or_undefined(0).to_number(context)?;
743
744        // 2. If n is NaN, n is +0𝔽, or n is -0𝔽, return n.
745        if n == 0.0 {
746            return Ok(n.into());
747        }
748        // 3. If n < +0𝔽, return -1𝔽.
749        // 4. Return 1𝔽.
750        Ok(n.signum().into())
751    }
752
753    /// Get the sine of a number.
754    ///
755    /// More information:
756    ///  - [ECMAScript reference][spec]
757    ///  - [MDN documentation][mdn]
758    ///
759    /// [spec]: https://tc39.es/ecma262/#sec-math.sin
760    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sin
761    pub(crate) fn sin(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
762        Ok(args
763            .get_or_undefined(0)
764            // 1. Let n be ? ToNumber(x).
765            .to_number(context)?
766            // 2. If n is NaN, n is +0𝔽, or n is -0𝔽, return n.
767            // 3. If n is +βˆžπ”½ or n is -βˆžπ”½, return NaN.
768            // 4. Return an implementation-approximated value representing the result of the sine of ℝ(n).
769            .sin()
770            .into())
771    }
772
773    /// Get the hyperbolic sine of a number.
774    ///
775    /// More information:
776    ///  - [ECMAScript reference][spec]
777    ///  - [MDN documentation][mdn]
778    ///
779    /// [spec]: https://tc39.es/ecma262/#sec-math.sinh
780    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sinh
781    pub(crate) fn sinh(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
782        Ok(args
783            .get_or_undefined(0)
784            // 1. Let n be ? ToNumber(x).
785            .to_number(context)?
786            // 2. If n is NaN, n is +0𝔽, n is -0𝔽, n is +βˆžπ”½, or n is -βˆžπ”½, return n.
787            // 3. Return an implementation-approximated value representing the result of the hyperbolic sine of ℝ(n).
788            .sinh()
789            .into())
790    }
791
792    /// Get the square root of a number.
793    ///
794    /// More information:
795    ///  - [ECMAScript reference][spec]
796    ///  - [MDN documentation][mdn]
797    ///
798    /// [spec]: https://tc39.es/ecma262/#sec-math.sqrt
799    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sqrt
800    pub(crate) fn sqrt(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
801        Ok(args
802            .get_or_undefined(0)
803            // 1. Let n be ? ToNumber(x).
804            .to_number(context)?
805            // 2. If n is NaN, n is +0𝔽, n is -0𝔽, or n is +βˆžπ”½, return n.
806            // 3. If n < +0𝔽, return NaN.
807            // 4. Return an implementation-approximated value representing the result of the square root of ℝ(n).
808            .sqrt()
809            .into())
810    }
811
812    /// Get the tangent of a number.
813    ///
814    /// More information:
815    ///  - [ECMAScript reference][spec]
816    ///  - [MDN documentation][mdn]
817    ///
818    /// [spec]: https://tc39.es/ecma262/#sec-math.tan
819    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/tan
820    pub(crate) fn tan(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
821        Ok(args
822            .get_or_undefined(0)
823            // 1. Let n be ? ToNumber(x).
824            .to_number(context)?
825            // 2. If n is NaN, n is +0𝔽, or n is -0𝔽, return n.
826            // 3. If n is +βˆžπ”½, or n is -βˆžπ”½, return NaN.
827            // 4. Return an implementation-approximated value representing the result of the tangent of ℝ(n).
828            .tan()
829            .into())
830    }
831
832    /// Get the hyperbolic tangent of a number.
833    ///
834    /// More information:
835    ///  - [ECMAScript reference][spec]
836    ///  - [MDN documentation][mdn]
837    ///
838    /// [spec]: https://tc39.es/ecma262/#sec-math.tanh
839    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/tanh
840    pub(crate) fn tanh(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
841        Ok(args
842            .get_or_undefined(0)
843            // 1. Let n be ? ToNumber(x).
844            .to_number(context)?
845            // 2. If n is NaN, n is +0𝔽, or n is -0𝔽, return n.
846            // 3. If n is +βˆžπ”½, return 1𝔽.
847            // 4. If n is -βˆžπ”½, return -1𝔽.
848            // 5. Return an implementation-approximated value representing the result of the hyperbolic tangent of ℝ(n).
849            .tanh()
850            .into())
851    }
852
853    /// Get the integer part of a number.
854    ///
855    /// More information:
856    ///  - [ECMAScript reference][spec]
857    ///  - [MDN documentation][mdn]
858    ///
859    /// [spec]: https://tc39.es/ecma262/#sec-math.trunc
860    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc
861    pub(crate) fn trunc(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
862        Ok(args
863            .get_or_undefined(0)
864            // 1. Let n be ? ToNumber(x).
865            .to_number(context)?
866            // 2. If n is NaN, n is +0𝔽, n is -0𝔽, n is +βˆžπ”½, or n is -βˆžπ”½, return n.
867            // 3. If n < 1𝔽 and n > +0𝔽, return +0𝔽.
868            // 4. If n < +0𝔽 and n > -1𝔽, return -0𝔽.
869            // 5. Return the integral Number nearest n in the direction of +0𝔽.
870            .trunc()
871            .into())
872    }
873}