Skip to main content

boa_engine/builtins/math/
mod.rs

1//! Boa's implementation of ECMAScript's 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    Context, JsArgs, JsResult, JsString, JsValue, builtins::BuiltInObject,
16    context::intrinsics::Intrinsics, js_string, object::JsObject, property::Attribute,
17    realm::Realm, string::StaticJsStrings, symbol::JsSymbol,
18};
19
20use super::{BuiltInBuilder, IntrinsicObject};
21
22#[cfg(test)]
23mod tests;
24
25/// Javascript `Math` object.
26#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
27pub(crate) struct Math;
28
29impl IntrinsicObject for Math {
30    fn init(realm: &Realm) {
31        let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT;
32        let builder = BuiltInBuilder::with_intrinsic::<Self>(realm)
33            .static_property(js_string!("E"), std::f64::consts::E, attribute)
34            .static_property(js_string!("LN10"), std::f64::consts::LN_10, attribute)
35            .static_property(js_string!("LN2"), std::f64::consts::LN_2, attribute)
36            .static_property(js_string!("LOG10E"), std::f64::consts::LOG10_E, attribute)
37            .static_property(js_string!("LOG2E"), std::f64::consts::LOG2_E, attribute)
38            .static_property(js_string!("PI"), std::f64::consts::PI, attribute)
39            .static_property(
40                js_string!("SQRT1_2"),
41                std::f64::consts::FRAC_1_SQRT_2,
42                attribute,
43            )
44            .static_property(js_string!("SQRT2"), std::f64::consts::SQRT_2, attribute)
45            .static_method(Self::abs, js_string!("abs"), 1)
46            .static_method(Self::acos, js_string!("acos"), 1)
47            .static_method(Self::acosh, js_string!("acosh"), 1)
48            .static_method(Self::asin, js_string!("asin"), 1)
49            .static_method(Self::asinh, js_string!("asinh"), 1)
50            .static_method(Self::atan, js_string!("atan"), 1)
51            .static_method(Self::atanh, js_string!("atanh"), 1)
52            .static_method(Self::atan2, js_string!("atan2"), 2)
53            .static_method(Self::cbrt, js_string!("cbrt"), 1)
54            .static_method(Self::ceil, js_string!("ceil"), 1)
55            .static_method(Self::clz32, js_string!("clz32"), 1)
56            .static_method(Self::cos, js_string!("cos"), 1)
57            .static_method(Self::cosh, js_string!("cosh"), 1)
58            .static_method(Self::exp, js_string!("exp"), 1)
59            .static_method(Self::expm1, js_string!("expm1"), 1)
60            .static_method(Self::floor, js_string!("floor"), 1)
61            .static_method(Self::fround, js_string!("fround"), 1)
62            .static_method(Self::hypot, js_string!("hypot"), 2)
63            .static_method(Self::imul, js_string!("imul"), 2)
64            .static_method(Self::log, js_string!("log"), 1)
65            .static_method(Self::log1p, js_string!("log1p"), 1)
66            .static_method(Self::log10, js_string!("log10"), 1)
67            .static_method(Self::log2, js_string!("log2"), 1)
68            .static_method(Self::max, js_string!("max"), 2)
69            .static_method(Self::min, js_string!("min"), 2)
70            .static_method(Self::pow, js_string!("pow"), 2)
71            .static_method(Self::random, js_string!("random"), 0)
72            .static_method(Self::round, js_string!("round"), 1)
73            .static_method(Self::sign, js_string!("sign"), 1)
74            .static_method(Self::sin, js_string!("sin"), 1)
75            .static_method(Self::sinh, js_string!("sinh"), 1)
76            .static_method(Self::sqrt, js_string!("sqrt"), 1)
77            .static_method(Self::tan, js_string!("tan"), 1)
78            .static_method(Self::tanh, js_string!("tanh"), 1)
79            .static_method(Self::trunc, js_string!("trunc"), 1)
80            .static_property(
81                JsSymbol::to_string_tag(),
82                Self::NAME,
83                Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
84            );
85
86        #[cfg(feature = "float16")]
87        let builder = builder.static_method(Self::f16round, js_string!("f16round"), 1);
88
89        #[cfg(feature = "xsum")]
90        let builder = builder.static_method(Self::sum_precise, js_string!("sumPrecise"), 1);
91
92        builder.build();
93    }
94
95    fn get(intrinsics: &Intrinsics) -> JsObject {
96        intrinsics.objects().math()
97    }
98}
99
100impl BuiltInObject for Math {
101    const NAME: JsString = StaticJsStrings::MATH;
102}
103
104impl Math {
105    /// Get the absolute value of a number.
106    ///
107    /// More information:
108    ///  - [ECMAScript reference][spec]
109    ///  - [MDN documentation][mdn]
110    ///
111    /// [spec]: https://tc39.es/ecma262/#sec-math.abs
112    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/abs
113    pub(crate) fn abs(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
114        Ok(args
115            .get_or_undefined(0)
116            // 1. Let n be ? ToNumber(x).
117            .to_number(context)?
118            // 3. If n is -0𝔽, return +0𝔽.
119            // 2. If n is NaN, return NaN.
120            // 4. If n is -βˆžπ”½, return +βˆžπ”½.
121            // 5. If n < +0𝔽, return -n.
122            // 6. Return n.
123            .abs()
124            .into())
125    }
126
127    /// Get the arccos of a number.
128    ///
129    /// More information:
130    ///  - [ECMAScript reference][spec]
131    ///  - [MDN documentation][mdn]
132    ///
133    /// [spec]: https://tc39.es/ecma262/#sec-math.acos
134    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/acos
135    pub(crate) fn acos(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
136        Ok(args
137            .get_or_undefined(0)
138            // 1. Let n be ? ToNumber(x).
139            .to_number(context)?
140            // 2. If n is NaN, n > 1𝔽, or n < -1𝔽, return NaN.
141            // 3. If n is 1𝔽, return +0𝔽.
142            // 4. Return an implementation-approximated value representing the result of the inverse cosine of ℝ(n).
143            .acos()
144            .into())
145    }
146
147    /// Get the hyperbolic arccos of a number.
148    ///
149    /// More information:
150    ///  - [ECMAScript reference][spec]
151    ///  - [MDN documentation][mdn]
152    ///
153    /// [spec]: https://tc39.es/ecma262/#sec-math.acosh
154    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/acosh
155    pub(crate) fn acosh(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
156        // 1/√f64::EPSILON, as established by the Boost math library.
157        const ACOSH_LARGE_INPUT_THRESHOLD: f64 = 67_108_864.0;
158
159        // 1. Let n be ? ToNumber(x).
160        let n = args.get_or_undefined(0).to_number(context)?;
161        if n.is_finite() && n > ACOSH_LARGE_INPUT_THRESHOLD {
162            return Ok((n.ln() + std::f64::consts::LN_2).into());
163        }
164
165        // 4. If n < 1𝔽, return NaN.
166        // 2. If n is NaN or n is +βˆžπ”½, return n.
167        // 3. If n is 1𝔽, return +0𝔽.
168        // 5. Return an implementation-approximated value representing the result of the inverse hyperbolic cosine of ℝ(n).
169        Ok(n.acosh().into())
170    }
171
172    /// Get the arcsine of a number.
173    ///
174    /// More information:
175    ///  - [ECMAScript reference][spec]
176    ///  - [MDN documentation][mdn]
177    ///
178    /// [spec]: https://tc39.es/ecma262/#sec-math.asin
179    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/asin
180    pub(crate) fn asin(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
181        Ok(args
182            .get_or_undefined(0)
183            // 1. Let n be ? ToNumber(x).
184            .to_number(context)?
185            // 2. If n is NaN, n is +0𝔽, or n is -0𝔽, return n.
186            // 3. If n > 1𝔽 or n < -1𝔽, return NaN.
187            // 4. Return an implementation-approximated value representing the result of the inverse sine of ℝ(n).
188            .asin()
189            .into())
190    }
191
192    /// Get the hyperbolic arcsine of a number.
193    ///
194    /// More information:
195    ///  - [ECMAScript reference][spec]
196    ///  - [MDN documentation][mdn]
197    ///
198    /// [spec]: https://tc39.es/ecma262/#sec-math.asinh
199    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/asinh
200    pub(crate) fn asinh(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
201        // 1/√f64::EPSILON, as established by the Boost math library.
202        const ASINH_LARGE_INPUT_THRESHOLD: f64 = 67_108_864.0;
203
204        // 1. Let n be ? ToNumber(x).
205        let n = args.get_or_undefined(0).to_number(context)?;
206        if n.is_finite() && n.abs() > ASINH_LARGE_INPUT_THRESHOLD {
207            return Ok((n.signum()
208                * (n.abs().ln() + std::f64::consts::LN_2 + 0.25 * n.recip() * n.recip()))
209            .into());
210        }
211
212        // 2. If n is NaN, n is +0𝔽, n is -0𝔽, n is +βˆžπ”½, or n is -βˆžπ”½, return n.
213        // 3. Return an implementation-approximated value representing the result of the inverse hyperbolic sine of ℝ(n).
214        Ok(n.asinh().into())
215    }
216
217    /// Get the arctangent of a number.
218    ///
219    /// More information:
220    ///  - [ECMAScript reference][spec]
221    ///  - [MDN documentation][mdn]
222    ///
223    /// [spec]: https://tc39.es/ecma262/#sec-math.atan
224    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan
225    pub(crate) fn atan(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
226        Ok(args
227            .get_or_undefined(0)
228            // 1. Let n be ? ToNumber(x).
229            .to_number(context)?
230            // 2. If n is NaN, n is +0𝔽, or n is -0𝔽, return n.
231            // 3. If n is +βˆžπ”½, return an implementation-approximated value representing Ο€ / 2.
232            // 4. If n is -βˆžπ”½, return an implementation-approximated value representing -Ο€ / 2.
233            // 5. Return an implementation-approximated value representing the result of the inverse tangent of ℝ(n).
234            .atan()
235            .into())
236    }
237
238    /// Get the hyperbolic arctangent of a number.
239    ///
240    /// More information:
241    ///  - [ECMAScript reference][spec]
242    ///  - [MDN documentation][mdn]
243    ///
244    /// [spec]: https://tc39.es/ecma262/#sec-math.atanh
245    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atanh
246    pub(crate) fn atanh(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
247        Ok(args
248            .get_or_undefined(0)
249            // 1. Let n be ? ToNumber(x).
250            .to_number(context)?
251            // 2. If n is NaN, n is +0𝔽, or n is -0𝔽, return n.
252            // 3. If n > 1𝔽 or n < -1𝔽, return NaN.
253            // 4. If n is 1𝔽, return +βˆžπ”½.
254            // 5. If n is -1𝔽, return -βˆžπ”½.
255            // 6. Return an implementation-approximated value representing the result of the inverse hyperbolic tangent of ℝ(n).
256            .atanh()
257            .into())
258    }
259
260    /// Get the four quadrant arctangent of the quotient y / x.
261    ///
262    /// More information:
263    ///  - [ECMAScript reference][spec]
264    ///  - [MDN documentation][mdn]
265    ///
266    /// [spec]: https://tc39.es/ecma262/#sec-math.atan2
267    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan2
268    pub(crate) fn atan2(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
269        // 1. Let ny be ? ToNumber(y).
270        let y = args.get_or_undefined(0).to_number(context)?;
271
272        // 2. Let nx be ? ToNumber(x).
273        let x = args.get_or_undefined(1).to_number(context)?;
274
275        // 4. If ny is +βˆžπ”½, then
276        // a. If nx is +βˆžπ”½, return an implementation-approximated value representing Ο€ / 4.
277        // b. If nx is -βˆžπ”½, return an implementation-approximated value representing 3Ο€ / 4.
278        // c. Return an implementation-approximated value representing Ο€ / 2.
279        // 5. If ny is -βˆžπ”½, then
280        // a. If nx is +βˆžπ”½, return an implementation-approximated value representing -Ο€ / 4.
281        // b. If nx is -βˆžπ”½, return an implementation-approximated value representing -3Ο€ / 4.
282        // c. Return an implementation-approximated value representing -Ο€ / 2.
283        // 6. If ny is +0𝔽, then
284        // a. If nx > +0𝔽 or nx is +0𝔽, return +0𝔽.
285        // b. Return an implementation-approximated value representing Ο€.
286        // 7. If ny is -0𝔽, then
287        // a. If nx > +0𝔽 or nx is +0𝔽, return -0𝔽.
288        // b. Return an implementation-approximated value representing -Ο€.
289        // 8. Assert: ny is finite and is neither +0𝔽 nor -0𝔽.
290        // 9. If ny > +0𝔽, then
291        // a. If nx is +βˆžπ”½, return +0𝔽.
292        // b. If nx is -βˆžπ”½, return an implementation-approximated value representing Ο€.
293        // c. If nx is +0𝔽 or nx is -0𝔽, return an implementation-approximated value representing Ο€ / 2.
294        // 10. If ny < +0𝔽, then
295        // a. If nx is +βˆžπ”½, return -0𝔽.
296        // b. If nx is -βˆžπ”½, return an implementation-approximated value representing -Ο€.
297        // c. If nx is +0𝔽 or nx is -0𝔽, return an implementation-approximated value representing -Ο€ / 2.
298        // 11. Assert: nx is finite and is neither +0𝔽 nor -0𝔽.
299        // 12. Return an implementation-approximated value representing the result of the inverse tangent of the quotient ℝ(ny) / ℝ(nx).
300        Ok(y.atan2(x).into())
301    }
302
303    /// Get the cubic root of a number.
304    ///
305    /// More information:
306    ///  - [ECMAScript reference][spec]
307    ///  - [MDN documentation][mdn]
308    ///
309    /// [spec]: https://tc39.es/ecma262/#sec-math.cbrt
310    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cbrt
311    pub(crate) fn cbrt(_: &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. Return an implementation-approximated value representing the result of the cube root of ℝ(n).
318            .cbrt()
319            .into())
320    }
321
322    /// Get lowest integer above a number.
323    ///
324    /// More information:
325    ///  - [ECMAScript reference][spec]
326    ///  - [MDN documentation][mdn]
327    ///
328    /// [spec]: https://tc39.es/ecma262/#sec-math.ceil
329    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/ceil
330    pub(crate) fn ceil(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
331        Ok(args
332            .get_or_undefined(0)
333            // 1. Let n be ? ToNumber(x).
334            .to_number(context)?
335            // 2. If n is NaN, n is +0𝔽, n is -0𝔽, n is +βˆžπ”½, or n is -βˆžπ”½, return n.
336            // 3. If n < +0𝔽 and n > -1𝔽, return -0𝔽.
337            // 4. If n is an integral Number, return n.
338            // 5. Return the smallest (closest to -∞) integral Number value that is not less than n.
339            .ceil()
340            .into())
341    }
342
343    /// Get the number of leading zeros in the 32 bit representation of a number
344    ///
345    /// More information:
346    ///  - [ECMAScript reference][spec]
347    ///  - [MDN documentation][mdn]
348    ///
349    /// [spec]: https://tc39.es/ecma262/#sec-math.clz32
350    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/clz32
351    pub(crate) fn clz32(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
352        Ok(args
353            .get_or_undefined(0)
354            // 1. Let n be ? ToUint32(x).
355            .to_u32(context)?
356            // 2. Let p be the number of leading zero bits in the unsigned 32-bit binary representation of n.
357            // 3. Return 𝔽(p).
358            .leading_zeros()
359            .into())
360    }
361
362    /// Get the cosine of a number.
363    ///
364    /// More information:
365    ///  - [ECMAScript reference][spec]
366    ///  - [MDN documentation][mdn]
367    ///
368    /// [spec]: https://tc39.es/ecma262/#sec-math.cos
369    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cos
370    pub(crate) fn cos(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
371        Ok(args
372            .get_or_undefined(0)
373            // 1. Let n be ? ToNumber(x).
374            .to_number(context)?
375            // 2. If n is NaN, n is +βˆžπ”½, or n is -βˆžπ”½, return NaN.
376            // 3. If n is +0𝔽 or n is -0𝔽, return 1𝔽.
377            // 4. Return an implementation-approximated value representing the result of the cosine of ℝ(n).
378            .cos()
379            .into())
380    }
381
382    /// Get the hyperbolic cosine of a number.
383    ///
384    /// More information:
385    ///  - [ECMAScript reference][spec]
386    ///  - [MDN documentation][mdn]
387    ///
388    /// [spec]: https://tc39.es/ecma262/#sec-math.cosh
389    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cosh
390    pub(crate) fn cosh(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
391        Ok(args
392            .get_or_undefined(0)
393            // 1. Let n be ? ToNumber(x).
394            .to_number(context)?
395            // 2. If n is NaN, return NaN.
396            // 3. If n is +βˆžπ”½ or n is -βˆžπ”½, return +βˆžπ”½.
397            // 4. If n is +0𝔽 or n is -0𝔽, return 1𝔽.
398            // 5. Return an implementation-approximated value representing the result of the hyperbolic cosine of ℝ(n).
399            .cosh()
400            .into())
401    }
402
403    /// Get the power to raise the natural logarithm to get the number.
404    ///
405    /// More information:
406    ///  - [ECMAScript reference][spec]
407    ///  - [MDN documentation][mdn]
408    ///
409    /// [spec]: https://tc39.es/ecma262/#sec-math.exp
410    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/exp
411    pub(crate) fn exp(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
412        Ok(args
413            .get_or_undefined(0)
414            // 1. Let n be ? ToNumber(x).
415            .to_number(context)?
416            // 2. If n is NaN or n is +βˆžπ”½, return n.
417            // 3. If n is +0𝔽 or n is -0𝔽, return 1𝔽.
418            // 4. If n is -βˆžπ”½, return +0𝔽.
419            // 5. Return an implementation-approximated value representing the result of the exponential function of ℝ(n).
420            .exp()
421            .into())
422    }
423
424    /// The `Math.expm1()` function returns e^x - 1, where x is the argument, and e the base of
425    /// the natural logarithms. The result is computed in a way that is accurate even when the
426    /// value of x is close 0
427    ///
428    /// More information:
429    ///  - [ECMAScript reference][spec]
430    ///  - [MDN documentation][mdn]
431    ///
432    /// [spec]: https://tc39.es/ecma262/#sec-math.expm1
433    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/expm1
434    pub(crate) fn expm1(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
435        Ok(args
436            .get_or_undefined(0)
437            // 1. Let n be ? ToNumber(x).
438            .to_number(context)?
439            // 2. If n is NaN, n is +0𝔽, n is -0𝔽, or n is +βˆžπ”½, return n.
440            // 3. If n is -βˆžπ”½, return -1𝔽.
441            // 4. Return an implementation-approximated value representing the result of subtracting 1 from the exponential function of ℝ(n).
442            .exp_m1()
443            .into())
444    }
445
446    /// Get the highest integer below a number.
447    ///
448    /// More information:
449    ///  - [ECMAScript reference][spec]
450    ///  - [MDN documentation][mdn]
451    ///
452    /// [spec]: https://tc39.es/ecma262/#sec-math.floor
453    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/floor
454    pub(crate) fn floor(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
455        Ok(args
456            .get_or_undefined(0)
457            // 1. Let n be ? ToNumber(x).
458            .to_number(context)?
459            // 2. If n is NaN, n is +0𝔽, n is -0𝔽, n is +βˆžπ”½, or n is -βˆžπ”½, return n.
460            // 3. If n < 1𝔽 and n > +0𝔽, return +0𝔽.
461            // 4. If n is an integral Number, return n.
462            // 5. Return the greatest (closest to +∞) integral Number value that is not greater than n.
463            .floor()
464            .into())
465    }
466
467    /// The `Math.f16round()` static method returns the nearest 16-bit half precision
468    /// float representation of a number.
469    ///
470    /// More information:
471    ///  - [ECMAScript reference][spec]
472    ///  - [MDN documentation][mdn]
473    ///
474    /// [spec]: https://tc39.es/ecma262/#sec-math.round
475    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/f16round
476    #[allow(clippy::float_cmp)]
477    #[cfg(feature = "float16")]
478    pub(crate) fn f16round(
479        _: &JsValue,
480        args: &[JsValue],
481        context: &mut Context,
482    ) -> JsResult<JsValue> {
483        let num = args
484            .get_or_undefined(0)
485            //1. Let n be ? ToNumber(x).
486            .to_number(context)?;
487
488        // 2. If n is NaN, return NaN.
489        // 3. If n is one of +0𝔽, -0𝔽, +βˆžπ”½, or -βˆžπ”½, return n.
490        // 4. Let n16 be the result of converting n to IEEE 754-2019 binary16 format using roundTiesToEven mode.
491        // 5. Let n64 be the result of converting n16 to IEEE 754-2019 binary64 format.
492        // 6. Return the ECMAScript Number value corresponding to n64.
493        Ok(float16::f16::from_f64(num).to_f64().into())
494    }
495
496    /// Get the nearest 32-bit single precision float representation of a number.
497    ///
498    /// More information:
499    ///  - [ECMAScript reference][spec]
500    ///  - [MDN documentation][mdn]
501    ///
502    /// [spec]: https://tc39.es/ecma262/#sec-math.fround
503    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/fround
504    pub(crate) fn fround(
505        _: &JsValue,
506        args: &[JsValue],
507        context: &mut Context,
508    ) -> JsResult<JsValue> {
509        // 1. Let n be ? ToNumber(x).
510        let x = args.get_or_undefined(0).to_number(context)?;
511
512        // 2. If n is NaN, return NaN.
513        // 3. If n is one of +0𝔽, -0𝔽, +βˆžπ”½, or -βˆžπ”½, return n.
514        // 4. Let n32 be the result of converting n to a value in IEEE 754-2019 binary32 format using roundTiesToEven mode.
515        // 5. Let n64 be the result of converting n32 to a value in IEEE 754-2019 binary64 format.
516        // 6. Return the ECMAScript Number value corresponding to n64.
517        Ok(f64::from(x as f32).into())
518    }
519
520    /// Get an approximation of the square root of the sum of squares of all arguments.
521    ///
522    /// More information:
523    ///  - [ECMAScript reference][spec]
524    ///  - [MDN documentation][mdn]
525    ///
526    /// [spec]: https://tc39.es/ecma262/#sec-math.hypot
527    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/hypot
528    pub(crate) fn hypot(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
529        // 1. Let coerced be a new empty List.
530        // 2. For each element arg of args, do
531        // a. Let n be ? ToNumber(arg).
532        // b. Append n to coerced.
533        // 3. For each element number of coerced, do
534        // 5. For each element number of coerced, do
535        let mut result = 0f64;
536        for arg in args {
537            let num = arg.to_number(context)?;
538            // a. If number is +βˆžπ”½ or number is -βˆžπ”½, return +βˆžπ”½.
539            // 4. Let onlyZero be true.
540            // a. If number is NaN, return NaN.
541            // b. If number is neither +0𝔽 nor -0𝔽, set onlyZero to false.
542            // 6. If onlyZero is true, return +0𝔽.
543            // 7. Return an implementation-approximated value representing the square root of the sum of squares of the mathematical values of the elements of coerced.
544            result = result.hypot(num);
545        }
546
547        Ok(result.into())
548    }
549
550    /// Get the result of the C-like 32-bit multiplication of the two parameters.
551    ///
552    /// More information:
553    ///  - [ECMAScript reference][spec]
554    ///  - [MDN documentation][mdn]
555    ///
556    /// [spec]: https://tc39.es/ecma262/#sec-math.imul
557    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul
558    pub(crate) fn imul(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
559        // 1. Let a be ℝ(? ToUint32(x)).
560        let x = args.get_or_undefined(0).to_u32(context)?;
561
562        // 2. Let b be ℝ(? ToUint32(y)).
563        let y = args.get_or_undefined(1).to_u32(context)?;
564
565        // 3. Let product be (a Γ— b) modulo 2^32.
566        // 4. If product β‰₯ 2^31, return 𝔽(product - 2^32); otherwise return 𝔽(product).
567        Ok((x.wrapping_mul(y) as i32).into())
568    }
569
570    /// Get the natural logarithm of a number.
571    ///
572    /// More information:
573    ///  - [ECMAScript reference][spec]
574    ///  - [MDN documentation][mdn]
575    ///
576    /// [spec]: https://tc39.es/ecma262/#sec-math.log
577    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log
578    pub(crate) fn log(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
579        Ok(args
580            .get_or_undefined(0)
581            // 1. Let n be ? ToNumber(x).
582            .to_number(context)?
583            // 2. If n is NaN or n is +βˆžπ”½, return n.
584            // 3. If n is 1𝔽, return +0𝔽.
585            // 4. If n is +0𝔽 or n is -0𝔽, return -βˆžπ”½.
586            // 5. If n < +0𝔽, return NaN.
587            // 6. Return an implementation-approximated value representing the result of the natural logarithm of ℝ(n).
588            .ln()
589            .into())
590    }
591
592    /// Get approximation to the natural logarithm of 1 + x.
593    ///
594    /// More information:
595    ///  - [ECMAScript reference][spec]
596    ///  - [MDN documentation][mdn]
597    ///
598    /// [spec]: https://tc39.es/ecma262/#sec-math.log1p
599    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log1p
600    pub(crate) fn log1p(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
601        Ok(args
602            .get_or_undefined(0)
603            // 1. Let n be ? ToNumber(x).
604            .to_number(context)?
605            // 2. If n is NaN, n is +0𝔽, n is -0𝔽, or n is +βˆžπ”½, return n.
606            // 3. If n is -1𝔽, return -βˆžπ”½.
607            // 4. If n < -1𝔽, return NaN.
608            // 5. Return an implementation-approximated value representing the result of the natural logarithm of 1 + ℝ(n).
609            .ln_1p()
610            .into())
611    }
612
613    /// Get the base 10 logarithm of the number.
614    ///
615    /// More information:
616    ///  - [ECMAScript reference][spec]
617    ///  - [MDN documentation][mdn]
618    ///
619    /// [spec]: https://tc39.es/ecma262/#sec-math.log10
620    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log10
621    pub(crate) fn log10(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
622        Ok(args
623            .get_or_undefined(0)
624            // 1. Let n be ? ToNumber(x).
625            .to_number(context)?
626            // 2. If n is NaN or n is +βˆžπ”½, return n.
627            // 3. If n is 1𝔽, return +0𝔽.
628            // 4. If n is +0𝔽 or n is -0𝔽, return -βˆžπ”½.
629            // 5. If n < +0𝔽, return NaN.
630            // 6. Return an implementation-approximated value representing the result of the base 10 logarithm of ℝ(n).
631            .log10()
632            .into())
633    }
634
635    /// Get the base 2 logarithm of the number.
636    ///
637    /// More information:
638    ///  - [ECMAScript reference][spec]
639    ///  - [MDN documentation][mdn]
640    ///
641    /// [spec]: https://tc39.es/ecma262/#sec-math.log2
642    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log2
643    pub(crate) fn log2(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
644        Ok(args
645            .get_or_undefined(0)
646            // 1. Let n be ? ToNumber(x).
647            .to_number(context)?
648            // 2. If n is NaN or n is +βˆžπ”½, return n.
649            // 3. If n is 1𝔽, return +0𝔽.
650            // 4. If n is +0𝔽 or n is -0𝔽, return -βˆžπ”½.
651            // 5. If n < +0𝔽, return NaN.
652            // 6. Return an implementation-approximated value representing the result of the base 2 logarithm of ℝ(n).
653            .log2()
654            .into())
655    }
656
657    /// Get the maximum of several numbers.
658    ///
659    /// More information:
660    ///  - [ECMAScript reference][spec]
661    ///  - [MDN documentation][mdn]
662    ///
663    /// [spec]: https://tc39.es/ecma262/#sec-math.max
664    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/max
665    pub(crate) fn max(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
666        // 1. Let coerced be a new empty List.
667        // 2. For each element arg of args, do
668        // b. Append n to coerced.
669        // 3. Let highest be -βˆžπ”½.
670        let mut highest = f64::NEG_INFINITY;
671
672        // 4. For each element number of coerced, do
673        for arg in args {
674            // a. Let n be ? ToNumber(arg).
675            let num = arg.to_number(context)?;
676
677            highest = if highest.is_nan() {
678                continue;
679            } else if num.is_nan() {
680                // a. If number is NaN, return NaN.
681                f64::NAN
682            } else {
683                match (highest, num) {
684                    // b. When x and y are +0𝔽 -0𝔽, return +0𝔽.
685                    (x, y) if x == 0f64 && y == 0f64 && x.signum() != y.signum() => 0f64,
686                    // c. Otherwise, return the maximum value.
687                    (x, y) => x.max(y),
688                }
689            };
690        }
691        // 5. Return highest.
692        Ok(highest.into())
693    }
694
695    /// Get the minimum of several numbers.
696    ///
697    /// More information:
698    ///  - [ECMAScript reference][spec]
699    ///  - [MDN documentation][mdn]
700    ///
701    /// [spec]: https://tc39.es/ecma262/#sec-math.min
702    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/min
703    pub(crate) fn min(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
704        // 1. Let coerced be a new empty List.
705        // 2. For each element arg of args, do
706        // b. Append n to coerced.
707        // 3. Let lowest be +βˆžπ”½.
708        let mut lowest = f64::INFINITY;
709
710        // 4. For each element number of coerced, do
711        for arg in args {
712            // a. Let n be ? ToNumber(arg).
713            let num = arg.to_number(context)?;
714
715            lowest = if lowest.is_nan() {
716                continue;
717            } else if num.is_nan() {
718                // a. If number is NaN, return NaN.
719                f64::NAN
720            } else {
721                match (lowest, num) {
722                    // b. When x and y are +0𝔽 -0𝔽, return -0𝔽.
723                    (x, y) if x == 0f64 && y == 0f64 && x.signum() != y.signum() => -0f64,
724                    // c. Otherwise, return the minimum value.
725                    (x, y) => x.min(y),
726                }
727            };
728        }
729        // 5. Return lowest.
730        Ok(lowest.into())
731    }
732
733    /// Raise a number to a power.
734    ///
735    /// More information:
736    ///  - [ECMAScript reference][spec]
737    ///  - [MDN documentation][mdn]
738    ///
739    /// [spec]: https://tc39.es/ecma262/#sec-math.pow
740    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/pow
741    #[allow(clippy::float_cmp)]
742    pub(crate) fn pow(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
743        // 1. Set base to ? ToNumber(base).
744        let x = args.get_or_undefined(0).to_number(context)?;
745
746        // 2. Set exponent to ? ToNumber(exponent).
747        let y = args.get_or_undefined(1).to_number(context)?;
748
749        // 3. Return Number::exponentiate(base, exponent).
750
751        // https://github.com/rust-lang/rust/issues/60468
752
753        // https://tc39.es/ecma262/multipage/ecmascript-data-types-and-values.html#sec-numeric-types-number-exponentiate
754        // 6.1.6.1.3 Number::exponentiate ( base, exponent )
755        //  1. If exponent is NaN, return NaN.
756        if y.is_nan() {
757            return Ok(f64::NAN.into());
758        }
759
760        // 9. If exponent is +βˆžπ”½, then
761        //   a. If abs(ℝ(base)) > 1, return +βˆžπ”½.
762        //   b. If abs(ℝ(base)) = 1, return NaN.
763        //   c. If abs(ℝ(base)) < 1, return +0𝔽.
764        // 10. If exponent is -βˆžπ”½, then
765        //   a. If abs(ℝ(base)) > 1, return +0𝔽.
766        //   b. If abs(ℝ(base)) = 1, return NaN.
767        //   c. If abs(ℝ(base)) < 1, return +βˆžπ”½.
768        if f64::abs(x) == 1f64 && y.is_infinite() {
769            return Ok(f64::NAN.into());
770        }
771
772        Ok(x.powf(y).into())
773    }
774
775    /// Generate a random floating-point number between `0` and `1`.
776    ///
777    /// More information:
778    ///  - [ECMAScript reference][spec]
779    ///  - [MDN documentation][mdn]
780    ///
781    /// [spec]: https://tc39.es/ecma262/#sec-math.random
782    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random
783    #[allow(clippy::unnecessary_wraps)]
784    pub(crate) fn random(_: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
785        // NOTE: Each Math.random function created for distinct realms must produce a distinct sequence of values from successive calls.
786        Ok(rand::random::<f64>().into())
787    }
788
789    /// Round a number to the nearest integer.
790    ///
791    /// More information:
792    ///  - [ECMAScript reference][spec]
793    ///  - [MDN documentation][mdn]
794    ///
795    /// [spec]: https://tc39.es/ecma262/#sec-math.round
796    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
797    #[allow(clippy::float_cmp)]
798    pub(crate) fn round(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
799        let num = args
800            .get_or_undefined(0)
801            //1. Let n be ? ToNumber(x).
802            .to_number(context)?;
803
804        //2. If n is NaN, +βˆžπ”½, -βˆžπ”½, or an integral Number, return n.
805        //3. If n < 0.5𝔽 and n > +0𝔽, return +0𝔽.
806        //4. If n < +0𝔽 and n β‰₯ -0.5𝔽, return -0𝔽.
807        //5. Return the integral Number closest to n, preferring the Number closer to +∞ in the case of a tie.
808
809        if num.fract() == -0.5 {
810            Ok(num.ceil().into())
811        } else {
812            Ok(num.round().into())
813        }
814    }
815
816    /// Get the sign of a number.
817    ///
818    /// More information:
819    ///  - [ECMAScript reference][spec]
820    ///  - [MDN documentation][mdn]
821    ///
822    /// [spec]: https://tc39.es/ecma262/#sec-math.sign
823    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign
824    pub(crate) fn sign(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
825        // 1. Let n be ? ToNumber(x).
826        let n = args.get_or_undefined(0).to_number(context)?;
827
828        // 2. If n is NaN, n is +0𝔽, or n is -0𝔽, return n.
829        if n == 0f64 {
830            return Ok(n.into());
831        }
832        // 3. If n < +0𝔽, return -1𝔽.
833        // 4. Return 1𝔽.
834        Ok(n.signum().into())
835    }
836
837    /// Get the sine of a number.
838    ///
839    /// More information:
840    ///  - [ECMAScript reference][spec]
841    ///  - [MDN documentation][mdn]
842    ///
843    /// [spec]: https://tc39.es/ecma262/#sec-math.sin
844    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sin
845    pub(crate) fn sin(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
846        Ok(args
847            .get_or_undefined(0)
848            // 1. Let n be ? ToNumber(x).
849            .to_number(context)?
850            // 2. If n is NaN, n is +0𝔽, or n is -0𝔽, return n.
851            // 3. If n is +βˆžπ”½ or n is -βˆžπ”½, return NaN.
852            // 4. Return an implementation-approximated value representing the result of the sine of ℝ(n).
853            .sin()
854            .into())
855    }
856
857    /// Get the hyperbolic sine of a number.
858    ///
859    /// More information:
860    ///  - [ECMAScript reference][spec]
861    ///  - [MDN documentation][mdn]
862    ///
863    /// [spec]: https://tc39.es/ecma262/#sec-math.sinh
864    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sinh
865    pub(crate) fn sinh(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
866        Ok(args
867            .get_or_undefined(0)
868            // 1. Let n be ? ToNumber(x).
869            .to_number(context)?
870            // 2. If n is NaN, n is +0𝔽, n is -0𝔽, n is +βˆžπ”½, or n is -βˆžπ”½, return n.
871            // 3. Return an implementation-approximated value representing the result of the hyperbolic sine of ℝ(n).
872            .sinh()
873            .into())
874    }
875
876    /// Get the square root of a number.
877    ///
878    /// More information:
879    ///  - [ECMAScript reference][spec]
880    ///  - [MDN documentation][mdn]
881    ///
882    /// [spec]: https://tc39.es/ecma262/#sec-math.sqrt
883    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sqrt
884    pub(crate) fn sqrt(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
885        Ok(args
886            .get_or_undefined(0)
887            // 1. Let n be ? ToNumber(x).
888            .to_number(context)?
889            // 2. If n is NaN, n is +0𝔽, n is -0𝔽, or n is +βˆžπ”½, return n.
890            // 3. If n < +0𝔽, return NaN.
891            // 4. Return an implementation-approximated value representing the result of the square root of ℝ(n).
892            .sqrt()
893            .into())
894    }
895
896    /// Get the tangent of a number.
897    ///
898    /// More information:
899    ///  - [ECMAScript reference][spec]
900    ///  - [MDN documentation][mdn]
901    ///
902    /// [spec]: https://tc39.es/ecma262/#sec-math.tan
903    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/tan
904    pub(crate) fn tan(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
905        Ok(args
906            .get_or_undefined(0)
907            // 1. Let n be ? ToNumber(x).
908            .to_number(context)?
909            // 2. If n is NaN, n is +0𝔽, or n is -0𝔽, return n.
910            // 3. If n is +βˆžπ”½, or n is -βˆžπ”½, return NaN.
911            // 4. Return an implementation-approximated value representing the result of the tangent of ℝ(n).
912            .tan()
913            .into())
914    }
915
916    /// Get the hyperbolic tangent of a number.
917    ///
918    /// More information:
919    ///  - [ECMAScript reference][spec]
920    ///  - [MDN documentation][mdn]
921    ///
922    /// [spec]: https://tc39.es/ecma262/#sec-math.tanh
923    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/tanh
924    pub(crate) fn tanh(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
925        Ok(args
926            .get_or_undefined(0)
927            // 1. Let n be ? ToNumber(x).
928            .to_number(context)?
929            // 2. If n is NaN, n is +0𝔽, or n is -0𝔽, return n.
930            // 3. If n is +βˆžπ”½, return 1𝔽.
931            // 4. If n is -βˆžπ”½, return -1𝔽.
932            // 5. Return an implementation-approximated value representing the result of the hyperbolic tangent of ℝ(n).
933            .tanh()
934            .into())
935    }
936
937    /// Get the integer part of a number.
938    ///
939    /// More information:
940    ///  - [ECMAScript reference][spec]
941    ///  - [MDN documentation][mdn]
942    ///
943    /// [spec]: https://tc39.es/ecma262/#sec-math.trunc
944    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc
945    pub(crate) fn trunc(_: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
946        Ok(args
947            .get_or_undefined(0)
948            // 1. Let n be ? ToNumber(x).
949            .to_number(context)?
950            // 2. If n is NaN, n is +0𝔽, n is -0𝔽, n is +βˆžπ”½, or n is -βˆžπ”½, return n.
951            // 3. If n < 1𝔽 and n > +0𝔽, return +0𝔽.
952            // 4. If n < +0𝔽 and n > -1𝔽, return -0𝔽.
953            // 5. Return the integral Number nearest n in the direction of +0𝔽.
954            .trunc()
955            .into())
956    }
957
958    /// Sum each value in an iterable
959    ///
960    /// More information:
961    ///  - [ECMAScript reference][spec]
962    ///  - [MDN documentation][mdn]
963    ///
964    /// [spec]: https://tc39.es/ecma262/#sec-math.sumprecise
965    /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sumPrecise
966    #[cfg(feature = "xsum")]
967    pub(crate) fn sum_precise(
968        _: &JsValue,
969        args: &[JsValue],
970        context: &mut Context,
971    ) -> JsResult<JsValue> {
972        const ITERATION_MAX: u64 = 2u64.pow(53) - 1;
973        use xsum::{Xsum, XsumLarge, XsumSmall, XsumVariant, constants::XSUM_THRESHOLD};
974
975        use crate::{builtins::iterable::IteratorHint, js_error};
976
977        let items = args.get_or_undefined(0);
978        // NOTE (nekevss): inline `RequireObjectCoercible`
979        // 1. Perform ? RequireObjectCoercible(items).
980        // RequireObjectCoercible, 1. If argument is either undefined or null, throw a TypeError exception.
981        // RequireObjectCoercible, 2. Return argument.
982        if items.is_null_or_undefined() {
983            return Err(js_error!(TypeError: "value must be object coercible."));
984        }
985
986        // 2. Let iteratorRecord be ? GetIterator(items, sync).
987        let mut iterator_record = items.get_iterator(IteratorHint::Sync, context)?;
988        // 3. Let state be minus-zero.
989        // 4. Let sum be 0.
990        let length = if iterator_record.iterator().is_array() {
991            iterator_record
992                .iterator()
993                .length_of_array_like(context)
994                .ok()
995        } else {
996            None
997        };
998        let mut sum = if let Some(length) = length {
999            if length <= XSUM_THRESHOLD as u64 {
1000                XsumVariant::Small(XsumSmall::new())
1001            } else {
1002                XsumVariant::Large(XsumLarge::new())
1003            }
1004        } else {
1005            XsumVariant::Small(XsumSmall::new())
1006        };
1007        // 5. Let count be 0.
1008        let mut count = 0;
1009        // 6. Let next be not-started.
1010        // 7. Repeat, while next is not done,
1011        // a. Set next to ? IteratorStepValue(iteratorRecord).
1012        while let Some(next) = iterator_record.step_value(context)? {
1013            // b. If next is not done, then
1014            // i. If count β‰₯ 2**53 - 1, then
1015            if count >= ITERATION_MAX {
1016                // 1. NOTE: This step is not expected to be reached in practice and is
1017                // included only so that implementations may rely on inputs being
1018                // "reasonably sized" without violating this specification.
1019                // 2. Let error be ThrowCompletion(a newly created RangeError object).
1020                let err = Err(js_error!(RangeError: "Summation exceeded maximum iterations"));
1021                // 3. Return ? IteratorClose(iteratorRecord, error).
1022                return iterator_record.close(err, context);
1023            }
1024            // ii. If next is not a Number, then
1025            let Some(number) = next.as_number() else {
1026                // 1. Let error be ThrowCompletion(a newly created TypeError object).
1027                let err = Err(js_error!(TypeError: "sumPrecise can only be called on a number."));
1028                // 2. Return ? IteratorClose(iteratorRecord, error).
1029                return iterator_record.close(err, context);
1030            };
1031            // iii. Let n be next.
1032            // iv. If state is not not-a-number, then
1033            // 1. If n is NaN, then
1034            // a. Set state to not-a-number.
1035            // 2. Else if n is +βˆžπ”½, then
1036            // a. If state is minus-infinity, set state to not-a-number.
1037            // b. Else, set state to plus-infinity.
1038            // 3. Else if n is -βˆžπ”½, then
1039            // a. If state is plus-infinity, set state to not-a-number.
1040            // b. Else, set state to minus-infinity.
1041            // 4. Else if n is not -0𝔽 and state is either minus-zero or finite, then
1042            // a. Set state to finite.
1043            // b. Set sum to sum + ℝ(n).
1044            sum.add(number);
1045            // v. Set count to count + 1.
1046            count += 1;
1047        }
1048        // 8. If state is not-a-number, return NaN.
1049        // 9. If state is plus-infinity, return +βˆžπ”½.
1050        // 10. If state is minus-infinity, return -βˆžπ”½.
1051        // 11. If state is minus-zero, return -0𝔽.
1052        // 12. Return 𝔽(sum).
1053        Ok(sum.sum().into())
1054    }
1055}