/*-------------------------------------------------------------------------
 * 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 indexing (arrays, vector, matrices) tests.
 *//*--------------------------------------------------------------------*/

#include "es3fShaderIndexingTests.hpp"
#include "glsShaderRenderCase.hpp"
#include "gluShaderUtil.hpp"
#include "tcuStringTemplate.hpp"

#include "deInt32.h"
#include "deMemory.h"

#include <map>

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

using namespace std;
using namespace tcu;
using namespace glu;
using namespace deqp::gls;

namespace deqp
{
namespace gles3
{
namespace Functional
{

enum IndexAccessType
{
	INDEXACCESS_STATIC = 0,
	INDEXACCESS_DYNAMIC,
	INDEXACCESS_STATIC_LOOP,
	INDEXACCESS_DYNAMIC_LOOP,

	INDEXACCESS_LAST
};

static const char* getIndexAccessTypeName (IndexAccessType accessType)
{
	static const char* s_names[INDEXACCESS_LAST] =
	{
		"static",
		"dynamic",
		"static_loop",
		"dynamic_loop"
	};

	DE_ASSERT(deInBounds32((int)accessType, 0, INDEXACCESS_LAST));
	return s_names[(int)accessType];
}

enum VectorAccessType
{
	DIRECT = 0,
	COMPONENT,
	SUBSCRIPT_STATIC,
	SUBSCRIPT_DYNAMIC,
	SUBSCRIPT_STATIC_LOOP,
	SUBSCRIPT_DYNAMIC_LOOP,

	VECTORACCESS_LAST
};

static const char* getVectorAccessTypeName (VectorAccessType accessType)
{
	static const char* s_names[VECTORACCESS_LAST] =
	{
		"direct",
		"component",
		"static_subscript",
		"dynamic_subscript",
		"static_loop_subscript",
		"dynamic_loop_subscript"
	};

	DE_ASSERT(deInBounds32((int)accessType, 0, VECTORACCESS_LAST));
	return s_names[(int)accessType];
}

void evalArrayCoordsFloat		(ShaderEvalContext& c) { c.color.x()	= 1.875f * c.coords.x(); }
void evalArrayCoordsVec2		(ShaderEvalContext& c) { c.color.xy()	= 1.875f * c.coords.swizzle(0,1); }
void evalArrayCoordsVec3		(ShaderEvalContext& c) { c.color.xyz()	= 1.875f * c.coords.swizzle(0,1,2); }
void evalArrayCoordsVec4		(ShaderEvalContext& c) { c.color		= 1.875f * c.coords; }

static ShaderEvalFunc getArrayCoordsEvalFunc (DataType dataType)
{
	if (dataType == TYPE_FLOAT)				return evalArrayCoordsFloat;
	else if (dataType == TYPE_FLOAT_VEC2)	return evalArrayCoordsVec2;
	else if (dataType == TYPE_FLOAT_VEC3)	return evalArrayCoordsVec3;
	else if (dataType == TYPE_FLOAT_VEC4)	return evalArrayCoordsVec4;

	DE_ASSERT(!"Invalid data type.");
	return NULL;
}

void evalArrayUniformFloat		(ShaderEvalContext& c) { c.color.x()	= 1.875f * c.constCoords.x(); }
void evalArrayUniformVec2		(ShaderEvalContext& c) { c.color.xy()	= 1.875f * c.constCoords.swizzle(0,1); }
void evalArrayUniformVec3		(ShaderEvalContext& c) { c.color.xyz()	= 1.875f * c.constCoords.swizzle(0,1,2); }
void evalArrayUniformVec4		(ShaderEvalContext& c) { c.color		= 1.875f * c.constCoords; }

static ShaderEvalFunc getArrayUniformEvalFunc (DataType dataType)
{
	if (dataType == TYPE_FLOAT)				return evalArrayUniformFloat;
	else if (dataType == TYPE_FLOAT_VEC2)	return evalArrayUniformVec2;
	else if (dataType == TYPE_FLOAT_VEC3)	return evalArrayUniformVec3;
	else if (dataType == TYPE_FLOAT_VEC4)	return evalArrayUniformVec4;

	DE_ASSERT(!"Invalid data type.");
	return NULL;
}

// ShaderIndexingCase

class ShaderIndexingCase : public ShaderRenderCase
{
public:
								ShaderIndexingCase		(Context& context, const char* name, const char* description, bool isVertexCase, DataType varType, ShaderEvalFunc evalFunc, const char* vertShaderSource, const char* fragShaderSource);
	virtual						~ShaderIndexingCase		(void);

private:
								ShaderIndexingCase		(const ShaderIndexingCase&);	// not allowed!
	ShaderIndexingCase&			operator=				(const ShaderIndexingCase&);	// not allowed!

	virtual void				setup					(int programID);
	virtual void				setupUniforms			(int programID, const Vec4& constCoords);

	DataType					m_varType;
};

ShaderIndexingCase::ShaderIndexingCase (Context& context, const char* name, const char* description, bool isVertexCase, DataType varType, ShaderEvalFunc evalFunc, const char* vertShaderSource, const char* fragShaderSource)
	: ShaderRenderCase(context.getTestContext(), context.getRenderContext(), context.getContextInfo(), name, description, isVertexCase, evalFunc)
{
	m_varType			= varType;
	m_vertShaderSource	= vertShaderSource;
	m_fragShaderSource	= fragShaderSource;
}

ShaderIndexingCase::~ShaderIndexingCase (void)
{
}

void ShaderIndexingCase::setup (int programID)
{
	DE_UNREF(programID);
}

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

	DE_UNREF(constCoords);

	int arrLoc = gl.getUniformLocation(programID, "u_arr");
	if (arrLoc != -1)
	{
		//int scalarSize = getDataTypeScalarSize(m_varType);
		if (m_varType == TYPE_FLOAT)
		{
			float arr[4];
			arr[0] = constCoords.x();
			arr[1] = constCoords.x() * 0.5f;
			arr[2] = constCoords.x() * 0.25f;
			arr[3] = constCoords.x() * 0.125f;
			gl.uniform1fv(arrLoc, 4, &arr[0]);
		}
		else if (m_varType == TYPE_FLOAT_VEC2)
		{
			Vec2 arr[4];
			arr[0] = constCoords.swizzle(0,1);
			arr[1] = constCoords.swizzle(0,1) * 0.5f;
			arr[2] = constCoords.swizzle(0,1) * 0.25f;
			arr[3] = constCoords.swizzle(0,1) * 0.125f;
			gl.uniform2fv(arrLoc, 4, arr[0].getPtr());
		}
		else if (m_varType == TYPE_FLOAT_VEC3)
		{
			Vec3 arr[4];
			arr[0] = constCoords.swizzle(0,1,2);
			arr[1] = constCoords.swizzle(0,1,2) * 0.5f;
			arr[2] = constCoords.swizzle(0,1,2) * 0.25f;
			arr[3] = constCoords.swizzle(0,1,2) * 0.125f;
			gl.uniform3fv(arrLoc, 4, arr[0].getPtr());
		}
		else if (m_varType == TYPE_FLOAT_VEC4)
		{
			Vec4 arr[4];
			arr[0] = constCoords.swizzle(0,1,2,3);
			arr[1] = constCoords.swizzle(0,1,2,3) * 0.5f;
			arr[2] = constCoords.swizzle(0,1,2,3) * 0.25f;
			arr[3] = constCoords.swizzle(0,1,2,3) * 0.125f;
			gl.uniform4fv(arrLoc, 4, arr[0].getPtr());
		}
		else
			throw tcu::TestError("u_arr should not have location assigned in this test case");
	}
}

// Helpers.

static ShaderIndexingCase* createVaryingArrayCase (Context& context, const char* caseName, const char* description, DataType varType, IndexAccessType vertAccess, IndexAccessType fragAccess)
{
	std::ostringstream vtx;
	vtx << "#version 300 es\n";
	vtx << "in highp vec4 a_position;\n";
	vtx << "in highp vec4 a_coords;\n";
	if (vertAccess == INDEXACCESS_DYNAMIC)
		vtx << "uniform mediump int ui_zero, ui_one, ui_two, ui_three;\n";
	else if (vertAccess == INDEXACCESS_DYNAMIC_LOOP)
		vtx << "uniform mediump int ui_four;\n";
	vtx << "out ${PRECISION} ${VAR_TYPE} var[${ARRAY_LEN}];\n";
	vtx << "\n";
	vtx << "void main()\n";
	vtx << "{\n";
	vtx << "	gl_Position = a_position;\n";
	if (vertAccess == INDEXACCESS_STATIC)
	{
		vtx << "	var[0] = ${VAR_TYPE}(a_coords);\n";
		vtx << "	var[1] = ${VAR_TYPE}(a_coords) * 0.5;\n";
		vtx << "	var[2] = ${VAR_TYPE}(a_coords) * 0.25;\n";
		vtx << "	var[3] = ${VAR_TYPE}(a_coords) * 0.125;\n";
	}
	else if (vertAccess == INDEXACCESS_DYNAMIC)
	{
		vtx << "	var[ui_zero]  = ${VAR_TYPE}(a_coords);\n";
		vtx << "	var[ui_one]   = ${VAR_TYPE}(a_coords) * 0.5;\n";
		vtx << "	var[ui_two]   = ${VAR_TYPE}(a_coords) * 0.25;\n";
		vtx << "	var[ui_three] = ${VAR_TYPE}(a_coords) * 0.125;\n";
	}
	else if (vertAccess == INDEXACCESS_STATIC_LOOP)
	{
		vtx << "	${PRECISION} ${VAR_TYPE} coords = ${VAR_TYPE}(a_coords);\n";
		vtx << "	for (int i = 0; i < 4; i++)\n";
		vtx << "	{\n";
		vtx << "		var[i] = ${VAR_TYPE}(coords);\n";
		vtx << "		coords = coords * 0.5;\n";
		vtx << "	}\n";
	}
	else
	{
		DE_ASSERT(vertAccess == INDEXACCESS_DYNAMIC_LOOP);
		vtx << "	${PRECISION} ${VAR_TYPE} coords = ${VAR_TYPE}(a_coords);\n";
		vtx << "	for (int i = 0; i < ui_four; i++)\n";
		vtx << "	{\n";
		vtx << "		var[i] = ${VAR_TYPE}(coords);\n";
		vtx << "		coords = coords * 0.5;\n";
		vtx << "	}\n";
	}
	vtx << "}\n";

	std::ostringstream frag;
	frag << "#version 300 es\n";
	frag << "precision mediump int;\n";
	frag << "layout(location = 0) out mediump vec4 o_color;\n";
	if (fragAccess == INDEXACCESS_DYNAMIC)
		frag << "uniform mediump int ui_zero, ui_one, ui_two, ui_three;\n";
	else if (fragAccess == INDEXACCESS_DYNAMIC_LOOP)
		frag << "uniform int ui_four;\n";
	frag << "in ${PRECISION} ${VAR_TYPE} var[${ARRAY_LEN}];\n";
	frag << "\n";
	frag << "void main()\n";
	frag << "{\n";
	frag << "	${PRECISION} ${VAR_TYPE} res = ${VAR_TYPE}(0.0);\n";
	if (fragAccess == INDEXACCESS_STATIC)
	{
		frag << "	res += var[0];\n";
		frag << "	res += var[1];\n";
		frag << "	res += var[2];\n";
		frag << "	res += var[3];\n";
	}
	else if (fragAccess == INDEXACCESS_DYNAMIC)
	{
		frag << "	res += var[ui_zero];\n";
		frag << "	res += var[ui_one];\n";
		frag << "	res += var[ui_two];\n";
		frag << "	res += var[ui_three];\n";
	}
	else if (fragAccess == INDEXACCESS_STATIC_LOOP)
	{
		frag << "	for (int i = 0; i < 4; i++)\n";
		frag << "		res += var[i];\n";
	}
	else
	{
		DE_ASSERT(fragAccess == INDEXACCESS_DYNAMIC_LOOP);
		frag << "	for (int i = 0; i < ui_four; i++)\n";
		frag << "		res += var[i];\n";
	}
	frag << "	o_color = vec4(res${PADDING});\n";
	frag << "}\n";

	// Fill in shader templates.
	map<string, string> params;
	params.insert(pair<string, string>("VAR_TYPE", getDataTypeName(varType)));
	params.insert(pair<string, string>("ARRAY_LEN", "4"));
	params.insert(pair<string, string>("PRECISION", "mediump"));

	if (varType == TYPE_FLOAT)
		params.insert(pair<string, string>("PADDING", ", 0.0, 0.0, 1.0"));
	else if (varType == TYPE_FLOAT_VEC2)
		params.insert(pair<string, string>("PADDING", ", 0.0, 1.0"));
	else if (varType == TYPE_FLOAT_VEC3)
		params.insert(pair<string, string>("PADDING", ", 1.0"));
	else
		params.insert(pair<string, string>("PADDING", ""));

	StringTemplate vertTemplate(vtx.str().c_str());
	StringTemplate fragTemplate(frag.str().c_str());
	string vertexShaderSource = vertTemplate.specialize(params);
	string fragmentShaderSource = fragTemplate.specialize(params);

	ShaderEvalFunc evalFunc = getArrayCoordsEvalFunc(varType);
	return new ShaderIndexingCase(context, caseName, description, true, varType, evalFunc, vertexShaderSource.c_str(), fragmentShaderSource.c_str());
}

static ShaderIndexingCase* createUniformArrayCase (Context& context, const char* caseName, const char* description, bool isVertexCase, DataType varType, IndexAccessType readAccess)
{
	std::ostringstream vtx;
	std::ostringstream frag;
	std::ostringstream& op = isVertexCase ? vtx : frag;

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

	vtx << "in highp vec4 a_position;\n";
	vtx << "in highp vec4 a_coords;\n";
	frag << "layout(location = 0) out mediump vec4 o_color;\n";

	if (isVertexCase)
	{
		vtx << "out mediump vec4 v_color;\n";
		frag << "in mediump vec4 v_color;\n";
	}
	else
	{
		vtx << "out mediump vec4 v_coords;\n";
		frag << "in mediump vec4 v_coords;\n";
	}

	if (readAccess == INDEXACCESS_DYNAMIC)
		op << "uniform mediump int ui_zero, ui_one, ui_two, ui_three;\n";
	else if (readAccess == INDEXACCESS_DYNAMIC_LOOP)
		op << "uniform mediump int ui_four;\n";

	op << "uniform ${PRECISION} ${VAR_TYPE} u_arr[${ARRAY_LEN}];\n";

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

	frag << "\n";
	frag << "void main()\n";
	frag << "{\n";

	// Read array.
	op << "	${PRECISION} ${VAR_TYPE} res = ${VAR_TYPE}(0.0);\n";
	if (readAccess == INDEXACCESS_STATIC)
	{
		op << "	res += u_arr[0];\n";
		op << "	res += u_arr[1];\n";
		op << "	res += u_arr[2];\n";
		op << "	res += u_arr[3];\n";
	}
	else if (readAccess == INDEXACCESS_DYNAMIC)
	{
		op << "	res += u_arr[ui_zero];\n";
		op << "	res += u_arr[ui_one];\n";
		op << "	res += u_arr[ui_two];\n";
		op << "	res += u_arr[ui_three];\n";
	}
	else if (readAccess == INDEXACCESS_STATIC_LOOP)
	{
		op << "	for (int i = 0; i < 4; i++)\n";
		op << "		res += u_arr[i];\n";
	}
	else
	{
		DE_ASSERT(readAccess == INDEXACCESS_DYNAMIC_LOOP);
		op << "	for (int i = 0; i < ui_four; i++)\n";
		op << "		res += u_arr[i];\n";
	}

	if (isVertexCase)
	{
		vtx << "	v_color = vec4(res${PADDING});\n";
		frag << "	o_color = v_color;\n";
	}
	else
	{
		vtx << "	v_coords = a_coords;\n";
		frag << "	o_color = vec4(res${PADDING});\n";
	}

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

	// Fill in shader templates.
	map<string, string> params;
	params.insert(pair<string, string>("VAR_TYPE", getDataTypeName(varType)));
	params.insert(pair<string, string>("ARRAY_LEN", "4"));
	params.insert(pair<string, string>("PRECISION", "mediump"));

	if (varType == TYPE_FLOAT)
		params.insert(pair<string, string>("PADDING", ", 0.0, 0.0, 1.0"));
	else if (varType == TYPE_FLOAT_VEC2)
		params.insert(pair<string, string>("PADDING", ", 0.0, 1.0"));
	else if (varType == TYPE_FLOAT_VEC3)
		params.insert(pair<string, string>("PADDING", ", 1.0"));
	else
		params.insert(pair<string, string>("PADDING", ""));

	StringTemplate vertTemplate(vtx.str().c_str());
	StringTemplate fragTemplate(frag.str().c_str());
	string vertexShaderSource = vertTemplate.specialize(params);
	string fragmentShaderSource = fragTemplate.specialize(params);

	ShaderEvalFunc evalFunc = getArrayUniformEvalFunc(varType);
	return new ShaderIndexingCase(context, caseName, description, isVertexCase, varType, evalFunc, vertexShaderSource.c_str(), fragmentShaderSource.c_str());
}

static ShaderIndexingCase* createTmpArrayCase (Context& context, const char* caseName, const char* description, bool isVertexCase, DataType varType, IndexAccessType writeAccess, IndexAccessType readAccess)
{
	std::ostringstream vtx;
	std::ostringstream frag;
	std::ostringstream& op = isVertexCase ? vtx : frag;

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

	vtx << "in highp vec4 a_position;\n";
	vtx << "in highp vec4 a_coords;\n";
	frag << "layout(location = 0) out mediump vec4 o_color;\n";

	if (isVertexCase)
	{
		vtx << "out mediump vec4 v_color;\n";
		frag << "in mediump vec4 v_color;\n";
	}
	else
	{
		vtx << "out mediump vec4 v_coords;\n";
		frag << "in mediump vec4 v_coords;\n";
	}

	if (writeAccess == INDEXACCESS_DYNAMIC || readAccess == INDEXACCESS_DYNAMIC)
		op << "uniform mediump int ui_zero, ui_one, ui_two, ui_three;\n";

	if (writeAccess == INDEXACCESS_DYNAMIC_LOOP || readAccess == INDEXACCESS_DYNAMIC_LOOP)
		op << "uniform mediump int ui_four;\n";

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

	frag << "\n";
	frag << "void main()\n";
	frag << "{\n";

	// Write array.
	if (isVertexCase)
		op << "	${PRECISION} ${VAR_TYPE} coords = ${VAR_TYPE}(a_coords);\n";
	else
		op << "	${PRECISION} ${VAR_TYPE} coords = ${VAR_TYPE}(v_coords);\n";

	op << "	${PRECISION} ${VAR_TYPE} arr[${ARRAY_LEN}];\n";
	if (writeAccess == INDEXACCESS_STATIC)
	{
		op << "	arr[0] = ${VAR_TYPE}(coords);\n";
		op << "	arr[1] = ${VAR_TYPE}(coords) * 0.5;\n";
		op << "	arr[2] = ${VAR_TYPE}(coords) * 0.25;\n";
		op << "	arr[3] = ${VAR_TYPE}(coords) * 0.125;\n";
	}
	else if (writeAccess == INDEXACCESS_DYNAMIC)
	{
		op << "	arr[ui_zero]  = ${VAR_TYPE}(coords);\n";
		op << "	arr[ui_one]   = ${VAR_TYPE}(coords) * 0.5;\n";
		op << "	arr[ui_two]   = ${VAR_TYPE}(coords) * 0.25;\n";
		op << "	arr[ui_three] = ${VAR_TYPE}(coords) * 0.125;\n";
	}
	else if (writeAccess == INDEXACCESS_STATIC_LOOP)
	{
		op << "	for (int i = 0; i < 4; i++)\n";
		op << "	{\n";
		op << "		arr[i] = ${VAR_TYPE}(coords);\n";
		op << "		coords = coords * 0.5;\n";
		op << "	}\n";
	}
	else
	{
		DE_ASSERT(writeAccess == INDEXACCESS_DYNAMIC_LOOP);
		op << "	for (int i = 0; i < ui_four; i++)\n";
		op << "	{\n";
		op << "		arr[i] = ${VAR_TYPE}(coords);\n";
		op << "		coords = coords * 0.5;\n";
		op << "	}\n";
	}

	// Read array.
	op << "	${PRECISION} ${VAR_TYPE} res = ${VAR_TYPE}(0.0);\n";
	if (readAccess == INDEXACCESS_STATIC)
	{
		op << "	res += arr[0];\n";
		op << "	res += arr[1];\n";
		op << "	res += arr[2];\n";
		op << "	res += arr[3];\n";
	}
	else if (readAccess == INDEXACCESS_DYNAMIC)
	{
		op << "	res += arr[ui_zero];\n";
		op << "	res += arr[ui_one];\n";
		op << "	res += arr[ui_two];\n";
		op << "	res += arr[ui_three];\n";
	}
	else if (readAccess == INDEXACCESS_STATIC_LOOP)
	{
		op << "	for (int i = 0; i < 4; i++)\n";
		op << "		res += arr[i];\n";
	}
	else
	{
		DE_ASSERT(readAccess == INDEXACCESS_DYNAMIC_LOOP);
		op << "	for (int i = 0; i < ui_four; i++)\n";
		op << "		res += arr[i];\n";
	}

	if (isVertexCase)
	{
		vtx << "	v_color = vec4(res${PADDING});\n";
		frag << "	o_color = v_color;\n";
	}
	else
	{
		vtx << "	v_coords = a_coords;\n";
		frag << "	o_color = vec4(res${PADDING});\n";
	}

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

	// Fill in shader templates.
	map<string, string> params;
	params.insert(pair<string, string>("VAR_TYPE", getDataTypeName(varType)));
	params.insert(pair<string, string>("ARRAY_LEN", "4"));
	params.insert(pair<string, string>("PRECISION", "mediump"));

	if (varType == TYPE_FLOAT)
		params.insert(pair<string, string>("PADDING", ", 0.0, 0.0, 1.0"));
	else if (varType == TYPE_FLOAT_VEC2)
		params.insert(pair<string, string>("PADDING", ", 0.0, 1.0"));
	else if (varType == TYPE_FLOAT_VEC3)
		params.insert(pair<string, string>("PADDING", ", 1.0"));
	else
		params.insert(pair<string, string>("PADDING", ""));

	StringTemplate vertTemplate(vtx.str().c_str());
	StringTemplate fragTemplate(frag.str().c_str());
	string vertexShaderSource = vertTemplate.specialize(params);
	string fragmentShaderSource = fragTemplate.specialize(params);

	ShaderEvalFunc evalFunc = getArrayCoordsEvalFunc(varType);
	return new ShaderIndexingCase(context, caseName, description, isVertexCase, varType, evalFunc, vertexShaderSource.c_str(), fragmentShaderSource.c_str());
}

// VECTOR SUBSCRIPT.

void evalSubscriptVec2 (ShaderEvalContext& c) { c.color.xyz() = Vec3(c.coords.x() + 0.5f*c.coords.y()); }
void evalSubscriptVec3 (ShaderEvalContext& c) { c.color.xyz() = Vec3(c.coords.x() + 0.5f*c.coords.y() + 0.25f*c.coords.z()); }
void evalSubscriptVec4 (ShaderEvalContext& c) { c.color.xyz() = Vec3(c.coords.x() + 0.5f*c.coords.y() + 0.25f*c.coords.z() + 0.125f*c.coords.w()); }

static ShaderEvalFunc getVectorSubscriptEvalFunc (DataType dataType)
{
	if (dataType == TYPE_FLOAT_VEC2)		return evalSubscriptVec2;
	else if (dataType == TYPE_FLOAT_VEC3)	return evalSubscriptVec3;
	else if (dataType == TYPE_FLOAT_VEC4)	return evalSubscriptVec4;

	DE_ASSERT(!"Invalid data type.");
	return NULL;
}

static ShaderIndexingCase* createVectorSubscriptCase (Context& context, const char* caseName, const char* description, bool isVertexCase, DataType varType, VectorAccessType writeAccess, VectorAccessType readAccess)
{
	std::ostringstream vtx;
	std::ostringstream frag;
	std::ostringstream& op = isVertexCase ? vtx : frag;

	int			vecLen		= getDataTypeScalarSize(varType);
	const char*	vecLenName	= getIntUniformName(vecLen);

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

	vtx << "in highp vec4 a_position;\n";
	vtx << "in highp vec4 a_coords;\n";
	frag << "layout(location = 0) out mediump vec4 o_color;\n";

	if (isVertexCase)
	{
		vtx << "out mediump vec3 v_color;\n";
		frag << "in mediump vec3 v_color;\n";
	}
	else
	{
		vtx << "out mediump vec4 v_coords;\n";
		frag << "in mediump vec4 v_coords;\n";
	}

	if (writeAccess == SUBSCRIPT_DYNAMIC || readAccess == SUBSCRIPT_DYNAMIC)
	{
		op << "uniform mediump int ui_zero";
		if (vecLen >= 2) op << ", ui_one";
		if (vecLen >= 3) op << ", ui_two";
		if (vecLen >= 4) op << ", ui_three";
		op << ";\n";
	}

	if (writeAccess == SUBSCRIPT_DYNAMIC_LOOP || readAccess == SUBSCRIPT_DYNAMIC_LOOP)
		op << "uniform mediump int " << vecLenName << ";\n";

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

	frag << "\n";
	frag << "void main()\n";
	frag << "{\n";

	// Write vector.
	if (isVertexCase)
		op << "	${PRECISION} ${VAR_TYPE} coords = ${VAR_TYPE}(a_coords);\n";
	else
		op << "	${PRECISION} ${VAR_TYPE} coords = ${VAR_TYPE}(v_coords);\n";

	op << "	${PRECISION} ${VAR_TYPE} tmp;\n";
	if (writeAccess == DIRECT)
		op << "	tmp = coords.${SWIZZLE} * vec4(1.0, 0.5, 0.25, 0.125).${SWIZZLE};\n";
	else if (writeAccess == COMPONENT)
	{
		op << "	tmp.x = coords.x;\n";
		if (vecLen >= 2) op << "	tmp.y = coords.y * 0.5;\n";
		if (vecLen >= 3) op << "	tmp.z = coords.z * 0.25;\n";
		if (vecLen >= 4) op << "	tmp.w = coords.w * 0.125;\n";
	}
	else if (writeAccess == SUBSCRIPT_STATIC)
	{
		op << "	tmp[0] = coords.x;\n";
		if (vecLen >= 2) op << "	tmp[1] = coords.y * 0.5;\n";
		if (vecLen >= 3) op << "	tmp[2] = coords.z * 0.25;\n";
		if (vecLen >= 4) op << "	tmp[3] = coords.w * 0.125;\n";
	}
	else if (writeAccess == SUBSCRIPT_DYNAMIC)
	{
		op << "	tmp[ui_zero]  = coords.x;\n";
		if (vecLen >= 2) op << "	tmp[ui_one]   = coords.y * 0.5;\n";
		if (vecLen >= 3) op << "	tmp[ui_two]   = coords.z * 0.25;\n";
		if (vecLen >= 4) op << "	tmp[ui_three] = coords.w * 0.125;\n";
	}
	else if (writeAccess == SUBSCRIPT_STATIC_LOOP)
	{
		op << "	for (int i = 0; i < " << vecLen << "; i++)\n";
		op << "	{\n";
		op << "		tmp[i] = coords.x;\n";
		op << "		coords = coords.${ROT_SWIZZLE} * 0.5;\n";
		op << "	}\n";
	}
	else
	{
		DE_ASSERT(writeAccess == SUBSCRIPT_DYNAMIC_LOOP);
		op << "	for (int i = 0; i < " << vecLenName << "; i++)\n";
		op << "	{\n";
		op << "		tmp[i] = coords.x;\n";
		op << "		coords = coords.${ROT_SWIZZLE} * 0.5;\n";
		op << "	}\n";
	}

	// Read vector.
	op << "	${PRECISION} float res = 0.0;\n";
	if (readAccess == DIRECT)
		op << "	res = dot(tmp, ${VAR_TYPE}(1.0));\n";
	else if (readAccess == COMPONENT)
	{
		op << "	res += tmp.x;\n";
		if (vecLen >= 2) op << "	res += tmp.y;\n";
		if (vecLen >= 3) op << "	res += tmp.z;\n";
		if (vecLen >= 4) op << "	res += tmp.w;\n";
	}
	else if (readAccess == SUBSCRIPT_STATIC)
	{
		op << "	res += tmp[0];\n";
		if (vecLen >= 2) op << "	res += tmp[1];\n";
		if (vecLen >= 3) op << "	res += tmp[2];\n";
		if (vecLen >= 4) op << "	res += tmp[3];\n";
	}
	else if (readAccess == SUBSCRIPT_DYNAMIC)
	{
		op << "	res += tmp[ui_zero];\n";
		if (vecLen >= 2) op << "	res += tmp[ui_one];\n";
		if (vecLen >= 3) op << "	res += tmp[ui_two];\n";
		if (vecLen >= 4) op << "	res += tmp[ui_three];\n";
	}
	else if (readAccess == SUBSCRIPT_STATIC_LOOP)
	{
		op << "	for (int i = 0; i < " << vecLen << "; i++)\n";
		op << "		res += tmp[i];\n";
	}
	else
	{
		DE_ASSERT(readAccess == SUBSCRIPT_DYNAMIC_LOOP);
		op << "	for (int i = 0; i < " << vecLenName << "; i++)\n";
		op << "		res += tmp[i];\n";
	}

	if (isVertexCase)
	{
		vtx << "	v_color = vec3(res);\n";
		frag << "	o_color = vec4(v_color.rgb, 1.0);\n";
	}
	else
	{
		vtx << "	v_coords = a_coords;\n";
		frag << "	o_color = vec4(vec3(res), 1.0);\n";
	}

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

	// Fill in shader templates.
	static const char* s_swizzles[5]	= { "", "x", "xy", "xyz", "xyzw" };
	static const char* s_rotSwizzles[5]	= { "", "x", "yx", "yzx", "yzwx" };

	map<string, string> params;
	params.insert(pair<string, string>("VAR_TYPE", getDataTypeName(varType)));
	params.insert(pair<string, string>("PRECISION", "mediump"));
	params.insert(pair<string, string>("SWIZZLE", s_swizzles[vecLen]));
	params.insert(pair<string, string>("ROT_SWIZZLE", s_rotSwizzles[vecLen]));

	StringTemplate vertTemplate(vtx.str().c_str());
	StringTemplate fragTemplate(frag.str().c_str());
	string vertexShaderSource = vertTemplate.specialize(params);
	string fragmentShaderSource = fragTemplate.specialize(params);

	ShaderEvalFunc evalFunc = getVectorSubscriptEvalFunc(varType);
	return new ShaderIndexingCase(context, caseName, description, isVertexCase, varType, evalFunc, vertexShaderSource.c_str(), fragmentShaderSource.c_str());
}

// MATRIX SUBSCRIPT.

void evalSubscriptMat2		(ShaderEvalContext& c) { c.color.xy()	= c.coords.swizzle(0,1) + 0.5f*c.coords.swizzle(1,2); }
void evalSubscriptMat2x3	(ShaderEvalContext& c) { c.color.xyz()	= c.coords.swizzle(0,1,2) + 0.5f*c.coords.swizzle(1,2,3); }
void evalSubscriptMat2x4	(ShaderEvalContext& c) { c.color		= c.coords.swizzle(0,1,2,3) + 0.5f*c.coords.swizzle(1,2,3,0); }

void evalSubscriptMat3x2	(ShaderEvalContext& c) { c.color.xy()	= c.coords.swizzle(0,1) + 0.5f*c.coords.swizzle(1,2) + 0.25f*c.coords.swizzle(2,3); }
void evalSubscriptMat3		(ShaderEvalContext& c) { c.color.xyz()	= c.coords.swizzle(0,1,2) + 0.5f*c.coords.swizzle(1,2,3) + 0.25f*c.coords.swizzle(2,3,0); }
void evalSubscriptMat3x4	(ShaderEvalContext& c) { c.color		= c.coords.swizzle(0,1,2,3) + 0.5f*c.coords.swizzle(1,2,3,0) + 0.25f*c.coords.swizzle(2,3,0,1); }

void evalSubscriptMat4x2	(ShaderEvalContext& c) { c.color.xy()	= c.coords.swizzle(0,1) + 0.5f*c.coords.swizzle(1,2) + 0.25f*c.coords.swizzle(2,3) + 0.125f*c.coords.swizzle(3,0); }
void evalSubscriptMat4x3	(ShaderEvalContext& c) { c.color.xyz()	= c.coords.swizzle(0,1,2) + 0.5f*c.coords.swizzle(1,2,3) + 0.25f*c.coords.swizzle(2,3,0) + 0.125f*c.coords.swizzle(3,0,1); }
void evalSubscriptMat4		(ShaderEvalContext& c) { c.color		= c.coords + 0.5f*c.coords.swizzle(1,2,3,0) + 0.25f*c.coords.swizzle(2,3,0,1) + 0.125f*c.coords.swizzle(3,0,1,2); }

static ShaderEvalFunc getMatrixSubscriptEvalFunc (DataType dataType)
{
	switch (dataType)
	{
		case TYPE_FLOAT_MAT2:		return evalSubscriptMat2;
		case TYPE_FLOAT_MAT2X3:		return evalSubscriptMat2x3;
		case TYPE_FLOAT_MAT2X4:		return evalSubscriptMat2x4;
		case TYPE_FLOAT_MAT3X2:		return evalSubscriptMat3x2;
		case TYPE_FLOAT_MAT3:		return evalSubscriptMat3;
		case TYPE_FLOAT_MAT3X4:		return evalSubscriptMat3x4;
		case TYPE_FLOAT_MAT4X2:		return evalSubscriptMat4x2;
		case TYPE_FLOAT_MAT4X3:		return evalSubscriptMat4x3;
		case TYPE_FLOAT_MAT4:		return evalSubscriptMat4;

		default:
			DE_ASSERT(!"Invalid data type.");
			return DE_NULL;
	}
}

static ShaderIndexingCase* createMatrixSubscriptCase (Context& context, const char* caseName, const char* description, bool isVertexCase, DataType varType, IndexAccessType writeAccess, IndexAccessType readAccess)
{
	std::ostringstream vtx;
	std::ostringstream frag;
	std::ostringstream& op = isVertexCase ? vtx : frag;

	int			numCols		= getDataTypeMatrixNumColumns(varType);
	int			numRows		= getDataTypeMatrixNumRows(varType);
	const char*	matSizeName	= getIntUniformName(numCols);
	DataType	vecType		= getDataTypeFloatVec(numRows);

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

	vtx << "in highp vec4 a_position;\n";
	vtx << "in highp vec4 a_coords;\n";
	frag << "layout(location = 0) out mediump vec4 o_color;\n";

	if (isVertexCase)
	{
		vtx << "out mediump vec4 v_color;\n";
		frag << "in mediump vec4 v_color;\n";
	}
	else
	{
		vtx << "out mediump vec4 v_coords;\n";
		frag << "in mediump vec4 v_coords;\n";
	}

	if (writeAccess == INDEXACCESS_DYNAMIC || readAccess == INDEXACCESS_DYNAMIC)
	{
		op << "uniform mediump int ui_zero";
		if (numCols >= 2) op << ", ui_one";
		if (numCols >= 3) op << ", ui_two";
		if (numCols >= 4) op << ", ui_three";
		op << ";\n";
	}

	if (writeAccess == INDEXACCESS_DYNAMIC_LOOP || readAccess == INDEXACCESS_DYNAMIC_LOOP)
		op << "uniform mediump int " << matSizeName << ";\n";

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

	frag << "\n";
	frag << "void main()\n";
	frag << "{\n";

	// Write matrix.
	if (isVertexCase)
		op << "	${PRECISION} vec4 coords = a_coords;\n";
	else
		op << "	${PRECISION} vec4 coords = v_coords;\n";

	op << "	${PRECISION} ${MAT_TYPE} tmp;\n";
	if (writeAccess == INDEXACCESS_STATIC)
	{
		op << "	tmp[0] = ${VEC_TYPE}(coords);\n";
		if (numCols >= 2) op << "	tmp[1] = ${VEC_TYPE}(coords.yzwx) * 0.5;\n";
		if (numCols >= 3) op << "	tmp[2] = ${VEC_TYPE}(coords.zwxy) * 0.25;\n";
		if (numCols >= 4) op << "	tmp[3] = ${VEC_TYPE}(coords.wxyz) * 0.125;\n";
	}
	else if (writeAccess == INDEXACCESS_DYNAMIC)
	{
		op << "	tmp[ui_zero]  = ${VEC_TYPE}(coords);\n";
		if (numCols >= 2) op << "	tmp[ui_one]   = ${VEC_TYPE}(coords.yzwx) * 0.5;\n";
		if (numCols >= 3) op << "	tmp[ui_two]   = ${VEC_TYPE}(coords.zwxy) * 0.25;\n";
		if (numCols >= 4) op << "	tmp[ui_three] = ${VEC_TYPE}(coords.wxyz) * 0.125;\n";
	}
	else if (writeAccess == INDEXACCESS_STATIC_LOOP)
	{
		op << "	for (int i = 0; i < " << numCols << "; i++)\n";
		op << "	{\n";
		op << "		tmp[i] = ${VEC_TYPE}(coords);\n";
		op << "		coords = coords.yzwx * 0.5;\n";
		op << "	}\n";
	}
	else
	{
		DE_ASSERT(writeAccess == INDEXACCESS_DYNAMIC_LOOP);
		op << "	for (int i = 0; i < " << matSizeName << "; i++)\n";
		op << "	{\n";
		op << "		tmp[i] = ${VEC_TYPE}(coords);\n";
		op << "		coords = coords.yzwx * 0.5;\n";
		op << "	}\n";
	}

	// Read matrix.
	op << "	${PRECISION} ${VEC_TYPE} res = ${VEC_TYPE}(0.0);\n";
	if (readAccess == INDEXACCESS_STATIC)
	{
		op << "	res += tmp[0];\n";
		if (numCols >= 2) op << "	res += tmp[1];\n";
		if (numCols >= 3) op << "	res += tmp[2];\n";
		if (numCols >= 4) op << "	res += tmp[3];\n";
	}
	else if (readAccess == INDEXACCESS_DYNAMIC)
	{
		op << "	res += tmp[ui_zero];\n";
		if (numCols >= 2) op << "	res += tmp[ui_one];\n";
		if (numCols >= 3) op << "	res += tmp[ui_two];\n";
		if (numCols >= 4) op << "	res += tmp[ui_three];\n";
	}
	else if (readAccess == INDEXACCESS_STATIC_LOOP)
	{
		op << "	for (int i = 0; i < " << numCols << "; i++)\n";
		op << "		res += tmp[i];\n";
	}
	else
	{
		DE_ASSERT(readAccess == INDEXACCESS_DYNAMIC_LOOP);
		op << "	for (int i = 0; i < " << matSizeName << "; i++)\n";
		op << "		res += tmp[i];\n";
	}

	if (isVertexCase)
	{
		vtx << "	v_color = vec4(res${PADDING});\n";
		frag << "	o_color = v_color;\n";
	}
	else
	{
		vtx << "	v_coords = a_coords;\n";
		frag << "	o_color = vec4(res${PADDING});\n";
	}

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

	// Fill in shader templates.
	map<string, string> params;
	params.insert(pair<string, string>("MAT_TYPE", getDataTypeName(varType)));
	params.insert(pair<string, string>("VEC_TYPE", getDataTypeName(vecType)));
	params.insert(pair<string, string>("PRECISION", "mediump"));

	if (numRows == 2)
		params.insert(pair<string, string>("PADDING", ", 0.0, 1.0"));
	else if (numRows == 3)
		params.insert(pair<string, string>("PADDING", ", 1.0"));
	else
		params.insert(pair<string, string>("PADDING", ""));

	StringTemplate vertTemplate(vtx.str().c_str());
	StringTemplate fragTemplate(frag.str().c_str());
	string vertexShaderSource = vertTemplate.specialize(params);
	string fragmentShaderSource = fragTemplate.specialize(params);

	ShaderEvalFunc evalFunc = getMatrixSubscriptEvalFunc(varType);
	return new ShaderIndexingCase(context, caseName, description, isVertexCase, varType, evalFunc, vertexShaderSource.c_str(), fragmentShaderSource.c_str());
}

// ShaderIndexingTests.

ShaderIndexingTests::ShaderIndexingTests(Context& context)
	: TestCaseGroup(context, "indexing", "Indexing Tests")
{
}

ShaderIndexingTests::~ShaderIndexingTests (void)
{
}

void ShaderIndexingTests::init (void)
{
	static const ShaderType s_shaderTypes[] =
	{
		SHADERTYPE_VERTEX,
		SHADERTYPE_FRAGMENT
	};

	static const DataType s_floatAndVecTypes[] =
	{
		TYPE_FLOAT,
		TYPE_FLOAT_VEC2,
		TYPE_FLOAT_VEC3,
		TYPE_FLOAT_VEC4
	};

	// Varying array access cases.
	{
		TestCaseGroup* varyingGroup = new TestCaseGroup(m_context, "varying_array", "Varying array access tests.");
		addChild(varyingGroup);

		for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(s_floatAndVecTypes); typeNdx++)
		{
			DataType varType = s_floatAndVecTypes[typeNdx];
			for (int vertAccess = 0; vertAccess < INDEXACCESS_LAST; vertAccess++)
			{
				for (int fragAccess = 0; fragAccess < INDEXACCESS_LAST; fragAccess++)
				{
					const char* vertAccessName = getIndexAccessTypeName((IndexAccessType)vertAccess);
					const char* fragAccessName = getIndexAccessTypeName((IndexAccessType)fragAccess);
					string name = string(getDataTypeName(varType)) + "_" + vertAccessName + "_write_" + fragAccessName + "_read";
					string desc = string("Varying array with ") + vertAccessName + " write in vertex shader and " + fragAccessName + " read in fragment shader.";
					varyingGroup->addChild(createVaryingArrayCase(m_context, name.c_str(), desc.c_str(), varType, (IndexAccessType)vertAccess, (IndexAccessType)fragAccess));
				}
			}
		}
	}

	// Uniform array access cases.
	{
		TestCaseGroup* uniformGroup = new TestCaseGroup(m_context, "uniform_array", "Uniform array access tests.");
		addChild(uniformGroup);

		for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(s_floatAndVecTypes); typeNdx++)
		{
			DataType varType = s_floatAndVecTypes[typeNdx];
			for (int readAccess = 0; readAccess < INDEXACCESS_LAST; readAccess++)
			{
				const char* readAccessName = getIndexAccessTypeName((IndexAccessType)readAccess);
				for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(s_shaderTypes); shaderTypeNdx++)
				{
					ShaderType	shaderType		= s_shaderTypes[shaderTypeNdx];
					const char*	shaderTypeName	= getShaderTypeName(shaderType);
					string		name			= string(getDataTypeName(varType)) + "_" + readAccessName + "_read_" + shaderTypeName;
					string		desc			= string("Uniform array with ") + readAccessName + " read in " + shaderTypeName + " shader.";
					bool isVertexCase = ((ShaderType)shaderType == SHADERTYPE_VERTEX);
					uniformGroup->addChild(createUniformArrayCase(m_context, name.c_str(), desc.c_str(), isVertexCase, varType, (IndexAccessType)readAccess));
				}
			}
		}
	}

	// Temporary array access cases.
	{
		TestCaseGroup* tmpGroup = new TestCaseGroup(m_context, "tmp_array", "Temporary array access tests.");
		addChild(tmpGroup);

		for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(s_floatAndVecTypes); typeNdx++)
		{
			DataType varType = s_floatAndVecTypes[typeNdx];
			for (int writeAccess = 0; writeAccess < INDEXACCESS_LAST; writeAccess++)
			{
				for (int readAccess = 0; readAccess < INDEXACCESS_LAST; readAccess++)
				{
					const char* writeAccessName = getIndexAccessTypeName((IndexAccessType)writeAccess);
					const char* readAccessName = getIndexAccessTypeName((IndexAccessType)readAccess);

					for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(s_shaderTypes); shaderTypeNdx++)
					{
						ShaderType	shaderType		= s_shaderTypes[shaderTypeNdx];
						const char* shaderTypeName	= getShaderTypeName(shaderType);
						string		name			= string(getDataTypeName(varType)) + "_" + writeAccessName + "_write_" + readAccessName + "_read_" + shaderTypeName;
						string		desc			= string("Temporary array with ") + writeAccessName + " write and " + readAccessName + " read in " + shaderTypeName + " shader.";
						bool		isVertexCase	= ((ShaderType)shaderType == SHADERTYPE_VERTEX);
						tmpGroup->addChild(createTmpArrayCase(m_context, name.c_str(), desc.c_str(), isVertexCase, varType, (IndexAccessType)writeAccess, (IndexAccessType)readAccess));
					}
				}
			}
		}
	}

	// Vector indexing with subscripts.
	{
		TestCaseGroup* vecGroup = new TestCaseGroup(m_context, "vector_subscript", "Vector subscript indexing.");
		addChild(vecGroup);

		static const DataType s_vectorTypes[] =
		{
			TYPE_FLOAT_VEC2,
			TYPE_FLOAT_VEC3,
			TYPE_FLOAT_VEC4
		};

		for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(s_vectorTypes); typeNdx++)
		{
			DataType varType = s_vectorTypes[typeNdx];
			for (int writeAccess = 0; writeAccess < VECTORACCESS_LAST; writeAccess++)
			{
				for (int readAccess = 0; readAccess < VECTORACCESS_LAST; readAccess++)
				{
					const char* writeAccessName = getVectorAccessTypeName((VectorAccessType)writeAccess);
					const char* readAccessName = getVectorAccessTypeName((VectorAccessType)readAccess);

					for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(s_shaderTypes); shaderTypeNdx++)
					{
						ShaderType	shaderType		= s_shaderTypes[shaderTypeNdx];
						const char* shaderTypeName	= getShaderTypeName(shaderType);
						string		name			= string(getDataTypeName(varType)) + "_" + writeAccessName + "_write_" + readAccessName + "_read_" + shaderTypeName;
						string		desc			= string("Vector subscript access with ") + writeAccessName + " write and " + readAccessName + " read in " + shaderTypeName + " shader.";
						bool		isVertexCase	= ((ShaderType)shaderType == SHADERTYPE_VERTEX);
						vecGroup->addChild(createVectorSubscriptCase(m_context, name.c_str(), desc.c_str(), isVertexCase, varType, (VectorAccessType)writeAccess, (VectorAccessType)readAccess));
					}
				}
			}
		}
	}

	// Matrix indexing with subscripts.
	{
		TestCaseGroup* matGroup = new TestCaseGroup(m_context, "matrix_subscript", "Matrix subscript indexing.");
		addChild(matGroup);

		static const DataType s_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
		};

		for (int typeNdx = 0; typeNdx < DE_LENGTH_OF_ARRAY(s_matrixTypes); typeNdx++)
		{
			DataType varType = s_matrixTypes[typeNdx];
			for (int writeAccess = 0; writeAccess < INDEXACCESS_LAST; writeAccess++)
			{
				for (int readAccess = 0; readAccess < INDEXACCESS_LAST; readAccess++)
				{
					const char* writeAccessName = getIndexAccessTypeName((IndexAccessType)writeAccess);
					const char* readAccessName = getIndexAccessTypeName((IndexAccessType)readAccess);

					for (int shaderTypeNdx = 0; shaderTypeNdx < DE_LENGTH_OF_ARRAY(s_shaderTypes); shaderTypeNdx++)
					{
						ShaderType	shaderType		= s_shaderTypes[shaderTypeNdx];
						const char* shaderTypeName	= getShaderTypeName(shaderType);
						string		name			= string(getDataTypeName(varType)) + "_" + writeAccessName + "_write_" + readAccessName + "_read_" + shaderTypeName;
						string		desc			= string("Vector subscript access with ") + writeAccessName + " write and " + readAccessName + " read in " + shaderTypeName + " shader.";
						bool		isVertexCase	= ((ShaderType)shaderType == SHADERTYPE_VERTEX);
						matGroup->addChild(createMatrixSubscriptCase(m_context, name.c_str(), desc.c_str(), isVertexCase, varType, (IndexAccessType)writeAccess, (IndexAccessType)readAccess));
					}
				}
			}
		}
	}
}

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