//
// Copyright 2025 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//

#include "test_utils/CompilerTest.h"

#include "test_utils/angle_test_configs.h"
#include "test_utils/gl_raii.h"

using namespace angle;

namespace
{
class GLSLConstantFoldingTest : public CompilerTest
{
  protected:
    GLSLConstantFoldingTest() {}

    virtual const char *version() { return "300"; }

    // Helper to verify constant folding result.  It's given:
    //
    // * The type of the constant: a varying `v` will be created of this type.
    // * The expression to define and set `c` of that type: The varying is assigned the value of `c`
    //   in the vertex shader.
    // * An expression involving `v` that compares it with the expectation in the fragment shader,
    //   resulting in a bool.
    void test(const char *type, const char *defineC, const char *compareWithV)
    {
        std::stringstream vsSrc;
        vsSrc << "#version " << version() << R"( es
precision highp float;
precision highp int;
flat out )" << type
              << R"( v;
void main()
{
    )" << defineC
              << R"(;
    v = c;

    vec2 pos = vec2(0.);
    switch (gl_VertexID) {
        // The tests often include 1 and 3, reduce the chance of checking for those values hitting
        // the values for position by using 1.1 and 3.1 instead of 1.0 and 3.0.
        case 0: pos = vec2(-1.5, -1.5); break;
        case 1: pos = vec2(3.5, -1.5); break;
        case 2: pos = vec2(-1.5, 3.5); break;
    };
    gl_Position = vec4(pos, 0.1, 1.1);
})";

        std::stringstream fsSrc;
        fsSrc << "#version " << version() << R"( es
precision highp float;
precision highp int;
flat in )" << type
              << R"( v;
out vec4 color;
void main()
{
    if ()" << compareWithV
              << R"()
    {
        color = vec4(0, 1, 0, 1);
    }
    else
    {
        color = vec4(1, 0, 0, 1);
    }
})";

        const CompiledShader &vs = compile(GL_VERTEX_SHADER, vsSrc.str().c_str());
        const CompiledShader &fs = compile(GL_FRAGMENT_SHADER, fsSrc.str().c_str());
        ASSERT_TRUE(vs.success());
        ASSERT_TRUE(fs.success());

        const GLuint program = link();
        glUseProgram(program);
        glDrawArrays(GL_TRIANGLES, 0, 3);
        EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
    }

    // Check that the expected constant (the result of folding) is present in the translated source
    // for the vertex shader (where constant folding is expected to have happened).
    //
    // Given the various backends and formatting details of the generators, we can't realistically
    // completely verify the translated output.  Often, it's enough to verify that a substring only
    // found if the constant folding is performed is present; the correctness check in test() makes
    // sure constant folding is functionally correct.
    //
    // Additionally, due to the numerous shader massaging in non-GLSL backends, it's easy for
    // expectations to mismatch.  Therefore, these expectations are only checked with the GLSL
    // backend.
    void verifyIsInTranslation(const char *expect)
    {
        if (IsOpenGL())
        {
            EXPECT_TRUE(getCompiledShader(GL_VERTEX_SHADER).verifyInTranslatedSource(expect))
                << '"' << expect << "\" should have been in:\n"
                << getCompiledShader(GL_VERTEX_SHADER).getTranslatedSource();
        }
    }
    void verifyIsNotInTranslation(const char *expect)
    {
        if (IsOpenGL())
        {
            EXPECT_TRUE(getCompiledShader(GL_VERTEX_SHADER).verifyNotInTranslatedSource(expect))
                << '"' << expect << "\" should not have been in:\n"
                << getCompiledShader(GL_VERTEX_SHADER).getTranslatedSource();
        }
    }
};

class GLSLConstantFoldingTest_ES31 : public GLSLConstantFoldingTest
{
  public:
    const char *version() override { return "310"; }
};

// Constant fold integer addition
TEST_P(GLSLConstantFoldingTest, IntegerAdd)
{
    test("int", "const int c = 1124 + 5", "v == 1129");
    verifyIsInTranslation(" 1129");
    verifyIsNotInTranslation("1124");
}

// Constant fold integer subtraction
TEST_P(GLSLConstantFoldingTest, IntegerSub)
{
    test("int", "const int c = 1124 - 5", "v == 1119");
    verifyIsInTranslation(" 1119");
    verifyIsNotInTranslation("1124");
}

// Constant fold integer multiplication
TEST_P(GLSLConstantFoldingTest, IntegerMul)
{
    test("int", "const int c = 1124 * 5", "v == 5620");
    verifyIsInTranslation(" 5620");
    verifyIsNotInTranslation("1124");
}

// Constant fold integer division
TEST_P(GLSLConstantFoldingTest, IntegerDiv)
{
    // Rounding mode of division is undefined in the spec but ANGLE can be expected to round down.
    test("int", "const int c = 1124 / 5", "v == 224");
    verifyIsInTranslation(" 224");
    verifyIsNotInTranslation("1124");
}

// Constant fold integer modulus
TEST_P(GLSLConstantFoldingTest, IntegerMod)
{
    test("int", "const int c = 1124 % 5", "v == 4");
    verifyIsInTranslation(" 4");
    verifyIsNotInTranslation("1124");
}

// Constant fold cross()
TEST_P(GLSLConstantFoldingTest, Cross)
{
    test("vec3", "const vec3 c = cross(vec3(1., 1., 1.), vec3(1., -1., 1.))",
         "all(equal(v, vec3(2., 0., -2.)))");
    verifyIsInTranslation("-2.0");
    verifyIsNotInTranslation("cross");
    verifyIsNotInTranslation("-1.0");
}

// Constant fold inverse()
TEST_P(GLSLConstantFoldingTest, Inverse2x2)
{
    test("mat2", "const mat2 c = inverse(mat2(2., 3., 5., 7.))",
         "all(equal(v[0], vec2(-7., 3.))) && all(equal(v[1], vec2(5., -2.)))");
    verifyIsInTranslation("-7.0");
    verifyIsNotInTranslation("5.0, 7.0");
    verifyIsNotInTranslation("inverse");
}

// Constant fold inverse()
TEST_P(GLSLConstantFoldingTest, Inverse3x3)
{
    test("mat3", "const mat3 c = inverse(mat3(11., 13., 19., 23., 29., 31., 37., 41., 43.))",
         "all(lessThan(abs(v[0] - vec3(31.*41.-29.*43., 13.*43.-19.*41., 19.*29.-13.*31.)/680.), "
         "vec3(0.000001))) && "
         "all(lessThan(abs(v[1] - vec3(23.*43.-31.*37., 19.*37.-11.*43., 11.*31.-19.*23.)/680.), "
         "vec3(0.000001))) && "
         "all(lessThan(abs(v[2] - vec3(29.*37.-23.*41., 11.*41.-13.*37., 13.*23.-11.*29.)/680.), "
         "vec3(0.000001)))");
    verifyIsInTranslation("0.0352");
    verifyIsNotInTranslation("29.0");
    verifyIsNotInTranslation("inverse");
}

// Constant fold inverse()
TEST_P(GLSLConstantFoldingTest, Inverse4x4)
{
    test("mat4",
         "const mat4 c = inverse(mat4(29., 31., 37., 41., "
         "43., 47., 53., 59., "
         "61., 67., 71., 73., "
         "79., 83., 89., 97.))",
         "all(lessThan(abs(v[0] - vec4(215., -330., -60., 155.)/630.), vec4(0.000001))) && "
         "all(lessThan(abs(v[1] - vec4(-450., 405., 45., -90.)/630.), vec4(0.000001))) && "
         "all(lessThan(abs(v[2] - vec4(425., -330., 129., -76.)/630.), vec4(0.000001))) && "
         "all(lessThan(abs(v[3] - vec4(-180., 225., -108., 27.)/630.), vec4(0.000001)))");
    verifyIsInTranslation("0.3412");
    verifyIsNotInTranslation("53.0");
    verifyIsNotInTranslation("inverse");
}

// Constant fold determinant()
TEST_P(GLSLConstantFoldingTest, Determinant2x2)
{
    test("float", "const float c = determinant(mat2(2., 3., 5., 7.))", "v == -1.");
    verifyIsInTranslation("-1.0");
    verifyIsNotInTranslation("7.0");
    verifyIsNotInTranslation("determinant");
}

// Constant fold determinant()
TEST_P(GLSLConstantFoldingTest, Determinant3x3)
{
    test("float", "const float c = determinant(mat3(11., 13., 19., 23., 29., 31., 37., 41., 43.))",
         "v == -680.");
    verifyIsInTranslation("-680.");
    verifyIsNotInTranslation("31.0");
    verifyIsNotInTranslation("determinant");
}

// Constant fold determinant()
TEST_P(GLSLConstantFoldingTest, Determinant4x4)
{
    test("float",
         "const float c = determinant(mat4(29., 31., 37., 41., "
         "43., 47., 53., 59., "
         "61., 67., 71., 73., "
         "79., 83., 89., 97.))",
         "v == -2520.");
    verifyIsInTranslation("-2520.0");
    verifyIsNotInTranslation("71.0");
    verifyIsNotInTranslation("determinant");
}

// Constant fold transpose()
TEST_P(GLSLConstantFoldingTest, Transpose3x3)
{
    test("mat3", "const mat3 c = transpose(mat3(11., 13., 19., 23., 29., 31., 37., 41., 43.))",
         "all(lessThan(abs(v[0] - vec3(11., 23., 37.)), vec3(0.000001))) && "
         "all(lessThan(abs(v[1] - vec3(13., 29., 41.)), vec3(0.000001))) && "
         "all(lessThan(abs(v[2] - vec3(19., 31., 43.)), vec3(0.000001)))");
    verifyIsInTranslation("11.0, 23.0");
    verifyIsNotInTranslation("11.0, 13.0");
    verifyIsNotInTranslation("transpose");
}

// 0xFFFFFFFF as int should evaluate to -1.
// This is featured in the examples of ESSL3 section 4.1.3. ESSL3 section 12.42
// means that any 32-bit unsigned integer value is a valid literal.
TEST_P(GLSLConstantFoldingTest, ParseWrappedHexIntLiteral)
{
    test("int", "const int c = 0xFFFFFFFF", "v == -1");
    verifyIsInTranslation("-1");
}

// 3000000000 as int should wrap to -1294967296.
// This is featured in the examples of GLSL 4.5, and ESSL behavior should match
// desktop GLSL when it comes to integer parsing.
TEST_P(GLSLConstantFoldingTest, ParseWrappedDecimalIntLiteral)
{
    test("int", "const int c = 3000000000", "v == -1294967296");
    verifyIsInTranslation("-1294967296");
}

// 0xFFFFFFFF as uint should be unchanged.
// This is featured in the examples of ESSL3 section 4.1.3. ESSL3 section 12.42
// means that any 32-bit unsigned integer value is a valid literal.
TEST_P(GLSLConstantFoldingTest, ParseMaxUintLiteral)
{
    test("uint", "const uint c = 0xFFFFFFFFu", "v == 0xFFFFFFFFu");
    verifyIsInTranslation("4294967295");
}

// -1 as uint should wrap to 0xFFFFFFFF.
// This is featured in the examples of ESSL3 section 4.1.3. ESSL3 section 12.42
// means that any 32-bit unsigned integer value is a valid literal.
TEST_P(GLSLConstantFoldingTest, ParseUnaryMinusOneUintLiteral)
{
    test("uint", "const uint c = -1u", "v == 0xFFFFFFFFu");
    verifyIsInTranslation("4294967295");
}

// Constant fold matrix constructor from matrix with identical size
TEST_P(GLSLConstantFoldingTest, ConstructMat2FromMat2)
{
    test("mat2", "const mat2 c = mat2(mat2(0., 1., 2., 3.))",
         "all(equal(v[0], vec2(0., 1.))) && "
         "all(equal(v[1], vec2(2., 3.)))");
}

// Constant fold matrix constructor from scalar
TEST_P(GLSLConstantFoldingTest, ConstructMat2FromScalar)
{
    test("mat2", "const mat2 c = mat2(3)",
         "all(equal(v[0], vec2(3., 0.))) && "
         "all(equal(v[1], vec2(0., 3.)))");
    verifyIsInTranslation("0.0");
}

// Constant fold matrix constructor from vector
TEST_P(GLSLConstantFoldingTest, ConstructMat2FromVector)
{
    test("mat2", "const mat2 c = mat2(vec4(0., 1., 2., 3.))",
         "all(equal(v[0], vec2(0., 1.))) && "
         "all(equal(v[1], vec2(2., 3.)))");
}

// Constant fold matrix constructor from multiple args
TEST_P(GLSLConstantFoldingTest, ConstructMat2FromMultiple)
{
    test("mat2", "const mat2 c = mat2(-1, vec2(0., 1.), vec4(2.))",
         "all(equal(v[0], vec2(-1., 0.))) && "
         "all(equal(v[1], vec2(1., 2.)))");
    verifyIsInTranslation("1.0, 2.0");
}

// Constant fold matrix constructor from larger matrix
TEST_P(GLSLConstantFoldingTest, ConstructMat2FromMat3)
{
    test("mat2", "const mat2 c = mat2(mat3(0., 1., 2., 3., 4., 5., 6., 7., 8.))",
         "all(equal(v[0], vec2(0., 1.))) && "
         "all(equal(v[1], vec2(3., 4.)))");
}

// Constant fold matrix constructor from smaller matrix
TEST_P(GLSLConstantFoldingTest, ConstructMat4x3FromMat3x2)
{
    test("mat4x3", "const mat4x3 c = mat4x3(mat3x2(1., 2., 3., 4., 5., 6.))",
         "all(equal(v[0], vec3(1., 2., 0.))) && "
         "all(equal(v[1], vec3(3., 4., 0.))) && "
         "all(equal(v[2], vec3(5., 6., 1.))) && "
         "all(equal(v[3], vec3(0., 0., 0.)))");
    verifyIsInTranslation("2.0, 0.0");
}

// Constant fold struct comparison when structs are different
TEST_P(GLSLConstantFoldingTest, StructEqualityFalse)
{
    test("int", R"(
struct nested {
    float f;
};
struct S {
    nested a;
    float f;
};
const S s1 = S(nested(0.), 2.);
const S s2 = S(nested(0.), 3.);
const int c = s1 == s2 ? 1 : 0;)",
         "!bool(v)");

    verifyIsNotInTranslation("2.0");
    verifyIsNotInTranslation("3.0");
    verifyIsNotInTranslation("==");
}

// Constant fold struct comparison when structs are identical
TEST_P(GLSLConstantFoldingTest, StructEqualityTrue)
{
    test("int", R"(
struct nested {
    float f;
};
struct S {
    nested a;
    float f;
    int i;
};
const S s1 = S(nested(0.), 2., 3);
const S s2 = S(nested(0.), 2., 3);
const int c = s1 == s2 ? 1 : 0;)",
         "bool(v)");

    verifyIsNotInTranslation("2.0");
    verifyIsNotInTranslation("3.0");
    verifyIsNotInTranslation("==");
}

// Constant fold indexing of a non-square matrix
TEST_P(GLSLConstantFoldingTest, NonSquareMatrixIndex)
{
    test("vec4", "const vec4 c = mat3x4(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)[1]",
         "all(equal(v, vec4(4, 5, 6, 7)))");
    verifyIsInTranslation("4.0");
    verifyIsNotInTranslation("[1]");
}

// Constant fold outerProduct with vectors of non-matching length
TEST_P(GLSLConstantFoldingTest, NonSquareOuterProduct)
{
    test("mat3x2", "const mat3x2 c = outerProduct(vec2(2., 3.), vec3(5., 7., 11.))",
         "all(equal(v[0], vec2(10., 15.))) &&"
         "all(equal(v[1], vec2(14., 21.))) &&"
         "all(equal(v[2], vec2(22., 33.)))");
    verifyIsInTranslation("15.0");
    verifyIsNotInTranslation("7.0");
    verifyIsNotInTranslation("outerProduct");
}

// Constant fold shift left with different non-matching signedness
TEST_P(GLSLConstantFoldingTest, ShiftLeftMismatchingSignedness)
{
    test("uint", "const uint c = 0xFFFFFFFFu << 31", "v == 0x80000000u");
    verifyIsInTranslation("2147483648");
    if (!IsOpenGLES())
    {
        // Do not check with GLES, because `#version 310 es` would match this.
        verifyIsNotInTranslation("31");
    }
    verifyIsNotInTranslation("<<");
}

// Constant fold shift right with different non-matching signedness
TEST_P(GLSLConstantFoldingTest, ShiftRightMismatchingSignedness)
{
    test("uint", "const uint c = 0xFFFFFFFFu >> 29", "v == 0x7u");
    verifyIsInTranslation("7");
    verifyIsNotInTranslation("29");
    verifyIsNotInTranslation(">>");
}

// Constant fold shift right, expecting sign extension
TEST_P(GLSLConstantFoldingTest, ShiftRightSignExtension)
{
    test("int", "const int c = 0x8FFFE000 >> 6", "v == 0xFE3FFF80");
    verifyIsInTranslation("29360256");
    verifyIsNotInTranslation(" 6");
    verifyIsNotInTranslation(">>");
}

// Constant fold shift left, such that the number turns from positive to negative
TEST_P(GLSLConstantFoldingTest, ShiftLeftChangeSign)
{
    test("int", "const int c = 0x1FFFFFFF << 7", "v == 0xFFFFFF80");
    verifyIsInTranslation("-128");
    verifyIsNotInTranslation(" 7");
    verifyIsNotInTranslation("<<");
}

// Constant fold divide minimum integer by -1.
// ESSL 3.00.6 section 4.1.3 Integers:
// > However, for the case where the minimum representable value is divided by -1, it is allowed to
// > return either the minimum representable value or the maximum representable value.
//
// ANGLE always returns the maximum value.
TEST_P(GLSLConstantFoldingTest, DivideMinimumIntegerByMinusOne)
{
    test("int", "const int c = 0x80000000 / -1", "v == 0x7FFFFFFF");
    verifyIsInTranslation("2147483647");
}

// Constant fold unsigned addition with overflow
// ESSL 3.00.6 section 4.1.3 Integers:
// > For all precisions, operations resulting in overflow or underflow will not cause any exception,
// > nor will they saturate, rather they will 'wrap' to yield the low-order n bits of the result
// > where n is the size in bits of the integer.
TEST_P(GLSLConstantFoldingTest, UnsignedIntegerAddOverflow)
{
    test("uint", "const uint c = 0xFFFFFFFFu + 43u", "v == 42u");
    verifyIsInTranslation("42");
    verifyIsNotInTranslation("43");
}

// Constant fold signed addition with overflow
// ESSL 3.00.6 section 4.1.3 Integers:
// > For all precisions, operations resulting in overflow or underflow will not cause any exception,
// > nor will they saturate, rather they will 'wrap' to yield the low-order n bits of the result
// > where n is the size in bits of the integer.
TEST_P(GLSLConstantFoldingTest, SignedIntegerAddOverflow)
{
    test("int", "const int c = 0x7FFFFFFF + 4", "v == -0x7FFFFFFD");
    verifyIsInTranslation("-2147483645");
}

// Constant fold unsigned subtraction with underflow
// ESSL 3.00.6 section 4.1.3 Integers:
// > For all precisions, operations resulting in overflow or underflow will not cause any exception,
// > nor will they saturate, rather they will 'wrap' to yield the low-order n bits of the result
// > where n is the size in bits of the integer.
TEST_P(GLSLConstantFoldingTest, UnsignedIntegerSubUnderflow)
{
    test("uint", "const uint c = 0u - 5u", "v == 0xFFFFFFFBu");
    verifyIsInTranslation("4294967291");
}

// Constant fold signed subtraction with underflow
// ESSL 3.00.6 section 4.1.3 Integers:
// > For all precisions, operations resulting in overflow or underflow will not cause any exception,
// > nor will they saturate, rather they will 'wrap' to yield the low-order n bits of the result
// > where n is the size in bits of the integer.
TEST_P(GLSLConstantFoldingTest, SignedIntegerSubUnderflow)
{
    test("int", "const int c = -0x7FFFFFFF - 7", "v == 0x7FFFFFFA");
    verifyIsInTranslation("2147483642");
}

// Constant fold unsigned multiplication with overflow
// ESSL 3.00.6 section 4.1.3 Integers:
// > For all precisions, operations resulting in overflow or underflow will not cause any exception,
// > nor will they saturate, rather they will 'wrap' to yield the low-order n bits of the result
// > where n is the size in bits of the integer.
TEST_P(GLSLConstantFoldingTest, UnsignedIntegerMulOverflow)
{
    test("uint", "const uint c = 0xFFFFFFFFu * 10u", "v == 0xFFFFFFF6u");
    verifyIsInTranslation("4294967286");
}

// Constant fold signed multiplication with overflow
// ESSL 3.00.6 section 4.1.3 Integers:
// > For all precisions, operations resulting in overflow or underflow will not cause any exception,
// > nor will they saturate, rather they will 'wrap' to yield the low-order n bits of the result
// > where n is the size in bits of the integer.
TEST_P(GLSLConstantFoldingTest, SignedIntegerMulOverflow)
{
    test("int", "const int c = 0x7FFFFFFF * 42", "v == -42");
    verifyIsInTranslation("-42");
}

// Constant fold negation of minimum integer value, which is 0x80000000.
// ESSL 3.00.6 section 4.1.3 Integers:
// > For all precisions, operations resulting in overflow or underflow will not cause any exception,
// > nor will they saturate, rather they will 'wrap' to yield the low-order n bits of the result
// > where n is the size in bits of the integer.
TEST_P(GLSLConstantFoldingTest, SignedIntegerNegateOverflow)
{
    test("int", "const int c = -0x80000000", "v == 0x80000000");
    verifyIsInTranslation("-2147483648");
}

// Constant fold shift right of minimum integer value, which is 0x80000000.
TEST_P(GLSLConstantFoldingTest, SignedIntegerShiftRightMinimumValue)
{
    test("int", "const int c = (0x80000000 >> 1) + (0x80000000 >> 7)", "v == -0x41000000");
    verifyIsInTranslation("-1090519040");
}

// Constant fold shift left by zero.
TEST_P(GLSLConstantFoldingTest, SignedIntegerShiftLeftZero)
{
    test("int", "const int c = 73 << 0", "v == 73");
}

// Constant fold shift right by zero.
TEST_P(GLSLConstantFoldingTest, SignedIntegerShiftRightZero)
{
    test("int", "const int c = 3 >> 0", "v == 3");
}

// Constant fold isinf with an out-of-range value
// ESSL 3.00.6 section 4.1.4 Floats:
// > If the value of the floating point number is too large (small) to be stored as a single
// > precision value, it is converted to positive (negative) infinity.
// ESSL 3.00.6 section 12.4:
// > Mandate support for signed infinities.
TEST_P(GLSLConstantFoldingTest, IsInfTrue)
{
    test("int", "const int c = isinf(1.0e2048) ? 1 : 0", "bool(v)");
    verifyIsNotInTranslation("isinf");
}

// Test that floats that are too small to be represented get flushed to zero.
// ESSL 3.00.6 section 4.1.4 Floats:
// > A value with a magnitude too small to be represented as a mantissa and exponent is converted to
// > zero.
TEST_P(GLSLConstantFoldingTest, TooSmallFloat)
{
    test("float", "const float c = 1.0e-2048", "v == 0.");
}

// IEEE 754 dictates that behavior of infinity is derived from limiting cases of real arithmetic.
// lim radians(x) x -> inf = inf
// ESSL 3.00.6 section 4.5.1:
// > Infinities and zeroes are generated as dictated by IEEE.
TEST_P(GLSLConstantFoldingTest, RadiansInfinity)
{
    test("float", "const float c = radians(1.0e2048)", "isinf(v) && v > 0.");
    verifyIsNotInTranslation("radians");
}

// IEEE 754 dictates that behavior of infinity is derived from limiting cases of real arithmetic.
// lim degrees(x) x -> inf = inf
// ESSL 3.00.6 section 4.5.1:
// > Infinities and zeroes are generated as dictated by IEEE.
TEST_P(GLSLConstantFoldingTest, DegreesInfinity)
{
    test("float", "const float c = degrees(1.0e2048)", "isinf(v) && v > 0.");
    verifyIsNotInTranslation("degress");
}

// IEEE 754 dictates that sinh(inf) = inf.
// ESSL 3.00.6 section 4.5.1:
// > Infinities and zeroes are generated as dictated by IEEE.
TEST_P(GLSLConstantFoldingTest, SinhInfinity)
{
    test("float", "const float c = sinh(1.0e2048)", "isinf(v) && v > 0.");
    verifyIsNotInTranslation("sinh");
}

// IEEE 754 dictates that sinh(-inf) = -inf.
// ESSL 3.00.6 section 4.5.1:
// > Infinities and zeroes are generated as dictated by IEEE.
TEST_P(GLSLConstantFoldingTest, SinhNegativeInfinity)
{
    test("float", "const float c = sinh(-1.0e2048)", "isinf(v) && v < 0.");
    verifyIsNotInTranslation("sinh");
}

// IEEE 754 dictates that cosh(inf) = inf.
// ESSL 3.00.6 section 4.5.1:
// > Infinities and zeroes are generated as dictated by IEEE.
TEST_P(GLSLConstantFoldingTest, CoshInfinity)
{
    test("float", "const float c = cosh(1.0e2048)", "isinf(v) && v > 0.");
    verifyIsNotInTranslation("cosh");
}

// IEEE 754 dictates that cosh(-inf) = inf.
// ESSL 3.00.6 section 4.5.1:
// > Infinities and zeroes are generated as dictated by IEEE.
TEST_P(GLSLConstantFoldingTest, CoshNegativeInfinity)
{
    test("float", "const float c = cosh(-1.0e2048)", "isinf(v) && v > 0.");
    verifyIsNotInTranslation("cosh");
}

// IEEE 754 dictates that asinh(inf) = inf.
// ESSL 3.00.6 section 4.5.1:
// > Infinities and zeroes are generated as dictated by IEEE.
TEST_P(GLSLConstantFoldingTest, AsinhInfinity)
{
    test("float", "const float c = asinh(1.0e2048)", "isinf(v) && v > 0.");
    verifyIsNotInTranslation("asinh");
}

// IEEE 754 dictates that asinh(-inf) = -inf.
// ESSL 3.00.6 section 4.5.1:
// > Infinities and zeroes are generated as dictated by IEEE.
TEST_P(GLSLConstantFoldingTest, AsinhNegativeInfinity)
{
    test("float", "const float c = asinh(-1.0e2048)", "isinf(v) && v < 0.");
    verifyIsNotInTranslation("asinh");
}

// IEEE 754 dictates that acosh(inf) = inf.
// ESSL 3.00.6 section 4.5.1:
// > Infinities and zeroes are generated as dictated by IEEE.
TEST_P(GLSLConstantFoldingTest, AcoshInfinity)
{
    test("float", "const float c = acosh(1.0e2048)", "isinf(v) && v > 0.");
    verifyIsNotInTranslation("acosh");
}

// IEEE 754 dictates that pow or powr(0, inf) = 0.
// ESSL 3.00.6 section 4.5.1:
// > Infinities and zeroes are generated as dictated by IEEE.
TEST_P(GLSLConstantFoldingTest, PowInfinity)
{
    test("float", "const float c = pow(0.0, 1.0e2048)", "v == 0.");
    verifyIsNotInTranslation("pow");
}

// IEEE 754 dictates that exp(inf) = inf.
// ESSL 3.00.6 section 4.5.1:
// > Infinities and zeroes are generated as dictated by IEEE.
TEST_P(GLSLConstantFoldingTest, ExpInfinity)
{
    test("float", "const float c = exp(1.0e2048)", "isinf(v) && v > 0.");
    verifyIsNotInTranslation("exp");
}

// IEEE 754 dictates that exp(-inf) = 0.
// ESSL 3.00.6 section 4.5.1:
// > Infinities and zeroes are generated as dictated by IEEE.
TEST_P(GLSLConstantFoldingTest, ExpNegativeInfinity)
{
    test("float", "const float c = exp(-1.0e2048)", "v == 0.");
    verifyIsNotInTranslation("exp");
}

// IEEE 754 dictates that log(inf) = inf.
// ESSL 3.00.6 section 4.5.1:
// > Infinities and zeroes are generated as dictated by IEEE.
TEST_P(GLSLConstantFoldingTest, LogInfinity)
{
    test("float", "const float c = log(1.0e2048)", "isinf(v) && v > 0.");
    verifyIsNotInTranslation("log");
}

// IEEE 754 dictates that exp2(inf) = inf.
// ESSL 3.00.6 section 4.5.1:
// > Infinities and zeroes are generated as dictated by IEEE.
TEST_P(GLSLConstantFoldingTest, Exp2Infinity)
{
    test("float", "const float c = exp2(1.0e2048)", "isinf(v) && v > 0.");
    verifyIsNotInTranslation("exp2");
}

// IEEE 754 dictates that exp2(-inf) = 0.
// ESSL 3.00.6 section 4.5.1:
// > Infinities and zeroes are generated as dictated by IEEE.
TEST_P(GLSLConstantFoldingTest, Exp2NegativeInfinity)
{
    test("float", "const float c = exp2(-1.0e2048)", "v == 0.");
    verifyIsNotInTranslation("exp2");
}

// IEEE 754 dictates that log2(inf) = inf.
// ESSL 3.00.6 section 4.5.1:
// > Infinities and zeroes are generated as dictated by IEEE.
TEST_P(GLSLConstantFoldingTest, Log2Infinity)
{
    test("float", "const float c = log2(1.0e2048)", "isinf(v) && v > 0.");
    verifyIsNotInTranslation("log2");
}

// IEEE 754 dictates that behavior of infinity is derived from limiting cases of real arithmetic.
// lim sqrt(x) x -> inf = inf
// ESSL 3.00.6 section 4.5.1:
// > Infinities and zeroes are generated as dictated by IEEE.
TEST_P(GLSLConstantFoldingTest, SqrtInfinity)
{
    test("float", "const float c = sqrt(1.0e2048)", "isinf(v) && v > 0.");
    verifyIsNotInTranslation("sqrt");
}

// IEEE 754 dictates that rSqrt(inf) = 0
// ESSL 3.00.6 section 4.5.1:
// > Infinities and zeroes are generated as dictated by IEEE.
TEST_P(GLSLConstantFoldingTest, InversesqrtInfinity)
{
    test("float", "const float c = inversesqrt(1.0e2048)", "v == 0.");
    verifyIsNotInTranslation("inversesqrt");
}

// IEEE 754 dictates that behavior of infinity is derived from limiting cases of real arithmetic.
// lim length(x) x -> inf = inf
// ESSL 3.00.6 section 4.5.1:
// > Infinities and zeroes are generated as dictated by IEEE.
TEST_P(GLSLConstantFoldingTest, LengthInfinity)
{
    test("float", "const float c = length(1.0e2048)", "isinf(v) && v > 0.");
    verifyIsNotInTranslation("length");
}

// IEEE 754 dictates that behavior of infinity is derived from limiting cases of real arithmetic.
// lim dot(x, y) x -> inf, y > 0 = inf
// ESSL 3.00.6 section 4.5.1:
// > Infinities and zeroes are generated as dictated by IEEE.
TEST_P(GLSLConstantFoldingTest, DotInfinity)
{
    test("float", "const float c = dot(1.0e2048, 1.)", "isinf(v) && v > 0.");
    verifyIsNotInTranslation("dot");
}

// IEEE 754 dictates that behavior of infinity is derived from limiting cases of real arithmetic.
// lim dot(vec2(x, y), vec2(z)) x -> inf, finite y, z > 0 = inf
// ESSL 3.00.6 section 4.5.1:
// > Infinities and zeroes are generated as dictated by IEEE.
TEST_P(GLSLConstantFoldingTest, DotInfinity2)
{
    test("float", "const float c = dot(vec2(1.0e2048, -1.), vec2(1.))", "isinf(v) && v > 0.");
    verifyIsNotInTranslation("dot");
}

// Faceforward behavior with infinity as a parameter can be derived from dot().
// ESSL 3.00.6 section 4.5.1:
// > Infinities and zeroes are generated as dictated by IEEE.
TEST_P(GLSLConstantFoldingTest, FaceForwardInfinity)
{
    test("float", "const float c = faceforward(4., 1.0e2048, 1.)", "v == -4.");
    verifyIsNotInTranslation("faceforward");
}

// Faceforward behavior with infinity as a parameter can be derived from dot().
// ESSL 3.00.6 section 4.5.1:
// > Infinities and zeroes are generated as dictated by IEEE.
TEST_P(GLSLConstantFoldingTest, FaceForwardInfinity2)
{
    test("float", "const float c = faceforward(vec2(4.), vec2(1.0e2048, -1.), vec2(1.)).x",
         "v == -4.");
    verifyIsNotInTranslation("faceforward");
}

// Test that infinity - finite value evaluates to infinity.
// ESSL 3.00.6 section 4.5.1:
// > Infinities and zeroes are generated as dictated by IEEE.
TEST_P(GLSLConstantFoldingTest, InfinityMinusFinite)
{
    test("float", "const float c = 1.0e2048 - 1.0e20", "isinf(v) && v > 0.");
}

// Test that -infinity + finite value evaluates to -infinity.
// ESSL 3.00.6 section 4.5.1:
// > Infinities and zeroes are generated as dictated by IEEE.
TEST_P(GLSLConstantFoldingTest, MinusInfinityPlusFinite)
{
    test("float", "const float c = (-1.0e2048) + 1.0e20", "isinf(v) && v < 0.");
}

// Test that infinity * finite value evaluates to infinity.
// ESSL 3.00.6 section 4.5.1:
// > Infinities and zeroes are generated as dictated by IEEE.
TEST_P(GLSLConstantFoldingTest, InfinityMultipliedByFinite)
{
    test("float", "const float c = 1.0e2048 * 1.0e-20", "isinf(v) && v > 0.");
}

// Test that infinity * infinity evaluates to infinity.
// ESSL 3.00.6 section 4.5.1:
// > Infinities and zeroes are generated as dictated by IEEE.
TEST_P(GLSLConstantFoldingTest, InfinityMultipliedByInfinity)
{
    test("float", "const float c = 1.0e2048 * 1.0e2048", "isinf(v) && v > 0.");
}

// Test that infinity * negative infinity evaluates to negative infinity.
// ESSL 3.00.6 section 4.5.1:
// > Infinities and zeroes are generated as dictated by IEEE.
TEST_P(GLSLConstantFoldingTest, InfinityMultipliedByNegativeInfinity)
{
    test("float", "const float c = 1.0e2048 * (-1.0e2048)", "isinf(v) && v < 0.");
}

// Test that dividing by minus zero results in the appropriately signed infinity.
// ESSL 3.00.6 section 4.5.1:
// > Infinities and zeroes are generated as dictated by IEEE.
// > If both positive and negative zeros are implemented, the correctly signed Inf will be
// > generated.
TEST_P(GLSLConstantFoldingTest, DivideByNegativeZero)
{
    test("float", "const float c = 1. / (-0.)", "isinf(v) && v < 0.");
    verifyIsNotInTranslation("-0.0");
}

// Test that infinity divided by zero evaluates to infinity.
// ESSL 3.00.6 section 4.5.1:
// > Infinities and zeroes are generated as dictated by IEEE.
TEST_P(GLSLConstantFoldingTest, InfinityDividedByZero)
{
    test("float", "const float c = 1.0e2048 / 0.", "isinf(v) && v > 0.");
}

// Test that negative infinity divided by zero evaluates to negative infinity.
// ESSL 3.00.6 section 4.5.1:
// > Infinities and zeroes are generated as dictated by IEEE.
TEST_P(GLSLConstantFoldingTest, MinusInfinityDividedByZero)
{
    test("float", "const float c = (-1.0e2048) / 0.", "isinf(v) && v < 0.");
}

// Test that dividing a finite number by infinity results in zero.
// ESSL 3.00.6 section 4.5.1:
// > Infinities and zeroes are generated as dictated by IEEE.
TEST_P(GLSLConstantFoldingTest, DivideByInfinity)
{
    test("float", "const float c = 1.0e30 / 1.0e2048", "v == 0.");
}

// Test that unsigned bitfieldExtract is folded correctly.
TEST_P(GLSLConstantFoldingTest_ES31, UnsignedBitfieldExtract)
{
    test("uint", "const uint c = bitfieldExtract(0x00110000u, 16, 5)", "v == 0x11u");
    verifyIsInTranslation("17");
    verifyIsNotInTranslation("16");
    verifyIsNotInTranslation("bitfieldExtract");
}

// Test that unsigned bitfieldExtract to extract 32 bits is folded correctly.
TEST_P(GLSLConstantFoldingTest_ES31, UnsignedBitfieldExtract32Bits)
{
    test("uint", "const uint c = bitfieldExtract(0xFF0000FFu, 0, 32)", "v == 0xFF0000FFu");
    verifyIsNotInTranslation("32");
    verifyIsNotInTranslation("bitfieldExtract");
}

// Test that signed bitfieldExtract is folded correctly. The higher bits should be set to 1 if the
// most significant bit of the extracted value is 1.
TEST_P(GLSLConstantFoldingTest_ES31, SignedBitfieldExtract)
{
    test("int", "const int c = bitfieldExtract(0x00110000, 16, 5)", "v == -15");
    verifyIsInTranslation("-15");
    verifyIsNotInTranslation("16");
    verifyIsNotInTranslation("bitfieldExtract");
}

// Test that bitfieldInsert is folded correctly.
TEST_P(GLSLConstantFoldingTest_ES31, BitfieldInsert)
{
    test("uint", "const uint c = bitfieldInsert(0x04501701u, 0x11u, 8, 5)", "v == 0x04501101u");
    verifyIsInTranslation("72356097");
    verifyIsNotInTranslation(" 8");
    verifyIsNotInTranslation("bitfieldInsert");
}

// Test that bitfieldInsert to insert 32 bits is folded correctly.
TEST_P(GLSLConstantFoldingTest_ES31, BitfieldInsert32Bits)
{
    test("uint", "const uint c = bitfieldInsert(0xFF0000FFu, 0x11u, 0, 32)", "v == 0x11u");
    verifyIsInTranslation("17");
    verifyIsNotInTranslation("32");
    verifyIsNotInTranslation("bitfieldInsert");
}

// Test that bitfieldReverse is folded correctly.
TEST_P(GLSLConstantFoldingTest_ES31, BitfieldReverse)
{
    test("uint", "const uint c = bitfieldReverse((1u << 4u) | (1u << 7u))", "v == 0x9000000u");
    verifyIsInTranslation("150994944");
    verifyIsNotInTranslation(" 7");
    verifyIsNotInTranslation("bitfieldReverse");
}

// Test that bitCount is folded correctly.
TEST_P(GLSLConstantFoldingTest_ES31, BitCount)
{
    test("int", "const int c = bitCount(0x17103121u)", "v == 10");
    verifyIsInTranslation("10");
    verifyIsNotInTranslation("386937121");
    verifyIsNotInTranslation("bitCount");
}

// Test that findLSB is folded correctly.
TEST_P(GLSLConstantFoldingTest_ES31, FindLSB)
{
    test("int", "const int c = findLSB(0x80010000u)", "v == 16");
    verifyIsInTranslation("16");
    verifyIsNotInTranslation("2147549184");
    verifyIsNotInTranslation("findLSB");
}

// Test that findLSB is folded correctly when the operand is zero.
TEST_P(GLSLConstantFoldingTest_ES31, FindLSBZero)
{
    test("int", "const int c = findLSB(0u)", "v == -1");
    verifyIsInTranslation("-1");
    verifyIsNotInTranslation("findLSB");
}

// Test that findMSB is folded correctly.
TEST_P(GLSLConstantFoldingTest_ES31, FindMSB)
{
    test("int", "const int c = findMSB(0x01000008u)", "v == 24");
    verifyIsInTranslation("24");
    verifyIsNotInTranslation("16777224");
    verifyIsNotInTranslation("findMSB");
}

// Test that findMSB is folded correctly when the operand is zero.
TEST_P(GLSLConstantFoldingTest_ES31, FindMSBZero)
{
    test("int", "const int c = findMSB(0u)", "v == -1");
    verifyIsInTranslation("-1");
    verifyIsNotInTranslation("findMSB");
}

// Test that findMSB is folded correctly for a negative integer.
// It is supposed to return the index of the most significant bit set to 0.
TEST_P(GLSLConstantFoldingTest_ES31, FindMSBNegativeInt)
{
    test("int", "const int c = findMSB(-8)", "v == 2");
    verifyIsInTranslation("2");
    verifyIsNotInTranslation("-8");
    verifyIsNotInTranslation("findMSB");
}

// Test that findMSB is folded correctly for -1.
TEST_P(GLSLConstantFoldingTest_ES31, FindMSBMinusOne)
{
    test("int", "const int c = findMSB(-1)", "v == -1");
    verifyIsNotInTranslation("findMSB");
}

// Test that packUnorm4x8 is folded correctly for a vector of zeroes.
TEST_P(GLSLConstantFoldingTest_ES31, PackUnorm4x8Zero)
{
    test("uint", "const uint c = packUnorm4x8(vec4(0.))", "v == 0u");
    verifyIsNotInTranslation("packUnorm4x8");
}

// Test that packUnorm4x8 is folded correctly for a vector of ones.
TEST_P(GLSLConstantFoldingTest_ES31, PackUnorm4x8One)
{
    test("uint", "const uint c = packUnorm4x8(vec4(1.))", "v == 0xFFFFFFFFu");
    verifyIsInTranslation("4294967295");
    verifyIsNotInTranslation("packUnorm4x8");
}

// Test that packSnorm4x8 is folded correctly for a vector of zeroes.
TEST_P(GLSLConstantFoldingTest_ES31, PackSnorm4x8Zero)
{
    test("uint", "const uint c = packSnorm4x8(vec4(0.))", "v == 0u");
    verifyIsNotInTranslation("packSnorm4x8");
}

// Test that packSnorm4x8 is folded correctly for a vector of ones.
TEST_P(GLSLConstantFoldingTest_ES31, PackSnorm4x8One)
{
    test("uint", "const uint c = packSnorm4x8(vec4(1.))", "v == 0x7F7F7F7Fu");
    verifyIsInTranslation("2139062143");
    verifyIsNotInTranslation("packSnorm4x8");
}

// Test that packSnorm4x8 is folded correctly for a vector of minus ones.
TEST_P(GLSLConstantFoldingTest_ES31, PackSnorm4x8MinusOne)
{
    test("uint", "const uint c = packSnorm4x8(vec4(-1.))", "v == 0x81818181u");
    verifyIsInTranslation("2172748161");
    verifyIsNotInTranslation("packSnorm4x8");
}

// Test that unpackSnorm4x8 is folded correctly when it needs to clamp the result.
TEST_P(GLSLConstantFoldingTest_ES31, UnpackSnorm4x8Clamp)
{
    test("float", "const float c = unpackSnorm4x8(0x00000080u).x", "v == -1.");
    verifyIsInTranslation("-1.0");
    verifyIsNotInTranslation("packSnorm4x8");
}

// Test that unpackUnorm4x8 is folded correctly.
TEST_P(GLSLConstantFoldingTest_ES31, UnpackUnorm4x8)
{
    test("float", "const float c = unpackUnorm4x8(0x007bbeefu).z", "abs(v - 123./255.) < 0.000001");
    verifyIsInTranslation("0.48235");
    verifyIsNotInTranslation("8109807");
    verifyIsNotInTranslation("packUnorm4x8");
}

// Test that ldexp is folded correctly.
TEST_P(GLSLConstantFoldingTest_ES31, Ldexp)
{
    test("float", "const float c = ldexp(0.625, 1)", "v == 1.25");
    verifyIsNotInTranslation("0.625");
    verifyIsNotInTranslation("ldexp");
}

// Constant fold ternary
TEST_P(GLSLConstantFoldingTest, Ternary)
{
    test("int", "int c = true ? 1 : v", "v == 1");
    verifyIsNotInTranslation("?");
    verifyIsNotInTranslation("if");
    verifyIsNotInTranslation("else");
}

// Constant fold ternary
TEST_P(GLSLConstantFoldingTest, TernaryInsideExpression)
{
    test("int", "int c = ivec2((true ? 1 : v) + 2, 4).x", "v == 3");
    verifyIsNotInTranslation("?");
    verifyIsNotInTranslation("if");
    verifyIsNotInTranslation("else");
}

// Constant fold indexing of an array
TEST_P(GLSLConstantFoldingTest, ArrayIndex)
{
    test("float", "const float c = float[3](-1., 1., 2.)[2]", "v == 2.");
    verifyIsNotInTranslation("[2]");
}

// Constant fold indexing of an array of array
TEST_P(GLSLConstantFoldingTest_ES31, ArrayOfArrayIndex)
{
    test("float", "const float c = float[2][2](float[2](-1., 1.), float[2](2., 3.))[1][0]",
         "v == 2.");
    verifyIsNotInTranslation("[1]");
    verifyIsNotInTranslation("[0]");
}

// Constant fold indexing of an array stashed in another variable
TEST_P(GLSLConstantFoldingTest, NamedArrayIndex)
{
    test("float", R"(const float[3] arr = float[3](-1., 1., 2.);
const float c = arr[1])",
         "v == 1.");
    verifyIsNotInTranslation("[1]");
}

// Constant fold indexing of an array of array stashed in another variable
TEST_P(GLSLConstantFoldingTest_ES31, NamedArrayOfArrayIndex)
{
    test("float[2]", R"(const float[2][2] arr = float[2][2](float[2](-1., 1.), float[2](2., 3.));
const float[2] c = arr[1])",
         "v[0] == 2. && v[1] == 3.");

    // The AST path does not remove the definition of `arr`.
    if (getEGLWindow()->isFeatureEnabled(Feature::UseIr))
    {
        verifyIsNotInTranslation("1.0");
    }
}

// Constant fold indexing of an array of mixed constant and non-constant values (without side
// effect).
TEST_P(GLSLConstantFoldingTest, ArrayMixedArgumentsIndex)
{
    test("float", R"(float c = float[2](v, 1.)[1])", "v == 1.");
    verifyIsNotInTranslation("[1]");
}

// Constant fold indexing of an nested array constructors.
TEST_P(GLSLConstantFoldingTest_ES31, NestedArrayMixedArgumentsIndex)
{
    test("float",
         R"(float c = float[2][4](float[4](0., 1., 2., 3.), float[4](4., v, 5., 6.))[1][2])",
         "v == 5.");
    verifyIsNotInTranslation("[1]");
    verifyIsNotInTranslation("[2]");
}

// Constant fold selecting field of a constructed struct
TEST_P(GLSLConstantFoldingTest_ES31, ConstructedStructSelectField)
{
    test("float", R"(struct S { int a; float b; };
float c = S(S(0, v).a, S(int(v), 3.).b).b)",
         "v == 3.");
    verifyIsNotInTranslation(".a");
    verifyIsNotInTranslation(".b");
}

// Constant fold indexing of an array of mixed constant and non-constant values with side
// effect should not discard the side effect.
TEST_P(GLSLConstantFoldingTest, ArrayMixedArgumentsWithSideEffectIndex)
{
    test("float", R"(float sideEffectTarget = 0.;
float indexedElement = float[3](sideEffectTarget = 5., 11., 102.)[1];
float c = indexedElement + sideEffectTarget)",
         "v == 16.");
}

// Constant fold equality of constructed arrays.
TEST_P(GLSLConstantFoldingTest, ArrayEqualityFalse)
{
    test("float", "const float c = float[3](2., 1., 1.) == float[3](2., 1., -1.) ? 1. : 2.",
         "v == 2.");
    verifyIsNotInTranslation("-1.0");
    verifyIsNotInTranslation("==");
}

// Constant fold equality of constructed arrays.
TEST_P(GLSLConstantFoldingTest, ArrayEqualityTrue)
{
    test("float", "const float c = float[3](2., 1., -1.) == float[3](2., 1., -1.) ? 1. : 2.",
         "v == 1.");
    verifyIsNotInTranslation("-1.0");
    verifyIsNotInTranslation("==");
}

// Constant fold equality of constructed arrays stashed in variables.
TEST_P(GLSLConstantFoldingTest, NamedArrayEqualityFalse)
{
    test("float", R"(const float[3] arrA = float[3](-1., 1., 2.);
const float[3] arrB = float[3](1., 1., 2.);
float c = arrA == arrB ? 1. : 2.)",
         "v == 2.");
    verifyIsNotInTranslation("-1.0");
    verifyIsNotInTranslation("==");
}

// Constant fold equality of constructed arrays stashed in variables.
TEST_P(GLSLConstantFoldingTest, NamedArrayEqualityTrue)
{
    test("float", R"(const float[3] arrA = float[3](-1., 1., 2.);
const float[3] arrB = float[3](-1., 1., 2.);
float c = arrA == arrB ? 1. : 2.)",
         "v == 1.");
    verifyIsNotInTranslation("-1.0");
    verifyIsNotInTranslation("==");
}

// Constant fold equality of constructed arrays of arrays.
TEST_P(GLSLConstantFoldingTest_ES31, ArrayOfArrayEqualityFalse)
{
    test("float", R"(const float c = float[2][2](float[2](-1., 1.), float[2](2., 3.))
                                  == float[2][2](float[2](-1., 1.), float[2](2., 1000.)) ? 1. : 2.)",
         "v == 2.");
    verifyIsNotInTranslation("-1.0");
    verifyIsNotInTranslation("==");
}

// Constant fold equality of constructed arrays.
TEST_P(GLSLConstantFoldingTest_ES31, ArrayOfArrayEqualityTrue)
{
    test("float", R"(const float c = float[2][2](float[2](-1., 1.), float[2](2., 3.))
                                  == float[2][2](float[2](-1., 1.), float[2](2., 3.)) ? 1. : 2.)",
         "v == 1.");
    verifyIsNotInTranslation("-1.0");
    verifyIsNotInTranslation("==");
}

// Constant fold casting a negative float to uint.
// ESSL 3.00.6 section 5.4.1 specifies this as an undefined conversion.
TEST_P(GLSLConstantFoldingTest, NegativeFloatToUint)
{
    test("uint", "const uint c = uint(-1.)", "v == 0xFFFFFFFFu");
    verifyIsInTranslation("4294967295");
}

// Constant fold casting a negative float to uint inside a uvec constructor.
// ESSL 3.00.6 section 5.4.1 specifies this as an undefined conversion.
TEST_P(GLSLConstantFoldingTest, NegativeFloatToUvec)
{
    test("uint", "const uint c = uvec4(2., 1., vec2(0., -1.)).w", "v == 0xFFFFFFFFu");
    verifyIsInTranslation("4294967295");
    verifyIsNotInTranslation("2.0");
}

// Constant fold casting a negative float to uint inside a uvec constructor, but that which is not
// used by the constructor.
TEST_P(GLSLConstantFoldingTest, NegativeFloatInsideUvecConstructorButOutOfRange)
{
    test("uint", "const uint c = uvec2(1., vec2(0., -1.)).x", "v == 1u");
    verifyIsNotInTranslation("-1.0");
}

// Constant fold a large float (above max signed int) to uint.
TEST_P(GLSLConstantFoldingTest, LargeFloatToUint)
{
    test("uint", "const uint c = uint(3221225472.)", "v == 3221225472u");
}

// Constant fold modulus with a negative dividend.
TEST_P(GLSLConstantFoldingTest, IntegerModulusNegativeDividend)
{
    test("int", "const int c = (-5) % 3", "v == 0");
    verifyIsNotInTranslation("%");
}

// Constant fold modulus with a negative divisor.
TEST_P(GLSLConstantFoldingTest, IntegerModulusNegativeDivisor)
{
    test("int", "const int c = 5 % (-3)", "v == 0");
    verifyIsNotInTranslation("%");
}

// Constant fold isnan with multiple components
TEST_P(GLSLConstantFoldingTest_ES31, IsnanMultipleComponents)
{
    test("ivec4", "const ivec4 c = ivec4(mix(ivec2(2), ivec2(3), isnan(vec2(1., 0. / 0.))), 4, 5)",
         "all(equal(v, ivec4(2, 3, 4, 5)))");
    verifyIsNotInTranslation("1.0");
    verifyIsNotInTranslation("isnan");
    verifyIsNotInTranslation("mix");
}

// Constant fold isinf with multiple components
TEST_P(GLSLConstantFoldingTest_ES31, IsinfMultipleComponents)
{
    test("ivec4",
         "const ivec4 c = ivec4(mix(ivec2(2), ivec2(3), isinf(vec2(0.0, 1.0e2048))), 4, 5)",
         "all(equal(v, ivec4(2, 3, 4, 5)))");
    verifyIsNotInTranslation("isnan");
    verifyIsNotInTranslation("mix");
}

// Constant fold floatBitsToInt with multiple components
TEST_P(GLSLConstantFoldingTest, FloatBitsToIntMultipleComponents)
{
    test("ivec4", "const ivec4 c = ivec4(floatBitsToInt(vec2(0.0, 1.0)), 4, 5)",
         "all(equal(v, ivec4(0, 0x3f800000, 4, 5)))");
    verifyIsNotInTranslation("1.0");
    verifyIsNotInTranslation("floatBitsToInt");
}

// Constant fold floatBitsToUint with multiple components
TEST_P(GLSLConstantFoldingTest, FloatBitsToUintMultipleComponents)
{
    test("ivec4", "const ivec4 c = ivec4(floatBitsToUint(vec2(0.0, 1.0)), 4, 5)",
         "all(equal(v, ivec4(0, 0x3f800000, 4, 5)))");
    verifyIsNotInTranslation("1.0");
    verifyIsNotInTranslation("floatBitsToInt");
}

// Constant fold intBitsToFloat with multiple components
TEST_P(GLSLConstantFoldingTest, IntBitsToFloatMultipleComponents)
{
    test("vec4", "const vec4 c = vec4(intBitsToFloat(ivec2(0, 0x3f800000)), 0.25, 0.5)",
         "all(equal(v, vec4(0., 1., 0.25, 0.5)))");
    verifyIsNotInTranslation("1065353216");
    verifyIsNotInTranslation("intBitsToFloat");
}

// Constant fold uintBitsToFloat with multiple components
TEST_P(GLSLConstantFoldingTest, UintBitsToFloatMultipleComponents)
{
    test("vec4", "const vec4 c = vec4(uintBitsToFloat(uvec2(0U, 0x3f800000U)), 0.25, 0.5)",
         "all(equal(v, vec4(0., 1., 0.25, 0.5)))");
    verifyIsNotInTranslation("1065353216");
    verifyIsNotInTranslation("uintBitsToFloat");
}

// Test that infinity - infinity evaluates to NaN.
TEST_P(GLSLConstantFoldingTest, InfinityMinusInfinity)
{
    test("float", "const float c = 1.0e2048 - 1.0e2048", "isnan(v)");
}

// Test that infinity + negative infinity evaluates to NaN.
TEST_P(GLSLConstantFoldingTest, InfinityPlusNegativeInfinity)
{
    test("float", "const float c = 1.0e2048 + (-1.0e2048)", "isnan(v)");
}

// Test that infinity multiplied by zero evaluates to NaN.
TEST_P(GLSLConstantFoldingTest, InfinityMultipliedByZero)
{
    test("float", "const float c = 1.0e2048 * 0.", "isnan(v)");
}

// Test that infinity divided by infinity evaluates to NaN.
TEST_P(GLSLConstantFoldingTest, InfinityDividedByInfinity)
{
    test("float", "const float c = 1.0e2048 / 1.0e2048", "isnan(v)");
}

// Test that zero divided by zero evaluates to NaN.
TEST_P(GLSLConstantFoldingTest, ZeroDividedByZero)
{
    test("float", "const float c = 0. / 0.", "isnan(v)");
}

// Test that addition that overflows is evaluated correctly.
TEST_P(GLSLConstantFoldingTest, FloatOverflowAdd)
{
    test("float", "const float c = 2.0e38 + 2.0e38", "isinf(v) && v > 0.");
}

// Test that subtraction that overflows is evaluated correctly.
TEST_P(GLSLConstantFoldingTest, FloatOverflowSubtract)
{
    test("float", "const float c = 2.0e38 - (-2.0e38)", "isinf(v) && v > 0.");
}

// Test that multiplication that overflows is evaluated correctly.
TEST_P(GLSLConstantFoldingTest, FloatOverflowMultiply)
{
    test("float", "const float c = 1.0e30 * 1.0e10", "isinf(v) && v > 0.");
}

// Test that division that overflows is evaluated correctly.
TEST_P(GLSLConstantFoldingTest, FloatOverflowDivide)
{
    test("float", "const float c = 1.0e30 / 1.0e-10", "isinf(v) && v > 0.");
}
}  // namespace

GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GLSLConstantFoldingTest);
ANGLE_INSTANTIATE_TEST_ES3(GLSLConstantFoldingTest);

GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GLSLConstantFoldingTest_ES31);
ANGLE_INSTANTIATE_TEST_ES31(GLSLConstantFoldingTest_ES31);
