1/*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "ecmascript/builtins/builtins_math.h"
17#include <random>
18#include <sys/time.h>
19#include "ecmascript/js_tagged_value-inl.h"
20
21namespace panda::ecmascript::builtins {
22using NumberHelper = base::NumberHelper;
23using RandomGenerator = base::RandomGenerator;
24
25// 20.2.2.1
26JSTaggedValue BuiltinsMath::Abs(EcmaRuntimeCallInfo *argv)
27{
28    ASSERT(argv);
29    BUILTINS_API_TRACE(argv->GetThread(), Math, Abs);
30    JSThread *thread = argv->GetThread();
31    [[maybe_unused]] EcmaHandleScope handleScope(thread);
32    JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
33    JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
34    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
35    if (numberValue.IsDouble()) {
36        // if number_value is double,NaN,Undefine, deal in this case
37        // if number_value is a String ,which can change to double. e.g."100",deal in this case
38        return GetTaggedDouble(std::fabs(numberValue.GetDouble()));
39    }
40    // if number_value is int,boolean,null, deal in this case
41    int value = numberValue.GetInt();
42    if (value == INT_MIN) {
43        return GetTaggedDouble(-static_cast<int64_t>(INT_MIN));
44    }
45    return GetTaggedInt(std::abs(value));
46}
47
48// 20.2.2.2
49JSTaggedValue BuiltinsMath::Acos(EcmaRuntimeCallInfo *argv)
50{
51    ASSERT(argv);
52    BUILTINS_API_TRACE(argv->GetThread(), Math, Acos);
53    JSThread *thread = argv->GetThread();
54    [[maybe_unused]] EcmaHandleScope handleScope(thread);
55    JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
56    JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
57    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
58    double value = numberValue.GetNumber();
59    double result = base::NAN_VALUE;
60    // value == -NaN , <-1  or > 1,result is  NaN
61    if (!std::isnan(std::abs(value)) && value <= 1 && value >= -1) {
62        result = std::acos(value);
63    }
64    return GetTaggedDouble(result);
65}
66
67// 20.2.2.3
68JSTaggedValue BuiltinsMath::Acosh(EcmaRuntimeCallInfo *argv)
69{
70    ASSERT(argv);
71    BUILTINS_API_TRACE(argv->GetThread(), Math, Acosh);
72    JSThread *thread = argv->GetThread();
73    [[maybe_unused]] EcmaHandleScope handleScope(thread);
74    JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
75    JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
76    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
77    double value = numberValue.GetNumber();
78    double result = base::NAN_VALUE;
79    if (value >= 1) {
80        result = std::acosh(value);
81    }
82    return GetTaggedDouble(result);
83}
84
85// 20.2.2.4
86JSTaggedValue BuiltinsMath::Asin(EcmaRuntimeCallInfo *argv)
87{
88    ASSERT(argv);
89    BUILTINS_API_TRACE(argv->GetThread(), Math, Asin);
90    JSThread *thread = argv->GetThread();
91    [[maybe_unused]] EcmaHandleScope handleScope(thread);
92    JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
93    JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
94    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
95    double value = numberValue.GetNumber();
96    double result = base::NAN_VALUE;
97    if (value >= -1 && value <= 1) {
98        result = std::asin(value);
99    }
100    return GetTaggedDouble(result);
101}
102
103// 20.2.2.5
104JSTaggedValue BuiltinsMath::Asinh(EcmaRuntimeCallInfo *argv)
105{
106    ASSERT(argv);
107    BUILTINS_API_TRACE(argv->GetThread(), Math, Asinh);
108    JSThread *thread = argv->GetThread();
109    [[maybe_unused]] EcmaHandleScope handleScope(thread);
110    JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
111    JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
112    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
113    double value = numberValue.GetNumber();
114    double result = base::NAN_VALUE;
115    // value == -NaN, NaN, result is  NaN
116    if (!std::isnan(std::abs(value))) {
117        result = base::MathHelper::Asinh(value);
118    }
119    return GetTaggedDouble(result);
120}
121
122// 20.2.2.6
123JSTaggedValue BuiltinsMath::Atan(EcmaRuntimeCallInfo *argv)
124{
125    ASSERT(argv);
126    BUILTINS_API_TRACE(argv->GetThread(), Math, Atan);
127    JSThread *thread = argv->GetThread();
128    [[maybe_unused]] EcmaHandleScope handleScope(thread);
129    JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
130    JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
131    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
132    double value = numberValue.GetNumber();
133    double result = base::NAN_VALUE;
134    // value == -NaN, NaN, result is  NaN
135    if (!std::isnan(std::abs(value))) {
136        result = std::atan(value);
137    }
138    return GetTaggedDouble(result);
139}
140
141// 20.2.2.7
142JSTaggedValue BuiltinsMath::Atanh(EcmaRuntimeCallInfo *argv)
143{
144    ASSERT(argv);
145    BUILTINS_API_TRACE(argv->GetThread(), Math, Atanh);
146    JSThread *thread = argv->GetThread();
147    [[maybe_unused]] EcmaHandleScope handleScope(thread);
148    JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
149    JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
150    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
151    double value = numberValue.GetNumber();
152    double result = base::NAN_VALUE;
153    if (value >= -1 && value <= 1) {
154        result = base::MathHelper::Atanh(value);
155    }
156    return GetTaggedDouble(result);
157}
158
159// 20.2.2.8
160JSTaggedValue BuiltinsMath::Atan2(EcmaRuntimeCallInfo *argv)
161{
162    ASSERT(argv);
163    BUILTINS_API_TRACE(argv->GetThread(), Math, Atan2);
164    JSThread *thread = argv->GetThread();
165    [[maybe_unused]] EcmaHandleScope handleScope(thread);
166    JSHandle<JSTaggedValue> msgY = GetCallArg(argv, 0);
167    JSHandle<JSTaggedValue> msgX = GetCallArg(argv, 1);
168    double result = base::NAN_VALUE;
169    JSTaggedNumber numberValueY = JSTaggedValue::ToNumber(thread, msgY);
170    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
171    JSTaggedNumber numberValueX = JSTaggedValue::ToNumber(thread, msgX);
172    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
173    double valueY = numberValueY.GetNumber();
174    double valueX = numberValueX.GetNumber();
175    // y = +0 and x > +0, return +0
176    // y = -0 and x > +0, return -0
177    if (valueY == 0 && valueX > 0) {
178        result = valueY;
179    } else if (std::isfinite(valueY) && valueX == std::numeric_limits<double>::infinity()) {
180        // y < 0 and y is finite and x is POSITIVE_INFINITY,return -0
181        // y >= 0 and y is finite and x is POSITIVE_INFINITY,return +0
182        result = valueY >= 0 ? 0 : -0.0;
183    } else if (!std::isnan(std::abs(valueY)) && !std::isnan(std::abs(valueX))) {
184        // If either x or y is NaN, the result is NaN
185        result = std::atan2(valueY, valueX);
186    }
187    return GetTaggedDouble(result);
188}
189
190// 20.2.2.9
191JSTaggedValue BuiltinsMath::Cbrt(EcmaRuntimeCallInfo *argv)
192{
193    ASSERT(argv);
194    BUILTINS_API_TRACE(argv->GetThread(), Math, Cbrt);
195    JSThread *thread = argv->GetThread();
196    [[maybe_unused]] EcmaHandleScope handleScope(thread);
197    JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
198    JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
199    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
200    double value = numberValue.GetNumber();
201    double result = base::NAN_VALUE;
202    // if value == -NaN, NaN, result is NaN
203    if (!std::isnan(std::abs(value))) {
204        result = std::cbrt(value);
205    }
206    return GetTaggedDouble(result);
207}
208
209// 20.2.2.10
210JSTaggedValue BuiltinsMath::Ceil(EcmaRuntimeCallInfo *argv)
211{
212    ASSERT(argv);
213    BUILTINS_API_TRACE(argv->GetThread(), Math, Ceil);
214    JSThread *thread = argv->GetThread();
215    [[maybe_unused]] EcmaHandleScope handleScope(thread);
216    JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
217    JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
218    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
219    double value = numberValue.GetNumber();
220    double result = base::NAN_VALUE;
221    // If value is NaN or -NaN, +infinite, -infinite,return value
222    if (!std::isfinite(value)) {
223        // if value is -NaN , return NaN, else return value
224        if (!std::isnan(std::abs(value))) {
225            result = value;
226        }
227    } else {
228        result = std::ceil(value);
229    }
230    return GetTaggedDouble(result);
231}
232
233// 20.2.2.11
234JSTaggedValue BuiltinsMath::Clz32(EcmaRuntimeCallInfo *argv)
235{
236    ASSERT(argv);
237    BUILTINS_API_TRACE(argv->GetThread(), Math, Clz32);
238    JSThread *thread = argv->GetThread();
239    [[maybe_unused]] EcmaHandleScope handleScope(thread);
240    constexpr int defaultValue = 32;
241    JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
242    JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
243    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
244    double value = numberValue.GetNumber();
245    auto tmpValue = std::abs(value);
246    auto result = numberValue.ToUint32();
247    if (!std::isfinite(tmpValue) || tmpValue == 0 || result == 0) {
248        // If value is NaN or -NaN, +infinite, -infinite, 0,return 32
249        return GetTaggedInt(defaultValue);
250    }
251    return GetTaggedInt(__builtin_clz(result));
252}
253
254// 20.2.2.12
255JSTaggedValue BuiltinsMath::Cos(EcmaRuntimeCallInfo *argv)
256{
257    ASSERT(argv);
258    BUILTINS_API_TRACE(argv->GetThread(), Math, Cos);
259    JSThread *thread = argv->GetThread();
260    [[maybe_unused]] EcmaHandleScope handleScope(thread);
261    JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
262    JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
263    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
264    double value = numberValue.GetNumber();
265    double result = base::NAN_VALUE;
266    //  If value is NaN or -NaN, +infinite, -infinite, result is NaN
267    if (std::isfinite(std::abs(value))) {
268        result = std::cos(value);
269    }
270    return GetTaggedDouble(result);
271}
272
273// 20.2.2.13
274JSTaggedValue BuiltinsMath::Cosh(EcmaRuntimeCallInfo *argv)
275{
276    ASSERT(argv);
277    BUILTINS_API_TRACE(argv->GetThread(), Math, Cosh);
278    JSThread *thread = argv->GetThread();
279    [[maybe_unused]] EcmaHandleScope handleScope(thread);
280    JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
281    JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
282    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
283    double value = numberValue.GetNumber();
284    double result = base::NAN_VALUE;
285    // if value is NaN or -NaN, result is NaN
286    if (!std::isnan(std::abs(value))) {
287        result = std::cosh(value);
288    }
289    return GetTaggedDouble(result);
290}
291
292// 20.2.2.14
293JSTaggedValue BuiltinsMath::Exp(EcmaRuntimeCallInfo *argv)
294{
295    ASSERT(argv);
296    BUILTINS_API_TRACE(argv->GetThread(), Math, Exp);
297    JSThread *thread = argv->GetThread();
298    [[maybe_unused]] EcmaHandleScope handleScope(thread);
299    JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
300    JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
301    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
302    double value = numberValue.GetNumber();
303    double result = base::NAN_VALUE;
304    // if value is NaN or -NaN, result is NaN
305    if (!std::isnan(std::abs(value))) {
306        result = std::exp(value);
307    }
308    return GetTaggedDouble(result);
309}
310
311// 20.2.2.15
312JSTaggedValue BuiltinsMath::Expm1(EcmaRuntimeCallInfo *argv)
313{
314    ASSERT(argv);
315    BUILTINS_API_TRACE(argv->GetThread(), Math, Expm1);
316    JSThread *thread = argv->GetThread();
317    [[maybe_unused]] EcmaHandleScope handleScope(thread);
318    JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
319    JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
320    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
321    double value = numberValue.GetNumber();
322    double result = base::NAN_VALUE;
323    // if value is NaN or -NaN, result is NaN
324    if (!std::isnan(std::abs(value))) {
325        result = std::expm1(value);
326    }
327    return GetTaggedDouble(result);
328}
329
330// 20.2.2.16
331JSTaggedValue BuiltinsMath::Floor(EcmaRuntimeCallInfo *argv)
332{
333    ASSERT(argv);
334    BUILTINS_API_TRACE(argv->GetThread(), Math, Floor);
335    JSThread *thread = argv->GetThread();
336    [[maybe_unused]] EcmaHandleScope handleScope(thread);
337    JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
338    JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
339    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
340    double value = numberValue.GetNumber();
341    double result = base::NAN_VALUE;
342    // If value is NaN or -NaN, +infinite, -infinite, +0, -0, return value
343    if (!std::isfinite(value) || value == 0) {
344        // If value is -NaN, return NaN, else return value
345        if (!std::isnan(std::abs(value))) {
346            result = value;
347        }
348    } else if (value > 0 && value < 1) {
349        // If x is greater than 0 but less than 1, the result is +0
350        result = 0;
351    } else {
352        result = std::floor(value);
353    }
354    return GetTaggedDouble(result);
355}
356
357// 20.2.2.17
358JSTaggedValue BuiltinsMath::Fround(EcmaRuntimeCallInfo *argv)
359{
360    ASSERT(argv);
361    BUILTINS_API_TRACE(argv->GetThread(), Math, Fround);
362    JSThread *thread = argv->GetThread();
363    [[maybe_unused]] EcmaHandleScope handleScope(thread);
364    JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
365    JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
366    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
367    double value = numberValue.GetNumber();
368    double result;
369    if (std::isnan(std::abs(value))) {
370        // If result is NaN or -NaN, the result is NaN
371        result = base::NAN_VALUE;
372    } else {
373        result = static_cast<float>(value);
374    }
375    return GetTaggedDouble(result);
376}
377
378// 20.2.2.18
379JSTaggedValue BuiltinsMath::Hypot(EcmaRuntimeCallInfo *argv)
380{
381    ASSERT(argv);
382    BUILTINS_API_TRACE(argv->GetThread(), Math, Hypot);
383    JSThread *thread = argv->GetThread();
384    [[maybe_unused]] EcmaHandleScope handleScope(thread);
385    double result = 0;
386    double value = 0;
387    uint32_t argLen = argv->GetArgsNumber();
388    auto numberValue = JSTaggedNumber(0);
389    for (uint32_t i = 0; i < argLen; i++) {
390        JSHandle<JSTaggedValue> msg = GetCallArg(argv, i);
391        numberValue = JSTaggedValue::ToNumber(thread, msg);
392        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
393        value = numberValue.GetNumber();
394        result = std::hypot(result, value);
395    }
396    return GetTaggedDouble(result);
397}
398
399// 20.2.2.19
400JSTaggedValue BuiltinsMath::Imul(EcmaRuntimeCallInfo *argv)
401{
402    ASSERT(argv);
403    BUILTINS_API_TRACE(argv->GetThread(), Math, Imul);
404    JSThread *thread = argv->GetThread();
405    [[maybe_unused]] EcmaHandleScope handleScope(thread);
406    JSHandle<JSTaggedValue> msg1 = GetCallArg(argv, 0);
407    JSHandle<JSTaggedValue> msg2 = GetCallArg(argv, 1);
408    JSTaggedNumber numberValue1 = JSTaggedValue::ToNumber(thread, msg1);
409    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
410    JSTaggedNumber numberValue2 = JSTaggedValue::ToNumber(thread, msg2);
411    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
412    auto value1 = numberValue1.GetNumber();
413    auto value2 = numberValue2.GetNumber();
414    if (!std::isfinite(value1) || !std::isfinite(value2)) {
415        // If value is NaN or -NaN, +infinite, -infinite
416        return GetTaggedInt(0);
417    }
418    value1 = numberValue1.ToInt32();
419    value2 = numberValue2.ToInt32();
420    // purposely ignoring overflow
421    auto result = static_cast<int32_t>(static_cast<int64_t>(value1) * static_cast<int64_t>(value2));
422    return GetTaggedInt(result);
423}
424
425// 20.2.2.20
426JSTaggedValue BuiltinsMath::Log(EcmaRuntimeCallInfo *argv)
427{
428    ASSERT(argv);
429    BUILTINS_API_TRACE(argv->GetThread(), Math, Log);
430    JSThread *thread = argv->GetThread();
431    [[maybe_unused]] EcmaHandleScope handleScope(thread);
432    JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
433    JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
434    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
435    double value = numberValue.GetNumber();
436    double result = base::NAN_VALUE;
437    // If value is NaN , -NaN , or < 0,result is NaN
438    if (!std::isnan(std::abs(value)) && value >= 0) {
439        result = std::log(value);
440    }
441    return GetTaggedDouble(result);
442}
443
444// 20.2.2.21
445JSTaggedValue BuiltinsMath::Log1p(EcmaRuntimeCallInfo *argv)
446{
447    ASSERT(argv);
448    BUILTINS_API_TRACE(argv->GetThread(), Math, Log1p);
449    JSThread *thread = argv->GetThread();
450    [[maybe_unused]] EcmaHandleScope handleScope(thread);
451    JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
452    JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
453    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
454    double value = numberValue.GetNumber();
455    double result = base::NAN_VALUE;
456    // If value is NaN , -NaN , or < -1,result is NaN
457    if (!std::isnan(std::abs(value)) && value >= -1) {
458        result = std::log1p(value);
459    }
460    return GetTaggedDouble(result);
461}
462
463// 20.2.2.22
464JSTaggedValue BuiltinsMath::Log10(EcmaRuntimeCallInfo *argv)
465{
466    ASSERT(argv);
467    BUILTINS_API_TRACE(argv->GetThread(), Math, Log10);
468    JSThread *thread = argv->GetThread();
469    [[maybe_unused]] EcmaHandleScope handleScope(thread);
470    JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
471    JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
472    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
473    double value = numberValue.GetNumber();
474    double result = base::NAN_VALUE;
475    // If value is NaN , -NaN , or < 0,result is NaN
476    if (!std::isnan(std::abs(value)) && value >= 0) {
477        result = std::log10(value);
478    }
479    return GetTaggedDouble(result);
480}
481
482// 20.2.2.23
483JSTaggedValue BuiltinsMath::Log2(EcmaRuntimeCallInfo *argv)
484{
485    ASSERT(argv);
486    BUILTINS_API_TRACE(argv->GetThread(), Math, Log2);
487    JSThread *thread = argv->GetThread();
488    [[maybe_unused]] EcmaHandleScope handleScope(thread);
489    JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
490    JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
491    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
492    double value = numberValue.GetNumber();
493    double result = base::NAN_VALUE;
494    // If value is NaN , -NaN , or < 0,result is NaN
495    if (!std::isnan(std::abs(value)) && value >= 0) {
496        result = std::log2(value);
497    }
498    return GetTaggedDouble(result);
499}
500
501inline bool IsNegZero(double value)
502{
503    return (value == 0.0 && (base::bit_cast<uint64_t>(value) & base::DOUBLE_SIGN_MASK) == base::DOUBLE_SIGN_MASK);
504}
505
506// 20.2.2.24
507JSTaggedValue BuiltinsMath::Max(EcmaRuntimeCallInfo *argv)
508{
509    ASSERT(argv);
510    BUILTINS_API_TRACE(argv->GetThread(), Math, Max);
511    JSThread *thread = argv->GetThread();
512    [[maybe_unused]] EcmaHandleScope handleScope(thread);
513    uint32_t argLen = argv->GetArgsNumber();
514    auto numberValue = JSTaggedNumber(-base::POSITIVE_INFINITY);
515    // If no arguments are given, the result is -inf
516    auto result = JSTaggedNumber(-base::POSITIVE_INFINITY);
517    auto tmpMax = -base::POSITIVE_INFINITY;
518    auto value = -base::POSITIVE_INFINITY;
519    bool flag = false;
520    uint32_t i = 0;
521    for (; i < argLen; i++) {
522        JSHandle<JSTaggedValue> msg = GetCallArg(argv, i);
523        numberValue = JSTaggedValue::ToNumber(thread, msg);
524        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
525        value = numberValue.GetNumber();
526        if (std::isnan(std::abs(value))) {
527            // If any value is NaN, or -NaN, the max result is NaN
528            result = numberValue;
529            flag = true;
530            i++;
531            break;
532        }
533        if (value > tmpMax) {
534            result = numberValue;
535            tmpMax = value;
536        } else if (value == 0 && tmpMax == 0 && IsNegZero(tmpMax) && !IsNegZero(value)) {
537            // if tmp_max is -0, value is 0, max is 0
538            result = numberValue;
539            tmpMax = value;
540        }
541    }
542    if (flag) {
543        for (; i < argLen; i++) {
544            JSHandle<JSTaggedValue> msg = GetCallArg(argv, i);
545            numberValue = JSTaggedValue::ToNumber(thread, msg);
546            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
547        }
548    }
549    return result;
550}
551
552// 20.2.2.25
553JSTaggedValue BuiltinsMath::Min(EcmaRuntimeCallInfo *argv)
554{
555    ASSERT(argv);
556    BUILTINS_API_TRACE(argv->GetThread(), Math, Min);
557    JSThread *thread = argv->GetThread();
558    [[maybe_unused]] EcmaHandleScope handleScope(thread);
559    uint32_t argLen = argv->GetArgsNumber();
560    auto numberValue = JSTaggedNumber(base::POSITIVE_INFINITY);
561    // If no arguments are given, the result is inf
562    auto result = JSTaggedNumber(base::POSITIVE_INFINITY);
563    auto tmpMin = base::POSITIVE_INFINITY;
564    auto value = base::POSITIVE_INFINITY;
565    bool flag = false;
566    uint32_t i = 0;
567    for (; i < argLen; i++) {
568        JSHandle<JSTaggedValue> msg = GetCallArg(argv, i);
569        numberValue = JSTaggedValue::ToNumber(thread, msg);
570        RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
571        value = numberValue.GetNumber();
572        if (std::isnan(std::abs(value))) {
573            // If any value is NaN or -NaN, the min result is NaN
574            result = numberValue;
575            flag = true;
576            i++;
577            break;
578        }
579        if (value < tmpMin) {
580            result = numberValue;
581            tmpMin = value;
582        } else if (value == 0 && tmpMin == 0 && !IsNegZero(tmpMin) && IsNegZero(value)) {
583            // if tmp_min is 0, value is -0, min is -0
584            result = numberValue;
585            tmpMin = value;
586        }
587    }
588    if (flag) {
589        for (; i < argLen; i++) {
590            JSHandle<JSTaggedValue> msg = GetCallArg(argv, i);
591            numberValue = JSTaggedValue::ToNumber(thread, msg);
592            RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
593        }
594    }
595    return result;
596}
597
598// 20.2.2.26
599JSTaggedValue BuiltinsMath::Pow(EcmaRuntimeCallInfo *argv)
600{
601    ASSERT(argv);
602    BUILTINS_API_TRACE(argv->GetThread(), Math, Pow);
603    JSThread *thread = argv->GetThread();
604    [[maybe_unused]] EcmaHandleScope handleScope(thread);
605    JSHandle<JSTaggedValue> msgX = GetCallArg(argv, 0);
606    JSHandle<JSTaggedValue> msgY = GetCallArg(argv, 1);
607    JSHandle<JSTaggedValue> baseVale = JSTaggedValue::ToNumeric(thread, msgX);
608    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
609    JSHandle<JSTaggedValue> exponentValue = JSTaggedValue::ToNumeric(thread, msgY);
610    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
611    if (baseVale->IsBigInt() || exponentValue->IsBigInt()) {
612        if (baseVale->IsBigInt() && exponentValue->IsBigInt()) {
613            JSHandle<BigInt> bigBaseVale(baseVale);
614            JSHandle<BigInt> bigExponentValue(exponentValue);
615            return  BigInt::Exponentiate(thread, bigBaseVale, bigExponentValue).GetTaggedValue();
616        }
617        THROW_TYPE_ERROR_AND_RETURN(thread, "Cannot mix BigInt and other types, use explicit conversions",
618                                    JSTaggedValue::Exception());
619    }
620    double valueX = baseVale->GetNumber();
621    double valueY = exponentValue->GetNumber();
622    // If abs(x) is 1 and y is inf or -inf, the result is NaN
623    if (std::abs(valueX) == 1 && !std::isfinite(valueY)) {
624        return GetTaggedDouble(base::NAN_VALUE);
625    }
626    double result = std::pow(valueX, valueY);
627    if (std::isnan(std::abs(result))) {
628        // If result is NaN or -NaN, the result is NaN
629        result = base::NAN_VALUE;
630    }
631    return GetTaggedDouble(result);
632}
633
634// 20.2.2.27
635JSTaggedValue BuiltinsMath::Random(EcmaRuntimeCallInfo *argv)
636{
637    ASSERT(argv);
638    JSThread *thread = argv->GetThread();
639    BUILTINS_API_TRACE(thread, Math, Random);
640    [[maybe_unused]] EcmaHandleScope handleScope(thread);
641    return GetTaggedDouble(RandomGenerator::NextDouble());
642}
643
644// 20.2.2.28
645JSTaggedValue BuiltinsMath::Round(EcmaRuntimeCallInfo *argv)
646{
647    ASSERT(argv);
648    BUILTINS_API_TRACE(argv->GetThread(), Math, Round);
649    JSThread *thread = argv->GetThread();
650    [[maybe_unused]] EcmaHandleScope handleScope(thread);
651    JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
652    JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
653    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
654    double value = numberValue.GetNumber();
655    auto result = base::NAN_VALUE;
656    const double diff = 0.5;
657    double absValue = std::abs(value);
658    if (!std::isfinite(absValue) || absValue == 0) {
659        // If value is NaN, +infinite, or -infinite, VRegisterTag is DOUBLE
660        if (!std::isnan(absValue)) {
661            // If value is NaN or -NaN, the result is default NaN, else is value
662            result = value;
663        }
664        return GetTaggedDouble(result);
665    }
666    // If x is less than 0 but greater than or equal to -0.5, the result is -0
667    if (value < 0 && value >= -diff) {
668        return GetTaggedDouble(-0.0);
669    }
670    // If x is greater than 0 but less than 0.5, the result is +0
671    if (value > 0 && value < diff) {
672        return GetTaggedInt(0);
673    }
674    // For huge integers
675    result = std::ceil(value);
676    if (result - value > diff) {
677        result -= 1;
678    }
679    return GetTaggedDouble(result);
680}
681
682// 20.2.2.29
683JSTaggedValue BuiltinsMath::Sign(EcmaRuntimeCallInfo *argv)
684{
685    ASSERT(argv);
686    BUILTINS_API_TRACE(argv->GetThread(), Math, Sign);
687    JSThread *thread = argv->GetThread();
688    [[maybe_unused]] EcmaHandleScope handleScope(thread);
689    JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
690    JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
691    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
692    double value = numberValue.GetNumber();
693    if (std::isnan(std::abs(value))) {
694        return GetTaggedDouble(std::abs(value));
695    }
696    if (value == 0.0) {
697        return GetTaggedDouble(value);
698    }
699    if (value < 0) {
700        return GetTaggedInt(-1);
701    }
702    return GetTaggedInt(1);
703}
704
705// 20.2.2.30
706JSTaggedValue BuiltinsMath::Sin(EcmaRuntimeCallInfo *argv)
707{
708    ASSERT(argv);
709    BUILTINS_API_TRACE(argv->GetThread(), Math, Sin);
710    JSThread *thread = argv->GetThread();
711    [[maybe_unused]] EcmaHandleScope handleScope(thread);
712    JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
713    JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
714    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
715    double value = numberValue.GetNumber();
716    double result = base::NAN_VALUE;
717    // If value is NaN or -NaN, the result is NaN
718    if (std::isfinite(std::abs(value))) {
719        result = std::sin(value);
720    }
721    return GetTaggedDouble(result);
722}
723
724// 20.2.2.31
725JSTaggedValue BuiltinsMath::Sinh(EcmaRuntimeCallInfo *argv)
726{
727    ASSERT(argv);
728    BUILTINS_API_TRACE(argv->GetThread(), Math, Sinh);
729    JSThread *thread = argv->GetThread();
730    [[maybe_unused]] EcmaHandleScope handleScope(thread);
731    JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
732    JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
733    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
734    double value = numberValue.GetNumber();
735    double result = base::NAN_VALUE;
736    // If value is NaN or -NaN, the result is NaN
737    if (!std::isnan(std::abs(value))) {
738        result = std::sinh(value);
739    }
740    return GetTaggedDouble(result);
741}
742
743// 20.2.2.32
744JSTaggedValue BuiltinsMath::Sqrt(EcmaRuntimeCallInfo *argv)
745{
746    ASSERT(argv);
747    BUILTINS_API_TRACE(argv->GetThread(), Math, Sqrt);
748    JSThread *thread = argv->GetThread();
749    [[maybe_unused]] EcmaHandleScope handleScope(thread);
750    JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
751    JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
752    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
753    double value = numberValue.GetNumber();
754    double result = base::NAN_VALUE;
755    // If value is negative, include -NaN and -Infinity but not -0.0, the result is NaN
756    if (std::signbit(value) && value != 0) {
757        return GetTaggedDouble(result);
758    }
759    // If value is NaN, the result is NaN
760    if (!std::isnan(value)) {
761        result = std::sqrt(value);
762    }
763    return GetTaggedDouble(result);
764}
765
766// 20.2.2.33
767JSTaggedValue BuiltinsMath::Tan(EcmaRuntimeCallInfo *argv)
768{
769    ASSERT(argv);
770    BUILTINS_API_TRACE(argv->GetThread(), Math, Tan);
771    JSThread *thread = argv->GetThread();
772    [[maybe_unused]] EcmaHandleScope handleScope(thread);
773    JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
774    JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
775    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
776    double value = numberValue.GetNumber();
777    double result = base::NAN_VALUE;
778    // If value is NaN or -NaN, +infinite, -infinite, result is NaN
779    if (std::isfinite(value)) {
780        result = std::tan(value);
781    }
782    return GetTaggedDouble(result);
783}
784
785// 20.2.2.34
786JSTaggedValue BuiltinsMath::Tanh(EcmaRuntimeCallInfo *argv)
787{
788    ASSERT(argv);
789    BUILTINS_API_TRACE(argv->GetThread(), Math, Tanh);
790    JSThread *thread = argv->GetThread();
791    [[maybe_unused]] EcmaHandleScope handleScope(thread);
792    JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
793    JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
794    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
795    double value = numberValue.GetNumber();
796    double result = base::NAN_VALUE;
797    if (!std::isnan(std::abs(value))) {
798        result = std::tanh(value);
799    }
800    return GetTaggedDouble(result);
801}
802
803// 20.2.2.35
804JSTaggedValue BuiltinsMath::Trunc(EcmaRuntimeCallInfo *argv)
805{
806    ASSERT(argv);
807    BUILTINS_API_TRACE(argv->GetThread(), Math, Trunc);
808    JSThread *thread = argv->GetThread();
809    [[maybe_unused]] EcmaHandleScope handleScope(thread);
810    JSHandle<JSTaggedValue> msg = GetCallArg(argv, 0);
811    JSTaggedNumber numberValue = JSTaggedValue::ToNumber(thread, msg);
812    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
813    double value = numberValue.GetNumber();
814    double result = base::NAN_VALUE;
815    if (!std::isfinite(value)) {
816        // if value is +infinite, -infinite, NaN, -NaN, VRegisterTag is double
817        if (!std::isnan(std::abs(value))) {
818            // if value is +infinite, -infinite, result is value
819            result = value;
820        }
821    } else {
822        result = std::trunc(value);
823    }
824    return GetTaggedDouble(result);
825}
826}  // namespace panda::ecmascript::builtins
827