/*-------------------------------------------------------------------------
 * drawElements Quality Program OpenGL ES 3.0 Module
 * -------------------------------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * 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.
 *
 *//*!
 * \file
 * \brief Shader matrix arithmetic tests.
 *
 * Variables:
 *  + operation
 *    - mat OP mat
 *    - mat OP vec
 *    - vec OP mat
 *    - mat OP scalar
 *    - OP ( mat )
 *    - vec OP vec
 *    - OP mat
 *  + matrix source
 *    - constant (ctor)
 *    - uniform
 *    - vertex input
 *    - fragment input
 *  + other operand: always dynamic data?
 *  + how to reduce to vec3?
 *//*--------------------------------------------------------------------*/

#include "es3fShaderMatrixTests.hpp"
#include "glsShaderRenderCase.hpp"
#include "gluShaderUtil.hpp"
#include "tcuVector.hpp"
#include "tcuMatrix.hpp"
#include "tcuMatrixUtil.hpp"
#include "deStringUtil.hpp"
#include "deFloat16.h"

#include "glwEnums.hpp"
#include "glwFunctions.hpp"

namespace deqp
{
namespace gles3
{
namespace Functional
{

using std::string;
using std::vector;
using namespace glu;
using namespace deqp::gls;

using tcu::Mat2;
using tcu::Mat2x3;
using tcu::Mat2x4;
using tcu::Mat3;
using tcu::Mat3x2;
using tcu::Mat3x4;
using tcu::Mat4;
using tcu::Mat4x2;
using tcu::Mat4x3;
using tcu::Vec2;
using tcu::Vec3;
using tcu::Vec4;

// Uniform / constant values for tests.
// \note Input1 should not contain 0 components as it is used as divisor in div cases.
// \todo [2012-02-14 pyry] Make these dynamic.
static const float s_constInFloat[2] = {0.5f, -0.2f};
static const Vec2 s_constInVec2[2]   = {Vec2(1.2f, 0.5f), Vec2(0.5f, 1.0f)};
static const Vec3 s_constInVec3[2]   = {Vec3(1.1f, 0.1f, 0.5f), Vec3(-0.2f, 0.5f, 0.8f)};
static const Vec4 s_constInVec4[2]   = {Vec4(1.4f, 0.2f, -0.5f, 0.7f), Vec4(0.2f, -1.0f, 0.5f, 0.8f)};

static const float s_constInMat2x2[2][4] = {
    {
        -0.1f,
        1.0f,
        -0.2f,
        0.0f,
    },
    {
        0.8f,
        0.1f,
        0.5f,
        -0.9f,
    },
};
static const float s_constInMat3x2[2][6] = {
    {
        0.8f,
        -0.3f,
        0.3f,
        1.0f,
        1.2f,
        -1.2f,
    },
    {
        1.2f,
        -1.0f,
        0.5f,
        -0.8f,
        1.1f,
        0.3f,
    },
};
static const float s_constInMat4x2[2][8] = {
    {
        -0.2f,
        0.5f,
        0.0f,
        -1.0f,
        1.2f,
        -0.5f,
        0.3f,
        -0.9f,
    },
    {
        1.0f,
        0.1f,
        -1.1f,
        0.6f,
        0.8f,
        -1.2f,
        -1.1f,
        0.7f,
    },
};
static const float s_constInMat2x3[2][6] = {
    {
        -0.6f,
        -0.1f,
        -0.7f,
        -1.2f,
        -0.2f,
        0.0f,
    },
    {
        1.1f,
        0.6f,
        0.8f,
        1.0f,
        0.7f,
        0.1f,
    },
};
static const float s_constInMat3x3[2][9] = {
    {
        -0.2f,
        1.1f,
        1.2f,
        -1.0f,
        1.2f,
        0.5f,
        0.7f,
        -0.2f,
        1.0f,
    },
    {
        -0.1f,
        -0.1f,
        0.1f,
        -0.1f,
        -0.2f,
        1.0f,
        -0.5f,
        0.1f,
        -0.4f,
    },
};
static const float s_constInMat4x3[2][12] = {
    {
        -0.9f,
        0.0f,
        0.6f,
        0.2f,
        0.9f,
        -0.1f,
        -0.3f,
        -0.7f,
        -0.1f,
        0.1f,
        1.0f,
        0.0f,
    },
    {
        0.5f,
        0.7f,
        0.7f,
        1.2f,
        1.1f,
        0.1f,
        1.0f,
        -1.0f,
        -0.2f,
        -0.2f,
        -0.3f,
        -0.5f,
    },
};
static const float s_constInMat2x4[2][8] = {
    {
        -0.6f,
        -1.1f,
        -0.6f,
        -0.6f,
        -0.2f,
        -0.6f,
        -0.1f,
        -0.1f,
    },
    {
        -1.2f,
        -1.0f,
        0.7f,
        -1.0f,
        0.7f,
        0.7f,
        -0.4f,
        -0.3f,
    },
};
static const float s_constInMat3x4[2][12] = {
    {
        0.6f,
        -0.4f,
        1.2f,
        0.9f,
        0.8f,
        0.4f,
        1.1f,
        0.3f,
        0.5f,
        -0.2f,
        0.0f,
        1.1f,
    },
    {
        -0.8f,
        1.2f,
        -0.2f,
        -1.1f,
        -0.9f,
        -0.5f,
        -1.2f,
        1.0f,
        1.2f,
        0.1f,
        -0.7f,
        -0.5f,
    },
};
static const float s_constInMat4x4[2][16] = {
    {
        0.3f,
        0.9f,
        -0.2f,
        1.0f,
        -0.4f,
        -0.6f,
        0.6f,
        -1.0f,
        -0.9f,
        -0.1f,
        0.3f,
        -0.2f,
        -0.3f,
        -0.9f,
        1.0f,
        0.1f,
    },
    {
        0.4f,
        -0.7f,
        -0.8f,
        0.7f,
        -0.4f,
        -0.8f,
        0.6f,
        -0.3f,
        0.7f,
        -1.0f,
        0.1f,
        -0.3f,
        0.2f,
        0.6f,
        0.4f,
        -1.0f,
    },
};

namespace MatrixCaseUtils
{

enum InputType
{
    INPUTTYPE_CONST = 0,
    INPUTTYPE_UNIFORM,
    INPUTTYPE_DYNAMIC,

    INPUTTYPE_LAST
};

struct ShaderInput
{
    ShaderInput(InputType inputType_, DataType dataType_, Precision precision_)
        : inputType(inputType_)
        , dataType(dataType_)
        , precision(precision_)
    {
    }

    InputType inputType;
    DataType dataType;
    Precision precision;
};

enum MatrixOp
{
    OP_ADD = 0,
    OP_SUB,
    OP_MUL,
    OP_DIV,
    OP_COMP_MUL,
    OP_OUTER_PRODUCT,
    OP_TRANSPOSE,
    OP_INVERSE,
    OP_DETERMINANT,
    OP_UNARY_PLUS,
    OP_NEGATION,
    OP_PRE_INCREMENT,
    OP_PRE_DECREMENT,
    OP_POST_INCREMENT,
    OP_POST_DECREMENT,
    OP_ADD_INTO,
    OP_SUBTRACT_FROM,
    OP_MULTIPLY_INTO,
    OP_DIVIDE_INTO,
    OP_LAST
};

// Type traits.

template <int DataT>
struct TypeTraits;

#define DECLARE_TYPE_TRAIT(DATATYPE, TYPE) \
    template <>                            \
    struct TypeTraits<DATATYPE>            \
    {                                      \
        typedef TYPE Type;                 \
    }

DECLARE_TYPE_TRAIT(TYPE_FLOAT, float);
DECLARE_TYPE_TRAIT(TYPE_FLOAT_VEC2, tcu::Vec2);
DECLARE_TYPE_TRAIT(TYPE_FLOAT_VEC3, tcu::Vec3);
DECLARE_TYPE_TRAIT(TYPE_FLOAT_VEC4, tcu::Vec4);
DECLARE_TYPE_TRAIT(TYPE_FLOAT_MAT2, tcu::Mat2);
DECLARE_TYPE_TRAIT(TYPE_FLOAT_MAT2X3, tcu::Mat2x3);
DECLARE_TYPE_TRAIT(TYPE_FLOAT_MAT2X4, tcu::Mat2x4);
DECLARE_TYPE_TRAIT(TYPE_FLOAT_MAT3X2, tcu::Mat3x2);
DECLARE_TYPE_TRAIT(TYPE_FLOAT_MAT3, tcu::Mat3);
DECLARE_TYPE_TRAIT(TYPE_FLOAT_MAT3X4, tcu::Mat3x4);
DECLARE_TYPE_TRAIT(TYPE_FLOAT_MAT4X2, tcu::Mat4x2);
DECLARE_TYPE_TRAIT(TYPE_FLOAT_MAT4X3, tcu::Mat4x3);
DECLARE_TYPE_TRAIT(TYPE_FLOAT_MAT4, tcu::Mat4);

// Operation info

enum OperationType
{
    OPERATIONTYPE_BINARY_OPERATOR = 0,
    OPERATIONTYPE_BINARY_FUNCTION,
    OPERATIONTYPE_UNARY_PREFIX_OPERATOR,
    OPERATIONTYPE_UNARY_POSTFIX_OPERATOR,
    OPERATIONTYPE_UNARY_FUNCTION,
    OPERATIONTYPE_ASSIGNMENT,

    OPERATIONTYPE_LAST
};

static const char *getOperationName(MatrixOp op)
{
    switch (op)
    {
    case OP_ADD:
        return "+";
    case OP_SUB:
        return "-";
    case OP_MUL:
        return "*";
    case OP_DIV:
        return "/";
    case OP_COMP_MUL:
        return "matrixCompMult";
    case OP_OUTER_PRODUCT:
        return "outerProduct";
    case OP_TRANSPOSE:
        return "transpose";
    case OP_INVERSE:
        return "inverse";
    case OP_DETERMINANT:
        return "determinant";
    case OP_UNARY_PLUS:
        return "+";
    case OP_NEGATION:
        return "-";
    case OP_PRE_INCREMENT:
        return "++";
    case OP_PRE_DECREMENT:
        return "--";
    case OP_POST_INCREMENT:
        return "++";
    case OP_POST_DECREMENT:
        return "--";
    case OP_ADD_INTO:
        return "+=";
    case OP_SUBTRACT_FROM:
        return "-=";
    case OP_MULTIPLY_INTO:
        return "*=";
    case OP_DIVIDE_INTO:
        return "/=";

    default:
        DE_ASSERT(false);
        return "";
    }
}

static OperationType getOperationType(MatrixOp op)
{
    switch (op)
    {
    case OP_ADD:
        return OPERATIONTYPE_BINARY_OPERATOR;
    case OP_SUB:
        return OPERATIONTYPE_BINARY_OPERATOR;
    case OP_MUL:
        return OPERATIONTYPE_BINARY_OPERATOR;
    case OP_DIV:
        return OPERATIONTYPE_BINARY_OPERATOR;
    case OP_COMP_MUL:
        return OPERATIONTYPE_BINARY_FUNCTION;
    case OP_OUTER_PRODUCT:
        return OPERATIONTYPE_BINARY_FUNCTION;
    case OP_TRANSPOSE:
        return OPERATIONTYPE_UNARY_FUNCTION;
    case OP_INVERSE:
        return OPERATIONTYPE_UNARY_FUNCTION;
    case OP_DETERMINANT:
        return OPERATIONTYPE_UNARY_FUNCTION;
    case OP_UNARY_PLUS:
        return OPERATIONTYPE_UNARY_PREFIX_OPERATOR;
    case OP_NEGATION:
        return OPERATIONTYPE_UNARY_PREFIX_OPERATOR;
    case OP_PRE_INCREMENT:
        return OPERATIONTYPE_UNARY_PREFIX_OPERATOR;
    case OP_PRE_DECREMENT:
        return OPERATIONTYPE_UNARY_PREFIX_OPERATOR;
    case OP_POST_INCREMENT:
        return OPERATIONTYPE_UNARY_POSTFIX_OPERATOR;
    case OP_POST_DECREMENT:
        return OPERATIONTYPE_UNARY_POSTFIX_OPERATOR;
    case OP_ADD_INTO:
        return OPERATIONTYPE_ASSIGNMENT;
    case OP_SUBTRACT_FROM:
        return OPERATIONTYPE_ASSIGNMENT;
    case OP_MULTIPLY_INTO:
        return OPERATIONTYPE_ASSIGNMENT;
    case OP_DIVIDE_INTO:
        return OPERATIONTYPE_ASSIGNMENT;
    default:
        DE_ASSERT(false);
        return OPERATIONTYPE_LAST;
    }
}

enum TestMatrixType
{
    TESTMATRIXTYPE_DEFAULT = 0,
    TESTMATRIXTYPE_NEGATED,
    TESTMATRIXTYPE_INCREMENTED,
    TESTMATRIXTYPE_DECREMENTED,
    TESTMATRIXTYPE_NEGATED_INCREMENTED,
    TESTMATRIXTYPE_INCREMENTED_LESS,

    TESTMATRIXTYPE_LAST
};

static TestMatrixType getOperationTestMatrixType(MatrixOp op)
{
    switch (op)
    {
    case OP_ADD:
        return TESTMATRIXTYPE_DEFAULT;
    case OP_SUB:
        return TESTMATRIXTYPE_DEFAULT;
    case OP_MUL:
        return TESTMATRIXTYPE_DEFAULT;
    case OP_DIV:
        return TESTMATRIXTYPE_DEFAULT;
    case OP_COMP_MUL:
        return TESTMATRIXTYPE_DEFAULT;
    case OP_OUTER_PRODUCT:
        return TESTMATRIXTYPE_DEFAULT;
    case OP_TRANSPOSE:
        return TESTMATRIXTYPE_DEFAULT;
    case OP_INVERSE:
        return TESTMATRIXTYPE_DEFAULT;
    case OP_DETERMINANT:
        return TESTMATRIXTYPE_DEFAULT;
    case OP_UNARY_PLUS:
        return TESTMATRIXTYPE_DECREMENTED;
    case OP_NEGATION:
        return TESTMATRIXTYPE_NEGATED_INCREMENTED;
    case OP_PRE_INCREMENT:
        return TESTMATRIXTYPE_NEGATED;
    case OP_PRE_DECREMENT:
        return TESTMATRIXTYPE_INCREMENTED;
    case OP_POST_INCREMENT:
        return TESTMATRIXTYPE_NEGATED;
    case OP_POST_DECREMENT:
        return TESTMATRIXTYPE_DEFAULT;
    case OP_ADD_INTO:
        return TESTMATRIXTYPE_DEFAULT;
    case OP_SUBTRACT_FROM:
        return TESTMATRIXTYPE_INCREMENTED_LESS;
    case OP_MULTIPLY_INTO:
        return TESTMATRIXTYPE_NEGATED;
    case OP_DIVIDE_INTO:
        return TESTMATRIXTYPE_DECREMENTED;

    default:
        DE_ASSERT(false);
        return TESTMATRIXTYPE_LAST;
    }
}

static bool isOperationBinary(MatrixOp op)
{
    return getOperationType(op) == OPERATIONTYPE_BINARY_OPERATOR ||
           getOperationType(op) == OPERATIONTYPE_BINARY_FUNCTION || getOperationType(op) == OPERATIONTYPE_ASSIGNMENT;
}

static bool isOperationMatrixScalar(MatrixOp op)
{
    return op == OP_ADD || op == OP_SUB || op == OP_MUL || op == OP_DIV;
}

static bool isOperationMatrixVector(MatrixOp op)
{
    return op == OP_MUL;
}

static bool isOperationArithmeticMatrixMatrix(MatrixOp op)
{
    return op == OP_MUL;
}

static bool isOperationComponentwiseMatrixMatrix(MatrixOp op)
{
    return op == OP_ADD || op == OP_SUB || op == OP_MUL || op == OP_DIV || op == OP_COMP_MUL;
}

static bool isOperationVectorVector(MatrixOp op)
{
    return op == OP_OUTER_PRODUCT;
}

static bool isOperationUnaryAnyMatrix(MatrixOp op)
{
    return op == OP_TRANSPOSE || op == OP_UNARY_PLUS || op == OP_NEGATION || op == OP_PRE_INCREMENT ||
           op == OP_PRE_DECREMENT || op == OP_POST_INCREMENT || op == OP_POST_DECREMENT;
}

static bool isOperationUnarySymmetricMatrix(MatrixOp op)
{
    return op == OP_INVERSE || op == OP_DETERMINANT;
}

static bool isOperationValueModifying(MatrixOp op)
{
    return op == OP_PRE_INCREMENT || op == OP_PRE_DECREMENT || op == OP_POST_INCREMENT || op == OP_POST_DECREMENT;
}

static bool isOperationAssignment(MatrixOp op)
{
    return op == OP_ADD_INTO || op == OP_SUBTRACT_FROM || op == OP_MULTIPLY_INTO || op == OP_DIVIDE_INTO;
}

static bool isOperationAssignmentAnyMatrix(MatrixOp op)
{
    return op == OP_ADD_INTO || op == OP_SUBTRACT_FROM || op == OP_DIVIDE_INTO;
}

static bool isOperationAssignmentSymmetricMatrix(MatrixOp op)
{
    return op == OP_MULTIPLY_INTO;
}

// Operation nature

enum OperationNature
{
    OPERATIONNATURE_PURE = 0,
    OPERATIONNATURE_MUTATING,
    OPERATIONNATURE_ASSIGNMENT,

    OPERATIONNATURE_LAST
};

static OperationNature getOperationNature(MatrixOp op)
{
    if (isOperationAssignment(op))
        return OPERATIONNATURE_ASSIGNMENT;

    if (isOperationValueModifying(op))
        return OPERATIONNATURE_MUTATING;

    return OPERATIONNATURE_PURE;
}

// Input value loader.

template <int InputT, int DataT>
typename TypeTraits<DataT>::Type getInputValue(const ShaderEvalContext &evalCtx, int inputNdx);

template <>
inline float getInputValue<INPUTTYPE_CONST, TYPE_FLOAT>(const ShaderEvalContext &evalCtx, int inputNdx)
{
    DE_UNREF(evalCtx);
    return s_constInFloat[inputNdx];
}
template <>
inline tcu::Vec2 getInputValue<INPUTTYPE_CONST, TYPE_FLOAT_VEC2>(const ShaderEvalContext &evalCtx, int inputNdx)
{
    DE_UNREF(evalCtx);
    return s_constInVec2[inputNdx];
}
template <>
inline tcu::Vec3 getInputValue<INPUTTYPE_CONST, TYPE_FLOAT_VEC3>(const ShaderEvalContext &evalCtx, int inputNdx)
{
    DE_UNREF(evalCtx);
    return s_constInVec3[inputNdx];
}
template <>
inline tcu::Vec4 getInputValue<INPUTTYPE_CONST, TYPE_FLOAT_VEC4>(const ShaderEvalContext &evalCtx, int inputNdx)
{
    DE_UNREF(evalCtx);
    return s_constInVec4[inputNdx];
}

template <>
inline tcu::Mat2 getInputValue<INPUTTYPE_CONST, TYPE_FLOAT_MAT2>(const ShaderEvalContext &evalCtx, int inputNdx)
{
    DE_UNREF(evalCtx);
    return tcu::Mat2(s_constInMat2x2[inputNdx]);
}
template <>
inline tcu::Mat2x3 getInputValue<INPUTTYPE_CONST, TYPE_FLOAT_MAT2X3>(const ShaderEvalContext &evalCtx, int inputNdx)
{
    DE_UNREF(evalCtx);
    return tcu::Mat2x3(s_constInMat2x3[inputNdx]);
}
template <>
inline tcu::Mat2x4 getInputValue<INPUTTYPE_CONST, TYPE_FLOAT_MAT2X4>(const ShaderEvalContext &evalCtx, int inputNdx)
{
    DE_UNREF(evalCtx);
    return tcu::Mat2x4(s_constInMat2x4[inputNdx]);
}
template <>
inline tcu::Mat3x2 getInputValue<INPUTTYPE_CONST, TYPE_FLOAT_MAT3X2>(const ShaderEvalContext &evalCtx, int inputNdx)
{
    DE_UNREF(evalCtx);
    return tcu::Mat3x2(s_constInMat3x2[inputNdx]);
}
template <>
inline tcu::Mat3 getInputValue<INPUTTYPE_CONST, TYPE_FLOAT_MAT3>(const ShaderEvalContext &evalCtx, int inputNdx)
{
    DE_UNREF(evalCtx);
    return tcu::Mat3(s_constInMat3x3[inputNdx]);
}
template <>
inline tcu::Mat3x4 getInputValue<INPUTTYPE_CONST, TYPE_FLOAT_MAT3X4>(const ShaderEvalContext &evalCtx, int inputNdx)
{
    DE_UNREF(evalCtx);
    return tcu::Mat3x4(s_constInMat3x4[inputNdx]);
}
template <>
inline tcu::Mat4x2 getInputValue<INPUTTYPE_CONST, TYPE_FLOAT_MAT4X2>(const ShaderEvalContext &evalCtx, int inputNdx)
{
    DE_UNREF(evalCtx);
    return tcu::Mat4x2(s_constInMat4x2[inputNdx]);
}
template <>
inline tcu::Mat4x3 getInputValue<INPUTTYPE_CONST, TYPE_FLOAT_MAT4X3>(const ShaderEvalContext &evalCtx, int inputNdx)
{
    DE_UNREF(evalCtx);
    return tcu::Mat4x3(s_constInMat4x3[inputNdx]);
}
template <>
inline tcu::Mat4 getInputValue<INPUTTYPE_CONST, TYPE_FLOAT_MAT4>(const ShaderEvalContext &evalCtx, int inputNdx)
{
    DE_UNREF(evalCtx);
    return tcu::Mat4(s_constInMat4x4[inputNdx]);
}

template <>
inline float getInputValue<INPUTTYPE_DYNAMIC, TYPE_FLOAT>(const ShaderEvalContext &evalCtx, int inputNdx)
{
    DE_UNREF(inputNdx);
    return evalCtx.coords.x();
}
template <>
inline tcu::Vec2 getInputValue<INPUTTYPE_DYNAMIC, TYPE_FLOAT_VEC2>(const ShaderEvalContext &evalCtx, int inputNdx)
{
    DE_UNREF(inputNdx);
    return evalCtx.coords.swizzle(0, 1);
}
template <>
inline tcu::Vec3 getInputValue<INPUTTYPE_DYNAMIC, TYPE_FLOAT_VEC3>(const ShaderEvalContext &evalCtx, int inputNdx)
{
    DE_UNREF(inputNdx);
    return evalCtx.coords.swizzle(0, 1, 2);
}
template <>
inline tcu::Vec4 getInputValue<INPUTTYPE_DYNAMIC, TYPE_FLOAT_VEC4>(const ShaderEvalContext &evalCtx, int inputNdx)
{
    DE_UNREF(inputNdx);
    return evalCtx.coords.swizzle(0, 1, 2, 3);
}

template <>
inline tcu::Mat2 getInputValue<INPUTTYPE_DYNAMIC, TYPE_FLOAT_MAT2>(const ShaderEvalContext &evalCtx, int inputNdx)
{
    DE_UNREF(inputNdx); // Not used.
    tcu::Mat2 m;
    m.setColumn(0, evalCtx.in[0].swizzle(0, 1));
    m.setColumn(1, evalCtx.in[1].swizzle(0, 1));
    return m;
}

template <>
inline tcu::Mat2x3 getInputValue<INPUTTYPE_DYNAMIC, TYPE_FLOAT_MAT2X3>(const ShaderEvalContext &evalCtx, int inputNdx)
{
    DE_UNREF(inputNdx); // Not used.
    tcu::Mat2x3 m;
    m.setColumn(0, evalCtx.in[0].swizzle(0, 1, 2));
    m.setColumn(1, evalCtx.in[1].swizzle(0, 1, 2));
    return m;
}

template <>
inline tcu::Mat2x4 getInputValue<INPUTTYPE_DYNAMIC, TYPE_FLOAT_MAT2X4>(const ShaderEvalContext &evalCtx, int inputNdx)
{
    DE_UNREF(inputNdx); // Not used.
    tcu::Mat2x4 m;
    m.setColumn(0, evalCtx.in[0]);
    m.setColumn(1, evalCtx.in[1]);
    return m;
}

template <>
inline tcu::Mat3x2 getInputValue<INPUTTYPE_DYNAMIC, TYPE_FLOAT_MAT3X2>(const ShaderEvalContext &evalCtx, int inputNdx)
{
    DE_UNREF(inputNdx); // Not used.
    tcu::Mat3x2 m;
    m.setColumn(0, evalCtx.in[0].swizzle(0, 1));
    m.setColumn(1, evalCtx.in[1].swizzle(0, 1));
    m.setColumn(2, evalCtx.in[2].swizzle(0, 1));
    return m;
}

template <>
inline tcu::Mat3 getInputValue<INPUTTYPE_DYNAMIC, TYPE_FLOAT_MAT3>(const ShaderEvalContext &evalCtx, int inputNdx)
{
    DE_UNREF(inputNdx); // Not used.
    tcu::Mat3 m;
    m.setColumn(0, evalCtx.in[0].swizzle(0, 1, 2));
    m.setColumn(1, evalCtx.in[1].swizzle(0, 1, 2));
    m.setColumn(2, evalCtx.in[2].swizzle(0, 1, 2));
    return m;
}

template <>
inline tcu::Mat3x4 getInputValue<INPUTTYPE_DYNAMIC, TYPE_FLOAT_MAT3X4>(const ShaderEvalContext &evalCtx, int inputNdx)
{
    DE_UNREF(inputNdx); // Not used.
    tcu::Mat3x4 m;
    m.setColumn(0, evalCtx.in[0]);
    m.setColumn(1, evalCtx.in[1]);
    m.setColumn(2, evalCtx.in[2]);
    return m;
}

template <>
inline tcu::Mat4x2 getInputValue<INPUTTYPE_DYNAMIC, TYPE_FLOAT_MAT4X2>(const ShaderEvalContext &evalCtx, int inputNdx)
{
    DE_UNREF(inputNdx); // Not used.
    tcu::Mat4x2 m;
    m.setColumn(0, evalCtx.in[0].swizzle(0, 1));
    m.setColumn(1, evalCtx.in[1].swizzle(0, 1));
    m.setColumn(2, evalCtx.in[2].swizzle(0, 1));
    m.setColumn(3, evalCtx.in[3].swizzle(0, 1));
    return m;
}

template <>
inline tcu::Mat4x3 getInputValue<INPUTTYPE_DYNAMIC, TYPE_FLOAT_MAT4X3>(const ShaderEvalContext &evalCtx, int inputNdx)
{
    DE_UNREF(inputNdx); // Not used.
    tcu::Mat4x3 m;
    m.setColumn(0, evalCtx.in[0].swizzle(0, 1, 2));
    m.setColumn(1, evalCtx.in[1].swizzle(0, 1, 2));
    m.setColumn(2, evalCtx.in[2].swizzle(0, 1, 2));
    m.setColumn(3, evalCtx.in[3].swizzle(0, 1, 2));
    return m;
}

template <>
inline tcu::Mat4 getInputValue<INPUTTYPE_DYNAMIC, TYPE_FLOAT_MAT4>(const ShaderEvalContext &evalCtx, int inputNdx)
{
    DE_UNREF(inputNdx); // Not used.
    tcu::Mat4 m;
    m.setColumn(0, evalCtx.in[0]);
    m.setColumn(1, evalCtx.in[1]);
    m.setColumn(2, evalCtx.in[2]);
    m.setColumn(3, evalCtx.in[3]);
    return m;
}

// Reduction from expression result to vec3.

inline tcu::Vec3 reduceToVec3(const tcu::Vec2 &value)
{
    return value.swizzle(0, 1, 0);
}
inline tcu::Vec3 reduceToVec3(const tcu::Vec3 &value)
{
    return value;
}
inline tcu::Vec3 reduceToVec3(const tcu::Vec4 &value)
{
    return tcu::Vec3(value.x(), value.y(), value.z() + value.w());
}
inline tcu::Vec3 reduceToVec3(const tcu::Mat2 &value)
{
    return tcu::Vec3(value(0, 0), value(0, 1), value(1, 0) + value(1, 1));
}
inline tcu::Vec3 reduceToVec3(const tcu::Mat2x3 &value)
{
    return value.getColumn(0) + value.getColumn(1);
}
inline tcu::Vec3 reduceToVec3(const tcu::Mat2x4 &value)
{
    return value.getColumn(0).swizzle(0, 1, 2) + value.getColumn(1).swizzle(1, 2, 3);
}
inline tcu::Vec3 reduceToVec3(const tcu::Mat3x2 &value)
{
    return tcu::Vec3(value(0, 0) + value(1, 0), value(0, 1) + value(1, 1), value(0, 2) + value(1, 2));
}
inline tcu::Vec3 reduceToVec3(const tcu::Mat3 &value)
{
    return value.getColumn(0) + value.getColumn(1) + value.getColumn(2);
}
inline tcu::Vec3 reduceToVec3(const tcu::Mat3x4 &value)
{
    return value.getColumn(0).swizzle(0, 1, 2) + value.getColumn(1).swizzle(1, 2, 3) +
           value.getColumn(2).swizzle(2, 3, 0);
}
inline tcu::Vec3 reduceToVec3(const tcu::Mat4x2 &value)
{
    return tcu::Vec3(value(0, 0) + value(1, 0) + value(0, 3), value(0, 1) + value(1, 1) + value(1, 3),
                     value(0, 2) + value(1, 2));
}
inline tcu::Vec3 reduceToVec3(const tcu::Mat4x3 &value)
{
    return value.getColumn(0) + value.getColumn(1) + value.getColumn(2) + value.getColumn(3);
}
inline tcu::Vec3 reduceToVec3(const tcu::Mat4 &value)
{
    return value.getColumn(0).swizzle(0, 1, 2) + value.getColumn(1).swizzle(1, 2, 3) +
           value.getColumn(2).swizzle(2, 3, 0) + value.getColumn(3).swizzle(3, 0, 1);
}

// matrixCompMult

template <typename T, int Rows, int Cols>
tcu::Matrix<T, Rows, Cols> matrixCompMult(const tcu::Matrix<T, Rows, Cols> &a, const tcu::Matrix<T, Rows, Cols> &b)
{
    tcu::Matrix<T, Rows, Cols> retVal;

    for (int r = 0; r < Rows; ++r)
        for (int c = 0; c < Cols; ++c)
            retVal(r, c) = a(r, c) * b(r, c);

    return retVal;
}

// outerProduct

template <typename T, int Rows, int Cols>
tcu::Matrix<T, Cols, Rows> outerProduct(const tcu::Vector<T, Cols> &a, const tcu::Vector<T, Rows> &b)
{
    tcu::Matrix<T, Rows, Cols> retVal;

    for (int r = 0; r < Rows; ++r)
        for (int c = 0; c < Cols; ++c)
            retVal(r, c) = a[c] * b[r];

    return transpose(retVal); // to gl-form (column-major)
}

// Determinant

template <int Size>
float determinant(const tcu::Matrix<float, Size, Size> &mat);

template <>
float determinant<2>(const tcu::Matrix<float, 2, 2> &mat)
{
    return mat(0, 0) * mat(1, 1) - mat(1, 0) * mat(0, 1);
}

template <>
float determinant<3>(const tcu::Matrix<float, 3, 3> &mat)
{
    return +mat(0, 0) * mat(1, 1) * mat(2, 2) + mat(0, 1) * mat(1, 2) * mat(2, 0) + mat(0, 2) * mat(1, 0) * mat(2, 1) -
           mat(0, 0) * mat(1, 2) * mat(2, 1) - mat(0, 1) * mat(1, 0) * mat(2, 2) - mat(0, 2) * mat(1, 1) * mat(2, 0);
}

template <>
float determinant<4>(const tcu::Matrix<float, 4, 4> &mat)
{
    const float minorMatrices[4][3 * 3] = {{
                                               mat(1, 1),
                                               mat(2, 1),
                                               mat(3, 1),
                                               mat(1, 2),
                                               mat(2, 2),
                                               mat(3, 2),
                                               mat(1, 3),
                                               mat(2, 3),
                                               mat(3, 3),
                                           },
                                           {
                                               mat(1, 0),
                                               mat(2, 0),
                                               mat(3, 0),
                                               mat(1, 2),
                                               mat(2, 2),
                                               mat(3, 2),
                                               mat(1, 3),
                                               mat(2, 3),
                                               mat(3, 3),
                                           },
                                           {
                                               mat(1, 0),
                                               mat(2, 0),
                                               mat(3, 0),
                                               mat(1, 1),
                                               mat(2, 1),
                                               mat(3, 1),
                                               mat(1, 3),
                                               mat(2, 3),
                                               mat(3, 3),
                                           },
                                           {
                                               mat(1, 0),
                                               mat(2, 0),
                                               mat(3, 0),
                                               mat(1, 1),
                                               mat(2, 1),
                                               mat(3, 1),
                                               mat(1, 2),
                                               mat(2, 2),
                                               mat(3, 2),
                                           }};

    return +mat(0, 0) * determinant(tcu::Mat3(minorMatrices[0])) -
           mat(0, 1) * determinant(tcu::Mat3(minorMatrices[1])) + mat(0, 2) * determinant(tcu::Mat3(minorMatrices[2])) -
           mat(0, 3) * determinant(tcu::Mat3(minorMatrices[3]));
}

// Inverse

template <int Size>
tcu::Matrix<float, Size, Size> inverse(const tcu::Matrix<float, Size, Size> &mat);

template <>
tcu::Matrix<float, 2, 2> inverse<2>(const tcu::Matrix<float, 2, 2> &mat)
{
    const float det = determinant(mat);
    tcu::Matrix<float, 2, 2> retVal;

    DE_ASSERT(det != 0.0f);

    retVal(0, 0) = mat(1, 1) / det;
    retVal(0, 1) = -mat(0, 1) / det;
    retVal(1, 0) = -mat(1, 0) / det;
    retVal(1, 1) = mat(0, 0) / det;

    return retVal;
}

template <>
tcu::Matrix<float, 3, 3> inverse<3>(const tcu::Matrix<float, 3, 3> &mat)
{
    // Blockwise inversion

    DE_ASSERT(determinant(mat) != 0.0f);

    const float areaA[2 * 2] = {mat(0, 0), mat(0, 1), mat(1, 0), mat(1, 1)};
    const float areaB[2]     = {
        mat(0, 2),
        mat(1, 2),
    };
    const float areaC[2] = {
        mat(2, 0),
        mat(2, 1),
    };
    const float areaD[1]     = {mat(2, 2)};
    const float nullField[4] = {0.0f};

    const tcu::Matrix<float, 2, 2> invA = inverse(tcu::Matrix<float, 2, 2>(areaA));
    const tcu::Matrix<float, 2, 1> matB = tcu::Matrix<float, 2, 1>(areaB);
    const tcu::Matrix<float, 1, 2> matC = tcu::Matrix<float, 1, 2>(areaC);
    const tcu::Matrix<float, 1, 1> matD = tcu::Matrix<float, 1, 1>(areaD);

    const float schurComplement            = 1.0f / (matD - matC * invA * matB)(0, 0);
    const tcu::Matrix<float, 2, 2> zeroMat = Mat2(nullField);

    const tcu::Matrix<float, 2, 2> blockA = invA + invA * matB * schurComplement * matC * invA;
    const tcu::Matrix<float, 2, 1> blockB = (zeroMat - invA) * matB * schurComplement;
    const tcu::Matrix<float, 1, 2> blockC = matC * invA * (-schurComplement);
    const float blockD                    = schurComplement;

    const float result[3 * 3] = {
        blockA(0, 0), blockA(0, 1), blockB(0, 0), blockA(1, 0), blockA(1, 1),
        blockB(1, 0), blockC(0, 0), blockC(0, 1), blockD,
    };

    return Mat3(result);
}

template <>
tcu::Matrix<float, 4, 4> inverse<4>(const tcu::Matrix<float, 4, 4> &mat)
{
    // Blockwise inversion

    DE_ASSERT(determinant(mat) != 0.0f);

    const float areaA[2 * 2] = {mat(0, 0), mat(0, 1), mat(1, 0), mat(1, 1)};
    const float areaB[2 * 2] = {mat(0, 2), mat(0, 3), mat(1, 2), mat(1, 3)};
    const float areaC[2 * 2] = {mat(2, 0), mat(2, 1), mat(3, 0), mat(3, 1)};
    const float areaD[2 * 2] = {mat(2, 2), mat(2, 3), mat(3, 2), mat(3, 3)};
    const float nullField[4] = {0.0f};

    const tcu::Matrix<float, 2, 2> invA = inverse(Mat2(areaA));
    const tcu::Matrix<float, 2, 2> matB = Mat2(areaB);
    const tcu::Matrix<float, 2, 2> matC = Mat2(areaC);
    const tcu::Matrix<float, 2, 2> matD = Mat2(areaD);

    const tcu::Matrix<float, 2, 2> schurComplement = inverse(matD - matC * invA * matB);
    const tcu::Matrix<float, 2, 2> zeroMat         = Mat2(nullField);

    const tcu::Matrix<float, 2, 2> blockA = invA + invA * matB * schurComplement * matC * invA;
    const tcu::Matrix<float, 2, 2> blockB = (zeroMat - invA) * matB * schurComplement;
    const tcu::Matrix<float, 2, 2> blockC = (zeroMat - schurComplement) * matC * invA;
    const tcu::Matrix<float, 2, 2> blockD = schurComplement;

    const float result[4 * 4] = {
        blockA(0, 0), blockA(0, 1), blockB(0, 0), blockB(0, 1), blockA(1, 0), blockA(1, 1), blockB(1, 0), blockB(1, 1),
        blockC(0, 0), blockC(0, 1), blockD(0, 0), blockD(0, 1), blockC(1, 0), blockC(1, 1), blockD(1, 0), blockD(1, 1),
    };

    return Mat4(result);
}

// negate

template <typename T, int Rows, int Cols>
tcu::Matrix<T, Rows, Cols> negate(const tcu::Matrix<T, Rows, Cols> &mat)
{
    tcu::Matrix<T, Rows, Cols> retVal;

    for (int r = 0; r < Rows; ++r)
        for (int c = 0; c < Cols; ++c)
            retVal(r, c) = -mat(r, c);

    return retVal;
}

// increment/decrement

template <typename T, int Rows, int Cols>
tcu::Matrix<T, Rows, Cols> increment(const tcu::Matrix<T, Rows, Cols> &mat)
{
    tcu::Matrix<T, Rows, Cols> retVal;

    for (int r = 0; r < Rows; ++r)
        for (int c = 0; c < Cols; ++c)
            retVal(r, c) = mat(r, c) + 1.0f;

    return retVal;
}

template <typename T, int Rows, int Cols>
tcu::Matrix<T, Rows, Cols> decrement(const tcu::Matrix<T, Rows, Cols> &mat)
{
    tcu::Matrix<T, Rows, Cols> retVal;

    for (int r = 0; r < Rows; ++r)
        for (int c = 0; c < Cols; ++c)
            retVal(r, c) = mat(r, c) - 1.0f;

    return retVal;
}

// Evaluator template.

typedef void (*MatrixShaderEvalFunc)(ShaderEvalContext &evalCtx, InputType in0Type, InputType in1Type);

template <int Op, int In0DataType, int In1DataType>
struct Evaluator;

template <int In0DataType, int In1DataType>
struct Evaluator<OP_ADD, In0DataType, In1DataType>
{
    static void evaluate(ShaderEvalContext &evalCtx, InputType in0Type, InputType in1Type)
    {
        typename TypeTraits<In0DataType>::Type in0 = (in0Type == INPUTTYPE_DYNAMIC) ?
                                                         getInputValue<INPUTTYPE_DYNAMIC, In0DataType>(evalCtx, 0) :
                                                         getInputValue<INPUTTYPE_CONST, In0DataType>(evalCtx, 0);
        typename TypeTraits<In1DataType>::Type in1 = (in1Type == INPUTTYPE_DYNAMIC) ?
                                                         getInputValue<INPUTTYPE_DYNAMIC, In1DataType>(evalCtx, 1) :
                                                         getInputValue<INPUTTYPE_CONST, In1DataType>(evalCtx, 1);
        evalCtx.color.xyz()                        = reduceToVec3(in0 + in1);
    }
};

template <int In0DataType, int In1DataType>
struct Evaluator<OP_SUB, In0DataType, In1DataType>
{
    static void evaluate(ShaderEvalContext &evalCtx, InputType in0Type, InputType in1Type)
    {
        typename TypeTraits<In0DataType>::Type in0 = (in0Type == INPUTTYPE_DYNAMIC) ?
                                                         getInputValue<INPUTTYPE_DYNAMIC, In0DataType>(evalCtx, 0) :
                                                         getInputValue<INPUTTYPE_CONST, In0DataType>(evalCtx, 0);
        typename TypeTraits<In1DataType>::Type in1 = (in1Type == INPUTTYPE_DYNAMIC) ?
                                                         getInputValue<INPUTTYPE_DYNAMIC, In1DataType>(evalCtx, 1) :
                                                         getInputValue<INPUTTYPE_CONST, In1DataType>(evalCtx, 1);
        evalCtx.color.xyz()                        = reduceToVec3(in0 - in1);
    }
};

template <int In0DataType, int In1DataType>
struct Evaluator<OP_MUL, In0DataType, In1DataType>
{
    static void evaluate(ShaderEvalContext &evalCtx, InputType in0Type, InputType in1Type)
    {
        typename TypeTraits<In0DataType>::Type in0 = (in0Type == INPUTTYPE_DYNAMIC) ?
                                                         getInputValue<INPUTTYPE_DYNAMIC, In0DataType>(evalCtx, 0) :
                                                         getInputValue<INPUTTYPE_CONST, In0DataType>(evalCtx, 0);
        typename TypeTraits<In1DataType>::Type in1 = (in1Type == INPUTTYPE_DYNAMIC) ?
                                                         getInputValue<INPUTTYPE_DYNAMIC, In1DataType>(evalCtx, 1) :
                                                         getInputValue<INPUTTYPE_CONST, In1DataType>(evalCtx, 1);
        evalCtx.color.xyz()                        = reduceToVec3(in0 * in1);
    }
};

template <int In0DataType, int In1DataType>
struct Evaluator<OP_DIV, In0DataType, In1DataType>
{
    static void evaluate(ShaderEvalContext &evalCtx, InputType in0Type, InputType in1Type)
    {
        typename TypeTraits<In0DataType>::Type in0 = (in0Type == INPUTTYPE_DYNAMIC) ?
                                                         getInputValue<INPUTTYPE_DYNAMIC, In0DataType>(evalCtx, 0) :
                                                         getInputValue<INPUTTYPE_CONST, In0DataType>(evalCtx, 0);
        typename TypeTraits<In1DataType>::Type in1 = (in1Type == INPUTTYPE_DYNAMIC) ?
                                                         getInputValue<INPUTTYPE_DYNAMIC, In1DataType>(evalCtx, 1) :
                                                         getInputValue<INPUTTYPE_CONST, In1DataType>(evalCtx, 1);
        evalCtx.color.xyz()                        = reduceToVec3(in0 / in1);
    }
};

template <int In0DataType, int In1DataType>
struct Evaluator<OP_COMP_MUL, In0DataType, In1DataType>
{
    static void evaluate(ShaderEvalContext &evalCtx, InputType in0Type, InputType in1Type)
    {
        typename TypeTraits<In0DataType>::Type in0 = (in0Type == INPUTTYPE_DYNAMIC) ?
                                                         getInputValue<INPUTTYPE_DYNAMIC, In0DataType>(evalCtx, 0) :
                                                         getInputValue<INPUTTYPE_CONST, In0DataType>(evalCtx, 0);
        typename TypeTraits<In1DataType>::Type in1 = (in1Type == INPUTTYPE_DYNAMIC) ?
                                                         getInputValue<INPUTTYPE_DYNAMIC, In1DataType>(evalCtx, 1) :
                                                         getInputValue<INPUTTYPE_CONST, In1DataType>(evalCtx, 1);
        evalCtx.color.xyz()                        = reduceToVec3(matrixCompMult(in0, in1));
    }
};

template <int In0DataType, int In1DataType>
struct Evaluator<OP_OUTER_PRODUCT, In0DataType, In1DataType>
{
    static void evaluate(ShaderEvalContext &evalCtx, InputType in0Type, InputType in1Type)
    {
        typename TypeTraits<In0DataType>::Type in0 = (in0Type == INPUTTYPE_DYNAMIC) ?
                                                         getInputValue<INPUTTYPE_DYNAMIC, In0DataType>(evalCtx, 0) :
                                                         getInputValue<INPUTTYPE_CONST, In0DataType>(evalCtx, 0);
        typename TypeTraits<In1DataType>::Type in1 = (in1Type == INPUTTYPE_DYNAMIC) ?
                                                         getInputValue<INPUTTYPE_DYNAMIC, In1DataType>(evalCtx, 1) :
                                                         getInputValue<INPUTTYPE_CONST, In1DataType>(evalCtx, 1);
        evalCtx.color.xyz()                        = reduceToVec3(outerProduct(in0, in1));
    }
};

template <int In0DataType, int In1DataType>
struct Evaluator<OP_TRANSPOSE, In0DataType, In1DataType>
{
    static void evaluate(ShaderEvalContext &evalCtx, InputType in0Type, InputType in1Type)
    {
        DE_UNREF(in1Type);
        typename TypeTraits<In0DataType>::Type in0 = (in0Type == INPUTTYPE_DYNAMIC) ?
                                                         getInputValue<INPUTTYPE_DYNAMIC, In0DataType>(evalCtx, 0) :
                                                         getInputValue<INPUTTYPE_CONST, In0DataType>(evalCtx, 0);
        evalCtx.color.xyz()                        = reduceToVec3(transpose(in0));
    }
};

template <int In0DataType, int In1DataType>
struct Evaluator<OP_INVERSE, In0DataType, In1DataType>
{
    static void evaluate(ShaderEvalContext &evalCtx, InputType in0Type, InputType in1Type)
    {
        DE_UNREF(in1Type);
        typename TypeTraits<In0DataType>::Type in0 = (in0Type == INPUTTYPE_DYNAMIC) ?
                                                         getInputValue<INPUTTYPE_DYNAMIC, In0DataType>(evalCtx, 0) :
                                                         getInputValue<INPUTTYPE_CONST, In0DataType>(evalCtx, 0);
        evalCtx.color.xyz()                        = reduceToVec3(inverse(in0));
    }
};

template <int In0DataType, int In1DataType>
struct Evaluator<OP_DETERMINANT, In0DataType, In1DataType>
{
    static void evaluate(ShaderEvalContext &evalCtx, InputType in0Type, InputType in1Type)
    {
        DE_UNREF(in1Type);
        typename TypeTraits<In0DataType>::Type in0 = (in0Type == INPUTTYPE_DYNAMIC) ?
                                                         getInputValue<INPUTTYPE_DYNAMIC, In0DataType>(evalCtx, 0) :
                                                         getInputValue<INPUTTYPE_CONST, In0DataType>(evalCtx, 0);
        evalCtx.color.xyz()                        = Vec3(determinant(in0));
    }
};

template <int In0DataType, int In1DataType>
struct Evaluator<OP_UNARY_PLUS, In0DataType, In1DataType>
{
    static void evaluate(ShaderEvalContext &evalCtx, InputType in0Type, InputType in1Type)
    {
        DE_UNREF(in1Type);
        typename TypeTraits<In0DataType>::Type in0 = (in0Type == INPUTTYPE_DYNAMIC) ?
                                                         getInputValue<INPUTTYPE_DYNAMIC, In0DataType>(evalCtx, 0) :
                                                         getInputValue<INPUTTYPE_CONST, In0DataType>(evalCtx, 0);
        evalCtx.color.xyz()                        = reduceToVec3(in0);
    }
};

template <int In0DataType, int In1DataType>
struct Evaluator<OP_NEGATION, In0DataType, In1DataType>
{
    static void evaluate(ShaderEvalContext &evalCtx, InputType in0Type, InputType in1Type)
    {
        DE_UNREF(in1Type);
        typename TypeTraits<In0DataType>::Type in0 = (in0Type == INPUTTYPE_DYNAMIC) ?
                                                         getInputValue<INPUTTYPE_DYNAMIC, In0DataType>(evalCtx, 0) :
                                                         getInputValue<INPUTTYPE_CONST, In0DataType>(evalCtx, 0);
        evalCtx.color.xyz()                        = reduceToVec3(negate(in0));
    }
};

template <int In0DataType, int In1DataType>
struct Evaluator<OP_PRE_INCREMENT, In0DataType, In1DataType>
{
    static void evaluate(ShaderEvalContext &evalCtx, InputType in0Type, InputType in1Type)
    {
        DE_UNREF(in1Type);
        typename TypeTraits<In0DataType>::Type in0 = (in0Type == INPUTTYPE_DYNAMIC) ?
                                                         getInputValue<INPUTTYPE_DYNAMIC, In0DataType>(evalCtx, 0) :
                                                         getInputValue<INPUTTYPE_CONST, In0DataType>(evalCtx, 0);

        // modifying reduction: sum modified value too
        evalCtx.color.xyz() = reduceToVec3(increment(in0)) + reduceToVec3(increment(in0));
    }
};

template <int In0DataType, int In1DataType>
struct Evaluator<OP_PRE_DECREMENT, In0DataType, In1DataType>
{
    static void evaluate(ShaderEvalContext &evalCtx, InputType in0Type, InputType in1Type)
    {
        DE_UNREF(in1Type);
        typename TypeTraits<In0DataType>::Type in0 = (in0Type == INPUTTYPE_DYNAMIC) ?
                                                         getInputValue<INPUTTYPE_DYNAMIC, In0DataType>(evalCtx, 0) :
                                                         getInputValue<INPUTTYPE_CONST, In0DataType>(evalCtx, 0);

        // modifying reduction: sum modified value too
        evalCtx.color.xyz() = reduceToVec3(decrement(in0)) + reduceToVec3(decrement(in0));
    }
};

template <int In0DataType, int In1DataType>
struct Evaluator<OP_POST_INCREMENT, In0DataType, In1DataType>
{
    static void evaluate(ShaderEvalContext &evalCtx, InputType in0Type, InputType in1Type)
    {
        DE_UNREF(in1Type);
        typename TypeTraits<In0DataType>::Type in0 = (in0Type == INPUTTYPE_DYNAMIC) ?
                                                         getInputValue<INPUTTYPE_DYNAMIC, In0DataType>(evalCtx, 0) :
                                                         getInputValue<INPUTTYPE_CONST, In0DataType>(evalCtx, 0);

        // modifying reduction: sum modified value too
        evalCtx.color.xyz() = reduceToVec3(in0) + reduceToVec3(increment(in0));
    }
};

template <int In0DataType, int In1DataType>
struct Evaluator<OP_POST_DECREMENT, In0DataType, In1DataType>
{
    static void evaluate(ShaderEvalContext &evalCtx, InputType in0Type, InputType in1Type)
    {
        DE_UNREF(in1Type);
        typename TypeTraits<In0DataType>::Type in0 = (in0Type == INPUTTYPE_DYNAMIC) ?
                                                         getInputValue<INPUTTYPE_DYNAMIC, In0DataType>(evalCtx, 0) :
                                                         getInputValue<INPUTTYPE_CONST, In0DataType>(evalCtx, 0);

        // modifying reduction: sum modified value too
        evalCtx.color.xyz() = reduceToVec3(in0) + reduceToVec3(decrement(in0));
    }
};

template <int In0DataType, int In1DataType>
struct Evaluator<OP_ADD_INTO, In0DataType, In1DataType>
{
    static void evaluate(ShaderEvalContext &evalCtx, InputType in0Type, InputType in1Type)
    {
        typename TypeTraits<In0DataType>::Type in0 = (in0Type == INPUTTYPE_DYNAMIC) ?
                                                         getInputValue<INPUTTYPE_DYNAMIC, In0DataType>(evalCtx, 0) :
                                                         getInputValue<INPUTTYPE_CONST, In0DataType>(evalCtx, 0);
        typename TypeTraits<In1DataType>::Type in1 = (in1Type == INPUTTYPE_DYNAMIC) ?
                                                         getInputValue<INPUTTYPE_DYNAMIC, In1DataType>(evalCtx, 1) :
                                                         getInputValue<INPUTTYPE_CONST, In1DataType>(evalCtx, 1);
        evalCtx.color.xyz()                        = reduceToVec3(in0 + in1);
    }
};

template <int In0DataType, int In1DataType>
struct Evaluator<OP_SUBTRACT_FROM, In0DataType, In1DataType>
{
    static void evaluate(ShaderEvalContext &evalCtx, InputType in0Type, InputType in1Type)
    {
        typename TypeTraits<In0DataType>::Type in0 = (in0Type == INPUTTYPE_DYNAMIC) ?
                                                         getInputValue<INPUTTYPE_DYNAMIC, In0DataType>(evalCtx, 0) :
                                                         getInputValue<INPUTTYPE_CONST, In0DataType>(evalCtx, 0);
        typename TypeTraits<In1DataType>::Type in1 = (in1Type == INPUTTYPE_DYNAMIC) ?
                                                         getInputValue<INPUTTYPE_DYNAMIC, In1DataType>(evalCtx, 1) :
                                                         getInputValue<INPUTTYPE_CONST, In1DataType>(evalCtx, 1);
        evalCtx.color.xyz()                        = reduceToVec3(in0 - in1);
    }
};

template <int In0DataType, int In1DataType>
struct Evaluator<OP_MULTIPLY_INTO, In0DataType, In1DataType>
{
    static void evaluate(ShaderEvalContext &evalCtx, InputType in0Type, InputType in1Type)
    {
        typename TypeTraits<In0DataType>::Type in0 = (in0Type == INPUTTYPE_DYNAMIC) ?
                                                         getInputValue<INPUTTYPE_DYNAMIC, In0DataType>(evalCtx, 0) :
                                                         getInputValue<INPUTTYPE_CONST, In0DataType>(evalCtx, 0);
        typename TypeTraits<In1DataType>::Type in1 = (in1Type == INPUTTYPE_DYNAMIC) ?
                                                         getInputValue<INPUTTYPE_DYNAMIC, In1DataType>(evalCtx, 1) :
                                                         getInputValue<INPUTTYPE_CONST, In1DataType>(evalCtx, 1);
        evalCtx.color.xyz()                        = reduceToVec3(in0 * in1);
    }
};

template <int In0DataType, int In1DataType>
struct Evaluator<OP_DIVIDE_INTO, In0DataType, In1DataType>
{
    static void evaluate(ShaderEvalContext &evalCtx, InputType in0Type, InputType in1Type)
    {
        typename TypeTraits<In0DataType>::Type in0 = (in0Type == INPUTTYPE_DYNAMIC) ?
                                                         getInputValue<INPUTTYPE_DYNAMIC, In0DataType>(evalCtx, 0) :
                                                         getInputValue<INPUTTYPE_CONST, In0DataType>(evalCtx, 0);
        typename TypeTraits<In1DataType>::Type in1 = (in1Type == INPUTTYPE_DYNAMIC) ?
                                                         getInputValue<INPUTTYPE_DYNAMIC, In1DataType>(evalCtx, 1) :
                                                         getInputValue<INPUTTYPE_CONST, In1DataType>(evalCtx, 1);
        evalCtx.color.xyz()                        = reduceToVec3(in0 / in1);
    }
};

MatrixShaderEvalFunc getEvalFunc(const ShaderInput &in0, const ShaderInput &in1, MatrixOp op)
{
    // Evaluator is selected based on op and input data types.
    // For efficient lookup the types and op enums are packed together to form a 19-bit key:
    // [18..14 OP] [13..7 TYPE0] [6..0 TYPE1]

    DE_STATIC_ASSERT(TYPE_LAST <= (1 << 7));
    DE_STATIC_ASSERT(OP_LAST <= (1 << 5));

#define PACK_EVAL_CASE(OP, IN0DATATYPE, IN1DATATYPE) (((OP) << 14) | ((IN0DATATYPE) << 7) | (IN1DATATYPE))

#define MAKE_EVAL_CASE(OP, IN0DATATYPE, IN1DATATYPE)   \
    case PACK_EVAL_CASE(OP, IN0DATATYPE, IN1DATATYPE): \
        return Evaluator<OP, IN0DATATYPE, IN1DATATYPE>::evaluate

#define MAKE_SCALAR_OPS(IN0DATATYPE, IN1DATATYPE)     \
    MAKE_EVAL_CASE(OP_ADD, IN0DATATYPE, IN1DATATYPE); \
    MAKE_EVAL_CASE(OP_SUB, IN0DATATYPE, IN1DATATYPE); \
    MAKE_EVAL_CASE(OP_MUL, IN0DATATYPE, IN1DATATYPE); \
    MAKE_EVAL_CASE(OP_DIV, IN0DATATYPE, IN1DATATYPE)

#define MAKE_CWISE_OPS(IN0DATATYPE, IN1DATATYPE)      \
    MAKE_EVAL_CASE(OP_ADD, IN0DATATYPE, IN1DATATYPE); \
    MAKE_EVAL_CASE(OP_SUB, IN0DATATYPE, IN1DATATYPE); \
    MAKE_EVAL_CASE(OP_DIV, IN0DATATYPE, IN1DATATYPE); \
    MAKE_EVAL_CASE(OP_COMP_MUL, IN0DATATYPE, IN1DATATYPE)

#define MAKE_MUL_OP(IN0DATATYPE, IN1DATATYPE) MAKE_EVAL_CASE(OP_MUL, IN0DATATYPE, IN1DATATYPE)

#define MAKE_VECVEC_OP(IN0DATATYPE, IN1DATATYPE) MAKE_EVAL_CASE(OP_OUTER_PRODUCT, IN0DATATYPE, IN1DATATYPE)

#define MAKE_UNARY_OP(IN0DATATYPE)                             \
    MAKE_EVAL_CASE(OP_TRANSPOSE, IN0DATATYPE, TYPE_LAST);      \
    MAKE_EVAL_CASE(OP_UNARY_PLUS, IN0DATATYPE, TYPE_LAST);     \
    MAKE_EVAL_CASE(OP_NEGATION, IN0DATATYPE, TYPE_LAST);       \
    MAKE_EVAL_CASE(OP_PRE_INCREMENT, IN0DATATYPE, TYPE_LAST);  \
    MAKE_EVAL_CASE(OP_PRE_DECREMENT, IN0DATATYPE, TYPE_LAST);  \
    MAKE_EVAL_CASE(OP_POST_INCREMENT, IN0DATATYPE, TYPE_LAST); \
    MAKE_EVAL_CASE(OP_POST_DECREMENT, IN0DATATYPE, TYPE_LAST)

#define MAKE_UNARY_SYMMETRIC_OP(IN0DATATYPE)                \
    MAKE_UNARY_OP(IN0DATATYPE);                             \
    MAKE_EVAL_CASE(OP_DETERMINANT, IN0DATATYPE, TYPE_LAST); \
    MAKE_EVAL_CASE(OP_INVERSE, IN0DATATYPE, TYPE_LAST)

#define MAKE_ASSIGNMENT_OP(IN0DATATYPE)                         \
    MAKE_EVAL_CASE(OP_ADD_INTO, IN0DATATYPE, IN0DATATYPE);      \
    MAKE_EVAL_CASE(OP_SUBTRACT_FROM, IN0DATATYPE, IN0DATATYPE); \
    MAKE_EVAL_CASE(OP_DIVIDE_INTO, IN0DATATYPE, IN0DATATYPE)

#define MAKE_ASSIGNMENT_SYMMETRIC_OP(IN0DATATYPE) \
    MAKE_ASSIGNMENT_OP(IN0DATATYPE);              \
    MAKE_EVAL_CASE(OP_MULTIPLY_INTO, IN0DATATYPE, IN0DATATYPE)

    switch (PACK_EVAL_CASE(op, in0.dataType, in1.dataType))
    {
        // Matrix-scalar.
        MAKE_SCALAR_OPS(TYPE_FLOAT_MAT2, TYPE_FLOAT);
        MAKE_SCALAR_OPS(TYPE_FLOAT_MAT2X3, TYPE_FLOAT);
        MAKE_SCALAR_OPS(TYPE_FLOAT_MAT2X4, TYPE_FLOAT);
        MAKE_SCALAR_OPS(TYPE_FLOAT_MAT3X2, TYPE_FLOAT);
        MAKE_SCALAR_OPS(TYPE_FLOAT_MAT3, TYPE_FLOAT);
        MAKE_SCALAR_OPS(TYPE_FLOAT_MAT3X4, TYPE_FLOAT);
        MAKE_SCALAR_OPS(TYPE_FLOAT_MAT4X2, TYPE_FLOAT);
        MAKE_SCALAR_OPS(TYPE_FLOAT_MAT4X3, TYPE_FLOAT);
        MAKE_SCALAR_OPS(TYPE_FLOAT_MAT4, TYPE_FLOAT);

        // Matrix-vector.
        MAKE_MUL_OP(TYPE_FLOAT_MAT2, TYPE_FLOAT_VEC2);
        MAKE_MUL_OP(TYPE_FLOAT_MAT2X3, TYPE_FLOAT_VEC2);
        MAKE_MUL_OP(TYPE_FLOAT_MAT2X4, TYPE_FLOAT_VEC2);
        MAKE_MUL_OP(TYPE_FLOAT_MAT3X2, TYPE_FLOAT_VEC3);
        MAKE_MUL_OP(TYPE_FLOAT_MAT3, TYPE_FLOAT_VEC3);
        MAKE_MUL_OP(TYPE_FLOAT_MAT3X4, TYPE_FLOAT_VEC3);
        MAKE_MUL_OP(TYPE_FLOAT_MAT4X2, TYPE_FLOAT_VEC4);
        MAKE_MUL_OP(TYPE_FLOAT_MAT4X3, TYPE_FLOAT_VEC4);
        MAKE_MUL_OP(TYPE_FLOAT_MAT4, TYPE_FLOAT_VEC4);

        // Vector-matrix.
        MAKE_MUL_OP(TYPE_FLOAT_VEC2, TYPE_FLOAT_MAT2);
        MAKE_MUL_OP(TYPE_FLOAT_VEC3, TYPE_FLOAT_MAT2X3);
        MAKE_MUL_OP(TYPE_FLOAT_VEC4, TYPE_FLOAT_MAT2X4);
        MAKE_MUL_OP(TYPE_FLOAT_VEC2, TYPE_FLOAT_MAT3X2);
        MAKE_MUL_OP(TYPE_FLOAT_VEC3, TYPE_FLOAT_MAT3);
        MAKE_MUL_OP(TYPE_FLOAT_VEC4, TYPE_FLOAT_MAT3X4);
        MAKE_MUL_OP(TYPE_FLOAT_VEC2, TYPE_FLOAT_MAT4X2);
        MAKE_MUL_OP(TYPE_FLOAT_VEC3, TYPE_FLOAT_MAT4X3);
        MAKE_MUL_OP(TYPE_FLOAT_VEC4, TYPE_FLOAT_MAT4);

        // Matrix-matrix.
        MAKE_CWISE_OPS(TYPE_FLOAT_MAT2, TYPE_FLOAT_MAT2);
        MAKE_MUL_OP(TYPE_FLOAT_MAT2, TYPE_FLOAT_MAT2);
        MAKE_MUL_OP(TYPE_FLOAT_MAT2, TYPE_FLOAT_MAT3X2);
        MAKE_MUL_OP(TYPE_FLOAT_MAT2, TYPE_FLOAT_MAT4X2);

        MAKE_CWISE_OPS(TYPE_FLOAT_MAT2X3, TYPE_FLOAT_MAT2X3);
        MAKE_MUL_OP(TYPE_FLOAT_MAT2X3, TYPE_FLOAT_MAT2);
        MAKE_MUL_OP(TYPE_FLOAT_MAT2X3, TYPE_FLOAT_MAT3X2);
        MAKE_MUL_OP(TYPE_FLOAT_MAT2X3, TYPE_FLOAT_MAT4X2);

        MAKE_CWISE_OPS(TYPE_FLOAT_MAT2X4, TYPE_FLOAT_MAT2X4);
        MAKE_MUL_OP(TYPE_FLOAT_MAT2X4, TYPE_FLOAT_MAT2);
        MAKE_MUL_OP(TYPE_FLOAT_MAT2X4, TYPE_FLOAT_MAT3X2);
        MAKE_MUL_OP(TYPE_FLOAT_MAT2X4, TYPE_FLOAT_MAT4X2);

        MAKE_CWISE_OPS(TYPE_FLOAT_MAT3X2, TYPE_FLOAT_MAT3X2);
        MAKE_MUL_OP(TYPE_FLOAT_MAT3X2, TYPE_FLOAT_MAT2X3);
        MAKE_MUL_OP(TYPE_FLOAT_MAT3X2, TYPE_FLOAT_MAT3);
        MAKE_MUL_OP(TYPE_FLOAT_MAT3X2, TYPE_FLOAT_MAT4X3);

        MAKE_CWISE_OPS(TYPE_FLOAT_MAT3, TYPE_FLOAT_MAT3);
        MAKE_MUL_OP(TYPE_FLOAT_MAT3, TYPE_FLOAT_MAT2X3);
        MAKE_MUL_OP(TYPE_FLOAT_MAT3, TYPE_FLOAT_MAT3);
        MAKE_MUL_OP(TYPE_FLOAT_MAT3, TYPE_FLOAT_MAT4X3);

        MAKE_CWISE_OPS(TYPE_FLOAT_MAT3X4, TYPE_FLOAT_MAT3X4);
        MAKE_MUL_OP(TYPE_FLOAT_MAT3X4, TYPE_FLOAT_MAT2X3);
        MAKE_MUL_OP(TYPE_FLOAT_MAT3X4, TYPE_FLOAT_MAT3);
        MAKE_MUL_OP(TYPE_FLOAT_MAT3X4, TYPE_FLOAT_MAT4X3);

        MAKE_CWISE_OPS(TYPE_FLOAT_MAT4X2, TYPE_FLOAT_MAT4X2);
        MAKE_MUL_OP(TYPE_FLOAT_MAT4X2, TYPE_FLOAT_MAT2X4);
        MAKE_MUL_OP(TYPE_FLOAT_MAT4X2, TYPE_FLOAT_MAT3X4);
        MAKE_MUL_OP(TYPE_FLOAT_MAT4X2, TYPE_FLOAT_MAT4);

        MAKE_CWISE_OPS(TYPE_FLOAT_MAT4X3, TYPE_FLOAT_MAT4X3);
        MAKE_MUL_OP(TYPE_FLOAT_MAT4X3, TYPE_FLOAT_MAT2X4);
        MAKE_MUL_OP(TYPE_FLOAT_MAT4X3, TYPE_FLOAT_MAT3X4);
        MAKE_MUL_OP(TYPE_FLOAT_MAT4X3, TYPE_FLOAT_MAT4);

        MAKE_CWISE_OPS(TYPE_FLOAT_MAT4, TYPE_FLOAT_MAT4);
        MAKE_MUL_OP(TYPE_FLOAT_MAT4, TYPE_FLOAT_MAT2X4);
        MAKE_MUL_OP(TYPE_FLOAT_MAT4, TYPE_FLOAT_MAT3X4);
        MAKE_MUL_OP(TYPE_FLOAT_MAT4, TYPE_FLOAT_MAT4);

        // Vector-vector.
        MAKE_VECVEC_OP(TYPE_FLOAT_VEC2, TYPE_FLOAT_VEC2);
        MAKE_VECVEC_OP(TYPE_FLOAT_VEC2, TYPE_FLOAT_VEC3);
        MAKE_VECVEC_OP(TYPE_FLOAT_VEC2, TYPE_FLOAT_VEC4);
        MAKE_VECVEC_OP(TYPE_FLOAT_VEC3, TYPE_FLOAT_VEC2);
        MAKE_VECVEC_OP(TYPE_FLOAT_VEC3, TYPE_FLOAT_VEC3);
        MAKE_VECVEC_OP(TYPE_FLOAT_VEC3, TYPE_FLOAT_VEC4);
        MAKE_VECVEC_OP(TYPE_FLOAT_VEC4, TYPE_FLOAT_VEC2);
        MAKE_VECVEC_OP(TYPE_FLOAT_VEC4, TYPE_FLOAT_VEC3);
        MAKE_VECVEC_OP(TYPE_FLOAT_VEC4, TYPE_FLOAT_VEC4);

        // Unary Matrix.
        MAKE_UNARY_SYMMETRIC_OP(TYPE_FLOAT_MAT2);
        MAKE_UNARY_OP(TYPE_FLOAT_MAT2X3);
        MAKE_UNARY_OP(TYPE_FLOAT_MAT2X4);
        MAKE_UNARY_OP(TYPE_FLOAT_MAT3X2);
        MAKE_UNARY_SYMMETRIC_OP(TYPE_FLOAT_MAT3);
        MAKE_UNARY_OP(TYPE_FLOAT_MAT3X4);
        MAKE_UNARY_OP(TYPE_FLOAT_MAT4X2);
        MAKE_UNARY_OP(TYPE_FLOAT_MAT4X3);
        MAKE_UNARY_SYMMETRIC_OP(TYPE_FLOAT_MAT4);

        // Assignments
        MAKE_ASSIGNMENT_SYMMETRIC_OP(TYPE_FLOAT_MAT2);
        MAKE_ASSIGNMENT_OP(TYPE_FLOAT_MAT2X3);
        MAKE_ASSIGNMENT_OP(TYPE_FLOAT_MAT2X4);
        MAKE_ASSIGNMENT_OP(TYPE_FLOAT_MAT3X2);
        MAKE_ASSIGNMENT_SYMMETRIC_OP(TYPE_FLOAT_MAT3);
        MAKE_ASSIGNMENT_OP(TYPE_FLOAT_MAT3X4);
        MAKE_ASSIGNMENT_OP(TYPE_FLOAT_MAT4X2);
        MAKE_ASSIGNMENT_OP(TYPE_FLOAT_MAT4X3);
        MAKE_ASSIGNMENT_SYMMETRIC_OP(TYPE_FLOAT_MAT4);

    default:
        DE_ASSERT(false);
        return DE_NULL;
    }

#undef PACK_EVAL_CASE
#undef MAKE_EVAL_CASE
#undef MUL_OP
#undef ALL_OPS
#undef MAKE_MAT_SCALAR_VEC_CASES
#undef MAKE_MAT_MAT_CASES
}

// Shader source format utilities.

template <int Size>
void writeVectorConstructor(std::ostream &str, const tcu::Vector<float, Size> &v)
{
    str << "vec" << Size << "(";
    for (int ndx = 0; ndx < Size; ndx++)
    {
        if (ndx != 0)
            str << ", ";
        str << de::floatToString(v[ndx], 1);
    }
    str << ")";
}

template <int Cols, int Rows>
void writeMatrixConstructor(std::ostream &str, const tcu::Matrix<float, Rows, Cols> &m)
{
    if (Rows == Cols)
        str << "mat" << Cols;
    else
        str << "mat" << Cols << "x" << Rows;

    str << "(";
    for (int colNdx = 0; colNdx < Cols; colNdx++)
    {
        for (int rowNdx = 0; rowNdx < Rows; rowNdx++)
        {
            if (rowNdx > 0 || colNdx > 0)
                str << ", ";
            str << de::floatToString(m(rowNdx, colNdx), 1);
        }
    }
    str << ")";
}

} // namespace MatrixCaseUtils

using namespace MatrixCaseUtils;

class MatrixShaderEvaluator : public ShaderEvaluator
{
public:
    MatrixShaderEvaluator(MatrixShaderEvalFunc evalFunc, InputType inType0, InputType inType1);

    virtual void evaluate(ShaderEvalContext &evalCtx);

private:
    MatrixShaderEvalFunc m_matEvalFunc;
    InputType m_inType0;
    InputType m_inType1;
};

MatrixShaderEvaluator::MatrixShaderEvaluator(MatrixShaderEvalFunc evalFunc, InputType inType0, InputType inType1)
    : m_matEvalFunc(evalFunc)
    , m_inType0(inType0)
    , m_inType1(inType1)
{
}

void MatrixShaderEvaluator::evaluate(ShaderEvalContext &evalCtx)
{
    m_matEvalFunc(evalCtx, m_inType0, m_inType1);
}

class ShaderMatrixCase : public ShaderRenderCase
{
public:
    ShaderMatrixCase(Context &context, const char *name, const char *desc, const ShaderInput &in0,
                     const ShaderInput &in1, MatrixOp op, bool isVertexCase);
    ~ShaderMatrixCase(void);

    void init(void);

protected:
    std::string genGLSLMatToVec3Reduction(const glu::DataType &matType, const char *varName);
    void setupUniforms(int programID, const tcu::Vec4 &constCoords);

private:
    ShaderInput m_in0;
    ShaderInput m_in1;
    MatrixOp m_op;
    MatrixShaderEvaluator m_matEvaluator;
};

ShaderMatrixCase::ShaderMatrixCase(Context &context, const char *name, const char *desc, const ShaderInput &in0,
                                   const ShaderInput &in1, MatrixOp op, bool isVertexCase)
    : ShaderRenderCase(context.getTestContext(), context.getRenderContext(), context.getContextInfo(), name, desc,
                       isVertexCase, m_matEvaluator)
    , m_in0(in0)
    , m_in1(in1)
    , m_op(op)
    , m_matEvaluator(getEvalFunc(in0, in1, op), in0.inputType, in1.inputType)
{
}

ShaderMatrixCase::~ShaderMatrixCase(void)
{
}

void ShaderMatrixCase::init(void)
{
    std::ostringstream vtx;
    std::ostringstream frag;
    std::ostringstream &op = m_isVertexCase ? vtx : frag;

    bool isInDynMat0 = isDataTypeMatrix(m_in0.dataType) && m_in0.inputType == INPUTTYPE_DYNAMIC;
    bool isInDynMat1 = isDataTypeMatrix(m_in1.dataType) && m_in1.inputType == INPUTTYPE_DYNAMIC;
    string inValue0;
    string inValue1;
    DataType resultType  = TYPE_LAST;
    Precision resultPrec = m_in0.precision;
    vector<string> passVars;
    int numInputs = (isOperationBinary(m_op)) ? (2) : (1);

    std::string operationValue0;
    std::string operationValue1;

    DE_ASSERT(!isInDynMat0 || !isInDynMat1); // Only single dynamic matrix input is allowed.
    DE_UNREF(isInDynMat0 && isInDynMat1);

    // Compute result type.
    if (m_op == OP_MUL && isDataTypeMatrix(m_in0.dataType) && isDataTypeMatrix(m_in1.dataType))
    {
        resultType =
            getDataTypeMatrix(getDataTypeMatrixNumColumns(m_in1.dataType), getDataTypeMatrixNumRows(m_in0.dataType));
    }
    else if (m_op == OP_OUTER_PRODUCT)
    {
        resultType = getDataTypeMatrix(getDataTypeScalarSize(m_in1.dataType), getDataTypeScalarSize(m_in0.dataType));
    }
    else if (m_op == OP_TRANSPOSE)
    {
        resultType =
            getDataTypeMatrix(getDataTypeMatrixNumRows(m_in0.dataType), getDataTypeMatrixNumColumns(m_in0.dataType));
    }
    else if (m_op == OP_INVERSE)
    {
        resultType = m_in0.dataType;
    }
    else if (m_op == OP_DETERMINANT)
    {
        resultType = TYPE_FLOAT;
    }
    else if (getOperationType(m_op) == OPERATIONTYPE_UNARY_PREFIX_OPERATOR ||
             getOperationType(m_op) == OPERATIONTYPE_UNARY_POSTFIX_OPERATOR)
    {
        resultType = m_in0.dataType;
    }
    else if (isDataTypeMatrix(m_in0.dataType) && isDataTypeMatrix(m_in1.dataType))
    {
        DE_ASSERT(m_in0.dataType == m_in1.dataType);
        resultType = m_in0.dataType;
    }
    else if (isDataTypeMatrix(m_in0.dataType) || isDataTypeMatrix(m_in1.dataType))
    {
        int matNdx          = isDataTypeMatrix(m_in0.dataType) ? 0 : 1;
        DataType matrixType = matNdx == 0 ? m_in0.dataType : m_in1.dataType;
        DataType otherType  = matNdx == 0 ? m_in1.dataType : m_in0.dataType;

        if (otherType == TYPE_FLOAT)
            resultType = matrixType;
        else
        {
            DE_ASSERT(isDataTypeVector(otherType));
            resultType = getDataTypeFloatVec(matNdx == 0 ? getDataTypeMatrixNumRows(matrixType) :
                                                           getDataTypeMatrixNumColumns(matrixType));
        }
    }
    else
    {
        DE_ASSERT(false);
    }

    vtx << "#version 300 es\n";
    frag << "#version 300 es\n";

    vtx << "in highp vec4 a_position;\n";
    frag << "layout(location = 0) out mediump vec4 dEQP_FragColor;\n";
    if (m_isVertexCase)
    {
        vtx << "out mediump vec4 v_color;\n";
        frag << "in mediump vec4 v_color;\n";
    }

    // Input declarations.
    for (int inNdx = 0; inNdx < numInputs; inNdx++)
    {
        const ShaderInput &in = inNdx > 0 ? m_in1 : m_in0;
        const char *precName  = getPrecisionName(in.precision);
        const char *typeName  = getDataTypeName(in.dataType);
        string &inValue       = inNdx > 0 ? inValue1 : inValue0;

        if (in.inputType == INPUTTYPE_DYNAMIC)
        {
            vtx << "in " << precName << " " << typeName << " a_";

            if (isDataTypeMatrix(in.dataType))
            {
                // a_matN, v_matN
                vtx << typeName << ";\n";
                if (!m_isVertexCase)
                {
                    vtx << "out " << precName << " " << typeName << " v_" << typeName << ";\n";
                    frag << "in " << precName << " " << typeName << " v_" << typeName << ";\n";
                    passVars.push_back(typeName);
                }

                inValue = string(m_isVertexCase ? "a_" : "v_") + getDataTypeName(in.dataType);
            }
            else
            {
                // a_coords, v_coords
                vtx << "coords;\n";
                if (!m_isVertexCase)
                {
                    vtx << "out " << precName << " " << typeName << " v_coords;\n";
                    frag << "in " << precName << " " << typeName << " v_coords;\n";
                    passVars.push_back("coords");
                }

                inValue = m_isVertexCase ? "a_coords" : "v_coords";
            }
        }
        else if (in.inputType == INPUTTYPE_UNIFORM)
        {
            op << "uniform " << precName << " " << typeName << " u_in" << inNdx << ";\n";
            inValue = string("u_in") + de::toString(inNdx);
        }
        else if (in.inputType == INPUTTYPE_CONST)
        {
            op << "const " << precName << " " << typeName << " in" << inNdx << " = ";

            // Generate declaration.
            switch (in.dataType)
            {
            case TYPE_FLOAT:
                op << de::floatToString(s_constInFloat[inNdx], 1);
                break;
            case TYPE_FLOAT_VEC2:
                writeVectorConstructor<2>(op, s_constInVec2[inNdx]);
                break;
            case TYPE_FLOAT_VEC3:
                writeVectorConstructor<3>(op, s_constInVec3[inNdx]);
                break;
            case TYPE_FLOAT_VEC4:
                writeVectorConstructor<4>(op, s_constInVec4[inNdx]);
                break;
            case TYPE_FLOAT_MAT2:
                writeMatrixConstructor<2, 2>(op, Mat2(s_constInMat2x2[inNdx]));
                break;
            case TYPE_FLOAT_MAT2X3:
                writeMatrixConstructor<2, 3>(op, Mat2x3(s_constInMat2x3[inNdx]));
                break;
            case TYPE_FLOAT_MAT2X4:
                writeMatrixConstructor<2, 4>(op, Mat2x4(s_constInMat2x4[inNdx]));
                break;
            case TYPE_FLOAT_MAT3X2:
                writeMatrixConstructor<3, 2>(op, Mat3x2(s_constInMat3x2[inNdx]));
                break;
            case TYPE_FLOAT_MAT3:
                writeMatrixConstructor<3, 3>(op, Mat3(s_constInMat3x3[inNdx]));
                break;
            case TYPE_FLOAT_MAT3X4:
                writeMatrixConstructor<3, 4>(op, Mat3x4(s_constInMat3x4[inNdx]));
                break;
            case TYPE_FLOAT_MAT4X2:
                writeMatrixConstructor<4, 2>(op, Mat4x2(s_constInMat4x2[inNdx]));
                break;
            case TYPE_FLOAT_MAT4X3:
                writeMatrixConstructor<4, 3>(op, Mat4x3(s_constInMat4x3[inNdx]));
                break;
            case TYPE_FLOAT_MAT4:
                writeMatrixConstructor<4, 4>(op, Mat4(s_constInMat4x4[inNdx]));
                break;

            default:
                DE_ASSERT(false);
            }

            op << ";\n";

            inValue = string("in") + de::toString(inNdx);
        }
    }

    vtx << "\n"
        << "void main (void)\n"
        << "{\n"
        << "    gl_Position = a_position;\n";
    frag << "\n"
         << "void main (void)\n"
         << "{\n";

    if (m_isVertexCase)
        frag << "    dEQP_FragColor = v_color;\n";
    else
    {
        for (vector<string>::const_iterator copyIter = passVars.begin(); copyIter != passVars.end(); copyIter++)
            vtx << "    v_" << *copyIter << " = "
                << "a_" << *copyIter << ";\n";
    }

    // Operation.

    switch (getOperationNature(m_op))
    {
    case OPERATIONNATURE_PURE:
        DE_ASSERT(getOperationType(m_op) != OPERATIONTYPE_ASSIGNMENT);

        operationValue0 = inValue0;
        operationValue1 = inValue1;
        break;

    case OPERATIONNATURE_MUTATING:
        DE_ASSERT(getOperationType(m_op) != OPERATIONTYPE_ASSIGNMENT);

        op << "    " << getPrecisionName(resultPrec) << " " << getDataTypeName(resultType) << " tmpValue = " << inValue0
           << ";\n";

        operationValue0 = "tmpValue";
        operationValue1 = inValue1;
        break;

    case OPERATIONNATURE_ASSIGNMENT:
        DE_ASSERT(getOperationType(m_op) == OPERATIONTYPE_ASSIGNMENT);

        operationValue0 = inValue0;
        operationValue1 = inValue1;
        break;

    default:
        DE_ASSERT(false);
    }

    switch (getOperationType(m_op))
    {
    case OPERATIONTYPE_BINARY_OPERATOR:
        op << "    " << getPrecisionName(resultPrec) << " " << getDataTypeName(resultType)
           << " res = " << operationValue0 << " " << getOperationName(m_op) << " " << operationValue1 << ";\n";
        break;

    case OPERATIONTYPE_UNARY_PREFIX_OPERATOR:
        op << "    " << getPrecisionName(resultPrec) << " " << getDataTypeName(resultType)
           << " res = " << getOperationName(m_op) << operationValue0 << ";\n";
        break;

    case OPERATIONTYPE_UNARY_POSTFIX_OPERATOR:
        op << "    " << getPrecisionName(resultPrec) << " " << getDataTypeName(resultType)
           << " res = " << operationValue0 << getOperationName(m_op) << ";\n";
        break;

    case OPERATIONTYPE_BINARY_FUNCTION:
        op << "    " << getPrecisionName(resultPrec) << " " << getDataTypeName(resultType)
           << " res = " << getOperationName(m_op) << "(" << operationValue0 << ", " << operationValue1 << ");\n";
        break;

    case OPERATIONTYPE_UNARY_FUNCTION:
        op << "    " << getPrecisionName(resultPrec) << " " << getDataTypeName(resultType)
           << " res = " << getOperationName(m_op) << "(" << operationValue0 << ");\n";
        break;

    case OPERATIONTYPE_ASSIGNMENT:
        op << "    " << getPrecisionName(resultPrec) << " " << getDataTypeName(resultType)
           << " res = " << operationValue0 << ";\n";
        op << "    res " << getOperationName(m_op) << " " << operationValue1 << ";\n";
        break;

    default:
        DE_ASSERT(false);
    }

    // Reduction to vec3 (rgb). Check the used value too if it was modified
    op << "    " << (m_isVertexCase ? "v_color" : "dEQP_FragColor") << " = ";

    if (isOperationValueModifying(m_op))
        op << "vec4(" << genGLSLMatToVec3Reduction(resultType, "res") << ", 1.0) + vec4("
           << genGLSLMatToVec3Reduction(resultType, "tmpValue") << ", 0.0);\n";
    else
        op << "vec4(" << genGLSLMatToVec3Reduction(resultType, "res") << ", 1.0);\n";

    vtx << "}\n";
    frag << "}\n";

    m_vertShaderSource = vtx.str();
    m_fragShaderSource = frag.str();

    // \todo [2012-02-14 pyry] Compute better values for matrix tests.
    m_userAttribTransforms.resize(4);
    for (int attribNdx = 0; attribNdx < 4; attribNdx++)
    {
        m_userAttribTransforms[attribNdx] = Mat4(0.0f);
        m_userAttribTransforms[attribNdx](0, 3) =
            0.1f + 0.1f * float(attribNdx); // !< prevent matrix*vec from going into zero (assuming vec.w != 0)
        m_userAttribTransforms[attribNdx](1, 3)                   = 0.2f + 0.1f * float(attribNdx); // !<
        m_userAttribTransforms[attribNdx](2, 3)                   = 0.3f + 0.1f * float(attribNdx); // !<
        m_userAttribTransforms[attribNdx](3, 3)                   = 0.4f + 0.1f * float(attribNdx); // !<
        m_userAttribTransforms[attribNdx]((0 + attribNdx) % 4, 0) = 1.0f;
        m_userAttribTransforms[attribNdx]((1 + attribNdx) % 4, 1) = 1.0f;
        m_userAttribTransforms[attribNdx]((2 + attribNdx) % 4, 2) = 1.0f;
        m_userAttribTransforms[attribNdx]((3 + attribNdx) % 4, 3) = 1.0f;
    }

    // prevent bad reference cases such as black result images by fine-tuning used matrices
    if (getOperationTestMatrixType(m_op) != TESTMATRIXTYPE_DEFAULT)
    {
        for (int attribNdx = 0; attribNdx < 4; attribNdx++)
        {
            for (int row = 0; row < 4; row++)
                for (int col = 0; col < 4; col++)
                {
                    switch (getOperationTestMatrixType(m_op))
                    {
                    case TESTMATRIXTYPE_NEGATED:
                        m_userAttribTransforms[attribNdx](row, col) = -m_userAttribTransforms[attribNdx](row, col);
                        break;
                    case TESTMATRIXTYPE_INCREMENTED:
                        m_userAttribTransforms[attribNdx](row, col) += 0.3f;
                        break;
                    case TESTMATRIXTYPE_DECREMENTED:
                        m_userAttribTransforms[attribNdx](row, col) -= 0.3f;
                        break;
                    case TESTMATRIXTYPE_NEGATED_INCREMENTED:
                        m_userAttribTransforms[attribNdx](row, col) =
                            -m_userAttribTransforms[attribNdx](row, col) + 0.3f;
                        break;
                    case TESTMATRIXTYPE_INCREMENTED_LESS:
                        m_userAttribTransforms[attribNdx](row, col) -= 0.1f;
                        break;

                    default:
                        DE_ASSERT(false);
                        break;
                    }
                }
        }
    }
    // The verification code doesn't deal with reduced precision, so we must quantize the data
    // here to try to avoid verification errors. No implementation seems to use lowp, so reduce
    // to mediump.
    if (resultPrec != PRECISION_HIGHP)
    {
        for (int attribNdx = 0; attribNdx < 4; attribNdx++)
        {
            for (int row = 0; row < 4; row++)
                for (int col = 0; col < 4; col++)
                {
                    m_userAttribTransforms[attribNdx](row, col) =
                        deFloat16To32(deFloat32To16(m_userAttribTransforms[attribNdx](row, col)));
                }
        }
    }

    ShaderRenderCase::init();

    // reassign grid size prevent matrix inverse inf value.
    m_gridSize = 64;
}

std::string ShaderMatrixCase::genGLSLMatToVec3Reduction(const glu::DataType &matType, const char *varName)
{
    std::ostringstream op;

    switch (matType)
    {
    case TYPE_FLOAT:
        op << varName << ", " << varName << ", " << varName << "";
        break;
    case TYPE_FLOAT_VEC2:
        op << varName << ".x, " << varName << ".y, " << varName << ".x";
        break;
    case TYPE_FLOAT_VEC3:
        op << varName << "";
        break;
    case TYPE_FLOAT_VEC4:
        op << varName << ".x, " << varName << ".y, " << varName << ".z+" << varName << ".w";
        break;
    case TYPE_FLOAT_MAT2:
        op << varName << "[0][0], " << varName << "[1][0], " << varName << "[0][1]+" << varName << "[1][1]";
        break;
    case TYPE_FLOAT_MAT2X3:
        op << varName << "[0] + " << varName << "[1]";
        break;
    case TYPE_FLOAT_MAT2X4:
        op << varName << "[0].xyz + " << varName << "[1].yzw";
        break;
    case TYPE_FLOAT_MAT3X2:
        op << varName << "[0][0]+" << varName << "[0][1], " << varName << "[1][0]+" << varName << "[1][1], " << varName
           << "[2][0]+" << varName << "[2][1]";
        break;
    case TYPE_FLOAT_MAT3:
        op << varName << "[0] + " << varName << "[1] + " << varName << "[2]";
        break;
    case TYPE_FLOAT_MAT3X4:
        op << varName << "[0].xyz + " << varName << "[1].yzw + " << varName << "[2].zwx";
        break;
    case TYPE_FLOAT_MAT4X2:
        op << varName << "[0][0]+" << varName << "[0][1]+" << varName << "[3][0], " << varName << "[1][0]+" << varName
           << "[1][1]+" << varName << "[3][1], " << varName << "[2][0]+" << varName << "[2][1]";
        break;
    case TYPE_FLOAT_MAT4X3:
        op << varName << "[0] + " << varName << "[1] + " << varName << "[2] + " << varName << "[3]";
        break;
    case TYPE_FLOAT_MAT4:
        op << varName << "[0].xyz+" << varName << "[1].yzw+" << varName << "[2].zwx+" << varName << "[3].wxy";
        break;

    default:
        DE_ASSERT(false);
    }

    return op.str();
}

void ShaderMatrixCase::setupUniforms(int programID, const tcu::Vec4 &constCoords)
{
    const glw::Functions &gl = m_renderCtx.getFunctions();

    DE_UNREF(constCoords);

    for (int inNdx = 0; inNdx < 2; inNdx++)
    {
        const ShaderInput &in = inNdx > 0 ? m_in1 : m_in0;

        if (in.inputType == INPUTTYPE_UNIFORM)
        {
            int loc = gl.getUniformLocation(programID, (string("u_in") + de::toString(inNdx)).c_str());

            if (loc < 0)
                continue;

            switch (in.dataType)
            {
            case TYPE_FLOAT:
                gl.uniform1f(loc, s_constInFloat[inNdx]);
                break;
            case TYPE_FLOAT_VEC2:
                gl.uniform2fv(loc, 1, s_constInVec2[inNdx].getPtr());
                break;
            case TYPE_FLOAT_VEC3:
                gl.uniform3fv(loc, 1, s_constInVec3[inNdx].getPtr());
                break;
            case TYPE_FLOAT_VEC4:
                gl.uniform4fv(loc, 1, s_constInVec4[inNdx].getPtr());
                break;
            // \note GLES3 supports transpose in matrix upload.
            case TYPE_FLOAT_MAT2:
                gl.uniformMatrix2fv(loc, 1, GL_TRUE, s_constInMat2x2[inNdx]);
                break;
            case TYPE_FLOAT_MAT2X3:
                gl.uniformMatrix2x3fv(loc, 1, GL_TRUE, s_constInMat2x3[inNdx]);
                break;
            case TYPE_FLOAT_MAT2X4:
                gl.uniformMatrix2x4fv(loc, 1, GL_TRUE, s_constInMat2x4[inNdx]);
                break;
            case TYPE_FLOAT_MAT3X2:
                gl.uniformMatrix3x2fv(loc, 1, GL_TRUE, s_constInMat3x2[inNdx]);
                break;
            case TYPE_FLOAT_MAT3:
                gl.uniformMatrix3fv(loc, 1, GL_TRUE, s_constInMat3x3[inNdx]);
                break;
            case TYPE_FLOAT_MAT3X4:
                gl.uniformMatrix3x4fv(loc, 1, GL_TRUE, s_constInMat3x4[inNdx]);
                break;
            case TYPE_FLOAT_MAT4X2:
                gl.uniformMatrix4x2fv(loc, 1, GL_TRUE, s_constInMat4x2[inNdx]);
                break;
            case TYPE_FLOAT_MAT4X3:
                gl.uniformMatrix4x3fv(loc, 1, GL_TRUE, s_constInMat4x3[inNdx]);
                break;
            case TYPE_FLOAT_MAT4:
                gl.uniformMatrix4fv(loc, 1, GL_TRUE, s_constInMat4x4[inNdx]);
                break;
            default:
                DE_ASSERT(false);
            }
        }
    }
}

ShaderMatrixTests::ShaderMatrixTests(Context &context) : TestCaseGroup(context, "matrix", "Matrix Tests")
{
}

ShaderMatrixTests::~ShaderMatrixTests(void)
{
}

void ShaderMatrixTests::init(void)
{
    static const struct
    {
        const char *name;
        const char *desc;
        MatrixOp op;
        bool extendedInputTypeCases; // !< test with const and uniform types too
        bool createInputTypeGroup;   // !< create group for input types
    } ops[] = {
        {"add", "Matrix addition tests", OP_ADD, true, true},
        {"sub", "Matrix subtraction tests", OP_SUB, true, true},
        {"mul", "Matrix multiplication tests", OP_MUL, true, true},
        {"div", "Matrix division tests", OP_DIV, true, true},
        {"matrixcompmult", "Matrix component-wise multiplication tests", OP_COMP_MUL, false, true},
        {"outerproduct", "Matrix outerProduct() tests", OP_OUTER_PRODUCT, false, true},
        {"transpose", "Matrix transpose() tests", OP_TRANSPOSE, false, true},
        {"determinant", "Matrix determinant() tests", OP_DETERMINANT, false, true},
        {"inverse", "Matrix inverse() tests", OP_INVERSE, false, true},
        {"unary_addition", "Matrix unary addition tests", OP_UNARY_PLUS, false, false},
        {"negation", "Matrix negation tests", OP_NEGATION, false, false},
        {"pre_increment", "Matrix prefix increment tests", OP_PRE_INCREMENT, false, false},
        {"pre_decrement", "Matrix prefix decrement tests", OP_PRE_DECREMENT, false, false},
        {"post_increment", "Matrix postfix increment tests", OP_POST_INCREMENT, false, false},
        {"post_decrement", "Matrix postfix decrement tests", OP_POST_DECREMENT, false, false},
        {"add_assign", "Matrix add into tests", OP_ADD_INTO, false, false},
        {"sub_assign", "Matrix subtract from tests", OP_SUBTRACT_FROM, false, false},
        {"mul_assign", "Matrix multiply into tests", OP_MULTIPLY_INTO, false, false},
        {"div_assign", "Matrix divide into tests", OP_DIVIDE_INTO, false, false},
    };

    struct InputTypeSpec
    {
        const char *name;
        const char *desc;
        InputType type;
    };
    static const InputTypeSpec extendedInputTypes[] = {{"const", "Constant matrix input", INPUTTYPE_CONST},
                                                       {"uniform", "Uniform matrix input", INPUTTYPE_UNIFORM},
                                                       {"dynamic", "Dynamic matrix input", INPUTTYPE_DYNAMIC}};
    static const InputTypeSpec reducedInputTypes[]  = {{"dynamic", "Dynamic matrix input", INPUTTYPE_DYNAMIC}};

    static const DataType matrixTypes[] = {TYPE_FLOAT_MAT2,   TYPE_FLOAT_MAT2X3, TYPE_FLOAT_MAT2X4,
                                           TYPE_FLOAT_MAT3X2, TYPE_FLOAT_MAT3,   TYPE_FLOAT_MAT3X4,
                                           TYPE_FLOAT_MAT4X2, TYPE_FLOAT_MAT4X3, TYPE_FLOAT_MAT4};

    static const Precision precisions[] = {PRECISION_LOWP, PRECISION_MEDIUMP, PRECISION_HIGHP};

    for (int opNdx = 0; opNdx < DE_LENGTH_OF_ARRAY(ops); opNdx++)
    {
        const InputTypeSpec *inTypeList =
            (ops[opNdx].extendedInputTypeCases) ? (extendedInputTypes) : (reducedInputTypes);
        const int inTypeListSize    = (ops[opNdx].extendedInputTypeCases) ? (DE_LENGTH_OF_ARRAY(extendedInputTypes)) :
                                                                            (DE_LENGTH_OF_ARRAY(reducedInputTypes));
        const MatrixOp op           = ops[opNdx].op;
        tcu::TestCaseGroup *opGroup = new tcu::TestCaseGroup(m_testCtx, ops[opNdx].name, ops[opNdx].desc);

        addChild(opGroup);

        for (int inTypeNdx = 0; inTypeNdx < inTypeListSize; inTypeNdx++)
        {
            const InputType inputType = inTypeList[inTypeNdx].type;
            tcu::TestCaseGroup *inGroup;

            if (ops[opNdx].createInputTypeGroup)
            {
                inGroup = new tcu::TestCaseGroup(m_testCtx, inTypeList[inTypeNdx].name, inTypeList[inTypeNdx].desc);
                opGroup->addChild(inGroup);
            }
            else
                inGroup = opGroup;

            for (int matTypeNdx = 0; matTypeNdx < DE_LENGTH_OF_ARRAY(matrixTypes); matTypeNdx++)
            {
                DataType matType        = matrixTypes[matTypeNdx];
                int numCols             = getDataTypeMatrixNumColumns(matType);
                int numRows             = getDataTypeMatrixNumRows(matType);
                const char *matTypeName = getDataTypeName(matType);

                for (int precNdx = 0; precNdx < DE_LENGTH_OF_ARRAY(precisions); precNdx++)
                {
                    Precision precision  = precisions[precNdx];
                    const char *precName = getPrecisionName(precision);
                    string baseName      = string(precName) + "_" + matTypeName + "_";
                    ShaderInput matIn(inputType, matType, precision);

                    if (isOperationMatrixScalar(op))
                    {
                        // Matrix-scalar \note For div cases we use uniform input.
                        ShaderInput scalarIn(op == OP_DIV ? INPUTTYPE_UNIFORM : INPUTTYPE_DYNAMIC, TYPE_FLOAT,
                                             precision);
                        inGroup->addChild(new ShaderMatrixCase(m_context, (baseName + "float_vertex").c_str(),
                                                               "Matrix-scalar case", matIn, scalarIn, op, true));
                        inGroup->addChild(new ShaderMatrixCase(m_context, (baseName + "float_fragment").c_str(),
                                                               "Matrix-scalar case", matIn, scalarIn, op, false));
                    }

                    if (isOperationMatrixVector(op))
                    {
                        // Matrix-vector.
                        DataType colVecType = getDataTypeFloatVec(numCols);
                        ShaderInput colVecIn(op == OP_DIV ? INPUTTYPE_UNIFORM : INPUTTYPE_DYNAMIC, colVecType,
                                             precision);

                        inGroup->addChild(new ShaderMatrixCase(
                            m_context, (baseName + getDataTypeName(colVecType) + "_vertex").c_str(),
                            "Matrix-vector case", matIn, colVecIn, op, true));
                        inGroup->addChild(new ShaderMatrixCase(
                            m_context, (baseName + getDataTypeName(colVecType) + "_fragment").c_str(),
                            "Matrix-vector case", matIn, colVecIn, op, false));

                        // Vector-matrix.
                        DataType rowVecType = getDataTypeFloatVec(numRows);
                        ShaderInput rowVecIn(op == OP_DIV ? INPUTTYPE_UNIFORM : INPUTTYPE_DYNAMIC, rowVecType,
                                             precision);
                        string vecMatName = string(precName) + "_" + getDataTypeName(rowVecType) + "_" + matTypeName;

                        inGroup->addChild(new ShaderMatrixCase(m_context, (vecMatName + "_vertex").c_str(),
                                                               "Vector-matrix case", rowVecIn, matIn, op, true));
                        inGroup->addChild(new ShaderMatrixCase(m_context, (vecMatName + "_fragment").c_str(),
                                                               "Vector-matrix case", rowVecIn, matIn, op, false));
                    }

                    if (isOperationArithmeticMatrixMatrix(op))
                    {
                        // Arithmetic matrix-matrix multiplication.
                        for (int otherCols = 2; otherCols <= 4; otherCols++)
                        {
                            ShaderInput otherMatIn(inputType == INPUTTYPE_DYNAMIC ? INPUTTYPE_UNIFORM : inputType,
                                                   getDataTypeMatrix(otherCols, numCols /* rows */), precision);
                            inGroup->addChild(new ShaderMatrixCase(
                                m_context, (baseName + getDataTypeName(otherMatIn.dataType) + "_vertex").c_str(),
                                "Matrix-matrix case", matIn, otherMatIn, op, true));
                            inGroup->addChild(new ShaderMatrixCase(
                                m_context, (baseName + getDataTypeName(otherMatIn.dataType) + "_fragment").c_str(),
                                "Matrix-matrix case", matIn, otherMatIn, op, false));
                        }
                    }
                    else if (isOperationComponentwiseMatrixMatrix(op))
                    {
                        // Component-wise.
                        ShaderInput otherMatIn(inputType == INPUTTYPE_DYNAMIC ? INPUTTYPE_UNIFORM : inputType, matType,
                                               precision);
                        inGroup->addChild(new ShaderMatrixCase(m_context, (baseName + matTypeName + "_vertex").c_str(),
                                                               "Matrix-matrix case", matIn, otherMatIn, op, true));
                        inGroup->addChild(new ShaderMatrixCase(m_context,
                                                               (baseName + matTypeName + "_fragment").c_str(),
                                                               "Matrix-matrix case", matIn, otherMatIn, op, false));
                    }

                    if (isOperationVectorVector(op))
                    {
                        ShaderInput vec1In(inputType, getDataTypeFloatVec(numRows), precision);
                        ShaderInput vec2In((inputType == INPUTTYPE_DYNAMIC) ? (INPUTTYPE_UNIFORM) : (inputType),
                                           getDataTypeFloatVec(numCols), precision);

                        inGroup->addChild(new ShaderMatrixCase(m_context, (baseName + "float_vertex").c_str(),
                                                               "Vector-vector case", vec1In, vec2In, op, true));
                        inGroup->addChild(new ShaderMatrixCase(m_context, (baseName + "float_fragment").c_str(),
                                                               "Vector-vector case", vec1In, vec2In, op, false));
                    }

                    if ((isOperationUnaryAnyMatrix(op)) || (isOperationUnarySymmetricMatrix(op) && numCols == numRows))
                    {
                        ShaderInput voidInput(INPUTTYPE_LAST, TYPE_LAST, PRECISION_LAST);
                        inGroup->addChild(new ShaderMatrixCase(m_context, (baseName + "float_vertex").c_str(),
                                                               "Matrix case", matIn, voidInput, op, true));
                        inGroup->addChild(new ShaderMatrixCase(m_context, (baseName + "float_fragment").c_str(),
                                                               "Matrix case", matIn, voidInput, op, false));
                    }

                    if ((isOperationAssignmentAnyMatrix(op)) ||
                        (isOperationAssignmentSymmetricMatrix(op) && numCols == numRows))
                    {
                        ShaderInput otherMatIn(inputType == INPUTTYPE_DYNAMIC ? INPUTTYPE_UNIFORM : inputType, matType,
                                               precision);
                        inGroup->addChild(new ShaderMatrixCase(m_context, (baseName + "float_vertex").c_str(),
                                                               "Matrix assignment case", matIn, otherMatIn, op, true));
                        inGroup->addChild(new ShaderMatrixCase(m_context, (baseName + "float_fragment").c_str(),
                                                               "Matrix assignment case", matIn, otherMatIn, op, false));
                    }
                }
            }
        }
    }
}

} // namespace Functional
} // namespace gles3
} // namespace deqp

#if defined(_MSC_VER) && _MSC_FULL_VER == 191125507
// Work around crbug.com/759402 which is a code-gen bug in VC++ 2017, version
// 15.3.2.
#pragma optimize("", off)
#endif
