<commit_before>/***************************************************************************
*   Copyright (C) 2003 by                                                 *
*   Cyril Bosselut (bosselut@b1project.com)                               *
*   Jason Kivlighn (mizunoami44@users.sourceforge.net)                    *
*                                                                         *
*   This program is free software; you can redistribute it and/or modify  *
*   it under the terms of the GNU General Public License as published by  *
*   the Free Software Foundation; either version 2 of the License, or     *
*   (at your option) any later version.                                   *
***************************************************************************/

#include "kreexport.h"

#include <qfile.h>
#include <qstylesheet.h>
#include <qbuffer.h>

#include <kdebug.h>
#include <klocale.h>
#include <kmdcodec.h>
#include <ktar.h>
#include <ktempfile.h>
#include <kglobal.h>
#include <kstandarddirs.h>

KreExporter::KreExporter( CategoryTree *_categories, const QString& filename, const QString &format ) :
		BaseExporter( filename, format ), categories( _categories )
{}

KreExporter::~KreExporter()
{
	delete categories;
}

void KreExporter::saveToFile( const RecipeList& recipes )
{
	if ( format == "kreml" ) {
		if ( file->open( IO_WriteOnly ) ) {
			QTextStream stream( file );
			stream << createContent( recipes );
			file->close();
		}
	}
	else {
		// create a temporary .kre file
		QString kreml = createContent( recipes );
		int size = kreml.length();
		// compress and save file
		KTar* kre = new KTar( file->name(), "application/x-gzip" );
		kre->open( IO_WriteOnly );
		kre->writeFile( filename + ".kreml", getenv( "LOGNAME" ), "", size, kreml.latin1() );
		kre->close();
		delete kre;
	}
}

/*!
    \fn KreManager::createContent()
 * return a QString containing XML encoded recipe
 */
//TODO: use QDOM (see recipemlexporter.cpp)?
QString KreExporter::createContent( const RecipeList& recipes )
{
	QString xml;

	xml += "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
	xml += "<krecipes version=\"" + krecipes_version() + "\" lang=\"" + ( KGlobal::locale() ) ->country() + "\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"krecipes.xsd\">\n";

	createCategoryStructure( xml, recipes );

	RecipeList::const_iterator recipe_it;
	for ( recipe_it = recipes.begin(); recipe_it != recipes.end(); ++recipe_it ) {

		xml += "<krecipes-recipe>\n";
		xml += "<krecipes-description>\n";
		xml += "<title>" + QStyleSheet::escape( ( *recipe_it ).title.utf8() ) + "</title>\n";

		for ( ElementList::const_iterator author_it = ( *recipe_it ).authorList.begin(); author_it != ( *recipe_it ).authorList.end(); ++author_it )
			xml += "<author>" + QStyleSheet::escape( ( *author_it ).name.utf8() ) + "</author>\n";

		xml += "<pictures>\n";
		if ( !( *recipe_it ).photo.isNull() ) {
			xml += "<pic format=\"JPEG\" id=\"1\"><![CDATA["; //fixed id until we implement multiple photos ability
			QByteArray data;
			QBuffer buffer( data );
			buffer.open( IO_WriteOnly );
			( *recipe_it ).photo.save( &buffer, "JPEG" );

			xml += KCodecs::base64Encode( data, true );

			xml += "]]></pic>\n";
		}
		xml += "</pictures>\n";
		xml += "<category>\n";

		for ( ElementList::const_iterator cat_it = ( *recipe_it ).categoryList.begin(); cat_it != ( *recipe_it ).categoryList.end(); ++cat_it )
			xml += "<cat>" + QStyleSheet::escape( ( *cat_it ).name.utf8() ) + "</cat>\n";

		xml += "</category>\n";
		xml += "<serving>";
		xml += QString::number( ( *recipe_it ).persons );
		xml += "</serving>\n";
		xml += "<preparation-time>";
		xml += ( *recipe_it ).prepTime.toString( "hh:mm" );
		xml += "</preparation-time>\n";
		xml += "</krecipes-description>\n";
		xml += "<krecipes-ingredients>\n";

		IngredientList list_copy = ( *recipe_it ).ingList;
		for ( IngredientList group_list = list_copy.firstGroup(); group_list.count() != 0; group_list = list_copy.nextGroup() ) {
			QString group = group_list[ 0 ].group; //just use the first's name... they're all the same
			if ( !group.isEmpty() )
				xml += "<ingredient-group name=\"" + group + "\">\n";

			for ( IngredientList::const_iterator ing_it = group_list.begin(); ing_it != group_list.end(); ++ing_it ) {
				xml += "<ingredient>\n";
				xml += "<name>" + QStyleSheet::escape( ( *ing_it ).name.utf8() ) + "</name>\n";
				xml += "<amount>";
				xml += QString::number( ( *ing_it ).amount );
				xml += "</amount>\n";
				QString unit_str = ( ( *ing_it ).amount > 1 ) ? ( *ing_it ).units.plural : ( *ing_it ).units.name;
				xml += "<unit>" + QStyleSheet::escape( unit_str.utf8() ) + "</unit>\n";
				if ( !( *ing_it ).prepMethod.isEmpty() )
					xml += "<prep>" + QStyleSheet::escape( ( *ing_it ).prepMethod.utf8() ) + "</prep>\n";
				xml += "</ingredient>\n";
			}

			if ( !group.isEmpty() )
				xml += "</ingredient-group>\n";
		}

		/// @todo add ingredient properties

		xml += "</krecipes-ingredients>\n";
		xml += "<krecipes-instructions>\n";
		xml += QStyleSheet::escape( ( *recipe_it ).instructions.utf8() );
		xml += "</krecipes-instructions>\n";
		xml += "</krecipes-recipe>\n";

		if ( progressBarCancelled() )
			return QString::null;
		advanceProgressBar();
	}

	xml += "</krecipes>\n";

	return xml;
}

void KreExporter::createCategoryStructure( QString &xml, const RecipeList &recipes )
{
	QValueList<int> categoriesUsed;
	for ( RecipeList::const_iterator recipe_it = recipes.begin(); recipe_it != recipes.end(); ++recipe_it ) {
		for ( ElementList::const_iterator cat_it = ( *recipe_it ).categoryList.begin(); cat_it != ( *recipe_it ).categoryList.end(); ++cat_it ) {
			if ( categoriesUsed.find( ( *cat_it ).id ) == categoriesUsed.end() )
				categoriesUsed << ( *cat_it ).id;
		}
	}

	//only keep the relevant category structure
	removeIfUnused( categoriesUsed, categories );

	xml += "<krecipes-category-structure>\n";
	writeCategoryStructure( xml, categories );
	xml += "</krecipes-category-structure>\n";
}

bool KreExporter::removeIfUnused( const QValueList<int> &cat_ids, CategoryTree *parent, bool parent_should_show )
{
	for ( CategoryTree * it = parent->firstChild(); it; it = it->nextSibling() ) {
		if ( cat_ids.find( it->category.id ) != cat_ids.end() ) {
			parent_should_show = true;
			removeIfUnused( cat_ids, it, true ); //still recurse, but doesn't affect 'parent'
		}
		else {
			bool result = removeIfUnused( cat_ids, it );
			if ( parent_should_show == false )
				parent_should_show = result;
		}
	}

	if ( !parent_should_show ) {
		delete parent;
	}

	return parent_should_show;
}

void KreExporter::writeCategoryStructure( QString &xml, const CategoryTree *categoryTree )
{
	if ( categoryTree->category.id != -1 )
		xml += "<category name=\"" + QStyleSheet::escape( categoryTree->category.name.utf8() ) + "\">\n";

	const CategoryTreeChildren *children = categoryTree->children();
	for ( CategoryTreeChildren::const_iterator child_it = children->begin(); child_it != children->end(); ++child_it ) {
		writeCategoryStructure( xml, *child_it );
	}

	if ( categoryTree->category.id != -1 )
		xml += "</category>\n";

}

<commit_msg>Store the language in the Krecipes file format rather than the country<commit_after>/***************************************************************************
*   Copyright (C) 2003 by                                                 *
*   Cyril Bosselut (bosselut@b1project.com)                               *
*   Jason Kivlighn (mizunoami44@users.sourceforge.net)                    *
*                                                                         *
*   This program is free software; you can redistribute it and/or modify  *
*   it under the terms of the GNU General Public License as published by  *
*   the Free Software Foundation; either version 2 of the License, or     *
*   (at your option) any later version.                                   *
***************************************************************************/

#include "kreexport.h"

#include <qfile.h>
#include <qstylesheet.h>
#include <qbuffer.h>

#include <kdebug.h>
#include <klocale.h>
#include <kmdcodec.h>
#include <ktar.h>
#include <ktempfile.h>
#include <kglobal.h>
#include <kstandarddirs.h>

KreExporter::KreExporter( CategoryTree *_categories, const QString& filename, const QString &format ) :
		BaseExporter( filename, format ), categories( _categories )
{}

KreExporter::~KreExporter()
{
	delete categories;
}

void KreExporter::saveToFile( const RecipeList& recipes )
{
	if ( format == "kreml" ) {
		if ( file->open( IO_WriteOnly ) ) {
			QTextStream stream( file );
			stream << createContent( recipes );
			file->close();
		}
	}
	else {
		// create a temporary .kre file
		QString kreml = createContent( recipes );
		int size = kreml.length();
		// compress and save file
		KTar* kre = new KTar( file->name(), "application/x-gzip" );
		kre->open( IO_WriteOnly );
		kre->writeFile( filename + ".kreml", getenv( "LOGNAME" ), "", size, kreml.latin1() );
		kre->close();
		delete kre;
	}
}

/*!
    \fn KreManager::createContent()
 * return a QString containing XML encoded recipe
 */
//TODO: use QDOM (see recipemlexporter.cpp)?
QString KreExporter::createContent( const RecipeList& recipes )
{
	QString xml;

	xml += "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n";
	xml += "<krecipes version=\"" + krecipes_version() + "\" lang=\"" + ( KGlobal::locale() )->language() + "\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:noNamespaceSchemaLocation=\"krecipes.xsd\">\n";

	createCategoryStructure( xml, recipes );

	RecipeList::const_iterator recipe_it;
	for ( recipe_it = recipes.begin(); recipe_it != recipes.end(); ++recipe_it ) {

		xml += "<krecipes-recipe>\n";
		xml += "<krecipes-description>\n";
		xml += "<title>" + QStyleSheet::escape( ( *recipe_it ).title.utf8() ) + "</title>\n";

		for ( ElementList::const_iterator author_it = ( *recipe_it ).authorList.begin(); author_it != ( *recipe_it ).authorList.end(); ++author_it )
			xml += "<author>" + QStyleSheet::escape( ( *author_it ).name.utf8() ) + "</author>\n";

		xml += "<pictures>\n";
		if ( !( *recipe_it ).photo.isNull() ) {
			xml += "<pic format=\"JPEG\" id=\"1\"><![CDATA["; //fixed id until we implement multiple photos ability
			QByteArray data;
			QBuffer buffer( data );
			buffer.open( IO_WriteOnly );
			( *recipe_it ).photo.save( &buffer, "JPEG" );

			xml += KCodecs::base64Encode( data, true );

			xml += "]]></pic>\n";
		}
		xml += "</pictures>\n";
		xml += "<category>\n";

		for ( ElementList::const_iterator cat_it = ( *recipe_it ).categoryList.begin(); cat_it != ( *recipe_it ).categoryList.end(); ++cat_it )
			xml += "<cat>" + QStyleSheet::escape( ( *cat_it ).name.utf8() ) + "</cat>\n";

		xml += "</category>\n";
		xml += "<serving>";
		xml += QString::number( ( *recipe_it ).persons );
		xml += "</serving>\n";
		xml += "<preparation-time>";
		xml += ( *recipe_it ).prepTime.toString( "hh:mm" );
		xml += "</preparation-time>\n";
		xml += "</krecipes-description>\n";
		xml += "<krecipes-ingredients>\n";

		IngredientList list_copy = ( *recipe_it ).ingList;
		for ( IngredientList group_list = list_copy.firstGroup(); group_list.count() != 0; group_list = list_copy.nextGroup() ) {
			QString group = group_list[ 0 ].group; //just use the first's name... they're all the same
			if ( !group.isEmpty() )
				xml += "<ingredient-group name=\"" + group + "\">\n";

			for ( IngredientList::const_iterator ing_it = group_list.begin(); ing_it != group_list.end(); ++ing_it ) {
				xml += "<ingredient>\n";
				xml += "<name>" + QStyleSheet::escape( ( *ing_it ).name.utf8() ) + "</name>\n";
				xml += "<amount>";
				xml += QString::number( ( *ing_it ).amount );
				xml += "</amount>\n";
				QString unit_str = ( ( *ing_it ).amount > 1 ) ? ( *ing_it ).units.plural : ( *ing_it ).units.name;
				xml += "<unit>" + QStyleSheet::escape( unit_str.utf8() ) + "</unit>\n";
				if ( !( *ing_it ).prepMethod.isEmpty() )
					xml += "<prep>" + QStyleSheet::escape( ( *ing_it ).prepMethod.utf8() ) + "</prep>\n";
				xml += "</ingredient>\n";
			}

			if ( !group.isEmpty() )
				xml += "</ingredient-group>\n";
		}

		/// @todo add ingredient properties

		xml += "</krecipes-ingredients>\n";
		xml += "<krecipes-instructions>\n";
		xml += QStyleSheet::escape( ( *recipe_it ).instructions.utf8() );
		xml += "</krecipes-instructions>\n";
		xml += "</krecipes-recipe>\n";

		if ( progressBarCancelled() )
			return QString::null;
		advanceProgressBar();
	}

	xml += "</krecipes>\n";

	return xml;
}

void KreExporter::createCategoryStructure( QString &xml, const RecipeList &recipes )
{
	QValueList<int> categoriesUsed;
	for ( RecipeList::const_iterator recipe_it = recipes.begin(); recipe_it != recipes.end(); ++recipe_it ) {
		for ( ElementList::const_iterator cat_it = ( *recipe_it ).categoryList.begin(); cat_it != ( *recipe_it ).categoryList.end(); ++cat_it ) {
			if ( categoriesUsed.find( ( *cat_it ).id ) == categoriesUsed.end() )
				categoriesUsed << ( *cat_it ).id;
		}
	}

	//only keep the relevant category structure
	removeIfUnused( categoriesUsed, categories );

	xml += "<krecipes-category-structure>\n";
	writeCategoryStructure( xml, categories );
	xml += "</krecipes-category-structure>\n";
}

bool KreExporter::removeIfUnused( const QValueList<int> &cat_ids, CategoryTree *parent, bool parent_should_show )
{
	for ( CategoryTree * it = parent->firstChild(); it; it = it->nextSibling() ) {
		if ( cat_ids.find( it->category.id ) != cat_ids.end() ) {
			parent_should_show = true;
			removeIfUnused( cat_ids, it, true ); //still recurse, but doesn't affect 'parent'
		}
		else {
			bool result = removeIfUnused( cat_ids, it );
			if ( parent_should_show == false )
				parent_should_show = result;
		}
	}

	if ( !parent_should_show ) {
		delete parent;
	}

	return parent_should_show;
}

void KreExporter::writeCategoryStructure( QString &xml, const CategoryTree *categoryTree )
{
	if ( categoryTree->category.id != -1 )
		xml += "<category name=\"" + QStyleSheet::escape( categoryTree->category.name.utf8() ) + "\">\n";

	const CategoryTreeChildren *children = categoryTree->children();
	for ( CategoryTreeChildren::const_iterator child_it = children->begin(); child_it != children->end(); ++child_it ) {
		writeCategoryStructure( xml, *child_it );
	}

	if ( categoryTree->category.id != -1 )
		xml += "</category>\n";

}

<|endoftext|>


<commit_before>/*
Nazara Engine - Assimp Plugin

Copyright (C) 2015 Jérôme "Lynix" Leclercq (lynix680@gmail.com)

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include <CustomStream.hpp>
#include <Nazara/Core/String.hpp>
#include <Nazara/Utility/Mesh.hpp>
#include <Nazara/Utility/IndexIterator.hpp>
#include <Nazara/Utility/IndexMapper.hpp>
#include <Nazara/Utility/StaticMesh.hpp>
#include <assimp/cfileio.h>
#include <assimp/cimport.h>
#include <assimp/config.h>
#include <assimp/mesh.h>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
#include <set>

using namespace Nz;

void ProcessJoints(aiNode* node, Skeleton* skeleton, const std::set<Nz::String>& joints)
{
	Nz::String jointName(node->mName.data, node->mName.length);
	if (joints.count(jointName))
	{
		Joint* joint = skeleton->GetJoint(jointName);
		if (joint)
		{
			if (node->mParent)
				joint->SetParent(skeleton->GetJoint(node->mParent->mName.C_Str()));

			Matrix4f transformMatrix(node->mTransformation.a1, node->mTransformation.a2, node->mTransformation.a3, node->mTransformation.a4,
			                         node->mTransformation.b1, node->mTransformation.b2, node->mTransformation.b3, node->mTransformation.b4,
			                         node->mTransformation.c1, node->mTransformation.c2, node->mTransformation.c3, node->mTransformation.c4,
			                         node->mTransformation.d1, node->mTransformation.d2, node->mTransformation.d3, node->mTransformation.d4);

			transformMatrix.InverseAffine();

			joint->SetInverseBindMatrix(transformMatrix);
		}
	}

	for (unsigned int i = 0; i < node->mNumChildren; ++i)
		ProcessJoints(node->mChildren[i], skeleton, joints);
}

bool IsSupported(const String& extension)
{
	String dotExt = '.' + extension;
	return (aiIsExtensionSupported(dotExt.GetConstBuffer()) == AI_TRUE);
}

Ternary Check(Stream& stream, const MeshParams& parameters)
{
    bool skip;
    if (parameters.custom.GetBooleanParameter("SkipAssimpLoader", &skip) && skip)
        return Ternary_False;

	return Ternary_Unknown;
}

bool Load(Mesh* mesh, Stream& stream, const MeshParams& parameters)
{
	Nz::String streamPath = stream.GetPath();

	FileIOUserdata userdata;
	userdata.originalFilePath = (!streamPath.IsEmpty()) ? streamPath.GetConstBuffer() : StreamPath;
	userdata.originalStream = &stream;

	aiFileIO fileIO;
	fileIO.CloseProc = StreamCloser;
	fileIO.OpenProc = StreamOpener;
	fileIO.UserData = reinterpret_cast<char*>(&userdata);

	unsigned int postProcess = aiProcess_CalcTangentSpace     | aiProcess_JoinIdenticalVertices
	                         | aiProcess_MakeLeftHanded       | aiProcess_Triangulate
	                         | aiProcess_RemoveComponent      | aiProcess_GenSmoothNormals
	                         | aiProcess_SplitLargeMeshes     | aiProcess_LimitBoneWeights
	                         | aiProcess_ImproveCacheLocality | aiProcess_RemoveRedundantMaterials
	                         | aiProcess_FixInfacingNormals   | aiProcess_SortByPType
	                         | aiProcess_FindInvalidData      | aiProcess_GenUVCoords
	                         | aiProcess_TransformUVCoords    | aiProcess_OptimizeMeshes
	                         | aiProcess_OptimizeGraph        | aiProcess_FlipWindingOrder
	                         | aiProcess_Debone;

	if (!parameters.flipUVs)
		postProcess |= aiProcess_FlipUVs;

	if (parameters.optimizeIndexBuffers)
		postProcess |= aiProcess_ImproveCacheLocality;

	float smoothingAngle = 80.f;
	parameters.custom.GetFloatParameter("AssimpLoader_SmoothingAngle", &smoothingAngle);

	int triangleLimit = 1'000'000;
	parameters.custom.GetIntegerParameter("AssimpLoader_TriangleLimit", &triangleLimit);

	int vertexLimit   = 1'000'000;
	parameters.custom.GetIntegerParameter("AssimpLoader_VertexLimit", &vertexLimit);

	aiPropertyStore* properties = aiCreatePropertyStore();
	aiSetImportPropertyFloat(properties,   AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE, smoothingAngle);
	aiSetImportPropertyInteger(properties, AI_CONFIG_PP_LBW_MAX_WEIGHTS,         4);
	aiSetImportPropertyInteger(properties, AI_CONFIG_PP_SBP_REMOVE,              ~aiPrimitiveType_TRIANGLE); //< We only want triangles
	aiSetImportPropertyInteger(properties, AI_CONFIG_PP_SLM_TRIANGLE_LIMIT,      triangleLimit);
	aiSetImportPropertyInteger(properties, AI_CONFIG_PP_SLM_VERTEX_LIMIT,        vertexLimit);
	aiSetImportPropertyInteger(properties, AI_CONFIG_PP_RVC_FLAGS,               aiComponent_COLORS);

	const aiScene* scene = aiImportFileExWithProperties(userdata.originalFilePath, postProcess, &fileIO, properties);
	aiReleasePropertyStore(properties);

	std::set<Nz::String> joints;

	bool animatedMesh = false;
	if (parameters.animated)
	{
		for (unsigned int i = 0; i < scene->mNumMeshes; ++i)
		{
			aiMesh* mesh = scene->mMeshes[i];
			if (mesh->HasBones()) // Inline functions can be safely called
			{
				animatedMesh = true;
				for (unsigned int j = 0; j < mesh->mNumBones; ++j)
					joints.insert(mesh->mBones[j]->mName.C_Str());
			}
		}
	}

	if (animatedMesh)
	{
		mesh->CreateSkeletal(joints.size());

		Skeleton* skeleton = mesh->GetSkeleton();

		// First, assign names
		unsigned int jointIndex = 0;
		for (const Nz::String& jointName : joints)
			skeleton->GetJoint(jointIndex++)->SetName(jointName);

		ProcessJoints(scene->mRootNode, skeleton, joints);

		return false;
	}
	else
	{
		mesh->CreateStatic();

		for (unsigned int i = 0; i < scene->mNumMeshes; ++i)
		{
			aiMesh* iMesh = scene->mMeshes[i];
			if (!iMesh->HasBones()) // Don't process skeletal meshs
			{
				unsigned int indexCount = iMesh->mNumFaces * 3;
				unsigned int vertexCount = iMesh->mNumVertices;

				// Index buffer
				bool largeIndices = (vertexCount > std::numeric_limits<UInt16>::max());

				IndexBufferRef indexBuffer = IndexBuffer::New(largeIndices, indexCount, parameters.storage);

				IndexMapper indexMapper(indexBuffer, BufferAccess_DiscardAndWrite);
				IndexIterator index = indexMapper.begin();

				for (unsigned int j = 0; j < iMesh->mNumFaces; ++j)
				{
					aiFace& face = iMesh->mFaces[j];
					if (face.mNumIndices != 3)
						NazaraWarning("Assimp plugin: This face is not a triangle!");

					*index++ = face.mIndices[0];
					*index++ = face.mIndices[1];
					*index++ = face.mIndices[2];
				}
				indexMapper.Unmap();

				// Vertex buffer
				VertexBufferRef vertexBuffer = VertexBuffer::New(VertexDeclaration::Get(VertexLayout_XYZ_Normal_UV_Tangent), vertexCount, parameters.storage);
				BufferMapper<VertexBuffer> vertexMapper(vertexBuffer, BufferAccess_WriteOnly);

				MeshVertex* vertex = static_cast<MeshVertex*>(vertexMapper.GetPointer());
				for (unsigned int j = 0; j < vertexCount; ++j)
				{
					aiVector3D position = iMesh->mVertices[j];
					aiVector3D normal = iMesh->mNormals[j];
					aiVector3D tangent = iMesh->mTangents[j];
					aiVector3D uv = iMesh->mTextureCoords[0][j];

					vertex->position = parameters.scale * Vector3f(position.x, position.y, position.z);
					vertex->normal.Set(normal.x, normal.y, normal.z);
					vertex->tangent.Set(tangent.x, tangent.y, tangent.z);
					vertex->uv.Set(uv.x, uv.y);
					vertex++;
				}

				vertexMapper.Unmap();

				// Submesh
				StaticMeshRef subMesh = StaticMesh::New(mesh);
				subMesh->Create(vertexBuffer);

				subMesh->SetIndexBuffer(indexBuffer);
				subMesh->GenerateAABB();
				subMesh->SetMaterialIndex(iMesh->mMaterialIndex);

				mesh->AddSubMesh(subMesh);
			}
		}

		if (parameters.center)
			mesh->Recenter();
	}

	aiReleaseImport(scene);

	return true;
}

extern "C"
{
	NAZARA_EXPORT int PluginLoad()
	{
		Nz::MeshLoader::RegisterLoader(IsSupported, Check, Load);
		return 1;
	}

	NAZARA_EXPORT void PluginUnload()
	{
		Nz::MeshLoader::UnregisterLoader(IsSupported, Check, Load);
	}
}
<commit_msg>Plugins/Assimp: Add material loading<commit_after>/*
Nazara Engine - Assimp Plugin

Copyright (C) 2015 Jérôme "Lynix" Leclercq (lynix680@gmail.com)

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

#include <CustomStream.hpp>
#include <Nazara/Core/String.hpp>
#include <Nazara/Utility/Mesh.hpp>
#include <Nazara/Utility/IndexIterator.hpp>
#include <Nazara/Utility/IndexMapper.hpp>
#include <Nazara/Utility/MaterialData.hpp>
#include <Nazara/Utility/StaticMesh.hpp>
#include <assimp/cfileio.h>
#include <assimp/cimport.h>
#include <assimp/config.h>
#include <assimp/mesh.h>
#include <assimp/postprocess.h>
#include <assimp/scene.h>
#include <set>

using namespace Nz;

void ProcessJoints(aiNode* node, Skeleton* skeleton, const std::set<Nz::String>& joints)
{
	Nz::String jointName(node->mName.data, node->mName.length);
	if (joints.count(jointName))
	{
		Joint* joint = skeleton->GetJoint(jointName);
		if (joint)
		{
			if (node->mParent)
				joint->SetParent(skeleton->GetJoint(node->mParent->mName.C_Str()));

			Matrix4f transformMatrix(node->mTransformation.a1, node->mTransformation.a2, node->mTransformation.a3, node->mTransformation.a4,
			                         node->mTransformation.b1, node->mTransformation.b2, node->mTransformation.b3, node->mTransformation.b4,
			                         node->mTransformation.c1, node->mTransformation.c2, node->mTransformation.c3, node->mTransformation.c4,
			                         node->mTransformation.d1, node->mTransformation.d2, node->mTransformation.d3, node->mTransformation.d4);

			transformMatrix.InverseAffine();

			joint->SetInverseBindMatrix(transformMatrix);
		}
	}

	for (unsigned int i = 0; i < node->mNumChildren; ++i)
		ProcessJoints(node->mChildren[i], skeleton, joints);
}

bool IsSupported(const String& extension)
{
	String dotExt = '.' + extension;
	return (aiIsExtensionSupported(dotExt.GetConstBuffer()) == AI_TRUE);
}

Ternary Check(Stream& stream, const MeshParams& parameters)
{
    bool skip;
    if (parameters.custom.GetBooleanParameter("SkipAssimpLoader", &skip) && skip)
        return Ternary_False;

	return Ternary_Unknown;
}

bool Load(Mesh* mesh, Stream& stream, const MeshParams& parameters)
{
	Nz::String streamPath = stream.GetPath();

	FileIOUserdata userdata;
	userdata.originalFilePath = (!streamPath.IsEmpty()) ? streamPath.GetConstBuffer() : StreamPath;
	userdata.originalStream = &stream;

	aiFileIO fileIO;
	fileIO.CloseProc = StreamCloser;
	fileIO.OpenProc = StreamOpener;
	fileIO.UserData = reinterpret_cast<char*>(&userdata);

	unsigned int postProcess = aiProcess_CalcTangentSpace     | aiProcess_JoinIdenticalVertices
	                         | aiProcess_MakeLeftHanded       | aiProcess_Triangulate
	                         | aiProcess_RemoveComponent      | aiProcess_GenSmoothNormals
	                         | aiProcess_SplitLargeMeshes     | aiProcess_LimitBoneWeights
	                         | aiProcess_ImproveCacheLocality | aiProcess_RemoveRedundantMaterials
	                         | aiProcess_FixInfacingNormals   | aiProcess_SortByPType
	                         | aiProcess_FindInvalidData      | aiProcess_GenUVCoords
	                         | aiProcess_TransformUVCoords    | aiProcess_OptimizeMeshes
	                         | aiProcess_OptimizeGraph        | aiProcess_FlipWindingOrder
	                         | aiProcess_Debone;

	if (!parameters.flipUVs)
		postProcess |= aiProcess_FlipUVs;

	if (parameters.optimizeIndexBuffers)
		postProcess |= aiProcess_ImproveCacheLocality;

	float smoothingAngle = 80.f;
	parameters.custom.GetFloatParameter("AssimpLoader_SmoothingAngle", &smoothingAngle);

	int triangleLimit = 1'000'000;
	parameters.custom.GetIntegerParameter("AssimpLoader_TriangleLimit", &triangleLimit);

	int vertexLimit   = 1'000'000;
	parameters.custom.GetIntegerParameter("AssimpLoader_VertexLimit", &vertexLimit);

	aiPropertyStore* properties = aiCreatePropertyStore();
	aiSetImportPropertyFloat(properties,   AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE, smoothingAngle);
	aiSetImportPropertyInteger(properties, AI_CONFIG_PP_LBW_MAX_WEIGHTS,         4);
	aiSetImportPropertyInteger(properties, AI_CONFIG_PP_SBP_REMOVE,              ~aiPrimitiveType_TRIANGLE); //< We only want triangles
	aiSetImportPropertyInteger(properties, AI_CONFIG_PP_SLM_TRIANGLE_LIMIT,      triangleLimit);
	aiSetImportPropertyInteger(properties, AI_CONFIG_PP_SLM_VERTEX_LIMIT,        vertexLimit);
	aiSetImportPropertyInteger(properties, AI_CONFIG_PP_RVC_FLAGS,               aiComponent_COLORS);

	const aiScene* scene = aiImportFileExWithProperties(userdata.originalFilePath, postProcess, &fileIO, properties);
	aiReleasePropertyStore(properties);

	std::set<Nz::String> joints;

	bool animatedMesh = false;
	if (parameters.animated)
	{
		for (unsigned int i = 0; i < scene->mNumMeshes; ++i)
		{
			aiMesh* mesh = scene->mMeshes[i];
			if (mesh->HasBones()) // Inline functions can be safely called
			{
				animatedMesh = true;
				for (unsigned int j = 0; j < mesh->mNumBones; ++j)
					joints.insert(mesh->mBones[j]->mName.C_Str());
			}
		}
	}

	if (animatedMesh)
	{
		mesh->CreateSkeletal(joints.size());

		Skeleton* skeleton = mesh->GetSkeleton();

		// First, assign names
		unsigned int jointIndex = 0;
		for (const Nz::String& jointName : joints)
			skeleton->GetJoint(jointIndex++)->SetName(jointName);

		ProcessJoints(scene->mRootNode, skeleton, joints);

		return false;
	}
	else
	{
		mesh->CreateStatic();

		// aiMaterial index in scene => Material index and data in Mesh
		std::unordered_map<unsigned int, std::pair<unsigned int, ParameterList>> materials;

		for (unsigned int i = 0; i < scene->mNumMeshes; ++i)
		{
			aiMesh* iMesh = scene->mMeshes[i];
			if (!iMesh->HasBones()) // Don't process skeletal meshs
			{
				unsigned int indexCount = iMesh->mNumFaces * 3;
				unsigned int vertexCount = iMesh->mNumVertices;

				// Index buffer
				bool largeIndices = (vertexCount > std::numeric_limits<UInt16>::max());

				IndexBufferRef indexBuffer = IndexBuffer::New(largeIndices, indexCount, parameters.storage);

				IndexMapper indexMapper(indexBuffer, BufferAccess_DiscardAndWrite);
				IndexIterator index = indexMapper.begin();

				for (unsigned int j = 0; j < iMesh->mNumFaces; ++j)
				{
					aiFace& face = iMesh->mFaces[j];
					if (face.mNumIndices != 3)
						NazaraWarning("Assimp plugin: This face is not a triangle!");

					*index++ = face.mIndices[0];
					*index++ = face.mIndices[1];
					*index++ = face.mIndices[2];
				}
				indexMapper.Unmap();

				// Vertex buffer
				VertexBufferRef vertexBuffer = VertexBuffer::New(VertexDeclaration::Get(VertexLayout_XYZ_Normal_UV_Tangent), vertexCount, parameters.storage);
				BufferMapper<VertexBuffer> vertexMapper(vertexBuffer, BufferAccess_WriteOnly);

				MeshVertex* vertex = static_cast<MeshVertex*>(vertexMapper.GetPointer());
				for (unsigned int j = 0; j < vertexCount; ++j)
				{
					aiVector3D position = iMesh->mVertices[j];
					aiVector3D normal = iMesh->mNormals[j];
					aiVector3D tangent = iMesh->mTangents[j];
					aiVector3D uv = iMesh->mTextureCoords[0][j];

					vertex->position = parameters.scale * Vector3f(position.x, position.y, position.z);
					vertex->normal.Set(normal.x, normal.y, normal.z);
					vertex->tangent.Set(tangent.x, tangent.y, tangent.z);
					vertex->uv.Set(uv.x, uv.y);
					vertex++;
				}

				vertexMapper.Unmap();

				// Submesh
				StaticMeshRef subMesh = StaticMesh::New(mesh);
				subMesh->Create(vertexBuffer);

				subMesh->SetIndexBuffer(indexBuffer);
				subMesh->GenerateAABB();
				subMesh->SetMaterialIndex(iMesh->mMaterialIndex);

				auto matIt = materials.find(iMesh->mMaterialIndex);
				if (matIt == materials.end())
				{
					ParameterList matData;
					aiMaterial* aiMat = scene->mMaterials[iMesh->mMaterialIndex];

					auto ConvertColor = [&] (const char* aiKey, unsigned int aiType, unsigned int aiIndex, const char* colorKey)
					{
						aiColor4D color;
						if (aiGetMaterialColor(aiMat, aiKey, aiType, aiIndex, &color) == aiReturn_SUCCESS)
						{
							matData.SetParameter(MaterialData::CustomDefined);

							matData.SetParameter(colorKey, Color(static_cast<UInt8>(color.r * 255), static_cast<UInt8>(color.g * 255), static_cast<UInt8>(color.b * 255), static_cast<UInt8>(color.a * 255)));
						}
					};

					auto ConvertTexture = [&] (aiTextureType aiType, const char* textureKey, const char* wrapKey = nullptr)
					{
						aiString path;
						aiTextureMapMode mapMode;
						if (aiGetMaterialTexture(aiMat, aiType, 0, &path, nullptr, nullptr, nullptr, nullptr, &mapMode, nullptr) == aiReturn_SUCCESS)
						{
							matData.SetParameter(MaterialData::CustomDefined);
							matData.SetParameter(textureKey, stream.GetDirectory() + String(path.data, path.length));

							if (wrapKey)
							{
								SamplerWrap wrap = SamplerWrap_Default;
								switch (mapMode)
								{
									case aiTextureMapMode_Clamp:
									case aiTextureMapMode_Decal:
										wrap = SamplerWrap_Clamp;
										break;

									case aiTextureMapMode_Mirror:
										wrap = SamplerWrap_MirroredRepeat;
										break;

									case aiTextureMapMode_Wrap:
										wrap = SamplerWrap_Repeat;
										break;

									default:
										NazaraWarning("Assimp texture map mode 0x" + String::Number(mapMode, 16) + " not handled");
										break;
								}

								matData.SetParameter(wrapKey, static_cast<int>(wrap));
							}
						}
					};

					ConvertColor(AI_MATKEY_COLOR_AMBIENT, MaterialData::AmbientColor);
					ConvertColor(AI_MATKEY_COLOR_DIFFUSE, MaterialData::DiffuseColor);
					ConvertColor(AI_MATKEY_COLOR_SPECULAR, MaterialData::SpecularColor);

					ConvertTexture(aiTextureType_DIFFUSE, MaterialData::DiffuseTexturePath, MaterialData::DiffuseWrap);
					ConvertTexture(aiTextureType_EMISSIVE, MaterialData::EmissiveTexturePath);
					ConvertTexture(aiTextureType_HEIGHT, MaterialData::HeightTexturePath);
					ConvertTexture(aiTextureType_NORMALS, MaterialData::NormalTexturePath);
					ConvertTexture(aiTextureType_OPACITY, MaterialData::AlphaTexturePath);
					ConvertTexture(aiTextureType_SPECULAR, MaterialData::SpecularTexturePath, MaterialData::SpecularWrap);

					int iValue;
					if (aiGetMaterialInteger(aiMat, AI_MATKEY_TWOSIDED, &iValue))
						matData.SetParameter(MaterialData::FaceCulling, !iValue);

					matIt = materials.insert(std::make_pair(iMesh->mMaterialIndex, std::make_pair(materials.size(), std::move(matData)))).first;
				}

				subMesh->SetMaterialIndex(matIt->first);

				mesh->AddSubMesh(subMesh);
			}

			mesh->SetMaterialCount(std::max<unsigned int>(materials.size(), 1));
			for (const auto& pair : materials)
				mesh->SetMaterialData(pair.second.first, pair.second.second);
		}

		if (parameters.center)
			mesh->Recenter();
	}

	aiReleaseImport(scene);

	return true;
}

extern "C"
{
	NAZARA_EXPORT int PluginLoad()
	{
		Nz::MeshLoader::RegisterLoader(IsSupported, Check, Load);
		return 1;
	}

	NAZARA_EXPORT void PluginUnload()
	{
		Nz::MeshLoader::UnregisterLoader(IsSupported, Check, Load);
	}
}
<|endoftext|>


<commit_before>// (C) 2014 Arek Olek

#pragma once

#include <algorithm>
#include <functional>
#include <utility>
#include <vector>

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/connected_components.hpp>
#include <boost/graph/kruskal_min_spanning_tree.hpp>
#include <boost/iterator/counting_iterator.hpp>
#include <boost/iterator/function_input_iterator.hpp>
#include <boost/function_output_iterator.hpp>

#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Triangulation_vertex_base_with_info_2.h>
#include <CGAL/Point_set_2.h>
#include <CGAL/squared_distance_2.h>

#include "range.hpp"

template<class Graph>
unsigned num_internal(Graph const & G) {
  unsigned internal = 0;
  for (auto v : range(vertices(G)))
    internal += out_degree(v, G) > 1;
  return internal;
}

template<class Graph>
unsigned upper_bound(Graph const & G) {
  return std::min(num_internal(G), (unsigned)num_vertices(G) - 2);
}

template<class Graph>
bool is_connected(Graph const & G) {
  std::vector<int> component(num_vertices(G));
  return connected_components(G, &component[0]) == 1;
}

template <class Input, class Output>
void copy_edges(const Input& in, Output& out) {
  for(auto e : range(edges(in)))
    add_edge(source(e, in), target(e, in), out);
}

template<class Graph, class Generator>
void add_spider(Graph& G, unsigned legs, Generator generator) {
  unsigned n = num_vertices(G);
  unsigned cutoff = n / legs;
  auto range_iterator = boost::make_counting_iterator<int>;
  std::vector<int> path(range_iterator(0), range_iterator(n));
  std::shuffle(path.begin(), path.end(), generator);
  for(unsigned i = 0; i < n-1; ++i)
    add_edge(path[i % cutoff == 0 ? 0 : i], path[i+1], G);
}

template<class Graph, class Generator>
void add_edges_uniform(Graph& G, double p, Generator generator, bool mst) {
  unsigned n = num_vertices(G);
  double connectedness = 20.0 / n;
  if(mst && p < connectedness) {
    typedef boost::property<boost::edge_weight_t, double> Weight;
    typedef boost::adjacency_list<boost::hash_setS, boost::vecS, boost::undirectedS, boost::no_property, Weight> WeightedGraph;
    typedef boost::graph_traits<WeightedGraph>::edge_descriptor WeightedEdge;
    std::uniform_real_distribution<> distribution(0, 1);
    auto trial = std::bind(distribution, generator);
    WeightedGraph g;
    for(unsigned i = 0; i < n; ++i) {
      for(unsigned j = i + 1; j < n; ++j) {
        auto w = trial();
        if(w < connectedness) add_edge(i, j, w, g);
        if(w < p) add_edge(i, j, G);
      }
    }
    std::vector<WeightedEdge> t;
    kruskal_minimum_spanning_tree(g, std::back_inserter(t));
    for(auto e : t) add_edge(source(e, g), target(e, g), G);
  } else {
    std::bernoulli_distribution distribution(p);
    auto trial = std::bind(distribution, generator);
    for(unsigned i = 0; i < n; ++i) {
      for(unsigned j = i + 1; j < n; ++j) {
        if(trial()) add_edge(i, j, G);
      }
    }
  }
}

class Geometric {
  CGAL::Exact_predicates_inexact_constructions_kernel           typedef Kernel;
  CGAL::Triangulation_vertex_base_with_info_2<unsigned, Kernel> typedef Vertex_struct;
  CGAL::Triangulation_data_structure_2<Vertex_struct>           typedef Triangulation_struct;
  CGAL::Point_set_2<Kernel, Triangulation_struct>               typedef Delaunay;
  Delaunay::Point                                               typedef Point;

  Delaunay D;

public:
  template<class Generator>
  Geometric(int n, Generator generator) {
    // Choose random points in square [0, 1) x [0, 1)
    std::uniform_real_distribution<> distribution(0, 1);
    auto r = std::bind(distribution, generator);
    // Generate index for every point
    int counter = 0;
    std::function<std::pair<Point, int>()> f =
      [&]() {return std::make_pair(Point(r(), r()), counter++); };
    // Initialize triangulation with n points
    D = Delaunay(boost::make_function_input_iterator(f, 0),
                 boost::make_function_input_iterator(f, n));
  }

  template<class Graph>
  void add_random_geometric(Graph& G, double d) {
    CGAL::Circle_2<Kernel>  typedef Circle;
    Delaunay::Vertex_handle typedef Vertex_handle;
    // Query each point for other points lying in the circle
    for(auto v : range(D.finite_vertices_begin(), D.finite_vertices_end()))
      D.range_search(Circle(v.point(), d*d), boost::make_function_output_iterator(
          [&](Vertex_handle u){
            if(v.info() < u->info())
              add_edge(v.info(), u->info(), G);
      }));
  }

  template<class Graph>
  void add_mst(Graph& G) {
    typedef boost::property<boost::edge_weight_t, Kernel::FT> Weight;
    typedef boost::adjacency_list<boost::hash_setS, boost::vecS, boost::undirectedS, boost::no_property, Weight> WeightedGraph;
    typedef boost::graph_traits<WeightedGraph>::edge_descriptor WeightedEdge;

    Delaunay::Finite_vertices_iterator vit;
    Delaunay::Vertex_circulator vc, done;

    WeightedGraph g;

    for(vit = D.finite_vertices_begin(); vit != D.finite_vertices_end(); ++vit) {
      unsigned s = vit->info();
      done = vc = vit->incident_vertices();
      if(vc != 0) do {
        if(D.is_infinite(vc)) continue;
        unsigned d = vc->info();
        add_edge(s, d, CGAL::squared_distance(vit->point(), vc->point()), g);
      } while(++vc != done);
    }

    std::vector<WeightedEdge> mst;
    kruskal_minimum_spanning_tree(g, std::back_inserter(mst));
    for(auto e : mst) add_edge(source(e, g), target(e, g), G);
  }
};
<commit_msg>do not insert multiple edges<commit_after>// (C) 2014 Arek Olek

#pragma once

#include <algorithm>
#include <functional>
#include <utility>
#include <vector>

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/connected_components.hpp>
#include <boost/graph/kruskal_min_spanning_tree.hpp>
#include <boost/iterator/counting_iterator.hpp>
#include <boost/iterator/function_input_iterator.hpp>
#include <boost/function_output_iterator.hpp>

#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Triangulation_vertex_base_with_info_2.h>
#include <CGAL/Point_set_2.h>
#include <CGAL/squared_distance_2.h>

#include "range.hpp"

template<class Graph>
unsigned num_internal(Graph const & G) {
  unsigned internal = 0;
  for (auto v : range(vertices(G)))
    internal += out_degree(v, G) > 1;
  return internal;
}

template<class Graph>
unsigned upper_bound(Graph const & G) {
  return std::min(num_internal(G), (unsigned)num_vertices(G) - 2);
}

template<class Graph>
bool is_connected(Graph const & G) {
  std::vector<int> component(num_vertices(G));
  return connected_components(G, &component[0]) == 1;
}

template<class G>
void add_edge_no_dup(
    typename boost::graph_traits<G>::vertex_descriptor v,
    typename boost::graph_traits<G>::vertex_descriptor u,
    G& g) {
  if(!edge(v, u, g).second) add_edge(v, u, g);
}

template <class Input, class Output>
void copy_edges(const Input& in, Output& out) {
  for(auto e : range(edges(in)))
    add_edge(source(e, in), target(e, in), out);
}

template<class Graph, class Generator>
void add_spider(Graph& G, unsigned legs, Generator generator) {
  unsigned n = num_vertices(G);
  unsigned cutoff = n / legs;
  auto range_iterator = boost::make_counting_iterator<int>;
  std::vector<int> path(range_iterator(0), range_iterator(n));
  std::shuffle(path.begin(), path.end(), generator);
  for(unsigned i = 0; i < n-1; ++i)
    add_edge_no_dup(path[i % cutoff == 0 ? 0 : i], path[i+1], G);
}

template<class Graph, class Generator>
void add_edges_uniform(Graph& G, double p, Generator generator, bool mst) {
  unsigned n = num_vertices(G);
  double connectedness = 20.0 / n;
  if(mst && p < connectedness) {
    typedef boost::property<boost::edge_weight_t, double> Weight;
    typedef boost::adjacency_list<boost::hash_setS, boost::vecS, boost::undirectedS, boost::no_property, Weight> WeightedGraph;
    typedef boost::graph_traits<WeightedGraph>::edge_descriptor WeightedEdge;
    std::uniform_real_distribution<> distribution(0, 1);
    auto trial = std::bind(distribution, generator);
    WeightedGraph g;
    for(unsigned i = 0; i < n; ++i) {
      for(unsigned j = i + 1; j < n; ++j) {
        auto w = trial();
        if(w < connectedness) add_edge(i, j, w, g);
        if(w < p) add_edge_no_dup(i, j, G);
      }
    }
    std::vector<WeightedEdge> t;
    kruskal_minimum_spanning_tree(g, std::back_inserter(t));
    for(auto e : t) add_edge_no_dup(source(e, g), target(e, g), G);
  } else {
    std::bernoulli_distribution distribution(p);
    auto trial = std::bind(distribution, generator);
    for(unsigned i = 0; i < n; ++i) {
      for(unsigned j = i + 1; j < n; ++j) {
        if(trial()) add_edge_no_dup(i, j, G);
      }
    }
  }
}

class Geometric {
  CGAL::Exact_predicates_inexact_constructions_kernel           typedef Kernel;
  CGAL::Triangulation_vertex_base_with_info_2<unsigned, Kernel> typedef Vertex_struct;
  CGAL::Triangulation_data_structure_2<Vertex_struct>           typedef Triangulation_struct;
  CGAL::Point_set_2<Kernel, Triangulation_struct>               typedef Delaunay;
  Delaunay::Point                                               typedef Point;

  Delaunay D;

public:
  template<class Generator>
  Geometric(int n, Generator generator) {
    // Choose random points in square [0, 1) x [0, 1)
    std::uniform_real_distribution<> distribution(0, 1);
    auto r = std::bind(distribution, generator);
    // Generate index for every point
    int counter = 0;
    std::function<std::pair<Point, int>()> f =
      [&]() {return std::make_pair(Point(r(), r()), counter++); };
    // Initialize triangulation with n points
    D = Delaunay(boost::make_function_input_iterator(f, 0),
                 boost::make_function_input_iterator(f, n));
  }

  template<class Graph>
  void add_random_geometric(Graph& G, double d) {
    CGAL::Circle_2<Kernel>  typedef Circle;
    Delaunay::Vertex_handle typedef Vertex_handle;
    // Query each point for other points lying in the circle
    for(auto v : range(D.finite_vertices_begin(), D.finite_vertices_end()))
      D.range_search(Circle(v.point(), d*d), boost::make_function_output_iterator(
          [&](Vertex_handle u){
            if(v.info() < u->info())
              add_edge_no_dup(v.info(), u->info(), G);
      }));
  }

  template<class Graph>
  void add_mst(Graph& G) {
    typedef boost::property<boost::edge_weight_t, Kernel::FT> Weight;
    typedef boost::adjacency_list<boost::hash_setS, boost::vecS, boost::undirectedS, boost::no_property, Weight> WeightedGraph;
    typedef boost::graph_traits<WeightedGraph>::edge_descriptor WeightedEdge;

    Delaunay::Finite_vertices_iterator vit;
    Delaunay::Vertex_circulator vc, done;

    WeightedGraph g;

    for(vit = D.finite_vertices_begin(); vit != D.finite_vertices_end(); ++vit) {
      unsigned s = vit->info();
      done = vc = vit->incident_vertices();
      if(vc != 0) do {
        if(D.is_infinite(vc)) continue;
        unsigned d = vc->info();
        add_edge(s, d, CGAL::squared_distance(vit->point(), vc->point()), g);
      } while(++vc != done);
    }

    std::vector<WeightedEdge> mst;
    kruskal_minimum_spanning_tree(g, std::back_inserter(mst));
    for(auto e : mst) add_edge_no_dup(source(e, g), target(e, g), G);
  }
};
<|endoftext|>


<commit_before>/*
 * File: tests/metrics/reservoir/edr.cpp
 * Part of commonpp.
 *
 * Distributed under the 2-clause BSD licence (See LICENCE.TXT file at the
 * project root).
 *
 * Copyright (c) 2015 Thomas Sanchez.  All rights reserved.
 *
 */

#include <boost/test/unit_test.hpp>
#include <iostream>

#include <commonpp/metric/reservoir/ExponentiallyDecaying.hpp>
#include <commonpp/metric/type/DescStat.hpp>

namespace reservoir = commonpp::metric::reservoir;

// Direct translation of the tests there:
// https://github.com/dropwizard/metrics/blob/master/metrics-core/src/test/java/io/dropwizard/metrics/ExponentiallyDecayingReservoirTest.java
struct Clock : std::chrono::system_clock
{
    using Base = std::chrono::system_clock;

    static Base::time_point now()
    {
        return Base::time_point(
            std::chrono::duration_cast<Base::time_point::duration>(now_));
    }

    static std::chrono::milliseconds now_;
};

std::chrono::milliseconds Clock::now_{0};

struct ResetClock
{
    ~ResetClock()
    {
        Clock::now_ = std::chrono::milliseconds::zero();
    }
};

template <typename R>
static std::vector<std::pair<double, double>> extract_values_and_weight(R& r)
{
    struct T
    {
        std::vector<std::pair<double, double>> vector;
        void operator()(double w, double v)
        {
            vector.emplace_back(w, v);
        }

        static T createAccumulator(size_t, reservoir::WeightedReservoirTag)
        {
            return T();
        }
    };

    return r.template visit<T>().vector;
}

template <typename R>
static std::vector<double> extract_values(R& r)
{
    struct T
    {
        std::vector<double> vector;
        void operator()(double w, double v)
        {
            vector.emplace_back(v);
        }

        static T createAccumulator(size_t, reservoir::WeightedReservoirTag)
        {
            return T();
        }
    };

    return r.template visit<T>().vector;
}

template <size_t s, size_t a>
using ExponentiallyDecaying =
    commonpp::metric::reservoir::ExponentiallyDecaying<s, a, Clock>;

using commonpp::metric::type::DescStat;

BOOST_AUTO_TEST_CASE(reservoir_1000_out_of_1000)
{
    ResetClock raii_reset;

    ::commonpp::metric::reservoir::ExponentiallyDecaying<100, 99, std::chrono::system_clock>
        reservoir;

    for (int i = 0; i < 1000; ++i)
    {
        reservoir.pushValue(i);
    }

    auto values = extract_values(reservoir);
    BOOST_CHECK_EQUAL(values.size(), 100);
    for (auto v : values)
    {
        BOOST_CHECK(v >= 0 && v <= 1000);
    }
}

BOOST_AUTO_TEST_CASE(reservoir_1000_out_of_10)
{
    ResetClock raii_reset;

    ::commonpp::metric::reservoir::ExponentiallyDecaying<100, 99, std::chrono::system_clock>
        reservoir;

    for (int i = 0; i < 10; ++i)
    {
        reservoir.pushValue(i);
    }

    auto values = extract_values(reservoir);
    BOOST_CHECK_EQUAL(values.size(), 10);
    for (auto v : values)
    {
        BOOST_CHECK(v >= 0 && v <= 10);
    }
}

BOOST_AUTO_TEST_CASE(heavily_biased_reservoir_1000_out_of_1000)
{
    ResetClock raii_reset;

    ::commonpp::metric::reservoir::ExponentiallyDecaying<1000, 1, std::chrono::system_clock>
        reservoir;

    for (int i = 0; i < 100; ++i)
    {
        reservoir.pushValue(i);
    }

    auto values = extract_values(reservoir);
    BOOST_CHECK_EQUAL(values.size(), 100);
    for (auto v : values)
    {
        BOOST_CHECK(v >= 0 && v <= 100);
    }
}

BOOST_AUTO_TEST_CASE(long_period_of_inactivity_should_not_corrupt_sampling_rate)
{
    ResetClock raii_reset;

    ExponentiallyDecaying<10, 15> reservoir;

    for (int i = 0; i < 1000; ++i)
    {
        reservoir.pushValue(1000 + i, Clock::now());
        Clock::now_ += std::chrono::milliseconds(100);
    }

    {
        auto values = extract_values(reservoir);
        BOOST_CHECK_EQUAL(values.size(), 10);
        for (auto v : values)
        {
            BOOST_CHECK(v >= 1000 && v <= 2000);
        }
    }

    Clock::now_ += std::chrono::hours(15);
    reservoir.pushValue(2000);
    {
        auto values = extract_values(reservoir);
        BOOST_CHECK_EQUAL(values.size(), 2);
        for (auto v : values)
        {
            BOOST_CHECK(v >= 1000 && v <= 3000);
        }
    }

    for (int i = 0; i < 1000; ++i)
    {
        reservoir.pushValue(3000 + i);
        Clock::now_ += std::chrono::milliseconds(100);
    }

    {
        auto values = extract_values(reservoir);
        BOOST_CHECK_EQUAL(values.size(), 10);
        for (auto v : values)
        {
            BOOST_CHECK(v >= 3000 && v <= 4000);
        }
    }
}

BOOST_AUTO_TEST_CASE(spot_lift)
{
    ResetClock raii_reset;
    ExponentiallyDecaying<1000, 15> reservoir;

    const int rate_per_min = 10;

    std::chrono::milliseconds interval = std::chrono::minutes(1);
    interval /= 10;

    for (int i = 0; i < 120 * rate_per_min; ++i)
    {
        reservoir.pushValue(177);
        Clock::now_ += interval;
    }

    for (int i = 0; i < 10 * rate_per_min; ++i)
    {
        reservoir.pushValue(9999);
        Clock::now_ += interval;
    }

    auto metrics = DescStat::getMetrics(reservoir);
    BOOST_CHECK_EQUAL(metrics.getDouble("median"), 9999);
}

BOOST_AUTO_TEST_CASE(spot_fall)
{
    ResetClock raii_reset;
    ExponentiallyDecaying<1000, 15> reservoir;

    const int rate_per_min = 10;

    std::chrono::milliseconds interval = std::chrono::minutes(1);
    interval /= 10;

    for (int i = 0; i < 120 * rate_per_min; ++i)
    {
        reservoir.pushValue(9998);
        Clock::now_ += interval;
    }

    for (int i = 0; i < 10 * rate_per_min; ++i)
    {
        reservoir.pushValue(178);
        Clock::now_ += interval;
    }

    auto metrics = DescStat::getMetrics(reservoir);
    BOOST_CHECK_EQUAL(metrics.getDouble("p95"), 178);
}

BOOST_AUTO_TEST_CASE(quantilies_should_be_based_on_weights)
{
    ResetClock raii_reset;
    ExponentiallyDecaying<1000, 15> reservoir;

    for (int i = 0; i < 40; ++i)
    {
        reservoir.pushValue(177);
    }

    Clock::now_ += std::chrono::seconds(120);

    for (int i = 0; i < 10; ++i)
    {
        reservoir.pushValue(9999);
    }

    auto values = extract_values_and_weight(reservoir);

    BOOST_CHECK_EQUAL(reservoir.size(), 50);

    auto metrics = DescStat::getMetrics(reservoir);

    BOOST_CHECK_EQUAL(metrics.getDouble("median"), 9999);
    BOOST_CHECK_EQUAL(metrics.getDouble("p75"), 9999);
}
<commit_msg>fixed a test case (inactivity should not delete samples)<commit_after>/*
 * File: tests/metrics/reservoir/edr.cpp
 * Part of commonpp.
 *
 * Distributed under the 2-clause BSD licence (See LICENCE.TXT file at the
 * project root).
 *
 * Copyright (c) 2015 Thomas Sanchez.  All rights reserved.
 *
 */

#include <boost/test/unit_test.hpp>
#include <iostream>

#include <commonpp/metric/reservoir/ExponentiallyDecaying.hpp>
#include <commonpp/metric/type/DescStat.hpp>

namespace reservoir = commonpp::metric::reservoir;

// Direct translation of the tests there:
// https://github.com/dropwizard/metrics/blob/master/metrics-core/src/test/java/io/dropwizard/metrics/ExponentiallyDecayingReservoirTest.java
struct Clock : std::chrono::system_clock
{
    using Base = std::chrono::system_clock;

    static Base::time_point now()
    {
        return Base::time_point(
            std::chrono::duration_cast<Base::time_point::duration>(now_));
    }

    static std::chrono::milliseconds now_;
};

std::chrono::milliseconds Clock::now_{0};

struct ResetClock
{
    ~ResetClock()
    {
        Clock::now_ = std::chrono::milliseconds::zero();
    }
};

template <typename R>
static std::vector<std::pair<double, double>> extract_values_and_weight(R& r)
{
    struct T
    {
        std::vector<std::pair<double, double>> vector;
        void operator()(double w, double v)
        {
            vector.emplace_back(w, v);
        }

        static T createAccumulator(size_t, reservoir::WeightedReservoirTag)
        {
            return T();
        }
    };

    return r.template visit<T>().vector;
}

template <typename R>
static std::vector<double> extract_values(R& r)
{
    struct T
    {
        std::vector<double> vector;
        void operator()(double w, double v)
        {
            vector.emplace_back(v);
        }

        static T createAccumulator(size_t, reservoir::WeightedReservoirTag)
        {
            return T();
        }
    };

    return r.template visit<T>().vector;
}

template <size_t s, size_t a>
using ExponentiallyDecaying =
    commonpp::metric::reservoir::ExponentiallyDecaying<s, a, Clock>;

using commonpp::metric::type::DescStat;

BOOST_AUTO_TEST_CASE(reservoir_1000_out_of_1000)
{
    ResetClock raii_reset;

    ::commonpp::metric::reservoir::ExponentiallyDecaying<100, 99, std::chrono::system_clock>
        reservoir;

    for (int i = 0; i < 1000; ++i)
    {
        reservoir.pushValue(i);
    }

    auto values = extract_values(reservoir);
    BOOST_CHECK_EQUAL(values.size(), 100);
    for (auto v : values)
    {
        BOOST_CHECK(v >= 0 && v <= 1000);
    }
}

BOOST_AUTO_TEST_CASE(reservoir_1000_out_of_10)
{
    ResetClock raii_reset;

    ::commonpp::metric::reservoir::ExponentiallyDecaying<100, 99, std::chrono::system_clock>
        reservoir;

    for (int i = 0; i < 10; ++i)
    {
        reservoir.pushValue(i);
    }

    auto values = extract_values(reservoir);
    BOOST_CHECK_EQUAL(values.size(), 10);
    for (auto v : values)
    {
        BOOST_CHECK(v >= 0 && v <= 10);
    }
}

BOOST_AUTO_TEST_CASE(heavily_biased_reservoir_1000_out_of_1000)
{
    ResetClock raii_reset;

    ::commonpp::metric::reservoir::ExponentiallyDecaying<1000, 1, std::chrono::system_clock>
        reservoir;

    for (int i = 0; i < 100; ++i)
    {
        reservoir.pushValue(i);
    }

    auto values = extract_values(reservoir);
    BOOST_CHECK_EQUAL(values.size(), 100);
    for (auto v : values)
    {
        BOOST_CHECK(v >= 0 && v <= 100);
    }
}

BOOST_AUTO_TEST_CASE(long_period_of_inactivity_should_not_corrupt_sampling_rate)
{
    ResetClock raii_reset;

    ExponentiallyDecaying<10, 15> reservoir;

    for (int i = 0; i < 1000; ++i)
    {
        reservoir.pushValue(1000 + i, Clock::now());
        Clock::now_ += std::chrono::milliseconds(100);
    }

    {
        auto values = extract_values(reservoir);
        BOOST_CHECK_EQUAL(values.size(), 10);
        for (auto v : values)
        {
            BOOST_CHECK(v >= 1000 && v <= 2000);
        }
    }

    Clock::now_ += std::chrono::hours(15);
    reservoir.pushValue(2000);
    {
        auto values = extract_values(reservoir);
        BOOST_CHECK_EQUAL(values.size(), 10);
        for (auto v : values)
        {
            BOOST_CHECK(v >= 1000 && v <= 3000);
        }
    }

    for (int i = 0; i < 1000; ++i)
    {
        reservoir.pushValue(3000 + i);
        Clock::now_ += std::chrono::milliseconds(100);
    }

    {
        auto values = extract_values(reservoir);
        BOOST_CHECK_EQUAL(values.size(), 10);
        for (auto v : values)
        {
            BOOST_CHECK(v >= 3000 && v <= 4000);
        }
    }
}

BOOST_AUTO_TEST_CASE(spot_lift)
{
    ResetClock raii_reset;
    ExponentiallyDecaying<1000, 15> reservoir;

    const int rate_per_min = 10;

    std::chrono::milliseconds interval = std::chrono::minutes(1);
    interval /= 10;

    for (int i = 0; i < 120 * rate_per_min; ++i)
    {
        reservoir.pushValue(177);
        Clock::now_ += interval;
    }

    for (int i = 0; i < 10 * rate_per_min; ++i)
    {
        reservoir.pushValue(9999);
        Clock::now_ += interval;
    }

    auto metrics = DescStat::getMetrics(reservoir);
    BOOST_CHECK_EQUAL(metrics.getDouble("median"), 9999);
}

BOOST_AUTO_TEST_CASE(spot_fall)
{
    ResetClock raii_reset;
    ExponentiallyDecaying<1000, 15> reservoir;

    const int rate_per_min = 10;

    std::chrono::milliseconds interval = std::chrono::minutes(1);
    interval /= 10;

    for (int i = 0; i < 120 * rate_per_min; ++i)
    {
        reservoir.pushValue(9998);
        Clock::now_ += interval;
    }

    for (int i = 0; i < 10 * rate_per_min; ++i)
    {
        reservoir.pushValue(178);
        Clock::now_ += interval;
    }

    auto metrics = DescStat::getMetrics(reservoir);
    BOOST_CHECK_EQUAL(metrics.getDouble("p95"), 178);
}

BOOST_AUTO_TEST_CASE(quantilies_should_be_based_on_weights)
{
    ResetClock raii_reset;
    ExponentiallyDecaying<1000, 15> reservoir;

    for (int i = 0; i < 40; ++i)
    {
        reservoir.pushValue(177);
    }

    Clock::now_ += std::chrono::seconds(120);

    for (int i = 0; i < 10; ++i)
    {
        reservoir.pushValue(9999);
    }

    auto values = extract_values_and_weight(reservoir);

    BOOST_CHECK_EQUAL(reservoir.size(), 50);

    auto metrics = DescStat::getMetrics(reservoir);

    BOOST_CHECK_EQUAL(metrics.getDouble("median"), 9999);
    BOOST_CHECK_EQUAL(metrics.getDouble("p75"), 9999);
}
<|endoftext|>


<commit_before>#include "compiler.h"
#include "dosio.h"

static	OEMCHAR	curpath[MAX_PATH];
static	OEMCHAR	*curfilep = curpath;

#define ISKANJI(c) (((((c) ^ 0x20) - 0xa1) & 0xff) < 0x3c)

// ----

static FATFS Fatfs;
static FILINFO _fi;

static char lfn[_MAX_LFN + 1];

void dosio_init(void) {

	f_mount(0, &Fatfs);

	_fi.lfname = lfn;
	_fi.lfsize = sizeof(lfn);
}

void dosio_term(void) {

	f_mount(0, NULL);
}

// t@C
FILEH file_open(const OEMCHAR *path)
{
	FRESULT res;
	FILEH fi;

	fi = (FILEH)malloc(sizeof(FIL));

	res = f_open(fi, path, FA_READ | FA_WRITE | FA_OPEN_EXISTING);

	if (res != FR_OK) {

		printf("%s failed %s %d\n", __func__, path, res);
		return FILEH_INVALID;
	}

	return(fi);
}

FILEH file_open_rb(const OEMCHAR *path)
{
	FRESULT res;
	FILEH fi;

	fi = (FILEH)malloc(sizeof(FIL));

	res = f_open(fi, path, FA_READ | FA_OPEN_EXISTING);

	if (res != FR_OK) {

		printf("%s failed %s %d\n", __func__, path, res);
		return FILEH_INVALID;
	}

	return(fi);
}

FILEH file_create(const OEMCHAR *path)
{
	FRESULT res;
	FILEH fi;

	fi = (FILEH)malloc(sizeof(FIL));

	res = f_open(fi, path, FA_READ | FA_WRITE | FA_CREATE_ALWAYS);

	if (res != FR_OK) {

		printf("%s failed %s %d\n", __func__, path, res);
		return FILEH_INVALID;
	}

	return(fi);
}

long file_seek(FILEH handle, long pointer, int method) {
	long	ret;
	FRESULT res;

	ret = 0;

	switch (method) {
	case 1:
		ret = f_tell(handle);
		break;
	case 2:
		ret = f_size(handle);
		break;
	}
	ret += pointer;

	if (ret < 0) {
		ret = 0;
	} else if (ret > (long)f_size(handle)) {
		ret = f_size(handle);
	}

	res = f_lseek (handle, ret);

	if (res != FR_OK)
		return -1;

	return ret;

}

UINT file_read(FILEH handle, void *data, UINT length)
{
	UINT	readsize;

	if(f_read (handle, data, length, &readsize) != FR_OK) {

		printf("%s failed 0x%x 0x%x\n", __func__, length, readsize);
		return(0);
	}

	return(readsize);
}

UINT file_write(FILEH handle, const void *data, UINT length)
{
	UINT	writesize;

	if (length) {
		if (f_write (handle, data, length, &writesize) == FR_OK) {

			return(writesize);
		}
		else {
			f_lseek (handle, f_size(handle));
		}
	}

	return(0);
}

short file_close(FILEH handle)
{
	f_close (handle);

	free(handle);

	return 0;
}

UINT file_getsize(FILEH handle)
{
	return f_size(handle);
}

static BOOL cnvdatetime(FILINFO *fno, DOSDATE *dosdate, DOSTIME *dostime)
{
	if (dosdate) {
		dosdate->year = (fno->fdate >> 9) + 1980;
		dosdate->month = (fno->fdate >> 5) & 0x0f;
		dosdate->day = (fno->fdate >> 0) & 0x0f;
	}
	if (dostime) {
		dostime->hour = (fno->ftime >> 11);
		dostime->minute = (fno->ftime >> 5) & 0x3f;
		dostime->second = (fno->ftime >> 0) & 0x0f;
	}
	return SUCCESS;
}

short file_getdatetime(const char *path, DOSDATE *dosdate, DOSTIME *dostime)
{
	FILINFO *fi = &_fi;

	if ((f_stat(path, fi) == FR_OK)
	 && (cnvdatetime(fi, dosdate, dostime)))
		return 0;
	return -1;
}

short file_delete(const OEMCHAR *path)
{
	return(f_unlink(path)? FAILURE:SUCCESS);
}

short file_attr(const OEMCHAR *path)
{
	FILINFO *fi = &_fi;

	f_stat(path, fi);

	return fi->fattrib;
}

short file_dircreate(const OEMCHAR *path)
{
	return(f_mkdir(path) ?FAILURE:SUCCESS);
}

// Jgt@C
void file_setcd(const OEMCHAR *exepath)
{
	file_cpyname(curpath, exepath, sizeof(curpath));
	curfilep = file_getname(curpath);
	*curfilep = '\0';
}

OEMCHAR *file_getcd(const OEMCHAR *path)
{
	*curfilep = '\0';
	file_catname(curpath, path, sizeof(curpath));
	return curpath;
}

FILEH file_open_c(const OEMCHAR *path)
{
	*curfilep = '\0';
	file_catname(curpath, path, sizeof(curpath));
	return file_open(curpath);
}

FILEH file_open_rb_c(const OEMCHAR *path)
{
	*curfilep = '\0';
	file_catname(curpath, path, sizeof(curpath));
	return file_open_rb(curpath);
}

FILEH file_create_c(const OEMCHAR *path)
{
	*curfilep = '\0';
	file_catname(curpath, path, sizeof(curpath));
	return file_create(curpath);
}

short file_delete_c(const OEMCHAR *path)
{
	*curfilep = '\0';
	file_catname(curpath, path, sizeof(curpath));
	return file_delete(curpath);
}

short file_attr_c(const OEMCHAR *path)
{
	*curfilep = '\0';
	file_catname(curpath, path, sizeof(curpath));
	return file_attr(curpath);
}

static BRESULT setflist(FILINFO *fi, FLINFO *fli) {

	fli->caps = FLICAPS_SIZE | FLICAPS_ATTR | FLICAPS_DATE | FLICAPS_TIME;
	fli->size = fi->fsize;
	fli->attr = fi->fattrib;
	cnvdatetime(fi, &fli->date, &fli->time);
	char *fn = *fi->lfname ? fi->lfname : fi->fname;
	file_cpyname(fli->path, fn, NELEMENTS(fli->path));

	return(SUCCESS);
}

FLISTH file_list1st(const OEMCHAR *dir, FLINFO *fli) {
	FRESULT res;
	FILINFO *fi = &_fi;

	OEMCHAR path[MAX_PATH];
	file_cpyname(path, dir, NELEMENTS(path));
//	file_setseparator(path, NELEMENTS(path));
//	printf("file_list1st %s\n", path);

	FLISTH dj = (FLISTH)malloc(sizeof(DIR));

	res = f_opendir(dj, path);
	if (res == FR_OK) {
		while ((f_readdir(dj, fi) == FR_OK) && fi->fname[0]) {
			if (setflist(fi, fli) == SUCCESS) {
				return(dj);
			}
		}
	}
	return(FLISTH_INVALID);
}

BRESULT file_listnext(FLISTH hdl, FLINFO *fli) {

	FILINFO *fi = &_fi;
	char *fn;

	if ((f_readdir(hdl, fi) != FR_OK) || fi->fname[0] == 0) {
		return(FAILURE);
	}

//	fn = *fi->lfname ? fi->lfname : fi->fname;
//	printf("file_listnext %s\n", fn);

	if (setflist(fi, fli) == SUCCESS) {
		return(SUCCESS);
	}
	printf("file_listnext fail %s\n", fi->fname);
	return(FAILURE);
}

void file_listclose(FLISTH hdl) {

	free(hdl);
}

OEMCHAR *file_getname(const OEMCHAR *path) {

const OEMCHAR	*ret;
	int			csize;

	ret = path;
	while((csize = milstr_charsize(path)) != 0) {
		if ((csize == 1) &&
			((*path == '\\') || (*path == '/') || (*path == ':'))) {
			ret = path + 1;
		}
		path += csize;
	}
	return((OEMCHAR *)ret);
}

void file_cutname(OEMCHAR *path) {

	OEMCHAR	*p;

	p = file_getname(path);
	p[0] = '\0';
}

OEMCHAR *file_getext(const OEMCHAR *path) {

const OEMCHAR	*p;
const OEMCHAR	*q;
	int			csize;

	p = file_getname(path);
	q = NULL;
	while((csize = milstr_charsize(p)) != 0) {
		if ((csize == 1) && (*p == '.')) {
			q = p + 1;
		}
		p += csize;
	}
	if (q == NULL) {
		q = p;
	}
	return((OEMCHAR *)q);
}

void file_cutext(OEMCHAR *path) {

	OEMCHAR	*p;
	OEMCHAR	*q;
	int		csize;

	p = file_getname(path);
	q = NULL;
	while((csize = milstr_charsize(p)) != 0) {
		if ((csize == 1) && (*p == '.')) {
			q = p;
		}
		p += csize;
	}
	if (q) {
		*q = '\0';
	}
}

void file_cutseparator(OEMCHAR *path) {

	int		pos;

	pos = OEMSTRLEN(path) - 1;
	if ((pos > 0) &&							// 2ȏŁ[
		(path[pos] == '\\') &&					// Pc \ Ł[
		(!milstr_kanji2nd(path, pos)) &&		// 2oCgڂȂā[
		((pos != 1) || (path[0] != '\\')) &&	// '\\' ł͂Ȃā[
		((pos != 2) || (path[1] != ':'))) {		// '?:\' ł͂Ȃ
		path[pos] = '\0';
	}
}

void file_setseparator(OEMCHAR *path, int maxlen) {

	int		pos;

	pos = OEMSTRLEN(path) - 1;
	if ((pos < 0) ||
		((pos == 1) && (path[1] == ':')) ||
		((path[pos] == '\\') && (!milstr_kanji2nd(path, pos))) ||
		((pos + 2) >= maxlen)) {
		return;
	}
	path[++pos] = '\\';
	path[++pos] = '\0';
}

<commit_msg>Add _USE_LFN<commit_after>#include "compiler.h"
#include "dosio.h"

static	OEMCHAR	curpath[MAX_PATH];
static	OEMCHAR	*curfilep = curpath;

#define ISKANJI(c) (((((c) ^ 0x20) - 0xa1) & 0xff) < 0x3c)

// ----

static FATFS Fatfs;
static FILINFO _fi;

#ifdef _USE_LFN
static char lfn[_MAX_LFN + 1];
#endif

void dosio_init(void) {

	f_mount(0, &Fatfs);

#ifdef _USE_LFN
	_fi.lfname = lfn;
	_fi.lfsize = sizeof(lfn);
#endif
}

void dosio_term(void) {

	f_mount(0, NULL);
}

// t@C
FILEH file_open(const OEMCHAR *path)
{
	FRESULT res;
	FILEH fi;

	fi = (FILEH)malloc(sizeof(FIL));

	res = f_open(fi, path, FA_READ | FA_WRITE | FA_OPEN_EXISTING);

	if (res != FR_OK) {
#ifndef NOSERIAL
		printf("%s failed %s %d\n", __func__, path, res);
#endif
		return FILEH_INVALID;
	}

	return(fi);
}

FILEH file_open_rb(const OEMCHAR *path)
{
	FRESULT res;
	FILEH fi;

	fi = (FILEH)malloc(sizeof(FIL));

	res = f_open(fi, path, FA_READ | FA_OPEN_EXISTING);

	if (res != FR_OK) {
#ifndef NOSERIAL
		printf("%s failed %s %d\n", __func__, path, res);
#endif
		return FILEH_INVALID;
	}

	return(fi);
}

FILEH file_create(const OEMCHAR *path)
{
	FRESULT res;
	FILEH fi;

	fi = (FILEH)malloc(sizeof(FIL));

	res = f_open(fi, path, FA_READ | FA_WRITE | FA_CREATE_ALWAYS);

	if (res != FR_OK) {
#ifndef NOSERIAL
		printf("%s failed %s %d\n", __func__, path, res);
#endif
		return FILEH_INVALID;
	}

	return(fi);
}

long file_seek(FILEH handle, long pointer, int method) {
	long	ret;
	FRESULT res;

	ret = 0;

	switch (method) {
	case 1:
		ret = f_tell(handle);
		break;
	case 2:
		ret = f_size(handle);
		break;
	}
	ret += pointer;

	if (ret < 0) {
		ret = 0;
	} else if (ret > (long)f_size(handle)) {
		ret = f_size(handle);
	}

	res = f_lseek (handle, ret);

	if (res != FR_OK)
		return -1;

	return ret;

}

UINT file_read(FILEH handle, void *data, UINT length)
{
	UINT	readsize;

	if(f_read (handle, data, length, &readsize) != FR_OK) {
#ifndef NOSERIAL
		printf("%s failed 0x%x 0x%x\n", __func__, length, readsize);
#endif
		return(0);
	}

	return(readsize);
}

UINT file_write(FILEH handle, const void *data, UINT length)
{
	UINT	writesize;

	if (length) {
		if (f_write (handle, data, length, &writesize) == FR_OK) {

			return(writesize);
		}
		else {
			f_lseek (handle, f_size(handle));
		}
	}

	return(0);
}

short file_close(FILEH handle)
{
	f_close (handle);

	free(handle);

	return 0;
}

UINT file_getsize(FILEH handle)
{
	return f_size(handle);
}

static BOOL cnvdatetime(FILINFO *fno, DOSDATE *dosdate, DOSTIME *dostime)
{
	if (dosdate) {
		dosdate->year = (fno->fdate >> 9) + 1980;
		dosdate->month = (fno->fdate >> 5) & 0x0f;
		dosdate->day = (fno->fdate >> 0) & 0x0f;
	}
	if (dostime) {
		dostime->hour = (fno->ftime >> 11);
		dostime->minute = (fno->ftime >> 5) & 0x3f;
		dostime->second = (fno->ftime >> 0) & 0x0f;
	}
	return SUCCESS;
}

short file_getdatetime(const char *path, DOSDATE *dosdate, DOSTIME *dostime)
{
	FILINFO *fi = &_fi;

	if ((f_stat(path, fi) == FR_OK)
	 && (cnvdatetime(fi, dosdate, dostime)))
		return 0;
	return -1;
}

short file_delete(const OEMCHAR *path)
{
	return(f_unlink(path)? FAILURE:SUCCESS);
}

short file_attr(const OEMCHAR *path)
{
	FILINFO *fi = &_fi;

	f_stat(path, fi);

	return fi->fattrib;
}

short file_dircreate(const OEMCHAR *path)
{
	return(f_mkdir(path) ?FAILURE:SUCCESS);
}

// Jgt@C
void file_setcd(const OEMCHAR *exepath)
{
	file_cpyname(curpath, exepath, sizeof(curpath));
	curfilep = file_getname(curpath);
	*curfilep = '\0';
}

OEMCHAR *file_getcd(const OEMCHAR *path)
{
	*curfilep = '\0';
	file_catname(curpath, path, sizeof(curpath));
	return curpath;
}

FILEH file_open_c(const OEMCHAR *path)
{
	*curfilep = '\0';
	file_catname(curpath, path, sizeof(curpath));
	return file_open(curpath);
}

FILEH file_open_rb_c(const OEMCHAR *path)
{
	*curfilep = '\0';
	file_catname(curpath, path, sizeof(curpath));
	return file_open_rb(curpath);
}

FILEH file_create_c(const OEMCHAR *path)
{
	*curfilep = '\0';
	file_catname(curpath, path, sizeof(curpath));
	return file_create(curpath);
}

short file_delete_c(const OEMCHAR *path)
{
	*curfilep = '\0';
	file_catname(curpath, path, sizeof(curpath));
	return file_delete(curpath);
}

short file_attr_c(const OEMCHAR *path)
{
	*curfilep = '\0';
	file_catname(curpath, path, sizeof(curpath));
	return file_attr(curpath);
}

static BRESULT setflist(FILINFO *fi, FLINFO *fli) {

	char *fn;

	fli->caps = FLICAPS_SIZE | FLICAPS_ATTR | FLICAPS_DATE | FLICAPS_TIME;
	fli->size = fi->fsize;
	fli->attr = fi->fattrib;
	cnvdatetime(fi, &fli->date, &fli->time);
#ifdef _USE_LFN
	fn = *fi->lfname ? fi->lfname : fi->fname;
#else
	fn = fi->fname;
#endif
	file_cpyname(fli->path, fn, NELEMENTS(fli->path));

	return(SUCCESS);
}

FLISTH file_list1st(const OEMCHAR *dir, FLINFO *fli) {
	FRESULT res;
	FILINFO *fi = &_fi;

	OEMCHAR path[MAX_PATH];
	file_cpyname(path, dir, NELEMENTS(path));
//	file_setseparator(path, NELEMENTS(path));
#ifndef NOSERIAL
	printf("file_list1st %s\n", path);
#endif
	FLISTH dj = (FLISTH)malloc(sizeof(DIR));

	res = f_opendir(dj, path);
	if (res == FR_OK) {
		while ((f_readdir(dj, fi) == FR_OK) && fi->fname[0]) {
			if (setflist(fi, fli) == SUCCESS) {
				return(dj);
			}
		}
	}
	return(FLISTH_INVALID);
}

BRESULT file_listnext(FLISTH hdl, FLINFO *fli) {

	FILINFO *fi = &_fi;

	if ((f_readdir(hdl, fi) != FR_OK) || fi->fname[0] == 0) {
		return(FAILURE);
	}

	if (setflist(fi, fli) == SUCCESS) {
		return(SUCCESS);
	}
#ifndef NOSERIAL
	printf("file_listnext fail %s\n", fi->fname);
#endif
	return(FAILURE);
}

void file_listclose(FLISTH hdl) {

	free(hdl);
}

OEMCHAR *file_getname(const OEMCHAR *path) {

const OEMCHAR	*ret;
	int			csize;

	ret = path;
	while((csize = milstr_charsize(path)) != 0) {
		if ((csize == 1) &&
			((*path == '\\') || (*path == '/') || (*path == ':'))) {
			ret = path + 1;
		}
		path += csize;
	}
	return((OEMCHAR *)ret);
}

void file_cutname(OEMCHAR *path) {

	OEMCHAR	*p;

	p = file_getname(path);
	p[0] = '\0';
}

OEMCHAR *file_getext(const OEMCHAR *path) {

const OEMCHAR	*p;
const OEMCHAR	*q;
	int			csize;

	p = file_getname(path);
	q = NULL;
	while((csize = milstr_charsize(p)) != 0) {
		if ((csize == 1) && (*p == '.')) {
			q = p + 1;
		}
		p += csize;
	}
	if (q == NULL) {
		q = p;
	}
	return((OEMCHAR *)q);
}

void file_cutext(OEMCHAR *path) {

	OEMCHAR	*p;
	OEMCHAR	*q;
	int		csize;

	p = file_getname(path);
	q = NULL;
	while((csize = milstr_charsize(p)) != 0) {
		if ((csize == 1) && (*p == '.')) {
			q = p;
		}
		p += csize;
	}
	if (q) {
		*q = '\0';
	}
}

void file_cutseparator(OEMCHAR *path) {

	int		pos;

	pos = OEMSTRLEN(path) - 1;
	if ((pos > 0) &&							// 2ȏŁ[
		(path[pos] == '\\') &&					// Pc \ Ł[
		(!milstr_kanji2nd(path, pos)) &&		// 2oCgڂȂā[
		((pos != 1) || (path[0] != '\\')) &&	// '\\' ł͂Ȃā[
		((pos != 2) || (path[1] != ':'))) {		// '?:\' ł͂Ȃ
		path[pos] = '\0';
	}
}

void file_setseparator(OEMCHAR *path, int maxlen) {

	int		pos;

	pos = OEMSTRLEN(path) - 1;
	if ((pos < 0) ||
		((pos == 1) && (path[1] == ':')) ||
		((path[pos] == '\\') && (!milstr_kanji2nd(path, pos))) ||
		((pos + 2) >= maxlen)) {
		return;
	}
	path[++pos] = '\\';
	path[++pos] = '\0';
}

<|endoftext|>


<commit_before>#include "catch.hpp"

#include <utility>
#include <type_traits>
#include <iterator>

#include <member_iterator.hpp>

#include <forward_list>

TEST_CASE("member_iterator based on ForwardIterator", "[member_iterator]")
{
    struct mock
    {
        char c;
        int i;
    };

    std::forward_list<mock> mocklist{{'a', 1}, {'b', 2}, {'c', 3}};

    typedef helene::
        member_iterator<int, mock, std::forward_list<mock>::iterator, &mock::i>
            mocklist_iterator_type;

    mocklist_iterator_type beg_it = mocklist.begin();
    mocklist_iterator_type end_it = mocklist.end();

    SECTION("construction")
    {
        auto elem = *beg_it;
        mocklist_iterator_type cop_it = beg_it;
        mocklist_iterator_type mov_it = std::move(beg_it);

        REQUIRE(*mov_it == elem);
        REQUIRE(cop_it == mov_it);
    }

    SECTION("assignment")
    {
        mocklist_iterator_type cop_it = beg_it;
        cop_it = end_it;

        REQUIRE(cop_it == end_it);
    }

    SECTION("move assignment")
    {
        mocklist_iterator_type an_it = beg_it;
        mocklist_iterator_type another_it = end_it;

        another_it = std::move(an_it);

        REQUIRE(another_it == beg_it);
        REQUIRE(*another_it == *beg_it);
    }

    SECTION("swappable")
    {
        using std::swap;

        mocklist_iterator_type beg_it2 = beg_it;
        mocklist_iterator_type end_it2 = end_it;

        swap(beg_it2, end_it2);

        REQUIRE(beg_it2 == end_it);
        REQUIRE(end_it2 == beg_it);
    }

    SECTION("traits")
    {
        std::iterator_traits<mocklist_iterator_type>::value_type i = 20;
        std::iterator_traits<mocklist_iterator_type>::difference_type j = -2;
        std::iterator_traits<mocklist_iterator_type>::reference k = i;
        std::iterator_traits<mocklist_iterator_type>::pointer l = &i;

        bool is_forward_iterator = std::is_same<
            std::iterator_traits<mocklist_iterator_type>::iterator_category,
            std::forward_iterator_tag>::value;

        REQUIRE(is_forward_iterator == true);
    }

    SECTION("dereference")
    {
        auto is_reference = std::is_same<decltype(*beg_it), int&>::value;

        REQUIRE(is_reference == true);
        REQUIRE(*beg_it == 1);
    }

    SECTION("preincrement")
    {
        auto ref_it = ++beg_it;

        bool is_iterator_reference =
            std::is_same<decltype(++beg_it), mocklist_iterator_type&>::value;

        REQUIRE(ref_it == beg_it);
        REQUIRE(*beg_it == 2);
        REQUIRE(is_iterator_reference == true);
    }

    SECTION("EqualityComparable")
    {
        bool equal = beg_it == end_it;

        REQUIRE(equal == false);
        REQUIRE(beg_it == mocklist.begin());
    }

    SECTION("!=")
    {
        bool not_equal = beg_it != mocklist.begin();

        REQUIRE(beg_it != end_it);
        REQUIRE(not_equal == false);
    }

}
<commit_msg>Add test for postincrement. 	modified:   tests/test_member_iterator2.cpp<commit_after>#include "catch.hpp"

#include <utility>
#include <type_traits>
#include <iterator>

#include <member_iterator.hpp>

#include <forward_list>

TEST_CASE("member_iterator based on ForwardIterator", "[member_iterator]")
{
    struct mock
    {
        char c;
        int i;
    };

    std::forward_list<mock> mocklist{{'a', 1}, {'b', 2}, {'c', 3}};

    typedef helene::
        member_iterator<int, mock, std::forward_list<mock>::iterator, &mock::i>
            mocklist_iterator_type;

    mocklist_iterator_type beg_it = mocklist.begin();
    mocklist_iterator_type end_it = mocklist.end();

    SECTION("construction")
    {
        auto elem = *beg_it;
        mocklist_iterator_type cop_it = beg_it;
        mocklist_iterator_type mov_it = std::move(beg_it);

        REQUIRE(*mov_it == elem);
        REQUIRE(cop_it == mov_it);
    }

    SECTION("assignment")
    {
        mocklist_iterator_type cop_it = beg_it;
        cop_it = end_it;

        REQUIRE(cop_it == end_it);
    }

    SECTION("move assignment")
    {
        mocklist_iterator_type an_it = beg_it;
        mocklist_iterator_type another_it = end_it;

        another_it = std::move(an_it);

        REQUIRE(another_it == beg_it);
        REQUIRE(*another_it == *beg_it);
    }

    SECTION("swappable")
    {
        using std::swap;

        mocklist_iterator_type beg_it2 = beg_it;
        mocklist_iterator_type end_it2 = end_it;

        swap(beg_it2, end_it2);

        REQUIRE(beg_it2 == end_it);
        REQUIRE(end_it2 == beg_it);
    }

    SECTION("traits")
    {
        std::iterator_traits<mocklist_iterator_type>::value_type i = 20;
        std::iterator_traits<mocklist_iterator_type>::difference_type j = -2;
        std::iterator_traits<mocklist_iterator_type>::reference k = i;
        std::iterator_traits<mocklist_iterator_type>::pointer l = &i;

        bool is_forward_iterator = std::is_same<
            std::iterator_traits<mocklist_iterator_type>::iterator_category,
            std::forward_iterator_tag>::value;

        REQUIRE(is_forward_iterator == true);
    }

    SECTION("dereference")
    {
        auto is_reference = std::is_same<decltype(*beg_it), int&>::value;

        REQUIRE(is_reference == true);
        REQUIRE(*beg_it == 1);
    }

    SECTION("preincrement")
    {
        auto ref_it = ++beg_it;

        bool is_iterator_reference =
            std::is_same<decltype(++beg_it), mocklist_iterator_type&>::value;

        REQUIRE(ref_it == beg_it);
        REQUIRE(*beg_it == 2);
        REQUIRE(is_iterator_reference == true);
    }

    SECTION("EqualityComparable")
    {
        bool equal = beg_it == end_it;

        REQUIRE(equal == false);
        REQUIRE(beg_it == mocklist.begin());
    }

    SECTION("!=")
    {
        bool not_equal = beg_it != mocklist.begin();

        REQUIRE(beg_it != end_it);
        REQUIRE(not_equal == false);
    }

    SECTION("postincrement")
    {
        bool is_conv_to_const_ref =
            std::is_convertible<decltype(beg_it++),
                                const mocklist_iterator_type&>::value;

        (void)beg_it++;

        REQUIRE(is_conv_to_const_ref == true);
        REQUIRE(*beg_it == 2);
    }
}
<|endoftext|>


<commit_before>#ifndef VEXCL_UTIL_HPP
#define VEXCL_UTIL_HPP

/*
The MIT License

Copyright (c) 2012 Denis Demidov <ddemidov@ksu.ru>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

/**
 * \file   util.hpp
 * \author Denis Demidov <ddemidov@ksu.ru>
 * \brief  OpenCL general utilities.
 */

#ifdef WIN32
#  pragma warning(disable : 4290)
#  define NOMINMAX
#endif

#define __CL_ENABLE_EXCEPTIONS

#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <algorithm>
#include <type_traits>
#include <functional>
#include <CL/cl.hpp>

typedef unsigned int  uint;
typedef unsigned char uchar;

namespace vex {

/// Convert typename to string.
template <class T> std::string type_name()  { return "undefined_type"; }
template <> std::string type_name<float>()  { return "float"; }
template <> std::string type_name<double>() { return "double"; }
template <> std::string type_name<int>()    { return "int"; }
template <> std::string type_name<char>()   { return "char"; }
template <> std::string type_name<uint>()   { return "unsigned int"; }
template <> std::string type_name<uchar>()  { return "unsigned char"; }

template <> std::string type_name<size_t>() {
    static_assert(sizeof(size_t) == 4 || sizeof(size_t) == 8,
	    "Only 32bit or 64bit architectures are supported");
    return sizeof(size_t) == 4 ? "uint" : "ulong";
}

template <> std::string type_name<ptrdiff_t>() {
    static_assert(sizeof(size_t) == 4 || sizeof(size_t) == 8,
	    "Only 32bit or 64bit architectures are supported");
    return sizeof(size_t) == 4 ? "int" : "long";
}

std::string standard_kernel_header = std::string(
	"#if defined(cl_khr_fp64)\n"
	"#  pragma OPENCL EXTENSION cl_khr_fp64: enable\n"
	"#elif defined(cl_amd_fp64)\n"
	"#  pragma OPENCL EXTENSION cl_amd_fp64: enable\n"
	"#endif\n"
	);

/// Return next power of 2.
size_t nextpow2(size_t x) {
    --x;
    x |= x >> 1U;
    x |= x >> 2U;
    x |= x >> 4U;
    x |= x >> 8U;
    x |= x >> 16U;
    return ++x;
}

/// Align n to the next multiple of m.
size_t alignup(size_t n, size_t m = 16U) {
    return n % m ? n - n % m + m : n;
}

/// Partitions vector wrt to vector performance of devices.
/**
 * Launches the following kernel on each device:
 * \code
 * a = b + c;
 * \endcode
 * where a, b and c are device vectors. Each device gets portion of the vector
 * proportional to the performance of this operation.
 */
std::vector<size_t> partition_by_vector_perf(
	size_t n, const std::vector<cl::CommandQueue> &queue);

/// Partitions vector wrt to spmv performance of devices.
/**
 * Launches the following kernel on each device:
 * \code
 * y = A * x;
 * \endcode
 * where x and y are vectors, and A is matrix for 3D Poisson problem in square
 * domain. Each device gets portion of the vector proportional to the
 * performance of this operation.
 */
std::vector<size_t> partition_by_spmv_perf(
	size_t n, const std::vector<cl::CommandQueue> &queue);

/// Partitions vector equally.
std::vector<size_t> partition_equally(
	size_t n, const std::vector<cl::CommandQueue> &queue)
{
    size_t m = queue.size();

    std::vector<size_t> part(m + 1);
    part[0] = 0;

    if (queue.size() > 1) {
	for(size_t d = 0, chunk_size = alignup((n + m - 1) / m); d < m; d++)
	    part[d + 1] = std::min(n, part[d] + chunk_size);
    } else {
	part.back() = n;
    }

    return part;
}

/// Partitioning scheme for vectors and matrices.
/**
 * Should be set once before any object of vector or matrix type is declared.
 * Otherwise default parttioning function (partition_by_vector_perf) is
 * selected.
 */
struct partitioning_scheme {
    typedef std::function<
	std::vector<size_t>(size_t, const std::vector<cl::CommandQueue>&)
	> function_type;

    static void set(function_type f) {
	if (!is_set) {
	    pfun = f;
	    is_set = true;
	} else {
	    std::cerr <<
		"Warning: "
		"partitioning function is already set and will be left as is."
		<< std::endl;
	}
    }

    std::vector<size_t> operator()(size_t n,
	    const std::vector<cl::CommandQueue> &queue) const
    {
	if (!is_set) {
	    pfun = partition_by_vector_perf;
	    is_set = true;
	}
	return pfun(n, queue);
    }

    private:
	static bool is_set;
	static function_type pfun;
} partition;

bool partitioning_scheme::is_set = false;
partitioning_scheme::function_type partitioning_scheme::pfun;

/// Create and build a program from source string.
cl::Program build_sources(
	const cl::Context &context, const std::string &source
	)
{
    cl::Program program(context, cl::Program::Sources(
		1, std::make_pair(source.c_str(), source.size())
		));

    auto device = context.getInfo<CL_CONTEXT_DEVICES>();

    try {
	program.build(device);
    } catch(const cl::Error&) {
	std::cerr << program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(device[0])
	          << std::endl;
	throw;
    }

    return program;
}

/// Get maximum possible workgroup size for given kernel.
size_t kernel_workgroup_size(
	const cl::Kernel &kernel,
	const std::vector<cl::Device> &device
	)
{
    size_t wgsz = 1024U;

    for(auto d = device.begin(); d != device.end(); d++) {
	size_t dev_wgsz = kernel.getWorkGroupInfo<CL_KERNEL_WORK_GROUP_SIZE>(*d);
	while(wgsz > dev_wgsz) wgsz /= 2;
    }

    return wgsz;
}

} // namespace vex

/// Output description of an OpenCL error to a stream.
std::ostream& operator<<(std::ostream &os, const cl::Error &e) {
    os << e.what() << "(";

    switch (e.err()) {
	case 0:
	    os << "Success";
	    break;
	case -1:
	    os << "Device not found";
	    break;
	case -2:
	    os << "Device not available";
	    break;
	case -3:
	    os << "Compiler not available";
	    break;
	case -4:
	    os << "Mem object allocation failure";
	    break;
	case -5:
	    os << "Out of resources";
	    break;
	case -6:
	    os << "Out of host memory";
	    break;
	case -7:
	    os << "Profiling info not available";
	    break;
	case -8:
	    os << "Mem copy overlap";
	    break;
	case -9:
	    os << "Image format mismatch";
	    break;
	case -10:
	    os << "Image format not supported";
	    break;
	case -11:
	    os << "Build program failure";
	    break;
	case -12:
	    os << "Map failure";
	    break;
	case -13:
	    os << "Misaligned sub buffer offset";
	    break;
	case -14:
	    os << "Exec status error for events in wait list";
	    break;
	case -30:
	    os << "Invalid value";
	    break;
	case -31:
	    os << "Invalid device type";
	    break;
	case -32:
	    os << "Invalid platform";
	    break;
	case -33:
	    os << "Invalid device";
	    break;
	case -34:
	    os << "Invalid context";
	    break;
	case -35:
	    os << "Invalid queue properties";
	    break;
	case -36:
	    os << "Invalid command queue";
	    break;
	case -37:
	    os << "Invalid host ptr";
	    break;
	case -38:
	    os << "Invalid mem object";
	    break;
	case -39:
	    os << "Invalid image format descriptor";
	    break;
	case -40:
	    os << "Invalid image size";
	    break;
	case -41:
	    os << "Invalid sampler";
	    break;
	case -42:
	    os << "Invalid binary";
	    break;
	case -43:
	    os << "Invalid build options";
	    break;
	case -44:
	    os << "Invalid program";
	    break;
	case -45:
	    os << "Invalid program executable";
	    break;
	case -46:
	    os << "Invalid kernel name";
	    break;
	case -47:
	    os << "Invalid kernel definition";
	    break;
	case -48:
	    os << "Invalid kernel";
	    break;
	case -49:
	    os << "Invalid arg index";
	    break;
	case -50:
	    os << "Invalid arg value";
	    break;
	case -51:
	    os << "Invalid arg size";
	    break;
	case -52:
	    os << "Invalid kernel args";
	    break;
	case -53:
	    os << "Invalid work dimension";
	    break;
	case -54:
	    os << "Invalid work group size";
	    break;
	case -55:
	    os << "Invalid work item size";
	    break;
	case -56:
	    os << "Invalid global offset";
	    break;
	case -57:
	    os << "Invalid event wait list";
	    break;
	case -58:
	    os << "Invalid event";
	    break;
	case -59:
	    os << "Invalid operation";
	    break;
	case -60:
	    os << "Invalid gl object";
	    break;
	case -61:
	    os << "Invalid buffer size";
	    break;
	case -62:
	    os << "Invalid mip level";
	    break;
	case -63:
	    os << "Invalid global work size";
	    break;
	case -64:
	    os << "Invalid property";
	    break;
	default:
	    os << "Unknown error";
	    break;
    }

    return os << ")";
}

#endif
<commit_msg>Show kernel source if it fails to compile<commit_after>#ifndef VEXCL_UTIL_HPP
#define VEXCL_UTIL_HPP

/*
The MIT License

Copyright (c) 2012 Denis Demidov <ddemidov@ksu.ru>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

/**
 * \file   util.hpp
 * \author Denis Demidov <ddemidov@ksu.ru>
 * \brief  OpenCL general utilities.
 */

#ifdef WIN32
#  pragma warning(disable : 4290)
#  define NOMINMAX
#endif

#define __CL_ENABLE_EXCEPTIONS

#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <algorithm>
#include <type_traits>
#include <functional>
#include <CL/cl.hpp>

typedef unsigned int  uint;
typedef unsigned char uchar;

namespace vex {

/// Convert typename to string.
template <class T> std::string type_name()  { return "undefined_type"; }
template <> std::string type_name<float>()  { return "float"; }
template <> std::string type_name<double>() { return "double"; }
template <> std::string type_name<int>()    { return "int"; }
template <> std::string type_name<char>()   { return "char"; }
template <> std::string type_name<uint>()   { return "unsigned int"; }
template <> std::string type_name<uchar>()  { return "unsigned char"; }

template <> std::string type_name<size_t>() {
    static_assert(sizeof(size_t) == 4 || sizeof(size_t) == 8,
	    "Only 32bit or 64bit architectures are supported");
    return sizeof(size_t) == 4 ? "uint" : "ulong";
}

template <> std::string type_name<ptrdiff_t>() {
    static_assert(sizeof(size_t) == 4 || sizeof(size_t) == 8,
	    "Only 32bit or 64bit architectures are supported");
    return sizeof(size_t) == 4 ? "int" : "long";
}

std::string standard_kernel_header = std::string(
	"#if defined(cl_khr_fp64)\n"
	"#  pragma OPENCL EXTENSION cl_khr_fp64: enable\n"
	"#elif defined(cl_amd_fp64)\n"
	"#  pragma OPENCL EXTENSION cl_amd_fp64: enable\n"
	"#endif\n"
	);

/// Return next power of 2.
size_t nextpow2(size_t x) {
    --x;
    x |= x >> 1U;
    x |= x >> 2U;
    x |= x >> 4U;
    x |= x >> 8U;
    x |= x >> 16U;
    return ++x;
}

/// Align n to the next multiple of m.
size_t alignup(size_t n, size_t m = 16U) {
    return n % m ? n - n % m + m : n;
}

/// Partitions vector wrt to vector performance of devices.
/**
 * Launches the following kernel on each device:
 * \code
 * a = b + c;
 * \endcode
 * where a, b and c are device vectors. Each device gets portion of the vector
 * proportional to the performance of this operation.
 */
std::vector<size_t> partition_by_vector_perf(
	size_t n, const std::vector<cl::CommandQueue> &queue);

/// Partitions vector wrt to spmv performance of devices.
/**
 * Launches the following kernel on each device:
 * \code
 * y = A * x;
 * \endcode
 * where x and y are vectors, and A is matrix for 3D Poisson problem in square
 * domain. Each device gets portion of the vector proportional to the
 * performance of this operation.
 */
std::vector<size_t> partition_by_spmv_perf(
	size_t n, const std::vector<cl::CommandQueue> &queue);

/// Partitions vector equally.
std::vector<size_t> partition_equally(
	size_t n, const std::vector<cl::CommandQueue> &queue)
{
    size_t m = queue.size();

    std::vector<size_t> part(m + 1);
    part[0] = 0;

    if (queue.size() > 1) {
	for(size_t d = 0, chunk_size = alignup((n + m - 1) / m); d < m; d++)
	    part[d + 1] = std::min(n, part[d] + chunk_size);
    } else {
	part.back() = n;
    }

    return part;
}

/// Partitioning scheme for vectors and matrices.
/**
 * Should be set once before any object of vector or matrix type is declared.
 * Otherwise default parttioning function (partition_by_vector_perf) is
 * selected.
 */
struct partitioning_scheme {
    typedef std::function<
	std::vector<size_t>(size_t, const std::vector<cl::CommandQueue>&)
	> function_type;

    static void set(function_type f) {
	if (!is_set) {
	    pfun = f;
	    is_set = true;
	} else {
	    std::cerr <<
		"Warning: "
		"partitioning function is already set and will be left as is."
		<< std::endl;
	}
    }

    std::vector<size_t> operator()(size_t n,
	    const std::vector<cl::CommandQueue> &queue) const
    {
	if (!is_set) {
	    pfun = partition_by_vector_perf;
	    is_set = true;
	}
	return pfun(n, queue);
    }

    private:
	static bool is_set;
	static function_type pfun;
} partition;

bool partitioning_scheme::is_set = false;
partitioning_scheme::function_type partitioning_scheme::pfun;

/// Create and build a program from source string.
cl::Program build_sources(
	const cl::Context &context, const std::string &source
	)
{
    cl::Program program(context, cl::Program::Sources(
		1, std::make_pair(source.c_str(), source.size())
		));

    auto device = context.getInfo<CL_CONTEXT_DEVICES>();

    try {
	program.build(device);
    } catch(const cl::Error&) {
	std::cerr << source
	          << std::endl
	          << program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(device[0])
	          << std::endl;
	throw;
    }

    return program;
}

/// Get maximum possible workgroup size for given kernel.
size_t kernel_workgroup_size(
	const cl::Kernel &kernel,
	const std::vector<cl::Device> &device
	)
{
    size_t wgsz = 1024U;

    for(auto d = device.begin(); d != device.end(); d++) {
	size_t dev_wgsz = kernel.getWorkGroupInfo<CL_KERNEL_WORK_GROUP_SIZE>(*d);
	while(wgsz > dev_wgsz) wgsz /= 2;
    }

    return wgsz;
}

} // namespace vex

/// Output description of an OpenCL error to a stream.
std::ostream& operator<<(std::ostream &os, const cl::Error &e) {
    os << e.what() << "(";

    switch (e.err()) {
	case 0:
	    os << "Success";
	    break;
	case -1:
	    os << "Device not found";
	    break;
	case -2:
	    os << "Device not available";
	    break;
	case -3:
	    os << "Compiler not available";
	    break;
	case -4:
	    os << "Mem object allocation failure";
	    break;
	case -5:
	    os << "Out of resources";
	    break;
	case -6:
	    os << "Out of host memory";
	    break;
	case -7:
	    os << "Profiling info not available";
	    break;
	case -8:
	    os << "Mem copy overlap";
	    break;
	case -9:
	    os << "Image format mismatch";
	    break;
	case -10:
	    os << "Image format not supported";
	    break;
	case -11:
	    os << "Build program failure";
	    break;
	case -12:
	    os << "Map failure";
	    break;
	case -13:
	    os << "Misaligned sub buffer offset";
	    break;
	case -14:
	    os << "Exec status error for events in wait list";
	    break;
	case -30:
	    os << "Invalid value";
	    break;
	case -31:
	    os << "Invalid device type";
	    break;
	case -32:
	    os << "Invalid platform";
	    break;
	case -33:
	    os << "Invalid device";
	    break;
	case -34:
	    os << "Invalid context";
	    break;
	case -35:
	    os << "Invalid queue properties";
	    break;
	case -36:
	    os << "Invalid command queue";
	    break;
	case -37:
	    os << "Invalid host ptr";
	    break;
	case -38:
	    os << "Invalid mem object";
	    break;
	case -39:
	    os << "Invalid image format descriptor";
	    break;
	case -40:
	    os << "Invalid image size";
	    break;
	case -41:
	    os << "Invalid sampler";
	    break;
	case -42:
	    os << "Invalid binary";
	    break;
	case -43:
	    os << "Invalid build options";
	    break;
	case -44:
	    os << "Invalid program";
	    break;
	case -45:
	    os << "Invalid program executable";
	    break;
	case -46:
	    os << "Invalid kernel name";
	    break;
	case -47:
	    os << "Invalid kernel definition";
	    break;
	case -48:
	    os << "Invalid kernel";
	    break;
	case -49:
	    os << "Invalid arg index";
	    break;
	case -50:
	    os << "Invalid arg value";
	    break;
	case -51:
	    os << "Invalid arg size";
	    break;
	case -52:
	    os << "Invalid kernel args";
	    break;
	case -53:
	    os << "Invalid work dimension";
	    break;
	case -54:
	    os << "Invalid work group size";
	    break;
	case -55:
	    os << "Invalid work item size";
	    break;
	case -56:
	    os << "Invalid global offset";
	    break;
	case -57:
	    os << "Invalid event wait list";
	    break;
	case -58:
	    os << "Invalid event";
	    break;
	case -59:
	    os << "Invalid operation";
	    break;
	case -60:
	    os << "Invalid gl object";
	    break;
	case -61:
	    os << "Invalid buffer size";
	    break;
	case -62:
	    os << "Invalid mip level";
	    break;
	case -63:
	    os << "Invalid global work size";
	    break;
	case -64:
	    os << "Invalid property";
	    break;
	default:
	    os << "Unknown error";
	    break;
    }

    return os << ")";
}

#endif
<|endoftext|>