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}