/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include <limits>
#include <math.h>

#include "jsapi-tests/tests.h"

using namespace std;

struct LooseEqualityFixture : public JSAPITest
{
    virtual ~LooseEqualityFixture() {}

    bool leq(JS::HandleValue x, JS::HandleValue y) {
        bool equal;
        CHECK(JS_LooselyEqual(cx, x, y, &equal) && equal);
        CHECK(JS_LooselyEqual(cx, y, x, &equal) && equal);
        return true;
    }

    bool nleq(JS::HandleValue x, JS::HandleValue y) {
        bool equal;
        CHECK(JS_LooselyEqual(cx, x, y, &equal) && !equal);
        CHECK(JS_LooselyEqual(cx, y, x, &equal) && !equal);
        return true;
    }
};

struct LooseEqualityData
{
    JS::RootedValue qNaN;
    JS::RootedValue sNaN;
    JS::RootedValue d42;
    JS::RootedValue i42;
    JS::RootedValue undef;
    JS::RootedValue null;
    JS::RootedValue obj;
    JS::RootedValue poszero;
    JS::RootedValue negzero;

    explicit LooseEqualityData(JSContext* cx)
      : qNaN(cx),
        sNaN(cx),
        d42(cx),
        i42(cx),
        undef(cx),
        null(cx),
        obj(cx),
        poszero(cx),
        negzero(cx)
    {
        qNaN = JS::CanonicalizedDoubleValue(numeric_limits<double>::quiet_NaN());
        sNaN = JS::CanonicalizedDoubleValue(numeric_limits<double>::signaling_NaN());
        d42 = JS::DoubleValue(42.0);
        i42 = JS::Int32Value(42);
        undef = JS::UndefinedValue();
        null = JS::NullValue();
        obj = JS::ObjectOrNullValue(JS::CurrentGlobalOrNull(cx));
        poszero = JS::DoubleValue(0.0);
        negzero = JS::DoubleValue(-0.0);
#ifdef XP_WIN
# define copysign _copysign
#endif
        MOZ_RELEASE_ASSERT(copysign(1.0, poszero.toDouble()) == 1.0);
        MOZ_RELEASE_ASSERT(copysign(1.0, negzero.toDouble()) == -1.0);
#ifdef XP_WIN
# undef copysign
#endif
    }
};

// 11.9.3 1a
BEGIN_FIXTURE_TEST(LooseEqualityFixture, test_undef_leq_undef)
{
    LooseEqualityData d(cx);
    CHECK(leq(d.undef, d.undef));
    return true;
}
END_FIXTURE_TEST(LooseEqualityFixture, test_undef_leq_undef)

// 11.9.3 1b
BEGIN_FIXTURE_TEST(LooseEqualityFixture, test_null_leq_null)
{
    LooseEqualityData d(cx);
    CHECK(leq(d.null, d.null));
    return true;
}
END_FIXTURE_TEST(LooseEqualityFixture, test_null_leq_null)

// 11.9.3 1ci
BEGIN_FIXTURE_TEST(LooseEqualityFixture, test_nan_nleq_all)
{
    LooseEqualityData d(cx);

    CHECK(nleq(d.qNaN, d.qNaN));
    CHECK(nleq(d.qNaN, d.sNaN));

    CHECK(nleq(d.sNaN, d.sNaN));
    CHECK(nleq(d.sNaN, d.qNaN));

    CHECK(nleq(d.qNaN, d.d42));
    CHECK(nleq(d.qNaN, d.i42));
    CHECK(nleq(d.qNaN, d.undef));
    CHECK(nleq(d.qNaN, d.null));
    CHECK(nleq(d.qNaN, d.obj));

    CHECK(nleq(d.sNaN, d.d42));
    CHECK(nleq(d.sNaN, d.i42));
    CHECK(nleq(d.sNaN, d.undef));
    CHECK(nleq(d.sNaN, d.null));
    CHECK(nleq(d.sNaN, d.obj));
    return true;
}
END_FIXTURE_TEST(LooseEqualityFixture, test_nan_nleq_all)

// 11.9.3 1cii
BEGIN_FIXTURE_TEST(LooseEqualityFixture, test_all_nleq_nan)
{
    LooseEqualityData d(cx);

    CHECK(nleq(d.qNaN, d.qNaN));
    CHECK(nleq(d.qNaN, d.sNaN));

    CHECK(nleq(d.sNaN, d.sNaN));
    CHECK(nleq(d.sNaN, d.qNaN));

    CHECK(nleq(d.d42,   d.qNaN));
    CHECK(nleq(d.i42,   d.qNaN));
    CHECK(nleq(d.undef, d.qNaN));
    CHECK(nleq(d.null,  d.qNaN));
    CHECK(nleq(d.obj,   d.qNaN));

    CHECK(nleq(d.d42,   d.sNaN));
    CHECK(nleq(d.i42,   d.sNaN));
    CHECK(nleq(d.undef, d.sNaN));
    CHECK(nleq(d.null,  d.sNaN));
    CHECK(nleq(d.obj,   d.sNaN));
    return true;
}
END_FIXTURE_TEST(LooseEqualityFixture, test_all_nleq_nan)

// 11.9.3 1ciii
BEGIN_FIXTURE_TEST(LooseEqualityFixture, test_leq_same_nums)
{
    LooseEqualityData d(cx);

    CHECK(leq(d.d42, d.d42));
    CHECK(leq(d.i42, d.i42));
    CHECK(leq(d.d42, d.i42));
    CHECK(leq(d.i42, d.d42));
    return true;
}
END_FIXTURE_TEST(LooseEqualityFixture, test_leq_same_nums)

// 11.9.3 1civ
BEGIN_FIXTURE_TEST(LooseEqualityFixture, test_pz_leq_nz)
{
    LooseEqualityData d(cx);
    CHECK(leq(d.poszero, d.negzero));
    return true;
}
END_FIXTURE_TEST(LooseEqualityFixture, test_pz_leq_nz)

// 11.9.3 1cv
BEGIN_FIXTURE_TEST(LooseEqualityFixture, test_nz_leq_pz)
{
    LooseEqualityData d(cx);
    CHECK(leq(d.negzero, d.poszero));
    return true;
}
END_FIXTURE_TEST(LooseEqualityFixture, test_nz_leq_pz)

// 1cvi onwards NOT TESTED

// 11.9.3 2
BEGIN_FIXTURE_TEST(LooseEqualityFixture, test_null_leq_undef)
{
    LooseEqualityData d(cx);
    CHECK(leq(d.null, d.undef));
    return true;
}
END_FIXTURE_TEST(LooseEqualityFixture, test_null_leq_undef)

// 11.9.3 3
BEGIN_FIXTURE_TEST(LooseEqualityFixture, test_undef_leq_null)
{
    LooseEqualityData d(cx);
    CHECK(leq(d.undef, d.null));
    return true;
}
END_FIXTURE_TEST(LooseEqualityFixture, test_undef_leq_null)

// 4 onwards NOT TESTED