/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <cmath>
#include <iostream>
#include <limits>
#include "ecmascript/base/number_helper.h"
#include "ecmascript/base/string_helper.h"
#include "ecmascript/builtins/builtins_number.h"
#include "ecmascript/ecma_runtime_call_info.h"
#include "ecmascript/ecma_string.h"
#include "ecmascript/ecma_vm.h"
#include "ecmascript/global_env.h"
#include "ecmascript/js_function.h"
#include "ecmascript/js_global_object.h"
#include "ecmascript/js_handle.h"
#include "ecmascript/js_primitive_ref.h"
#include "ecmascript/js_tagged_value-inl.h"
#include "ecmascript/js_thread.h"
#include "ecmascript/object_factory.h"
#include "ecmascript/tests/test_helper.h"

using namespace panda::ecmascript;
using namespace panda::ecmascript::builtins;

namespace panda::test {
class BuiltinsNumberTest : public testing::Test {
public:
    static void SetUpTestCase()
    {
        GTEST_LOG_(INFO) << "SetUpTestCase";
    }

    static void TearDownTestCase()
    {
        GTEST_LOG_(INFO) << "TearDownCase";
    }

    void SetUp() override
    {
        TestHelper::CreateEcmaVMWithScope(instance, thread, scope);
    }

    void TearDown() override
    {
        TestHelper::DestroyEcmaVMWithScope(instance, scope);
    }

    PandaVM *instance {nullptr};
    EcmaHandleScope *scope {nullptr};
    JSThread *thread {nullptr};
};

// new Number(10)
HWTEST_F_L0(BuiltinsNumberTest, NumberConstructor)
{
    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();

    JSHandle<JSFunction> number(env->GetNumberFunction());
    JSHandle<JSObject> globalObject(thread, env->GetGlobalObject());

    auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*number), 6);
    ecmaRuntimeCallInfo->SetFunction(number.GetTaggedValue());
    ecmaRuntimeCallInfo->SetThis(globalObject.GetTaggedValue());
    ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast<double>(5)));

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get());
    JSTaggedValue result = BuiltinsNumber::NumberConstructor(ecmaRuntimeCallInfo.get());
    JSTaggedValue value(static_cast<JSTaggedType>(result.GetRawData()));
    ASSERT_TRUE(value.IsECMAObject());
    JSPrimitiveRef *ref = JSPrimitiveRef::Cast(value.GetTaggedObject());
    ASSERT_EQ(ref->GetValue().GetDouble(), 5.0);
}

// Number.isFinite(-10)
HWTEST_F_L0(BuiltinsNumberTest, IsFinite)
{
    const double value = -10;
    auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(static_cast<double>(value)));

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get());
    JSTaggedValue result = BuiltinsNumber::IsFinite(ecmaRuntimeCallInfo.get());
    ASSERT_EQ(result.GetRawData(), JSTaggedValue::True().GetRawData());
}

// Number.isFinite(Number.MAX_VALUE)
HWTEST_F_L0(BuiltinsNumberTest, IsFinite1)
{
    auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::MAX_VALUE));

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get());
    JSTaggedValue result = BuiltinsNumber::IsFinite(ecmaRuntimeCallInfo.get());
    ASSERT_EQ(result.GetRawData(), JSTaggedValue::True().GetRawData());
}

// Number.isFinite("helloworld")
HWTEST_F_L0(BuiltinsNumberTest, IsFinite2)
{
    JSHandle<EcmaString> test = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("helloworld");
    auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetCallArg(0, test.GetTaggedValue());

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get());
    JSTaggedValue result = BuiltinsNumber::IsFinite(ecmaRuntimeCallInfo.get());
    ASSERT_EQ(result.GetRawData(), JSTaggedValue::False().GetRawData());
}

// Number.isFinite(NaN)
HWTEST_F_L0(BuiltinsNumberTest, IsFinite3)
{
    auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::NAN_VALUE));

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get());
    JSTaggedValue result = BuiltinsNumber::IsFinite(ecmaRuntimeCallInfo.get());
    ASSERT_EQ(result.GetRawData(), JSTaggedValue::False().GetRawData());
}

// Number.isFinite(Infinity)
HWTEST_F_L0(BuiltinsNumberTest, IsFinite4)
{
    auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(base::POSITIVE_INFINITY));

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get());
    JSTaggedValue result = BuiltinsNumber::IsFinite(ecmaRuntimeCallInfo.get());
    ASSERT_EQ(result.GetRawData(), JSTaggedValue::False().GetRawData());
}

// Number.isFinite(undefined)
HWTEST_F_L0(BuiltinsNumberTest, IsFinite5)
{
    auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Undefined());

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get());
    JSTaggedValue result = BuiltinsNumber::IsFinite(ecmaRuntimeCallInfo.get());
    ASSERT_EQ(result.GetRawData(), JSTaggedValue::False().GetRawData());
}

// Number.isFinite(null)
HWTEST_F_L0(BuiltinsNumberTest, IsFinite6)
{
    auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue::Null());

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get());
    JSTaggedValue result = BuiltinsNumber::IsFinite(ecmaRuntimeCallInfo.get());
    ASSERT_EQ(result.GetRawData(), JSTaggedValue::False().GetRawData());
}

// Number.isInteger(0.1)
HWTEST_F_L0(BuiltinsNumberTest, IsInteger)
{
    auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(0.1));

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get());
    JSTaggedValue result = BuiltinsNumber::IsInteger(ecmaRuntimeCallInfo.get());
    ASSERT_EQ(result.GetRawData(), JSTaggedValue::False().GetRawData());
}

// Number.isNaN(0.1)
HWTEST_F_L0(BuiltinsNumberTest, IsNaN)
{
    auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(0.1));

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get());
    JSTaggedValue result = BuiltinsNumber::IsNaN(ecmaRuntimeCallInfo.get());
    ASSERT_EQ(result.GetRawData(), JSTaggedValue::False().GetRawData());
}

// new Number(123.456).toString(7)
HWTEST_F_L0(BuiltinsNumberTest, ToString)
{
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    auto ecmaVM = thread->GetEcmaVM();
    JSHandle<GlobalEnv> env = ecmaVM->GetGlobalEnv();

    JSHandle<JSFunction> numberObject(env->GetNumberFunction());
    JSHandle<JSTaggedValue> value(thread, JSTaggedValue(123.456));
    JSHandle<JSPrimitiveRef> number = thread->GetEcmaVM()->GetFactory()->NewJSPrimitiveRef(numberObject, value);

    auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetThis(number.GetTaggedValue());
    ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(7.0));

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get());
    JSTaggedValue result = BuiltinsNumber::ToString(ecmaRuntimeCallInfo.get());
    ASSERT_TRUE(result.IsString());
    JSHandle<EcmaString> res(thread, reinterpret_cast<EcmaString *>(result.GetRawData()));
    JSHandle<EcmaString> correctResult = factory->NewFromCanBeCompressString("234.312256641535441");
    CVector<uint8_t> test(res->GetLength() + 1);
    res->CopyDataUtf8(test.data(), res->GetLength());
    ASSERT_TRUE(EcmaString::StringsAreEqual(*res, *correctResult));
}

// new Number(123.456).toExponential(5)
HWTEST_F_L0(BuiltinsNumberTest, IsExponential)
{
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    auto ecmaVM = thread->GetEcmaVM();
    JSHandle<GlobalEnv> env = ecmaVM->GetGlobalEnv();

    JSHandle<JSFunction> numberObject(env->GetNumberFunction());
    JSHandle<JSTaggedValue> value(thread, JSTaggedValue(123.456));
    JSHandle<JSPrimitiveRef> number = factory->NewJSPrimitiveRef(numberObject, value);

    auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetThis(number.GetTaggedValue());
    ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(5.0));

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get());
    JSTaggedValue result = BuiltinsNumber::ToExponential(ecmaRuntimeCallInfo.get());
    ASSERT_TRUE(result.IsString());
    JSHandle<EcmaString> res(thread, reinterpret_cast<EcmaString *>(result.GetRawData()));
    JSHandle<EcmaString> correctResult = factory->NewFromCanBeCompressString("1.23456e+2");
    CVector<uint8_t> test(res->GetLength() + 1);
    res->CopyDataUtf8(test.data(), res->GetLength());
    ASSERT_TRUE(EcmaString::StringsAreEqual(*res, *correctResult));
}

// new Number(123.456).toFixed(10)
HWTEST_F_L0(BuiltinsNumberTest, ToFixed)
{
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    auto ecmaVM = thread->GetEcmaVM();
    JSHandle<GlobalEnv> env = ecmaVM->GetGlobalEnv();

    JSHandle<JSFunction> numberObject(env->GetNumberFunction());
    JSHandle<JSTaggedValue> value(thread, JSTaggedValue(123.456));
    JSHandle<JSPrimitiveRef> number = factory->NewJSPrimitiveRef(numberObject, value);

    auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetThis(number.GetTaggedValue());
    ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(10.0));

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get());
    JSTaggedValue result = BuiltinsNumber::ToFixed(ecmaRuntimeCallInfo.get());
    ASSERT_TRUE(result.IsString());
    JSHandle<EcmaString> res(thread, reinterpret_cast<EcmaString *>(result.GetRawData()));
    JSHandle<EcmaString> correctResult = factory->NewFromCanBeCompressString("123.4560000000");
    ASSERT_TRUE(EcmaString::StringsAreEqual(*res, *correctResult));
}

// new Number(123.456).toFixed(30)
HWTEST_F_L0(BuiltinsNumberTest, ToFixed1)
{
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    auto ecmaVM = thread->GetEcmaVM();
    JSHandle<GlobalEnv> env = ecmaVM->GetGlobalEnv();

    JSHandle<JSFunction> numberObject(env->GetNumberFunction());
    JSHandle<JSTaggedValue> value(thread, JSTaggedValue(123.456));
    JSHandle<JSPrimitiveRef> number = factory->NewJSPrimitiveRef(numberObject, value);

    auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetThis(number.GetTaggedValue());
    ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(30.0));

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get());
    JSTaggedValue result = BuiltinsNumber::ToFixed(ecmaRuntimeCallInfo.get());
    ASSERT_TRUE(result.IsString());
    JSHandle<EcmaString> res(thread, reinterpret_cast<EcmaString *>(result.GetRawData()));
    JSHandle<EcmaString> correctResult = factory->NewFromCanBeCompressString("123.456000000000003069544618483633");
    ASSERT_TRUE(EcmaString::StringsAreEqual(*res, *correctResult));
}

// new Number(1e21).toFixed(20)
HWTEST_F_L0(BuiltinsNumberTest, ToFixed2) {
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    auto ecmaVM = thread->GetEcmaVM();
    JSHandle<GlobalEnv> env = ecmaVM->GetGlobalEnv();

    JSHandle<JSFunction> numberObject(env->GetNumberFunction());
    JSHandle<JSTaggedValue> value(thread, JSTaggedValue(1e21));
    JSHandle<JSPrimitiveRef> number = factory->NewJSPrimitiveRef(numberObject, value);

    auto ecmaRuntimeCallInfo =
        TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetThis(number.GetTaggedValue());
    ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(20.0));

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get());
    JSTaggedValue result = BuiltinsNumber::ToFixed(ecmaRuntimeCallInfo.get());
    ASSERT_TRUE(result.IsString());
    JSHandle<EcmaString> res(thread, reinterpret_cast<EcmaString *>(result.GetRawData()));
    JSHandle<EcmaString> correctResult = factory->NewFromCanBeCompressString("1e+21");
    CVector<uint8_t> test(res->GetLength() + 1);
    res->CopyDataUtf8(test.data(), res->GetLength());
    std::cout << test.data();
    ASSERT_TRUE(EcmaString::StringsAreEqual(*res, *correctResult));
}

// new Number(123.456).toPrecision(8)
HWTEST_F_L0(BuiltinsNumberTest, ToPrecision)
{
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    auto ecmaVM = thread->GetEcmaVM();
    JSHandle<GlobalEnv> env = ecmaVM->GetGlobalEnv();

    JSHandle<JSFunction> numberObject(env->GetNumberFunction());
    JSHandle<JSTaggedValue> value(thread, JSTaggedValue(123.456));
    JSHandle<JSPrimitiveRef> number = factory->NewJSPrimitiveRef(numberObject, value);

    auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetThis(number.GetTaggedValue());
    ecmaRuntimeCallInfo->SetCallArg(0, JSTaggedValue(8.0));

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get());
    JSTaggedValue result = BuiltinsNumber::ToPrecision(ecmaRuntimeCallInfo.get());
    ASSERT_TRUE(result.IsString());
    JSHandle<EcmaString> res(thread, reinterpret_cast<EcmaString *>(result.GetRawData()));
    JSHandle<EcmaString> correctResult = factory->NewFromCanBeCompressString("123.45600");
    ASSERT_TRUE(EcmaString::StringsAreEqual(*res, *correctResult));
}

// Number.parseFloat(0x123)
HWTEST_F_L0(BuiltinsNumberTest, parseFloat)
{
    JSHandle<EcmaString> param = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0x123");
    auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetCallArg(0, param.GetTaggedValue());

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get());
    JSTaggedValue result = BuiltinsNumber::ParseFloat(ecmaRuntimeCallInfo.get());
    ASSERT_EQ(result.GetRawData(), JSTaggedValue(static_cast<double>(0)).GetRawData());
}

// Number.parseFloat(0x123xx)
HWTEST_F_L0(BuiltinsNumberTest, parseFloat1)
{
    JSHandle<EcmaString> param = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0x123xx");
    auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
    ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetCallArg(0, param.GetTaggedValue());

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get());
    JSTaggedValue result = BuiltinsNumber::ParseFloat(ecmaRuntimeCallInfo.get());
    ASSERT_EQ(result.GetRawData(), JSTaggedValue(static_cast<double>(0)).GetRawData());
}

// Number.parseInt(0x123)
HWTEST_F_L0(BuiltinsNumberTest, parseInt)
{
    const char *number = "0x123";

    JSHandle<EcmaString> param = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(number);
    auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 8);
    ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetThis(JSTaggedValue::Undefined());
    ecmaRuntimeCallInfo->SetCallArg(0, param.GetTaggedValue());
    ecmaRuntimeCallInfo->SetCallArg(1, JSTaggedValue(16.0));

    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo.get());
    JSTaggedValue result = BuiltinsNumber::ParseInt(ecmaRuntimeCallInfo.get());
    ASSERT_EQ(result.GetRawData(), JSTaggedValue(static_cast<double>(291)).GetRawData());
}

// testcases of StringToDouble flags
HWTEST_F_L0(BuiltinsNumberTest, StringToDoubleFlags)
{
    JSHandle<EcmaString> str;
    Span<const uint8_t> sp;

    // flags of IGNORE_TRAILING

    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0a");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::IGNORE_TRAILING), 0);
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0b");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::IGNORE_TRAILING), 0);
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0o");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::IGNORE_TRAILING), 0);
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" 00x");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::IGNORE_TRAILING), 0);
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" 000.4_");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::IGNORE_TRAILING), 0.4);
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" 0010.s ");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::IGNORE_TRAILING), 10);
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" 0010e2");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::IGNORE_TRAILING), 1000);
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" 0010e+3_0");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::IGNORE_TRAILING), 10000);

    // flags of ALLOW_HEX
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0x");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_TRUE(std::isnan(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::ALLOW_HEX)));
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("  0x10 ");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::ALLOW_HEX), 16);
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0x1g");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::ALLOW_HEX + base::IGNORE_TRAILING), 1);
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0xh");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_TRUE(std::isnan(
        base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::ALLOW_HEX + base::IGNORE_TRAILING)));

    // flags of ALLOW_OCTAL
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0O");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_TRUE(std::isnan(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::ALLOW_OCTAL)));
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("  0o10 ");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::ALLOW_OCTAL), 8);
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0o1d");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::ALLOW_OCTAL | base::IGNORE_TRAILING),
              1);
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0o8");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_TRUE(std::isnan(
        base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::ALLOW_OCTAL | base::IGNORE_TRAILING)));

    // flags of ALLOW_BINARY
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0b");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_TRUE(std::isnan(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::ALLOW_BINARY)));
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("  0b10 ");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::ALLOW_BINARY), 2);
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0b1d");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::ALLOW_BINARY | base::IGNORE_TRAILING),
              1);
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("0b2");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_TRUE(std::isnan(
        base::NumberHelper::StringToDouble(sp.begin(), sp.end(), 0, base::ALLOW_BINARY | base::IGNORE_TRAILING)));
}

// testcases of StringToDouble radix
HWTEST_F_L0(BuiltinsNumberTest, StringToDoubleRadix)
{
    JSHandle<EcmaString> str;
    Span<const uint8_t> sp;
    int radix;

    radix = 0;  // default 10
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" 100 ");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 100);
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString(" 100.3e2 ");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 10030);
    radix = 1;
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("  0000 ");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 0);
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("  0001 ");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_TRUE(std::isnan(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS)));
    radix = 2;
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("  100 ");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 4);
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("  11 ");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 3);
    radix = 3;
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("  100 ");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 9);
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("  21 ");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 7);
    radix = 4;
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("  100 ");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 16);
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("  31 ");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 13);
    radix = 8;
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("  100 ");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 64);
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("  71 ");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 57);
    radix = 10;
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("  100 ");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 100);
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("  0020 ");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 20);
    radix = 16;
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("  100 ");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 256);
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("  1e ");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 30);
    radix = 18;
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("  100 ");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 324);
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("  1g ");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 34);
    radix = 25;
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("  100 ");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 625);
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("  1g ");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 41);
    radix = 36;
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("  100 ");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 1296);
    str = thread->GetEcmaVM()->GetFactory()->NewFromCanBeCompressString("  1z ");
    sp = Span<const uint8_t>(str->GetDataUtf8(), str->GetUtf8Length() - 1);
    ASSERT_EQ(base::NumberHelper::StringToDouble(sp.begin(), sp.end(), radix, base::NO_FLAGS), 71);
}

HWTEST_F_L0(BuiltinsNumberTest, NumberToString)
{
    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
    JSHandle<EcmaString> res = factory->NewFromCanBeCompressString("100");
    ASSERT_EQ(base::NumberHelper::NumberToString(thread, JSTaggedValue(100))->Compare(*res), 0);
    res = factory->NewFromCanBeCompressString("11223344");
    ASSERT_EQ(base::NumberHelper::NumberToString(thread, JSTaggedValue(11223344))->Compare(*res), 0);
    res = factory->NewFromCanBeCompressString("1234567890");
    ASSERT_EQ(base::NumberHelper::NumberToString(thread, JSTaggedValue(1234567890))->Compare(*res), 0);
    res = factory->NewFromCanBeCompressString("100");
    ASSERT_EQ(base::NumberHelper::NumberToString(thread, JSTaggedValue(double(100.0)))->Compare(*res), 0);
    res = factory->NewFromCanBeCompressString("100.5");
    ASSERT_EQ(base::NumberHelper::NumberToString(thread, JSTaggedValue(double(100.5)))->Compare(*res), 0);
    res = factory->NewFromCanBeCompressString("100.25");
    ASSERT_EQ(base::NumberHelper::NumberToString(thread, JSTaggedValue(double(100.25)))->Compare(*res), 0);
    res = factory->NewFromCanBeCompressString("100.125");
    ASSERT_EQ(base::NumberHelper::NumberToString(thread, JSTaggedValue(double(100.125)))->Compare(*res), 0);
    res = factory->NewFromCanBeCompressString("100.6125");
    ASSERT_EQ(base::NumberHelper::NumberToString(thread, JSTaggedValue(double(100.6125)))->Compare(*res), 0);
    res = factory->NewFromCanBeCompressString("0.0006125");
    ASSERT_EQ(base::NumberHelper::NumberToString(thread, JSTaggedValue(double(0.0006125)))->Compare(*res), 0);
    res = factory->NewFromCanBeCompressString("-0.0006125");
    ASSERT_EQ(base::NumberHelper::NumberToString(thread, JSTaggedValue(double(-0.0006125)))->Compare(*res), 0);
    res = factory->NewFromCanBeCompressString("-1234567890.0006125");
    ASSERT_EQ(base::NumberHelper::NumberToString(thread, JSTaggedValue(double(-1234567890.0006125)))->Compare(*res), 0);
    res = factory->NewFromCanBeCompressString("1234567890.0006125");
    ASSERT_EQ(base::NumberHelper::NumberToString(thread, JSTaggedValue(double(1234567890.0006125)))->Compare(*res), 0);
    res = factory->NewFromCanBeCompressString("11234567890.000612");
    ASSERT_EQ(base::NumberHelper::NumberToString(thread, JSTaggedValue(double(11234567890.0006125)))->Compare(*res), 0);
}
}  // namespace panda::test