/*
 *
 * This file is part of Amnesia: The Dark Descent.
 *
 */

#include "../Common/EdObjArea.h"

#include "../Common/EdWorld.h"
#include "../Common/Editor.h"
#include "../Common/EdResource.h"

#include "../Common/EdObjectIcon.h"

#include "../Common/EdEditPaneArea.h"

cTypeRootArea::cTypeRootArea() : iUserTypeRoot(_W("Area"), "Area", "AreaClasses.def")
{
	RegisterVarCatName(eEdVarCategory_Editor, "EditorSetupVars");
	RegisterVarCatName(eEdVarCategory_Instance, "Vars");

	AddBool(eScnBool_Active, "Active");
	AddString(eAreaStr_Mesh, "Mesh", "");
}

iEdObjectType* cTypeRootArea::GetTypeByXmlElement(cXmlElement* apElem)
{
	if(iUserTypeRoot::GetTypeByXmlElement(apElem)==NULL)
		return NULL;

	tString sAreaType = apElem->GetAttributeString("AreaType", "");

	return GetTypeByName(cString::To16Char(sAreaType));
}

iUserTypeNode* cTypeRootArea::CreateSpecificUserTypeNode(iEdClass* apClass)
{
	return hplNew(cTypeArea,(this, apClass));
}

cTypeArea::cTypeArea(iUserTypeRoot* apRoot, iEdClass* apClass) : iUserTypeNode(apRoot, apClass)
{
	mColor = cColor(1,0,1,1);
	mbShowOrientation = false;
	mbDrawAsSphere = false;
	mvDefaultSize = 1;
	mbRotateable = true;

	msIcon = "Area";

	/*////////////////////////////////////////
	// Set up defaults
	mColor = cColor(1,0,1,1);
	mbShowOrientation = false;
	mbDrawAsSphere = false;
	mvDefaultSize = 1;
	mbCanRotate = true;
	mScaleType = eScaleType_Normal;

	msIcon = "Area";
	*/

	// Retrieve type characteristics

	cEdUserClass* pClass = static_cast<cEdUserClass*>(apClass);
	cEdClassInstance* pClassInst = pClass->CreateInstance(eEdVarCategory_Editor);

	tString sColor, sShowOrientation, sSphere, sDefaultSize, sIcon, sAllowRotate, sAllowScale;

	for(int i=0;i<pClassInst->GetVarInstanceNum();++i)
	{
		cEdVarInstance* pVar = pClassInst->GetVarInstance(i);
		const tWString& sName = pVar->GetName();
		tString sValue = cString::To8Char(pVar->GetValue());

		if(sName==_W("Color"))
			mColor = cString::ToColor(sValue.c_str(), mColor);

		else if(sName==_W("ShowOrientation"))
			mbShowOrientation = cString::ToBool(sValue.c_str(), mbShowOrientation);

		else if(sName==_W("Sphere"))
			mbDrawAsSphere = cString::ToBool(sValue.c_str(), mbDrawAsSphere);

		else if(sName==_W("DefaultSize"))
			mvDefaultSize = cString::ToVector3f(sValue.c_str(), mvDefaultSize);

		else if(sName==_W("AllowRotate"))
			mbRotateable = cString::ToBool(sValue.c_str(), mbRotateable);

		else if(sName==_W("AllowScale"))
		{
		}

		else if(sName==_W("Icon"))
			msIcon = sValue==""? msIcon : sValue;
	}
	hplDelete(pClassInst);
}

/*
tString cEdObjTypeArea::ToString()
{
	return iEdObjectType::ToString() + " " + GetAreaType();
}
*/

/*
bool cEdObjTypeArea::IsAppropriateType(cXmlElement* apElement)
{
	if(iEdObjectType::IsAppropriateType(apElement)==false)
		return false;

	return apElement->GetAttributeString("AreaType")==GetAreaType();
}
*/

/*
const tString& cEdObjTypeArea::GetAreaType()
{
	// Name is contained in parent type
	return mpUserType->GetParent()->GetName();
}
*/

iEdObjectData* cTypeArea::CreateTypeSpecificData()
{
	return hplNew(cEdObjAreaData,(this));
}

iEdObject* cEdObjAreaData::CreateTypeSpecificObject()
{
	return hplNew(cEdObjArea,(this));
}

cEdObjAreaData::cEdObjAreaData(iEdObjectType* apType) : iUserObjData(apType)
{
	SetName(apType->GetName() + _W("Area"));
}

bool cEdObjAreaData::SaveSpecificData(cXmlElement* apElem)
{
	if(iUserObjData::SaveSpecificData(apElem)==false)
		return false;

	apElem->SetAttributeString("AreaType", cString::To8Char(mpType->GetName()));

	return true;
}

// CONSTRUCTORS

cEdObjArea::cEdObjArea(iEdObjectData* apData) : iUserObject(apData)
{
	mpMesh = NULL;
}

cEdObjArea::~cEdObjArea()
{
}

// PUBLIC METHODS

bool cEdObjArea::SetProperty(int alPropID, const tString& asX)
{
	switch(alPropID)
	{
	case eAreaStr_Mesh:
		SetMeshFile(asX);
		break;
	case eAreaStr_Type:
		break;
	default:
		return iEdScnObject::SetProperty(alPropID, asX);
	}

	return true;
}

bool cEdObjArea::GetProperty(int alPropID, tString& asX)
{
	switch(alPropID)
	{
	case eAreaStr_Mesh:
		asX = GetMeshFile();
		break;
	case eAreaStr_Type:
		break;
	default:
		return iEdScnObject::GetProperty(alPropID, asX);
	}

	return true;
}

void cEdObjArea::SetMeshFile(const tString& asX)
{
	// Try to load a mesh for the area
	cResources* pRes = GetWorld()->GetEditor()->GetEngine()->GetResources();
	cMesh* pMesh = NULL;
	cEdLoader::LoadModel(asX, &pMesh);

	if(pMesh==NULL && asX.empty()==false)
		return;

	// Destroy previous entity
	msMeshFile = asX;

	cWorld* pWorld = GetWorld()->GetEngWorld();
	if(mpMesh)
	{
		pWorld->DestroyMeshEntity(mpMesh);
		mpMesh = NULL;
	}

	if(pMesh==NULL)
		return;

	// Create a new one using the new mesh and
	// set it up with a custom material
	cMaterialManager* pMatMgr = pRes->GetMaterialManager();

	mpMesh = pWorld->CreateMeshEntity(cString::To8Char(msName), pMesh, false);
	mpMesh->SetVisible(false);
	for(int i=0;i<mpMesh->GetSubMeshEntityNum();++i)
	{
		cSubMeshEntity* pSubMeshEnt = mpMesh->GetSubMeshEntity(i);
		pSubMeshEnt->SetCustomMaterial(pMatMgr->CreateMaterial("editor_area_mesh.mat"));
	}
}

iEdEditPane* cEdObjArea::CreateEditPane()
{
	return hplNew(cEdEditPaneArea,(this));
}

// PROTECTED METHODS

void cEdObjArea::OnDraw(const cModuleDrawData& aData)
{
	iEdScnObject::OnDraw(aData);

	cTypeArea* pType = static_cast<cTypeArea*>(mpType);

	cColor col;
	if(mbSelected)
		col = cColor(1);
	else
		col = pType->GetColor();

	cMatrixf mtxRT = cMath::MatrixMul(GetTranslationMatrix(), GetRotationMatrix());

	aData.mpFunctions->SetDepthTest(true);
	aData.mpFunctions->SetDepthWrite(false);
	aData.mpFunctions->SetMatrix(&mtxRT);

	aData.mpFunctions->GetLowLevelGfx()->DrawLine(cVector3f(-0.05f,0,0), cVector3f(0.05f,0,0), col);
	aData.mpFunctions->GetLowLevelGfx()->DrawLine(cVector3f(0,-0.05f,0), cVector3f(0,0.05f,0), col);
	aData.mpFunctions->GetLowLevelGfx()->DrawLine(cVector3f(0,0,-0.05f), cVector3f(0,0,0.05f), col);

	cVector3f vHalfScale;
	if(pType->IsScalable())
		vHalfScale = mvScale*0.5f;
	else
		vHalfScale = pType->GetDefaultSize()*0.5f;

	if(pType->GetDrawAsSphere())
	{
		vHalfScale = vHalfScale*0.5f;
		aData.mpFunctions->GetLowLevelGfx()->DrawSphere(0, vHalfScale.x, col);
	}
	else
	{
		aData.mpFunctions->GetLowLevelGfx()->DrawBoxMinMax(vHalfScale*-1, vHalfScale, col);
	}

	if(pType->GetShowOrientation())
	{
		cVector3f vLength = vHalfScale-0.1f;
		for(int i=0;i<3;++i)
		{
			cVector3f vDir = 0;
			cColor col = 0;
			vDir.v[i] = 1;
			vDir = vDir*vLength;
			col.v[i] = 1;
			aData.mpFunctions->GetLowLevelGfx()->DrawLine(0 , vDir, col);
		}
	}

	if(mpMesh)
		mpMesh->SetVisible(mbSelected);

	aData.mpFunctions->SetMatrix(NULL);

}

bool cEdObjArea::OnUpdate(bool abForce)
{
	if(mpMesh)
	{
		cMatrixf mtxRT = cMath::MatrixMul(GetTranslationMatrix(), GetRotationMatrix());
		mpMesh->SetMatrix(mtxRT);
	}

	return true;
}

cObjectIcon* cEdObjArea::CreateIcon()
{
	cTypeArea* pType = static_cast<cTypeArea*>(mpType);
	return hplNew( cObjectIcon, (this,


iEdObjectData* cTypeArea::CreateTypeSpecificData()
{
	return hplNew(cEdObjAreaData,(this));
}

/*
 *
 * This file is part of Amnesia: The Dark Descent.
 *
 */

#include "../Common/EdObjArea.h"

#include "../Common/EdWorld.h"
#include "../Common/Editor.h"
#include "../Common/EdResource.h"

#include "../Common/EdObjectIcon.h"

#include "../Common/EdEditPaneArea.h"

cTypeRootArea::cTypeRootArea() : iUserTypeRoot(_W("Area"), "Area", "AreaClasses.def")
{
	RegisterVarCatName(eEdVarCategory_Editor, "EditorSetupVars");
	RegisterVarCatName(eEdVarCategory_Instance, "Vars");

	AddBool(eScnBool_Active, "Active");
	AddString(eAreaStr_Mesh, "Mesh", "");
}

iEdObjectType* cTypeRootArea::GetTypeByXmlElement(cXmlElement* apElem)
{
	if(iUserTypeRoot::GetTypeByXmlElement(apElem)==NULL)
		return NULL;

	tString sAreaType = apElem->GetAttributeString("AreaType", "");

	return GetTypeByName(cString::To16Char(sAreaType));
}

iUserTypeNode* cTypeRootArea::CreateSpecificUserTypeNode(iEdClass* apClass)
{
	return hplNew(cTypeArea,(this, apClass));
}

cTypeArea::cTypeArea(iUserTypeRoot* apRoot, iEdClass* apClass) : iUserTypeNode(apRoot, apClass)
{
	mColor = cColor(1,0,1,1);
	mbShowOrientation = false;
	mbDrawAsSphere = false;
	mvDefaultSize = 1;
	mbRotateable = true;

	msIcon = "Area";

	/*////////////////////////////////////////
	// Set up defaults
	mColor = cColor(1,0,1,1);
	mbShowOrientation = false;
	mbDrawAsSphere = false;
	mvDefaultSize = 1;
	mbCanRotate = true;
	mScaleType = eScaleType_Normal;

	msIcon = "Area";
	*/

	// Retrieve type characteristics

	cEdUserClass* pClass = static_cast<cEdUserClass*>(apClass);
	cEdClassInstance* pClassInst = pClass->CreateInstance(eEdVarCategory_Editor);

	tString sColor, sShowOrientation, sSphere, sDefaultSize, sIcon, sAllowRotate, sAllowScale;

	for(int i=0;i<pClassInst->GetVarInstanceNum();++i)
	{
		cEdVarInstance* pVar = pClassInst->GetVarInstance(i);
		const tWString& sName = pVar->GetName();
		tString sValue = cString::To8Char(pVar->GetValue());

		if(sName==_W("Color"))
			mColor = cString::ToColor(sValue.c_str(), mColor);

		else if(sName==_W("ShowOrientation"))
			mbShowOrientation = cString::ToBool(sValue.c_str(), mbShowOrientation);

		else if(sName==_W("Sphere"))
			mbDrawAsSphere = cString::ToBool(sValue.c_str(), mbDrawAsSphere);

		else if(sName==_W("DefaultSize"))
			mvDefaultSize = cString::ToVector3f(sValue.c_str(), mvDefaultSize);

		else if(sName==_W("AllowRotate"))
			mbRotateable = cString::ToBool(sValue.c_str(), mbRotateable);

		else if(sName==_W("AllowScale"))
		{
		}

		else if(sName==_W("Icon"))
			msIcon = sValue==""? msIcon : sValue;
	}
	hplDelete(pClassInst);
}

/*
tString cEdObjTypeArea::ToString()
{
	return iEdObjectType::ToString() + " " + GetAreaType();
}
*/

/*
bool cEdObjTypeArea::IsAppropriateType(cXmlElement* apElement)
{
	if(iEdObjectType::IsAppropriateType(apElement)==false)
		return false;

	return apElement->GetAttributeString("AreaType")==GetAreaType();
}
*/

/*
const tString& cEdObjTypeArea::GetAreaType()
{
	// Name is contained in parent type
	return mpUserType->GetParent()->GetName();
}
*/

iEdObject* cEdObjAreaData::CreateTypeSpecificObject()
{
	return hplNew(cEdObjArea,(this));
}

cEdObjAreaData::cEdObjAreaData(iEdObjectType* apType) : iUserObjData(apType)
{
	SetName(apType->GetName() + _W("Area"));
}

bool cEdObjAreaData::SaveSpecificData(cXmlElement* apElem)
{
	if(iUserObjData::SaveSpecificData(apElem)==false)
		return false;

	apElem->SetAttributeString("AreaType", cString::To8Char(mpType->GetName()));

	return true;
}

// CONSTRUCTORS

cEdObjArea::cEdObjArea(iEdObjectData* apData) : iUserObject(apData)
{
	mpMesh = NULL;
}

cEdObjArea::~cEdObjArea()
{
}

// PUBLIC METHODS

bool cEdObjArea::SetProperty(int alPropID, const tString& asX)
{
	switch(alPropID)
	{
	case eAreaStr_Mesh:
		SetMeshFile(asX);
		break;
	case eAreaStr_Type:
		break;
	default:
		return iEdScnObject::SetProperty(alPropID, asX);
	}

	return true;
}

bool cEdObjArea::GetProperty(int alPropID, tString& asX)
{
	switch(alPropID)
	{
	case eAreaStr_Mesh:
		asX = GetMeshFile();
		break;
	case eAreaStr_Type:
		break;
	default:
		return iEdScnObject::GetProperty(alPropID, asX);
	}

	return true;
}

void cEdObjArea::SetMeshFile(const tString& asX)
{
	// Try to load a mesh for the area
	cResources* pRes = GetWorld()->GetEditor()->GetEngine()->GetResources();
	cMesh* pMesh = NULL;
	cEdLoader::LoadModel(asX, &pMesh);

	if(pMesh==NULL && asX.empty()==false)
		return;

	// Destroy previous entity
	msMeshFile = asX;

	cWorld* pWorld = GetWorld()->GetEngWorld();
	if(mpMesh)
	{
		pWorld->DestroyMeshEntity(mpMesh);
		mpMesh = NULL;
	}

	if(pMesh==NULL)
		return;

	// Create a new one using the new mesh and
	// set it up with a custom material
	cMaterialManager* pMatMgr = pRes->GetMaterialManager();

	mpMesh = pWorld->CreateMeshEntity(cString::To8Char(msName), pMesh, false);
	mpMesh->SetVisible(false);
	for(int i=0;i<mpMesh->GetSubMeshEntityNum();++i)
	{
		cSubMeshEntity* pSubMeshEnt = mpMesh->GetSubMeshEntity(i);
		pSubMeshEnt->SetCustomMaterial(pMatMgr->CreateMaterial("editor_area_mesh.mat"));
	}
}

iEdEditPane* cEdObjArea::CreateEditPane()
{
	return hplNew(cEdEditPaneArea,(this));
}

// PROTECTED METHODS

void cEdObjArea::OnDraw(const cModuleDrawData& aData)
{
	iEdScnObject::OnDraw(aData);

	cTypeArea* pType = static_cast<cTypeArea*>(mpType);

	cColor col;
	if(mbSelected)
		col = cColor(1);
	else
		col = pType->GetColor();

	cMatrixf mtxRT = cMath::MatrixMul(GetTranslationMatrix(), GetRotationMatrix());

	aData.mpFunctions->SetDepthTest(true);
	aData.mpFunctions->SetDepthWrite(false);
	aData.mpFunctions->SetMatrix(&mtxRT);

	aData.mpFunctions->GetLowLevelGfx()->DrawLine(cVector3f(-0.05f,0,0), cVector3f(0.05f,0,0), col);
	aData.mpFunctions->GetLowLevelGfx()->DrawLine(cVector3f(0,-0.05f,0), cVector3f(0,0.05f,0), col);
	aData.mpFunctions->GetLowLevelGfx()->DrawLine(cVector3f(0,0,-0.05f), cVector3f(0,0,0.05f), col);

	cVector3f vHalfScale;
	if(pType->IsScalable())
		vHalfScale = mvScale*0.5f;
	else
		vHalfScale = pType->GetDefaultSize()*0.5f;

	if(pType->GetDrawAsSphere())
	{
		vHalfScale = vHalfScale*0.5f;
		aData.mpFunctions->GetLowLevelGfx()->DrawSphere(0, vHalfScale.x, col);
	}
	else
	{
		aData.mpFunctions->GetLowLevelGfx()->DrawBoxMinMax(vHalfScale*-1, vHalfScale, col);
	}

	if(pType->GetShowOrientation())
	{
		cVector3f vLength = vHalfScale-0.1f;
		for(int i=0;i<3;++i)
		{
			cVector3f vDir = 0;
			cColor col = 0;
			vDir.v[i] = 1;
			vDir = vDir*vLength;
			col.v[i] = 1;
			aData.mpFunctions->GetLowLevelGfx()->DrawLine(0 , vDir, col);
		}
	}

	if(mpMesh)
		mpMesh->SetVisible(mbSelected);

	aData.mpFunctions->SetMatrix(NULL);

}

bool cEdObjArea::OnUpdate(bool abForce)
{
	if(mpMesh)
	{
		cMatrixf mtxRT = cMath::MatrixMul(GetTranslationMatrix(), GetRotationMatrix());
		mpMesh->SetMatrix(mtxRT);
	}

	return true;
}

cObjectIcon* cEdObjArea::CreateIcon()
{
	cTypeArea* pType = static_cast<cTypeArea*>(mpType);
	return hplNew( cObjectIcon, (this, pType->GetIcon()));
}


switch(alPropID)
	{
	case eAreaStr_Mesh:
		SetMeshFile(asX);
		break;
	case eAreaStr_Type:
		break;
	default:
		return iEdScnObject::SetProperty(alPropID, asX);
	}

/*
 *
 * This file is part of Amnesia: The Dark Descent.
 *
 */

#include "../Common/EdObjArea.h"

#include "../Common/EdWorld.h"
#include "../Common/Editor.h"
#include "../Common/EdResource.h"

#include "../Common/EdObjectIcon.h"

#include "../Common/EdEditPaneArea.h"

cTypeRootArea::cTypeRootArea() : iUserTypeRoot(_W("Area"), "Area", "AreaClasses.def")
{
	RegisterVarCatName(eEdVarCategory_Editor, "EditorSetupVars");
	RegisterVarCatName(eEdVarCategory_Instance, "Vars");

	AddBool(eScnBool_Active, "Active");
	AddString(eAreaStr_Mesh, "Mesh", "");
}

iEdObjectType* cTypeRootArea::GetTypeByXmlElement(cXmlElement* apElem)
{
	if(iUserTypeRoot::GetTypeByXmlElement(apElem)==NULL)
		return NULL;

	tString sAreaType = apElem->GetAttributeString("AreaType", "");

	return GetTypeByName(cString::To16Char(sAreaType));
}

iUserTypeNode* cTypeRootArea::CreateSpecificUserTypeNode(iEdClass* apClass)
{
	return hplNew(cTypeArea,(this, apClass));
}

cTypeArea::cTypeArea(iUserTypeRoot* apRoot, iEdClass* apClass) : iUserTypeNode(apRoot, apClass)
{
	mColor = cColor(1,0,1,1);
	mbShowOrientation = false;
	mbDrawAsSphere = false;
	mvDefaultSize = 1;
	mbRotateable = true;

	msIcon = "Area";

	/*////////////////////////////////////////
	// Set up defaults
	mColor = cColor(1,0,1,1);
	mbShowOrientation = false;
	mbDrawAsSphere = false;
	mvDefaultSize = 1;
	mbCanRotate = true;
	mScaleType = eScaleType_Normal;

	msIcon = "Area";
	*/

	// Retrieve type characteristics

	cEdUserClass* pClass = static_cast<cEdUserClass*>(apClass);
	cEdClassInstance* pClassInst = pClass->CreateInstance(eEdVarCategory_Editor);

	tString sColor, sShowOrientation, sSphere, sDefaultSize, sIcon, sAllowRotate, sAllowScale;

	for(int i=0;i<pClassInst->GetVarInstanceNum();++i)
	{
		cEdVarInstance* pVar = pClassInst->GetVarInstance(i);
		const tWString& sName = pVar->GetName();
		tString sValue = cString::To8Char(pVar->GetValue());

		if(sName==_W("Color"))
			mColor = cString::ToColor(sValue.c_str(), mColor);

		else if(sName==_W("ShowOrientation"))
			mbShowOrientation = cString::ToBool(sValue.c_str(), mbShowOrientation);

		else if(sName==_W("Sphere"))
			mbDrawAsSphere = cString::ToBool(sValue.c_str(), mbDrawAsSphere);

		else if(sName==_W("DefaultSize"))
			mvDefaultSize = cString::ToVector3f(sValue.c_str(), mvDefaultSize);

		else if(sName==_W("AllowRotate"))
			mbRotateable = cString::ToBool(sValue.c_str(), mbRotateable);

		else if(sName==_W("AllowScale"))
		{
		}

		else if(sName==_W("Icon"))
			msIcon = sValue==""? msIcon : sValue;
	}
	hplDelete(pClassInst);
}

/*
tString cEdObjTypeArea::ToString()
{
	return iEdObjectType::ToString() + " " + GetAreaType();
}
*/

/*
bool cEdObjTypeArea::IsAppropriateType(cXmlElement* apElement)
{
	if(iEdObjectType::IsAppropriateType(apElement)==false)
		return false;

	return apElement->GetAttributeString("AreaType")==GetAreaType();
}
*/

/*
const tString& cEdObjTypeArea::GetAreaType()
{
	// Name is contained in parent type
	return mpUserType->GetParent()->GetName();
}
*/

iEdObjectData* cTypeArea::CreateTypeSpecificData()
{
	return hplNew(cEdObjAreaData,(this));
}

iEdObject* cEdObjAreaData::CreateTypeSpecificObject()
{
	return hplNew(cEdObjArea,(this));
}

cEdObjAreaData::cEdObjAreaData(iEdObjectType* apType) : iUserObjData(apType)
{
	SetName(apType->GetName() + _W("Area"));
}

bool cEdObjAreaData::SaveSpecificData(cXmlElement* apElem)
{
	if(iUserObjData::SaveSpecificData(apElem)==false)
		return false;

	apElem->SetAttributeString("AreaType", cString::To8Char(mpType->GetName()));

	return true;
}

// CONSTRUCTORS

cEdObjArea::cEdObjArea(iEdObjectData* apData) : iUserObject(apData)
{
	mpMesh = NULL;
}

cEdObjArea::~cEdObjArea()
{
}

// PUBLIC METHODS

bool cEdObjArea::SetProperty(int alPropID, const tString& asX)
{

	return true;
}

bool cEdObjArea::GetProperty(int alPropID, tString& asX)
{
	switch(alPropID)
	{
	case eAreaStr_Mesh:
		asX = GetMeshFile();
		break;
	case eAreaStr_Type:
		break;
	default:
		return iEdScnObject::GetProperty(alPropID, asX);
	}

	return true;
}

void cEdObjArea::SetMeshFile(const tString& asX)
{
	// Try to load a mesh for the area
	cResources* pRes = GetWorld()->GetEditor()->GetEngine()->GetResources();
	cMesh* pMesh = NULL;
	cEdLoader::LoadModel(asX, &pMesh);

	if(pMesh==NULL && asX.empty()==false)
		return;

	// Destroy previous entity
	msMeshFile = asX;

	cWorld* pWorld = GetWorld()->GetEngWorld();
	if(mpMesh)
	{
		pWorld->DestroyMeshEntity(mpMesh);
		mpMesh = NULL;
	}

	if(pMesh==NULL)
		return;

	// Create a new one using the new mesh and
	// set it up with a custom material
	cMaterialManager* pMatMgr = pRes->GetMaterialManager();

	mpMesh = pWorld->CreateMeshEntity(cString::To8Char(msName), pMesh, false);
	mpMesh->SetVisible(false);
	for(int i=0;i<mpMesh->GetSubMeshEntityNum();++i)
	{
		cSubMeshEntity* pSubMeshEnt = mpMesh->GetSubMeshEntity(i);
		pSubMeshEnt->SetCustomMaterial(pMatMgr->CreateMaterial("editor_area_mesh.mat"));
	}
}

iEdEditPane* cEdObjArea::CreateEditPane()
{
	return hplNew(cEdEditPaneArea,(this));
}

// PROTECTED METHODS

void cEdObjArea::OnDraw(const cModuleDrawData& aData)
{
	iEdScnObject::OnDraw(aData);

	cTypeArea* pType = static_cast<cTypeArea*>(mpType);

	cColor col;
	if(mbSelected)
		col = cColor(1);
	else
		col = pType->GetColor();

	cMatrixf mtxRT = cMath::MatrixMul(GetTranslationMatrix(), GetRotationMatrix());

	aData.mpFunctions->SetDepthTest(true);
	aData.mpFunctions->SetDepthWrite(false);
	aData.mpFunctions->SetMatrix(&mtxRT);

	aData.mpFunctions->GetLowLevelGfx()->DrawLine(cVector3f(-0.05f,0,0), cVector3f(0.05f,0,0), col);
	aData.mpFunctions->GetLowLevelGfx()->DrawLine(cVector3f(0,-0.05f,0), cVector3f(0,0.05f,0), col);
	aData.mpFunctions->GetLowLevelGfx()->DrawLine(cVector3f(0,0,-0.05f), cVector3f(0,0,0.05f), col);

	cVector3f vHalfScale;
	if(pType->IsScalable())
		vHalfScale = mvScale*0.5f;
	else
		vHalfScale = pType->GetDefaultSize()*0.5f;

	if(pType->GetDrawAsSphere())
	{
		vHalfScale = vHalfScale*0.5f;
		aData.mpFunctions->GetLowLevelGfx()->DrawSphere(0, vHalfScale.x, col);
	}
	else
	{
		aData.mpFunctions->GetLowLevelGfx()->DrawBoxMinMax(vHalfScale*-1, vHalfScale, col);
	}

	if(pType->GetShowOrientation())
	{
		cVector3f vLength = vHalfScale-0.1f;
		for(int i=0;i<3;++i)
		{
			cVector3f vDir = 0;
			cColor col = 0;
			vDir.v[i] = 1;
			vDir = vDir*vLength;
			col.v[i] = 1;
			aData.mpFunctions->GetLowLevelGfx()->DrawLine(0 , vDir, col);
		}
	}

	if(mpMesh)
		mpMesh->SetVisible(mbSelected);

	aData.mpFunctions->SetMatrix(NULL);

}

bool cEdObjArea::OnUpdate(bool abForce)
{
	if(mpMesh)
	{
		cMatrixf mtxRT = cMath::MatrixMul(GetTranslationMatrix(), GetRotationMatrix());
		mpMesh->SetMatrix(mtxRT);
	}

	return true;
}

cObjectIcon* cEdObjArea::CreateIcon()
{
	cTypeArea* pType = static_cast<cTypeArea*>(mpType);
	return hplNew( cObjectIcon, (this, pType->GetIcon()));
}


// Original code at: http://example.com

#include <celero\Celero.h>
#include <emmintrin.h>
#include <omp.h>

#include <array>
#include <bitset>
#include <iostream>
#include <random>

CELERO_MAIN;

static const int SamplesCount = 1000;
static const int IterationsCount = 5;
static const int ThresholdValue = 127;
static const int MaximumDistribution = 255;

void Checker(bool val, bool reference, uint64_t i)
{
	if(val != reference)
	{
		std::cout << "Mismatch at index: " << i << "\n";
	}
}

bool GetBoolAt(int64_t pos, uint8_t* outputValues)
{
	const auto bytePos = pos / 8;
	const auto bitPos = pos % 8;
	return ((*(outputValues + bytePos)) & (1 << bitPos)) > 0;
}

/// \struct MyExperimentValue
/// \brief Simple extension of the ExperimentValue struct.
class MyExperimentValue : public celero::TestFixture::ExperimentValue
{
public:
	MyExperimentValue() : celero::TestFixture::ExperimentValue(){};
	virtual ~MyExperimentValue() = default;

	uint64_t NumBytes{0};
	uint64_t NumFullBytes{0};
};

class CompressBoolsFixture : public celero::TestFixture
{
public:
	std::vector<celero::TestFixture::ExperimentValue> getExperimentValues() const override
	{
		std::vector<celero::TestFixture::ExperimentValue> problemSpace;

		const auto stepCount = MaxArrayLength / BenchmarkSteps;

		for(int64_t i = 0; i < BenchmarkSteps; i++)
		{
			// ExperimentValues is part of the base class and allows us to specify
			// some values to control various test runs to end up building a nice graph.
			MyExperimentValue ev;
			ev.Value = stepCount + i * stepCount;
			ev.Iterations = int64_t(i + 1);
			ev.NumBytes = static_cast<unsigned int>((ev.Value + 7) / 8);
			ev.NumFullBytes = static_cast<unsigned int>((ev.Value) / 8);
			problemSpace.push_back(ev);
		}

		return problemSpace;
	}

	/// Before each run, build a vector of random integers.
	void setUp(const celero::TestFixture::ExperimentValue& experimentValue) override
	{
		this->arrayLength = static_cast<size_t>(experimentValue.Value);
		this->inputValues.reset(new int[arrayLength]);
		this->referenceValues.reset(new bool[arrayLength]);

		std::mt19937 gen(0); // Standard mersenne_twister_engine seeded with 0, constant
		std::uniform_int_distribution<> dist(0, MaximumDistribution);

		// set every byte, copute reference values
		for(int64_t i = 0; i < experimentValue.Value; ++i)
		{
			this->inputValues[i] = dist(gen);
			this->referenceValues[i] = this->inputValues[i] > ThresholdValue;
		}
	}

#ifdef _DEBUG
	static const int64_t MaxArrayLength{100};
	static const int64_t BenchmarkSteps{1};
#else
	static const int64_t MaxArrayLength{5000000};
	static const int64_t BenchmarkSteps{5};
#endif

	size_t arrayLength{0};
	std::unique_ptr<int[]> inputValues;
	std::unique_ptr<bool[]> referenceValues;
};

class NoPackingFixture : public CompressBoolsFixture
{
public:
	void setUp(const celero::TestFixture::ExperimentValue& x) override
	{
		CompressBoolsFixture::setUp(x);
		this->outputValues.reset(new bool[static_cast<unsigned int>(arrayLength)]);
	}

	void tearDown() override
	{
		for(size_t i = 0; i < arrayLength; ++i)
		{
			Checker(outputValues[i], referenceValues[i], i);
		}
	}

	std::unique_ptr<bool[]> outputValues;
};

BASELINE_F(CompressBoolsTest, NoPackingVersion, NoPackingFixture, SamplesCount, IterationsCount)
{
	for(size_t i = 0; i < this->arrayLength; ++i)
	{
		this->outputValues[i] = this->inputValues[i] > ThresholdValue;
	}
}

class StdBitsetFixture : public CompressBoolsFixture
{
public:
	void tearDown() override
	{
		for(size_t i = 0; i < this->arrayLength; ++i)
		{
			Checker(this->outputBitset[i], this->referenceValues[i], i);
		}
	}

	std::bitset<MaxArrayLength> outputBitset;
};

// Run an automatic baseline.
// Celero will help make sure enough samples are taken to get a reasonable measurement
BENCHMARK_F(CompressBoolsTest, StdBitset, StdBitsetFixture, SamplesCount, IterationsCount)
{
	for(size_t i = 0; i < this->arrayLength; ++i)
	{
		this->outputBitset.set(i, this->inputValues[i] > ThresholdValue);
	}
}

class StdVectorFixture : public CompressBoolsFixture
{
public:
	void setUp(const celero::TestFixture::ExperimentValue& experimentValue) override
	{
		CompressBoolsFixture::setUp(experimentValue);
		this->outputVector.resize(static_cast<unsigned int>(experimentValue.Value));
	}

	void tearDown() override
	{
		for(size_t i = 0; i < arrayLength; ++i)
		{
			Checker(this->outputVector[i], this->referenceValues[i], i);
		}
	}

	std::vector<bool> outputVector;
};

BENCHMARK_F(CompressBoolsTest, StdVector, StdVectorFixture, SamplesCount, IterationsCount)
{
	for(size_t i = 0; i < this->arrayLength; ++i)
	{
		this->outputVector[i] = this->inputValues[i] > ThresholdValue;
	}
}

class ManualVersionFixture : public CompressBoolsFixture
{
public:
	ManualVersionFixture() : numBytes(0), numFullBytes(0)
	{
	}

	void setUp(const celero::TestFixture::ExperimentValue& experimentValue) override
	{
		CompressBoolsFixture::setUp(experimentValue);

		auto ev = dynamic_cast<const MyExperimentValue&>(experimentValue);
		this->numBytes = ev.NumBytes;
		this->numFullBytes = ev.NumFullBytes;
		this->outputValues.reset(new uint8_t[this->numBytes]);
	}

{
		for(size_t i = 0; i < this->arrayLength; ++i)
		{
			Checker(GetBoolAt(i, outputValues.get()), referenceValues[i], i);
		}
	}

	uint64_t numBytes{0};
	uint64_t numFullBytes{0};
	std::unique_ptr<uint8_t[]> outputValues;
};

BENCHMARK_F(CompressBoolsTest, SingleVarVersion, ManualVersionFixture, SamplesCount, IterationsCount)
{
	uint8_t OutByte = 0;
	int shiftCounter = 0;

	auto pInputData = inputValues.get();
	auto pOutputByte = outputValues.get();

	for(size_t i = 0; i < this->arrayLength; ++i)
	{
		if(*pInputData > ThresholdValue)
		{
			OutByte |= (1 << shiftCounter);
		}

		pInputData++;
		shiftCounter++;

		if(shiftCounter > 7)
		{
			*pOutputByte++ = OutByte;
			OutByte = 0;
			shiftCounter = 0;
		}
	}

	// our byte might be incomplete, so we need to handle this:
	if(this->arrayLength & 7)
	{
		*pOutputByte++ = OutByte;
	}
}

BENCHMARK_F(CompressBoolsTest, NotDepentendVersion, ManualVersionFixture, SamplesCount, IterationsCount)
{
	uint8_t Bits[8] = {0};
	const int64_t lenDivBy8 = (this->arrayLength / 8) * 8;

	auto pInputData = inputValues.get();
	auto pOutputByte = outputValues.get();

	for(int64_t j = 0; j < lenDivBy8; j += 8)
	{
		Bits[0] = pInputData[0] > ThresholdValue ? 0x01 : 0;
		Bits[1] = pInputData[1] > ThresholdValue ? 0x02 : 0;
		Bits[2] = pInputData[2] > ThresholdValue ? 0x04 : 0;
		Bits[3] = pInputData[3] > ThresholdValue ? 0x08 : 0;
		Bits[4] = pInputData[4] > ThresholdValue ? 0x10 : 0;
		Bits[5] = pInputData[5] > ThresholdValue ? 0x20 : 0;
		Bits[6] = pInputData[6] > ThresholdValue ? 0x40 : 0;
		Bits[7] = pInputData[7] > ThresholdValue ? 0x80 : 0;

		*pOutputByte++ = Bits[0] | Bits[1] | Bits[2] | Bits[3] | Bits[4] | Bits[5] | Bits[6] | Bits[7];
		pInputData += 8;
	}

	if(arrayLength & 7)
	{
		// note that we'll use max 7 elements, so max is Bits[6]... (otherwise we would get another full byte)
		auto RestW = arrayLength & 7;
		memset(Bits, 0, 8);

		for(unsigned long long i = 0; i < RestW; ++i)
		{
			Bits[i] = *pInputData > ThresholdValue ? 1 << i : 0;
			pInputData++;
		}

		*pOutputByte++ = Bits[0] | Bits[1] | Bits[2] | Bits[3] | Bits[4] | Bits[5] | Bits[6];
	}
}

struct bool8
{
	bool8() : val0(0), val1(0), val2(0), val3(0), val4(0), val5(0), val6(0), val7(0)
	{
	}

	bool8(bool v0, bool v1, bool v2, bool v3, bool v4, bool v5, bool v6, bool v7)
		: val0(v0), val1(v1), val2(v2), val3(v3), val4(v4), val5(v5), val6(v6), val7(v7)
	{
	}

	uint8_t val0 : 1;
	uint8_t val1 : 1;
	uint8_t val2 : 1;
	uint8_t val3 : 1;
	uint8_t val4 : 1;
	uint8_t val5 : 1;
	uint8_t val6 : 1;
	uint8_t val7 : 1;
};

class PackedStructFixture : public CompressBoolsFixture
{
public:
	void setUp(const celero::TestFixture::ExperimentValue& experimentValue) override
	{
		CompressBoolsFixture::setUp(experimentValue);

		this->numBytes = static_cast<unsigned int>((experimentValue.Value + 7) / 8);
		this->numFullBytes = static_cast<unsigned int>((experimentValue.Value) / 8);
		this->outputValues.reset(new bool8[numBytes]);
	}

	void tearDown() override
	{
		for(size_t i = 0; i < arrayLength; ++i)
		{
			Checker(getBoolAt(i), referenceValues[i], i);
		}
	}

	bool getBoolAt(int64_t pos)
	{
		const auto bytePos = pos / 8;
		const auto bitPos = pos % 8;

		switch(bitPos)
		{
			case 0:
				return (*(outputValues.get() + bytePos)).val0;
			case 1:
				return (*(outputValues.get() + bytePos)).val1;
			case 2:
				return (*(outputValues.get() + bytePos)).val2;
			case 3:
				return (*(outputValues.get() + bytePos)).val3;
			case 4:
				return (*(outputValues.get() + bytePos)).val4;
			case 5:
				return (*(outputValues.get() + bytePos)).val5;
			case 6:
				return (*(outputValues.get() + bytePos)).val6;
			case 7:
				return (*(outputValues.get() + bytePos)).val7;
		}

		return (*(outputValues.get() + bytePos)).val7;
	}

	unsigned int numBytes{0};
	unsigned int numFullBytes{0};
	std::unique_ptr<bool8[]> outputValues;
};

BENCHMARK_F(CompressBoolsTest, PackedStructVersion, PackedStructFixture, SamplesCount, IterationsCount)
{
	const int64_t lenDivBy8 = (arrayLength / 8) * 8;

	auto pInputData = inputValues.get();
	auto pOutputByte = outputValues.get();

	bool8 out;

	for(int64_t j = 0; j < lenDivBy8; j += 8)
	{
		out.val0 = pInputData[0] > ThresholdValue;
		out.val1 = pInputData[1] > ThresholdValue;
		out.val2 = pInputData[2] > ThresholdValue;
		out.val3 = pInputData[3] > ThresholdValue;
		out.val4 = pInputData[4] > ThresholdValue;
		out.val5 = pInputData[5] > ThresholdValue;
		out.val6 = pInputData[6] > ThresholdValue;
		out.val7 = pInputData[7] > ThresholdValue;

		*pOutputByte++ = out;
		pInputData += 8;
	}

	if(arrayLength & 7)
	{
		auto RestW = arrayLength & 7;
		out = {false, false, false, false, false, false, false, false};

		if(RestW > 6)
		{
			out.val6 = pInputData[6] > ThresholdValue;
		}

		if(RestW > 5)
		{
			out.val5 = pInputData[5] > ThresholdValue;
		}

		if(RestW > 4)
		{
			out.val4 = pInputData[4] > ThresholdValue;
		}

		if(RestW > 3)
		{
			out.val3 = pInputData[3] > ThresholdValue;
		}

		if(RestW > 2)
		{
			out.val2 = pInputData[2] > ThresholdValue;
		}

		if(RestW > 1)
		{
			out.val1 = pInputData[1] > ThresholdValue;
		}

		if(RestW > 0)
		{
			out.val0 = pInputData[0] > ThresholdValue;
		}

		*pOutputByte++ = out;
	}
}

BENCHMARK_F(CompressBoolsTest, WithOpenMP, ManualVersionFixture, SamplesCount, IterationsCount)
{
	uint8_t Bits[8];
#pragma omp parallel for private(Bits)
	for(unsigned int i = 0; i < numFullBytes; ++i)
	{
		auto pInputData = inputValues.get() + i * 8;
		Bits[0] = pInputData[0] > ThresholdValue ? 0x01 : 0;
		Bits[1] = pInputData[1] > ThresholdValue ? 0x02 : 0;
		Bits[2] = pInputData[2] > ThresholdValue ? 0x04 : 0;
		Bits[3] = pInputData[3] > ThresholdValue ? 0x08 : 0;
		Bits[4] = pInputData[4] > ThresholdValue ? 0x10 : 0;
		Bits[5] = pInputData[5] > ThresholdValue ? 0x20 : 0;
		Bits[6] = pInputData[6] > ThresholdValue ? 0x40 : 0;
		Bits[7] = pInputData[7] > ThresholdValue ? 0x80 : 0;

		this->outputValues.get()[i] = Bits[0] | Bits[1] | Bits[2] | Bits[3] | Bits[4] | Bits[5] | Bits[6] | Bits[7];
	}

	if(numFullBytes < numBytes)
	{
		uint8_t Bits2[8] = {0};
		auto RestW = arrayLength & 7;
		auto pInputData = inputValues.get() + numFullBytes * 8;

		for(unsigned long long i = 0; i < RestW; ++i)
		{
			Bits2[i] = *pInputData > ThresholdValue ? 1 << i : 0;
			pInputData++;
		}

		this->outputValues.get()[numFullBytes] = Bits2[0] | Bits2[1] | Bits2[2] | Bits2[3] | Bits2[4] | Bits2[5] | Bits2[6];
	}
}

class SimdVersionFixture : public CompressBoolsFixture
{
public:
	void setUp(const celero::TestFixture::ExperimentValue& experimentValue) override
	{
		CompressBoolsFixture::setUp(experimentValue);

		this->numBytes = static_cast<unsigned int>((experimentValue.Value + 7) / 8);
		this->numFullBytes = static_cast<unsigned int>((experimentValue.Value) / 8);
		this->alignedOutputValues = (uint8_t*)_aligned_malloc(numBytes, 16);
		this->signedInputValues.reset(new int8_t[arrayLength]);

		for(size_t i = 0; i < this->arrayLength; ++i)
		{
			this->signedInputValues[i] = static_cast<int8_t>(this->inputValues[i] - 128);
		}
	}

	void tearDown() override
	{
		for(size_t i = 0; i < this->arrayLength; ++i)
		{
			Checker(GetBoolAt(i, this->alignedOutputValues), this->referenceValues[i], i);
		}

		if(this->alignedOutputValues)
		{
			_aligned_free(this->alignedOutputValues);
			this->alignedOutputValues = nullptr;
		}
	}

	unsigned int numBytes{0};
	unsigned int numFullBytes{0};
	uint8_t* alignedOutputValues{nullptr};
	std::unique_ptr<int8_t[]> signedInputValues;
};

BENCHMARK_F(CompressBoolsTest, SimdVersion, SimdVersionFixture, SamplesCount, IterationsCount)
{
	const size_t lenDiv16y16 = (arrayLength / 16) * 16; // full packs of 16 values...

	const __m128i sse127 = _mm_set1_epi8(127);
	const int8_t ConvertedThreshold = ThresholdValue - 128;
	const __m128i sseThresholds = _mm_set1_epi8(ConvertedThreshold);

	auto pInputData = signedInputValues.get();
	auto pOutputByte = alignedOutputValues;

	for(size_t j = 0; j < lenDiv16y16; j += 16)
	{
		const auto in16Values =
			_mm_set_epi8(pInputData[15], pInputData[14], pInputData[13], pInputData[12], pInputData[11], pInputData[10], pInputData[9], pInputData[8],
						 pInputData[7], pInputData[6], pInputData[5], pInputData[4], pInputData[3], pInputData[2], pInputData[1], pInputData[0]);
		const auto cmpRes = _mm_cmpgt_epi8(in16Values, sseThresholds);
		const auto packed = _mm_movemask_epi8(cmpRes);
		*((uint16_t*)pOutputByte) = static_cast<uint16_t>(packed);
		pOutputByte += 2;
		pInputData += 16;
	}

	if(arrayLength & 15)
	{
		auto RestW = arrayLength & 15;

		std::array<uint16_t, 16> Bits;

		for(size_t i = 0; i < RestW; ++i)
		{
			Bits[i] = *pInputData > ConvertedThreshold ? 1 << i : 0;
			pInputData++;
		}

		*pOutputByte++ = static_cast<uint8_t>(Bits[0] | Bits[1] | Bits[2] | Bits[3] | Bits[4] | Bits[5] | Bits[6] | Bits[7]);

		if(RestW > 8)
		{
			*pOutputByte++ = (Bits[8] | Bits[9] | Bits[10] | Bits[11] | Bits[12] | Bits[13] | Bits[14] | Bits[15]) >> 8;
		}
	}
}


{
		for(size_t i = 0; i < this->arrayLength; ++i)
		{
			Checker(this->outputBitset[i], this->referenceValues[i], i);
		}
	}

// Original code at: http://example.com

#include <celero\Celero.h>
#include <emmintrin.h>
#include <omp.h>

#include <array>
#include <bitset>
#include <iostream>
#include <random>

CELERO_MAIN;

static const int SamplesCount = 1000;
static const int IterationsCount = 5;
static const int ThresholdValue = 127;
static const int MaximumDistribution = 255;

void Checker(bool val, bool reference, uint64_t i)
{
	if(val != reference)
	{
		std::cout << "Mismatch at index: " << i << "\n";
	}
}

bool GetBoolAt(int64_t pos, uint8_t* outputValues)
{
	const auto bytePos = pos / 8;
	const auto bitPos = pos % 8;
	return ((*(outputValues + bytePos)) & (1 << bitPos)) > 0;
}

/// \struct MyExperimentValue
/// \brief Simple extension of the ExperimentValue struct.
class MyExperimentValue : public celero::TestFixture::ExperimentValue
{
public:
	MyExperimentValue() : celero::TestFixture::ExperimentValue(){};
	virtual ~MyExperimentValue() = default;

	uint64_t NumBytes{0};
	uint64_t NumFullBytes{0};
};

class CompressBoolsFixture : public celero::TestFixture
{
public:
	std::vector<celero::TestFixture::ExperimentValue> getExperimentValues() const override
	{
		std::vector<celero::TestFixture::ExperimentValue> problemSpace;

		const auto stepCount = MaxArrayLength / BenchmarkSteps;

		for(int64_t i = 0; i < BenchmarkSteps; i++)
		{
			// ExperimentValues is part of the base class and allows us to specify
			// some values to control various test runs to end up building a nice graph.
			MyExperimentValue ev;
			ev.Value = stepCount + i * stepCount;
			ev.Iterations = int64_t(i + 1);
			ev.NumBytes = static_cast<unsigned int>((ev.Value + 7) / 8);
			ev.NumFullBytes = static_cast<unsigned int>((ev.Value) / 8);
			problemSpace.push_back(ev);
		}

		return problemSpace;
	}

	/// Before each run, build a vector of random integers.
	void setUp(const celero::TestFixture::ExperimentValue& experimentValue) override
	{
		this->arrayLength = static_cast<size_t>(experimentValue.Value);
		this->inputValues.reset(new int[arrayLength]);
		this->referenceValues.reset(new bool[arrayLength]);

		std::mt19937 gen(0); // Standard mersenne_twister_engine seeded with 0, constant
		std::uniform_int_distribution<> dist(0, MaximumDistribution);

		// set every byte, copute reference values
		for(int64_t i = 0; i < experimentValue.Value; ++i)
		{
			this->inputValues[i] = dist(gen);
			this->referenceValues[i] = this->inputValues[i] > ThresholdValue;
		}
	}

#ifdef _DEBUG
	static const int64_t MaxArrayLength{100};
	static const int64_t BenchmarkSteps{1};
#else
	static const int64_t MaxArrayLength{5000000};
	static const int64_t BenchmarkSteps{5};
#endif

	size_t arrayLength{0};
	std::unique_ptr<int[]> inputValues;
	std::unique_ptr<bool[]> referenceValues;
};

class NoPackingFixture : public CompressBoolsFixture
{
public:
	void setUp(const celero::TestFixture::ExperimentValue& x) override
	{
		CompressBoolsFixture::setUp(x);
		this->outputValues.reset(new bool[static_cast<unsigned int>(arrayLength)]);
	}

	void tearDown() override
	{
		for(size_t i = 0; i < arrayLength; ++i)
		{
			Checker(outputValues[i], referenceValues[i], i);
		}
	}

	std::unique_ptr<bool[]> outputValues;
};

BASELINE_F(CompressBoolsTest, NoPackingVersion, NoPackingFixture, SamplesCount, IterationsCount)
{
	for(size_t i = 0; i < this->arrayLength; ++i)
	{
		this->outputValues[i] = this->inputValues[i] > ThresholdValue;
	}
}

class StdBitsetFixture : public CompressBoolsFixture
{
public:
	void tearDown() override

	std::bitset<MaxArrayLength> outputBitset;
};

// Run an automatic baseline.
// Celero will help make sure enough samples are taken to get a reasonable measurement
BENCHMARK_F(CompressBoolsTest, StdBitset, StdBitsetFixture, SamplesCount, IterationsCount)
{
	for(size_t i = 0; i < this->arrayLength; ++i)
	{
		this->outputBitset.set(i, this->inputValues[i] > ThresholdValue);
	}
}

class StdVectorFixture : public CompressBoolsFixture
{
public:
	void setUp(const celero::TestFixture::ExperimentValue& experimentValue) override
	{
		CompressBoolsFixture::setUp(experimentValue);
		this->outputVector.resize(static_cast<unsigned int>(experimentValue.Value));
	}

	void tearDown() override
	{
		for(size_t i = 0; i < arrayLength; ++i)
		{
			Checker(this->outputVector[i], this->referenceValues[i], i);
		}
	}

	std::vector<bool> outputVector;
};

BENCHMARK_F(CompressBoolsTest, StdVector, StdVectorFixture, SamplesCount, IterationsCount)
{
	for(size_t i = 0; i < this->arrayLength; ++i)
	{
		this->outputVector[i] = this->inputValues[i] > ThresholdValue;
	}
}

class ManualVersionFixture : public CompressBoolsFixture
{
public:
	ManualVersionFixture() : numBytes(0), numFullBytes(0)
	{
	}

	void setUp(const celero::TestFixture::ExperimentValue& experimentValue) override
	{
		CompressBoolsFixture::setUp(experimentValue);

		auto ev = dynamic_cast<const MyExperimentValue&>(experimentValue);
		this->numBytes = ev.NumBytes;
		this->numFullBytes = ev.NumFullBytes;
		this->outputValues.reset(new uint8_t[this->numBytes]);
	}

	void tearDown() override
	{
		for(size_t i = 0; i < this->arrayLength; ++i)
		{
			Checker(GetBoolAt(i, outputValues.get()), referenceValues[i], i);
		}
	}

	uint64_t numBytes{0};
	uint64_t numFullBytes{0};
	std::unique_ptr<uint8_t[]> outputValues;
};

BENCHMARK_F(CompressBoolsTest, SingleVarVersion, ManualVersionFixture, SamplesCount, IterationsCount)
{
	uint8_t OutByte = 0;
	int shiftCounter = 0;

	auto pInputData = inputValues.get();
	auto pOutputByte = outputValues.get();

	for(size_t i = 0; i < this->arrayLength; ++i)
	{
		if(*pInputData > ThresholdValue)
		{
			OutByte |= (1 << shiftCounter);
		}

		pInputData++;
		shiftCounter++;

		if(shiftCounter > 7)
		{
			*pOutputByte++ = OutByte;
			OutByte = 0;
			shiftCounter = 0;
		}
	}

	// our byte might be incomplete, so we need to handle this:
	if(this->arrayLength & 7)
	{
		*pOutputByte++ = OutByte;
	}
}

BENCHMARK_F(CompressBoolsTest, NotDepentendVersion, ManualVersionFixture, SamplesCount, IterationsCount)
{
	uint8_t Bits[8] = {0};
	const int64_t lenDivBy8 = (this->arrayLength / 8) * 8;

	auto pInputData = inputValues.get();
	auto pOutputByte = outputValues.get();

	for(int64_t j = 0; j < lenDivBy8; j += 8)
	{
		Bits[0] = pInputData[0] > ThresholdValue ? 0x01 : 0;
		Bits[1] = pInputData[1] > ThresholdValue ? 0x02 : 0;
		Bits[2] = pInputData[2] > ThresholdValue ? 0x04 : 0;
		Bits[3] = pInputData[3] > ThresholdValue ? 0x08 : 0;
		Bits[4] = pInputData[4] > ThresholdValue ? 0x10 : 0;
		Bits[5] = pInputData[5] > ThresholdValue ? 0x20 : 0;
		Bits[6] = pInputData[6] > ThresholdValue ? 0x40 : 0;
		Bits[7] = pInputData[7] > ThresholdValue ? 0x80 : 0;

		*pOutputByte++ = Bits[0] | Bits[1] | Bits[2] | Bits[3] | Bits[4] | Bits[5] | Bits[6] | Bits[7];
		pInputData += 8;
	}

	if(arrayLength & 7)
	{
		// note that we'll use max 7 elements, so max is Bits[6]... (otherwise we would get another full byte)
		auto RestW = arrayLength & 7;
		memset(Bits, 0, 8);

		for(unsigned long long i = 0; i < RestW; ++i)
		{
			Bits[i] = *pInputData > ThresholdValue ? 1 << i : 0;
			pInputData++;
		}

		*pOutputByte++ = Bits[0] | Bits[1] | Bits[2] | Bits[3] | Bits[4] | Bits[5] | Bits[6];
	}
}

struct bool8
{
	bool8() : val0(0), val1(0), val2(0), val3(0), val4(0), val5(0), val6(0), val7(0)
	{
	}

	bool8(bool v0, bool v1, bool v2, bool v3, bool v4, bool v5, bool v6, bool v7)
		: val0(v0), val1(v1), val2(v2), val3(v3), val4(v4), val5(v5), val6(v6), val7(v7)
	{
	}

	uint8_t val0 : 1;
	uint8_t val1 : 1;
	uint8_t val2 : 1;
	uint8_t val3 : 1;
	uint8_t val4 : 1;
	uint8_t val5 : 1;
	uint8_t val6 : 1;
	uint8_t val7 : 1;
};

class PackedStructFixture : public CompressBoolsFixture
{
public:
	void setUp(const celero::TestFixture::ExperimentValue& experimentValue) override
	{
		CompressBoolsFixture::setUp(experimentValue);

		this->numBytes = static_cast<unsigned int>((experimentValue.Value + 7) / 8);
		this->numFullBytes = static_cast<unsigned int>((experimentValue.Value) / 8);
		this->outputValues.reset(new bool8[numBytes]);
	}

	void tearDown() override
	{
		for(size_t i = 0; i < arrayLength; ++i)
		{
			Checker(getBoolAt(i), referenceValues[i], i);
		}
	}

	bool getBoolAt(int64_t pos)
	{
		const auto bytePos = pos / 8;
		const auto bitPos = pos % 8;

		switch(bitPos)
		{
			case 0:
				return (*(outputValues.get() + bytePos)).val0;
			case 1:
				return (*(outputValues.get() + bytePos)).val1;
			case 2:
				return (*(outputValues.get() + bytePos)).val2;
			case 3:
				return (*(outputValues.get() + bytePos)).val3;
			case 4:
				return (*(outputValues.get() + bytePos)).val4;
			case 5:
				return (*(outputValues.get() + bytePos)).val5;
			case 6:
				return (*(outputValues.get() + bytePos)).val6;
			case 7:
				return (*(outputValues.get() + bytePos)).val7;
		}

		return (*(outputValues.get() + bytePos)).val7;
	}

	unsigned int numBytes{0};
	unsigned int numFullBytes{0};
	std::unique_ptr<bool8[]> outputValues;
};

BENCHMARK_F(CompressBoolsTest, PackedStructVersion, PackedStructFixture, SamplesCount, IterationsCount)
{
	const int64_t lenDivBy8 = (arrayLength / 8) * 8;

	auto pInputData = inputValues.get();
	auto pOutputByte = outputValues.get();

	bool8 out;

	for(int64_t j = 0; j < lenDivBy8; j += 8)
	{
		out.val0 = pInputData[0] > ThresholdValue;
		out.val1 = pInputData[1] > ThresholdValue;
		out.val2 = pInputData[2] > ThresholdValue;
		out.val3 = pInputData[3] > ThresholdValue;
		out.val4 = pInputData[4] > ThresholdValue;
		out.val5 = pInputData[5] > ThresholdValue;
		out.val6 = pInputData[6] > ThresholdValue;
		out.val7 = pInputData[7] > ThresholdValue;

		*pOutputByte++ = out;
		pInputData += 8;
	}

	if(arrayLength & 7)
	{
		auto RestW = arrayLength & 7;
		out = {false, false, false, false, false, false, false, false};

		if(RestW > 6)
		{
			out.val6 = pInputData[6] > ThresholdValue;
		}

		if(RestW > 5)
		{
			out.val5 = pInputData[5] > ThresholdValue;
		}

		if(RestW > 4)
		{
			out.val4 = pInputData[4] > ThresholdValue;
		}

		if(RestW > 3)
		{
			out.val3 = pInputData[3] > ThresholdValue;
		}

		if(RestW > 2)
		{
			out.val2 = pInputData[2] > ThresholdValue;
		}

		if(RestW > 1)
		{
			out.val1 = pInputData[1] > ThresholdValue;
		}

		if(RestW > 0)
		{
			out.val0 = pInputData[0] > ThresholdValue;
		}

		*pOutputByte++ = out;
	}
}

BENCHMARK_F(CompressBoolsTest, WithOpenMP, ManualVersionFixture, SamplesCount, IterationsCount)
{
	uint8_t Bits[8];
#pragma omp parallel for private(Bits)
	for(unsigned int i = 0; i < numFullBytes; ++i)
	{
		auto pInputData = inputValues.get() + i * 8;
		Bits[0] = pInputData[0] > ThresholdValue ? 0x01 : 0;
		Bits[1] = pInputData[1] > ThresholdValue ? 0x02 : 0;
		Bits[2] = pInputData[2] > ThresholdValue ? 0x04 : 0;
		Bits[3] = pInputData[3] > ThresholdValue ? 0x08 : 0;
		Bits[4] = pInputData[4] > ThresholdValue ? 0x10 : 0;
		Bits[5] = pInputData[5] > ThresholdValue ? 0x20 : 0;
		Bits[6] = pInputData[6] > ThresholdValue ? 0x40 : 0;
		Bits[7] = pInputData[7] > ThresholdValue ? 0x80 : 0;

		this->outputValues.get()[i] = Bits[0] | Bits[1] | Bits[2] | Bits[3] | Bits[4] | Bits[5] | Bits[6] | Bits[7];
	}

	if(numFullBytes < numBytes)
	{
		uint8_t Bits2[8] = {0};
		auto RestW = arrayLength & 7;
		auto pInputData = inputValues.get() + numFullBytes * 8;

		for(unsigned long long i = 0; i < RestW; ++i)
		{
			Bits2[i] = *pInputData > ThresholdValue ? 1 << i : 0;
			pInputData++;
		}

		this->outputValues.get()[numFullBytes] = Bits2[0] | Bits2[1] | Bits2[2] | Bits2[3] | Bits2[4] | Bits2[5] | Bits2[6];
	}
}

class SimdVersionFixture : public CompressBoolsFixture
{
public:
	void setUp(const celero::TestFixture::ExperimentValue& experimentValue) override
	{
		CompressBoolsFixture::setUp(experimentValue);

		this->numBytes = static_cast<unsigned int>((experimentValue.Value + 7) / 8);
		this->numFullBytes = static_cast<unsigned int>((experimentValue.Value) / 8);
		this->alignedOutputValues = (uint8_t*)_aligned_malloc(numBytes, 16);
		this->signedInputValues.reset(new int8_t[arrayLength]);

		for(size_t i = 0; i < this->arrayLength; ++i)
		{
			this->signedInputValues[i] = static_cast<int8_t>(this->inputValues[i] - 128);
		}
	}

	void tearDown() override
	{
		for(size_t i = 0; i < this->arrayLength; ++i)
		{
			Checker(GetBoolAt(i, this->alignedOutputValues), this->referenceValues[i], i);
		}

		if(this->alignedOutputValues)
		{
			_aligned_free(this->alignedOutputValues);
			this->alignedOutputValues = nullptr;
		}
	}

	unsigned int numBytes{0};
	unsigned int numFullBytes{0};
	uint8_t* alignedOutputValues{nullptr};
	std::unique_ptr<int8_t[]> signedInputValues;
};

BENCHMARK_F(CompressBoolsTest, SimdVersion, SimdVersionFixture, SamplesCount, IterationsCount)
{
	const size_t lenDiv16y16 = (arrayLength / 16) * 16; // full packs of 16 values...

	const __m128i sse127 = _mm_set1_epi8(127);
	const int8_t ConvertedThreshold = ThresholdValue - 128;
	const __m128i sseThresholds = _mm_set1_epi8(ConvertedThreshold);

	auto pInputData = signedInputValues.get();
	auto pOutputByte = alignedOutputValues;

	for(size_t j = 0; j < lenDiv16y16; j += 16)
	{
		const auto in16Values =
			_mm_set_epi8(pInputData[15], pInputData[14], pInputData[13], pInputData[12], pInputData[11], pInputData[10], pInputData[9], pInputData[8],
						 pInputData[7], pInputData[6], pInputData[5], pInputData[4], pInputData[3], pInputData[2], pInputData[1], pInputData[0]);
		const auto cmpRes = _mm_cmpgt_epi8(in16Values, sseThresholds);
		const auto packed = _mm_movemask_epi8(cmpRes);
		*((uint16_t*)pOutputByte) = static_cast<uint16_t>(packed);
		pOutputByte += 2;
		pInputData += 16;
	}

	if(arrayLength & 15)
	{
		auto RestW = arrayLength & 15;

		std::array<uint16_t, 16> Bits;

		for(size_t i = 0; i < RestW; ++i)
		{
			Bits[i] = *pInputData > ConvertedThreshold ? 1 << i : 0;
			pInputData++;
		}

		*pOutputByte++ = static_cast<uint8_t>(Bits[0] | Bits[1] | Bits[2] | Bits[3] | Bits[4] | Bits[5] | Bits[6] | Bits[7]);

		if(RestW > 8)
		{
			*pOutputByte++ = (Bits[8] | Bits[9] | Bits[10] | Bits[11] | Bits[12] | Bits[13] | Bits[14] | Bits[15]) >> 8;
		}
	}
}


// Original code at: http://example.com

#include <celero\Celero.h>
#include <emmintrin.h>
#include <omp.h>

#include <array>
#include <bitset>
#include <iostream>
#include <random>

CELERO_MAIN;

static const int SamplesCount = 1000;
static const int IterationsCount = 5;
static const int ThresholdValue = 127;
static const int MaximumDistribution = 255;

void Checker(bool val, bool reference, uint64_t i)
{
	if(val != reference)
	{
		std::cout << "Mismatch at index: " << i << "\n";
	}
}

bool GetBoolAt(int64_t pos, uint8_t* outputValues)
{
	const auto bytePos = pos / 8;
	const auto bitPos = pos % 8;
	return ((*(outputValues + bytePos)) & (1 << bitPos)) > 0;
}

/// \struct MyExperimentValue
/// \brief Simple extension of the ExperimentValue struct.
class MyExperimentValue : public celero::TestFixture::ExperimentValue
{
public:
	MyExperimentValue() : celero::TestFixture::ExperimentValue(){};
	virtual ~MyExperimentValue() = default;

	uint64_t NumBytes{0};
	uint64_t NumFullBytes{0};
};

class CompressBoolsFixture : public celero::TestFixture
{
public:
	std::vector<celero::TestFixture::ExperimentValue> getExperimentValues() const override
	{
		std::vector<celero::TestFixture::ExperimentValue> problemSpace;

		const auto stepCount = MaxArrayLength / BenchmarkSteps;

		for(int64_t i = 0; i < BenchmarkSteps; i++)
		{
			// ExperimentValues is part of the base class and allows us to specify
			// some values to control various test runs to end up building a nice graph.
			MyExperimentValue ev;
			ev.Value = stepCount + i * stepCount;
			ev.Iterations = int64_t(i + 1);
			ev.NumBytes = static_cast<unsigned int>((ev.Value + 7) / 8);
			ev.NumFullBytes = static_cast<unsigned int>((ev.Value) / 8);
			problemSpace.push_back(ev);
		}

		return problemSpace;
	}

	/// Before each run, build a vector of random integers.
	void setUp(const celero::TestFixture::ExperimentValue& experimentValue) override
	{
		this->arrayLength = static_cast<size_t>(experimentValue.Value);
		this->inputValues.reset(new int[arrayLength]);
		this->referenceValues.reset(new bool[arrayLength]);

		std::mt19937 gen(0); // Standard mersenne_twister_engine seeded with 0, constant
		std::uniform_int_distribution<> dist(0, MaximumDistribution);

		// set every byte, copute reference values
		for(int64_t i = 0; i < experimentValue.Value; ++i)
		{
			this->inputValues[i] = dist(gen);
			this->referenceValues[i] = this->inputValues[i] > ThresholdValue;
		}
	}

#ifdef _DEBUG
	static const int64_t MaxArrayLength{100};
	static const int64_t BenchmarkSteps{1};
#else
	static const int64_t MaxArrayLength{5000000};
	static const int64_t BenchmarkSteps{5};
#endif

	size_t arrayLength{0};
	std::unique_ptr<int[]> inputValues;
	std::unique_ptr<bool[]> referenceValues;
};

class NoPackingFixture : public CompressBoolsFixture
{
public:
	void setUp(const celero::TestFixture::ExperimentValue& x) override
	{
		CompressBoolsFixture::setUp(x);
		this->outputValues.reset(new bool[static_cast<unsigned int>(arrayLength)]);
	}

	void tearDown() override
	{
		for(size_t i = 0; i < arrayLength; ++i)
		{
			Checker(outputValues[i], referenceValues[i], i);
		}
	}

	std::unique_ptr<bool[]> outputValues;
};

BASELINE_F(CompressBoolsTest, NoPackingVersion, NoPackingFixture, SamplesCount, IterationsCount)
{
	for(size_t i = 0; i < this->arrayLength; ++i)
	{
		this->outputValues[i] = this->inputValues[i] > ThresholdValue;
	}
}

class StdBitsetFixture : public CompressBoolsFixture
{
public:
	void tearDown() override
	{
		for(size_t i = 0; i < this->arrayLength; ++i)
		{
			Checker(this->outputBitset[i], this->referenceValues[i], i);
		}
	}

	std::bitset<MaxArrayLength> outputBitset;
};

// Run an automatic baseline.
// Celero will help make sure enough samples are taken to get a reasonable measurement
BENCHMARK_F(CompressBoolsTest, StdBitset, StdBitsetFixture, SamplesCount, IterationsCount)
{
	for(size_t i = 0; i < this->arrayLength; ++i)
	{
		this->outputBitset.set(i, this->inputValues[i] > ThresholdValue);
	}
}

class StdVectorFixture : public CompressBoolsFixture
{
public:
	void setUp(const celero::TestFixture::ExperimentValue& experimentValue) override
	{
		CompressBoolsFixture::setUp(experimentValue);
		this->outputVector.resize(static_cast<unsigned int>(experimentValue.Value));
	}

	void tearDown() override
	{
		for(size_t i = 0; i < arrayLength; ++i)
		{
			Checker(this->outputVector[i], this->referenceValues[i], i);
		}
	}

	std::vector<bool> outputVector;
};

BENCHMARK_F(CompressBoolsTest, StdVector, StdVectorFixture, SamplesCount, IterationsCount)
{
	for(size_t i = 0; i < this->arrayLength; ++i)
	{
		this->outputVector[i] = this->inputValues[i] > ThresholdValue;
	}
}

class ManualVersionFixture : public CompressBoolsFixture
{
public:
	ManualVersionFixture() : numBytes(0), numFullBytes(0)
	{
	}

	void setUp(const celero::TestFixture::ExperimentValue& experimentValue) override
	{
		CompressBoolsFixture::setUp(experimentValue);

		auto ev = dynamic_cast<const MyExperimentValue&>(experimentValue);
		this->numBytes = ev.NumBytes;
		this->numFullBytes = ev.NumFullBytes;
		this->outputValues.reset(new uint8_t[this->numBytes]);
	}

	void tearDown() override
	{
		for(size_t i = 0; i < this->arrayLength; ++i)
		{
			Checker(GetBoolAt(i, outputValues.get()), referenceValues[i], i);
		}
	}

	uint64_t numBytes{0};
	uint64_t numFullBytes{0};
	std::unique_ptr<uint8_t[]> outputValues;
};

BENCHMARK_F(CompressBoolsTest, SingleVarVersion, ManualVersionFixture, SamplesCount, IterationsCount)
{
	uint8_t OutByte = 0;
	int shiftCounter = 0;

	auto pInputData = inputValues.get();
	auto pOutputByte = outputValues.get();

	for(size_t i = 0; i < this->arrayLength; ++i)
	{
		if(*pInputData > ThresholdValue)
		{
			OutByte |= (1 << shiftCounter);
		}

		pInputData++;
		shiftCounter++;

		if(shiftCounter > 7)
		{
			*pOutputByte++ = OutByte;
			OutByte = 0;
			shiftCounter = 0;
		}
	}

	// our byte might be incomplete, so we need to handle this:
	if(this->arrayLength & 7)
	{
		*pOutputByte++ = OutByte;
	}
}

BENCHMARK_F(CompressBoolsTest, NotDepentendVersion, ManualVersionFixture, SamplesCount, IterationsCount)
{
	uint8_t Bits[8] = {0};
	const int64_t lenDivBy8 = (this->arrayLength / 8) * 8;

	auto pInputData = inputValues.get();
	auto pOutputByte = outputValues.get();

	for(int64_t j = 0; j < lenDivBy8; j += 8)
	{
		Bits[0] = pInputData[0] > ThresholdValue ? 0x01 : 0;
		Bits[1] = pInputData[1] > ThresholdValue ? 0x02 : 0;
		Bits[2] = pInputData[2] > ThresholdValue ? 0x04 : 0;
		Bits[3] = pInputData[3] > ThresholdValue ? 0x08 : 0;
		Bits[4] = pInputData[4] > ThresholdValue ? 0x10 : 0;
		Bits[5] = pInputData[5] > ThresholdValue ? 0x20 : 0;
		Bits[6] = pInputData[6] > ThresholdValue ? 0x40 : 0;
		Bits[7] = pInputData[7] > ThresholdValue ? 0x80 : 0;

		*pOutputByte++ = Bits[0] | Bits[1] | Bits[2] | Bits[3] | Bits[4] | Bits[5] | Bits[6] | Bits[7];
		pInputData += 8;
	}

	if(arrayLength & 7)
	{
		// note that we'll use max 7 elements, so max is Bits[6]... (otherwise we would get another full byte)
		auto RestW = arrayLength & 7;
		memset(Bits, 0, 8);

		for(unsigned long long i = 0; i < RestW; ++i)
		{
			Bits[i] = *pInputData > ThresholdValue ? 1 << i : 0;
			pInputData++;
		}

		*pOutputByte++ = Bits[0] | Bits[1] | Bits[2] | Bits[3] | Bits[4] | Bits[5] | Bits[6];
	}
}

struct bool8
{
	bool8() : val0(0), val1(0), val2(0), val3(0), val4(0), val5(0), val6(0), val7(0)
	{
	}

	bool8(bool v0, bool v1, bool v2, bool v3, bool v4, bool v5, bool v6, bool v7)
		: val0(v0), val1(v1), val2(v2), val3(v3), val4(v4), val5(v5), val6(v6), val7(v7)
	{
	}

	uint8_t val0 : 1;
	uint8_t val1 : 1;
	uint8_t val2 : 1;
	uint8_t val3 : 1;
	uint8_t val4 : 1;
	uint8_t val5 : 1;
	uint8_t val6 : 1;
	uint8_t val7 : 1;
};

class PackedStructFixture : public CompressBoolsFixture
{
public:
	void setUp(const celero::TestFixture::ExperimentValue& experimentValue) override
	{
		CompressBoolsFixture::setUp(experimentValue);

		this->numBytes = static_cast<unsigned int>((experimentValue.Value + 7) / 8);
		this->numFullBytes = static_cast<unsigned int>((experimentValue.Value) / 8);
		this->outputValues.reset(new bool8[numBytes]);
	}

	void tearDown() override
	{
		for(size_t i = 0; i < arrayLength; ++i)
		{
			Checker(getBoolAt(i), referenceValues[i], i);
		}
	}

	bool getBoolAt(int64_t pos)
	{
		const auto bytePos = pos / 8;
		const auto bitPos = pos % 8;

		switch(bitPos)
		{
			case 0:
				return (*(outputValues.get() + bytePos)).val0;
			case 1:
				return (*(outputValues.get() + bytePos)).val1;
			case 2:
				return (*(outputValues.get() + bytePos)).val2;
			case 3:
				return (*(outputValues.get() + bytePos)).val3;
			case 4:
				return (*(outputValues.get() + bytePos)).val4;
			case 5:
				return (*(outputValues.get() + bytePos)).val5;
			case 6:
				return (*(outputValues.get() + bytePos)).val6;
			case 7:
				return (*(outputValues.get() + bytePos)).val7;
		}

		return (*(outputValues.get() + bytePos)).val7;
	}

	unsigned int numBytes{0};
	unsigned int numFullBytes{0};
	std::unique_ptr<bool8[]> outputValues;
};

BENCHMARK_F(CompressBoolsTest, PackedStructVersion, PackedStructFixture, SamplesCount, IterationsCount)
{
	const int64_t lenDivBy8 = (arrayLength / 8) * 8;

	auto pInputData = inputValues.get();
	auto pOutputByte = outputValues.get();

	bool8 out;

	for(int64_t j = 0; j < lenDivBy8; j += 8)
	{
		out.val0 = pInputData[0] > ThresholdValue;
		out.val1 = pInputData[1] > ThresholdValue;
		out.val2 = pInputData[2] > ThresholdValue;
		out.val3 = pInputData[3] > ThresholdValue;
		out.val4 = pInputData[4] > ThresholdValue;
		out.val5 = pInputData[5] > ThresholdValue;
		out.val6 = pInputData[6] > ThresholdValue;
		out.val7 = pInputData[7] > ThresholdValue;

		*pOutputByte++ = out;
		pInputData += 8;
	}

	if(arrayLength & 7)
	{
		auto RestW = arrayLength & 7;
		out = {false, false, false, false, false, false, false, false};

		if(RestW > 6)
		{
			out.val6 = pInputData[6] > ThresholdValue;
		}

		if(RestW > 5)
		{
			out.val5 = pInputData[5] > ThresholdValue;
		}

		if(RestW > 4)
		{
			out.val4 = pInputData[4] > ThresholdValue;
		}

		if(RestW > 3)
		{
			out.val3 = pInputData[3] > ThresholdValue;
		}

		if(RestW > 2)
		{
			out.val2 = pInputData[2] > ThresholdValue;
		}

		if(RestW > 1)
		{
			out.val1 = pInputData[1] > ThresholdValue;
		}

		if(RestW > 0)
		{
			out.val0 = pInputData[0] > ThresholdValue;
		}

		*pOutputByte++ = out;
	}
}

BENCHMARK_F(CompressBoolsTest, WithOpenMP, ManualVersionFixture, SamplesCount, IterationsCount)
{
	uint8_t Bits[8];
#pragma omp parallel for private(Bits)
	for(unsigned int i = 0; i < numFullBytes; ++i)
	{
		auto pInputData = inputValues.get() + i * 8;
		Bits[0] = pInputData[0] > ThresholdValue ? 0x01 : 0;
		Bits[1] = pInputData[1] > ThresholdValue ? 0x02 : 0;
		Bits[2] = pInputData[2] > ThresholdValue ? 0x04 : 0;
		Bits[3] = pInputData[3] > ThresholdValue ? 0x08 : 0;
		Bits[4] = pInputData[4] > ThresholdValue ? 0x10 : 0;
		Bits[5] = pInputData[5] > ThresholdValue ? 0x20 : 0;
		Bits[6] = pInputData[6] > ThresholdValue ? 0x40 : 0;
		Bits[7] = pInputData[7] > ThresholdValue ? 0x80 : 0;

		this->outputValues.get()[i] = Bits[0] | Bits[1] | Bits[2] | Bits[3] | Bits[4] | Bits[5] | Bits[6] | Bits[7];
	}

	if(numFullBytes < numBytes)
	{
		uint8_t Bits2[8] = {0};
		auto RestW = arrayLength & 7;
		auto pInputData = inputValues.get() + numFullBytes * 8;

		for(unsigned long long i = 0; i < RestW; ++i)
		{
			Bits2[i] = *pInputData > ThresholdValue ? 1 << i : 0;
			pInputData++;
		}

		this->outputValues.get()[numFullBytes] = Bits2[0] | Bits2[1] | Bits2[2] | Bits2[3] | Bits2[4] | Bits2[5] | Bits2[6];
	}
}

class SimdVersionFixture : public CompressBoolsFixture
{
public:
	void setUp(const celero::TestFixture::ExperimentValue& experimentValue) override
	{
		CompressBoolsFixture::setUp(experimentValue);

		this->numBytes = static_cast<unsigned int>((experimentValue.Value + 7) / 8);
		this->numFullBytes = static_cast<unsigned int>((experimentValue.Value) / 8);
		this->alignedOutputValues = (uint8_t*)_aligned_malloc(numBytes, 16);
		this->signedInputValues.reset(new int8_t[arrayLength]);

		for(size_t i = 0; i < this->arrayLength; ++i)
		{
			this->signedInputValues[i] = static_cast<int8_t>(this->inputValues[i] - 128);
		}
	}

	void tearDown() override
	{
		for(size_t i = 0; i < this->arrayLength; ++i)
		{
			Checker(GetBoolAt(i, this->alignedOutputValues), this->referenceValues[i], i);
		}

		if(this->alignedOutputValues)
		{
			_aligned_free(this->alignedOutputValues);
			this->alignedOutputValues = nullptr;
		}
	}

	unsigned int numBytes{0};
	unsigned int numFullBytes{0};
	uint8_t* alignedOutputValues{nullptr};
	std::unique_ptr<int8_t[]> signedInputValues;
};

BENCHMARK_F(CompressBoolsTest, SimdVersion, SimdVersionFixture, SamplesCount, IterationsCount)
{
	const size_t lenDiv16y16 = (arrayLength / 16) * 16; // full packs of 16 values...

	const __m128i sse127 = _mm_set1_epi8(127);
	const int8_t ConvertedThreshold = ThresholdValue - 128;
	const __m128i sseThresholds = _mm_set1_epi8(ConvertedThreshold);

	auto pInputData = signedInputValues.get();
	auto pOutputByte = aligned

	for(size_t j = 0; j < lenDiv16y16; j += 16)
	{
		const auto in16Values =
			_mm_set_epi8(pInputData[15], pInputData[14], pInputData[13], pInputData[12], pInputData[11], pInputData[10], pInputData[9], pInputData[8],
						 pInputData[7], pInputData[6], pInputData[5], pInputData[4], pInputData[3], pInputData[2], pInputData[1], pInputData[0]);
		const auto cmpRes = _mm_cmpgt_epi8(in16Values, sseThresholds);
		const auto packed = _mm_movemask_epi8(cmpRes);
		*((uint16_t*)pOutputByte) = static_cast<uint16_t>(packed);
		pOutputByte += 2;
		pInputData += 16;
	}

	if(arrayLength & 15)
	{
		auto RestW = arrayLength & 15;

		std::array<uint16_t, 16> Bits;

		for(size_t i = 0; i < RestW; ++i)
		{
			Bits[i] = *pInputData > ConvertedThreshold ? 1 << i : 0;
			pInputData++;
		}

		*pOutputByte++ = static_cast<uint8_t>(Bits[0] | Bits[1] | Bits[2] | Bits[3] | Bits[4] | Bits[5] | Bits[6] | Bits[7]);

		if(RestW > 8)
		{
			*pOutputByte++ = (Bits[8] | Bits[9] | Bits[10] | Bits[11] | Bits[12] | Bits[13] | Bits[14] | Bits[15]) >> 8;
		}
	}
}


BENCHMARK_F(CompressBoolsTest, StdBitset, StdBitsetFixture, SamplesCount, IterationsCount)
{
	for(size_t i = 0; i < this->arrayLength; ++i)
	{
		this->outputBitset.set(i, this->inputValues[i] > ThresholdValue);
	}
}

// Original code at: http://example.com

#include <celero\Celero.h>
#include <emmintrin.h>
#include <omp.h>

#include <array>
#include <bitset>
#include <iostream>
#include <random>

CELERO_MAIN;

static const int SamplesCount = 1000;
static const int IterationsCount = 5;
static const int ThresholdValue = 127;
static const int MaximumDistribution = 255;

void Checker(bool val, bool reference, uint64_t i)
{
	if(val != reference)
	{
		std::cout << "Mismatch at index: " << i << "\n";
	}
}

bool GetBoolAt(int64_t pos, uint8_t* outputValues)
{
	const auto bytePos = pos / 8;
	const auto bitPos = pos % 8;
	return ((*(outputValues + bytePos)) & (1 << bitPos)) > 0;
}

/// \struct MyExperimentValue
/// \brief Simple extension of the ExperimentValue struct.
class MyExperimentValue : public celero::TestFixture::ExperimentValue
{
public:
	MyExperimentValue() : celero::TestFixture::ExperimentValue(){};
	virtual ~MyExperimentValue() = default;

	uint64_t NumBytes{0};
	uint64_t NumFullBytes{0};
};

class CompressBoolsFixture : public celero::TestFixture
{
public:
	std::vector<celero::TestFixture::ExperimentValue> getExperimentValues() const override
	{
		std::vector<celero::TestFixture::ExperimentValue> problemSpace;

		const auto stepCount = MaxArrayLength / BenchmarkSteps;

		for(int64_t i = 0; i < BenchmarkSteps; i++)
		{
			// ExperimentValues is part of the base class and allows us to specify
			// some values to control various test runs to end up building a nice graph.
			MyExperimentValue ev;
			ev.Value = stepCount + i * stepCount;
			ev.Iterations = int64_t(i + 1);
			ev.NumBytes = static_cast<unsigned int>((ev.Value + 7) / 8);
			ev.NumFullBytes = static_cast<unsigned int>((ev.Value) / 8);
			problemSpace.push_back(ev);
		}

		return problemSpace;
	}

	/// Before each run, build a vector of random integers.
	void setUp(const celero::TestFixture::ExperimentValue& experimentValue) override
	{
		this->arrayLength = static_cast<size_t>(experimentValue.Value);
		this->inputValues.reset(new int[arrayLength]);
		this->referenceValues.reset(new bool[arrayLength]);

		std::mt19937 gen(0); // Standard mersenne_twister_engine seeded with 0, constant
		std::uniform_int_distribution<> dist(0, MaximumDistribution);

		// set every byte, copute reference values
		for(int64_t i = 0; i < experimentValue.Value; ++i)
		{
			this->inputValues[i] = dist(gen);
			this->referenceValues[i] = this->inputValues[i] > ThresholdValue;
		}
	}

#ifdef _DEBUG
	static const int64_t MaxArrayLength{100};
	static const int64_t BenchmarkSteps{1};
#else
	static const int64_t MaxArrayLength{5000000};
	static const int64_t BenchmarkSteps{5};
#endif

	size_t arrayLength{0};
	std::unique_ptr<int[]> inputValues;
	std::unique_ptr<bool[]> referenceValues;
};

class NoPackingFixture : public CompressBoolsFixture
{
public:
	void setUp(const celero::TestFixture::ExperimentValue& x) override
	{
		CompressBoolsFixture::setUp(x);
		this->outputValues.reset(new bool[static_cast<unsigned int>(arrayLength)]);
	}

	void tearDown() override
	{
		for(size_t i = 0; i < arrayLength; ++i)
		{
			Checker(outputValues[i], referenceValues[i], i);
		}
	}

	std::unique_ptr<bool[]> outputValues;
};

BASELINE_F(CompressBoolsTest, NoPackingVersion, NoPackingFixture, SamplesCount, IterationsCount)
{
	for(size_t i = 0; i < this->arrayLength; ++i)
	{
		this->outputValues[i] = this->inputValues[i] > ThresholdValue;
	}
}

class StdBitsetFixture : public CompressBoolsFixture
{
public:
	void tearDown() override
	{
		for(size_t i = 0; i < this->arrayLength; ++i)
		{
			Checker(this->outputBitset[i], this->referenceValues[i], i);
		}
	}

	std::bitset<MaxArrayLength> outputBitset;
};

// Run an automatic baseline.
// Celero will help make sure enough samples are taken to get a reasonable measurement

class StdVectorFixture : public CompressBoolsFixture
{
public:
	void setUp(const celero::TestFixture::ExperimentValue& experimentValue) override
	{
		CompressBoolsFixture::setUp(experimentValue);
		this->outputVector.resize(static_cast<unsigned int>(experimentValue.Value));
	}

	void tearDown() override
	{
		for(size_t i = 0; i < arrayLength; ++i)
		{
			Checker(this->outputVector[i], this->referenceValues[i], i);
		}
	}

	std::vector<bool> outputVector;
};

BENCHMARK_F(CompressBoolsTest, StdVector, StdVectorFixture, SamplesCount, IterationsCount)
{
	for(size_t i = 0; i < this->arrayLength; ++i)
	{
		this->outputVector[i] = this->inputValues[i] > ThresholdValue;
	}
}

class ManualVersionFixture : public CompressBoolsFixture
{
public:
	ManualVersionFixture() : numBytes(0), numFullBytes(0)
	{
	}

	void setUp(const celero::TestFixture::ExperimentValue& experimentValue) override
	{
		CompressBoolsFixture::setUp(experimentValue);

		auto ev = dynamic_cast<const MyExperimentValue&>(experimentValue);
		this->numBytes = ev.NumBytes;
		this->numFullBytes = ev.NumFullBytes;
		this->outputValues.reset(new uint8_t[this->numBytes]);
	}

	void tearDown() override
	{
		for(size_t i = 0; i < this->arrayLength; ++i)
		{
			Checker(GetBoolAt(i, outputValues.get()), referenceValues[i], i);
		}
	}

	uint64_t numBytes{0};
	uint64_t numFullBytes{0};
	std::unique_ptr<uint8_t[]> outputValues;
};

BENCHMARK_F(CompressBoolsTest, SingleVarVersion, ManualVersionFixture, SamplesCount, IterationsCount)
{
	uint8_t OutByte = 0;
	int shiftCounter = 0;

	auto pInputData = inputValues.get();
	auto pOutputByte = outputValues.get();

	for(size_t i = 0; i < this->arrayLength; ++i)
	{
		if(*pInputData > ThresholdValue)
		{
			OutByte |= (1 << shiftCounter);
		}

		pInputData++;
		shiftCounter++;

		if(shiftCounter > 7)
		{
			*pOutputByte++ = OutByte;
			OutByte = 0;
			shiftCounter = 0;
		}
	}

	// our byte might be incomplete, so we need to handle this:
	if(this->arrayLength & 7)
	{
		*pOutputByte++ = OutByte;
	}
}

BENCHMARK_F(CompressBoolsTest, NotDepentendVersion, ManualVersionFixture, SamplesCount, IterationsCount)
{
	uint8_t Bits[8] = {0};
	const int64_t lenDivBy8 = (this->arrayLength / 8) * 8;

	auto pInputData = inputValues.get();
	auto pOutputByte = outputValues.get();

	for(int64_t j = 0; j < lenDivBy8; j += 8)
	{
		Bits[0] = pInputData[0] > ThresholdValue ? 0x01 : 0;
		Bits[1] = pInputData[1] > ThresholdValue ? 0x02 : 0;
		Bits[2] = pInputData[2] > ThresholdValue ? 0x04 : 0;
		Bits[3] = pInputData[3] > ThresholdValue ? 0x08 : 0;
		Bits[4] = pInputData[4] > ThresholdValue ? 0x10 : 0;
		Bits[5] = pInputData[5] > ThresholdValue ? 0x20 : 0;
		Bits[6] = pInputData[6] > ThresholdValue ? 0x40 : 0;
		Bits[7] = pInputData[7] > ThresholdValue ? 0x80 : 0;

		*pOutputByte++ = Bits[0] | Bits[1] | Bits[2] | Bits[3] | Bits[4] | Bits[5] | Bits[6] | Bits[7];
		pInputData += 8;
	}

	if(arrayLength & 7)
	{
		// note that we'll use max 7 elements, so max is Bits[6]... (otherwise we would get another full byte)
		auto RestW = arrayLength & 7;
		memset(Bits, 0, 8);

		for(unsigned long long i = 0; i < RestW; ++i)
		{
			Bits[i] = *pInputData > ThresholdValue ? 1 << i : 0;
			pInputData++;
		}

		*pOutputByte++ = Bits[0] | Bits[1] | Bits[2] | Bits[3] | Bits[4] | Bits[5] | Bits[6];
	}
}

struct bool8
{
	bool8() : val0(0), val1(0), val2(0), val3(0), val4(0), val5(0), val6(0), val7(0)
	{
	}

	bool8(bool v0, bool v1, bool v2, bool v3, bool v4, bool v5, bool v6, bool v7)
		: val0(v0), val1(v1), val2(v2), val3(v3), val4(v4), val5(v5), val6(v6), val7(v7)
	{
	}

	uint8_t val0 : 1;
	uint8_t val1 : 1;
	uint8_t val2 : 1;
	uint8_t val3 : 1;
	uint8_t val4 : 1;
	uint8_t val5 : 1;
	uint8_t val6 : 1;
	uint8_t val7 : 1;
};

class PackedStructFixture : public CompressBoolsFixture
{
public:
	void setUp(const celero::TestFixture::ExperimentValue& experimentValue) override
	{
		CompressBoolsFixture::setUp(experimentValue);

		this->numBytes = static_cast<unsigned int>((experimentValue.Value + 7) / 8);
		this->numFullBytes = static_cast<unsigned int>((experimentValue.Value) / 8);
		this->outputValues.reset(new bool8[numBytes]);
	}

	void tearDown() override
	{
		for(size_t i = 0; i < arrayLength; ++i)
		{
			Checker(getBoolAt(i), referenceValues[i], i);
		}
	}

	bool getBoolAt(int64_t pos)
	{
		const auto bytePos = pos / 8;
		const auto bitPos = pos % 8;

		switch(bitPos)
		{
			case 0:
				return (*(outputValues.get() + bytePos)).val0;
			case 1:
				return (*(outputValues.get() + bytePos)).val1;
			case 2:
				return (*(outputValues.get() + bytePos)).val2;
			case 3:
				return (*(outputValues.get() + bytePos)).val3;
			case 4:
				return (*(outputValues.get() + bytePos)).val4;
			case 5:
				return (*(outputValues.get() + bytePos)).val5;
			case 6:
				return (*(outputValues.get() + bytePos)).val6;
			case 7:
				return (*(outputValues.get() + bytePos)).val7;
		}

		return (*(outputValues.get() + bytePos)).val7;
	}

	unsigned int numBytes{0};
	unsigned int numFullBytes{0};
	std::unique_ptr<bool8[]> outputValues;
};

BENCHMARK_F(CompressBoolsTest, PackedStructVersion, PackedStructFixture, SamplesCount, IterationsCount)
{
	const int64_t lenDivBy8 = (arrayLength / 8) * 8;

	auto pInputData = inputValues.get();
	auto pOutputByte = outputValues.get();

	bool8 out;

	for(int64_t j = 0; j < lenDivBy8; j += 8)
	{
		out.val0 = pInputData[0] > ThresholdValue;
		out.val1 = pInputData[1] > ThresholdValue;
		out.val2 = pInputData[2] > ThresholdValue;
		out.val3 = pInputData[3] > ThresholdValue;
		out.val4 = pInputData[4] > ThresholdValue;
		out.val5 = pInputData[5] > ThresholdValue;
		out.val6 = pInputData[6] > ThresholdValue;
		out.val7 = pInputData[7] > ThresholdValue;

		*pOutputByte++ = out;
		pInputData += 8;
	}

	if(arrayLength & 7)
	{
		auto RestW = arrayLength & 7;
		out = {false, false, false, false, false, false, false, false};

		if(RestW > 6)
		{
			out.val6 = pInputData[6] > ThresholdValue;
		}

		if(RestW > 5)
		{
			out.val5 = pInputData[5] > ThresholdValue;
		}

		if(RestW > 4)
		{
			out.val4 = pInputData[4] > ThresholdValue;
		}

		if(RestW > 3)
		{
			out.val3 = pInputData[3] > ThresholdValue;
		}

		if(RestW > 2)
		{
			out.val2 = pInputData[2] > ThresholdValue;
		}

		if(RestW > 1)
		{
			out.val1 = pInputData[1] > ThresholdValue;
		}

		if(RestW > 0)
		{
			out.val0 = pInputData[0] > ThresholdValue;
		}

		*pOutputByte++ = out;
	}
}

BENCHMARK_F(CompressBoolsTest, WithOpenMP, ManualVersionFixture, SamplesCount, IterationsCount)
{
	uint8_t Bits[8];
#pragma omp parallel for private(Bits)
	for(unsigned int i = 0; i < numFullBytes; ++i)
	{
		auto pInputData = inputValues.get() + i * 8;
		Bits[0] = pInputData[0] > ThresholdValue ? 0x01 : 0;
		Bits[1] = pInputData[1] > ThresholdValue ? 0x02 : 0;
		Bits[2] = pInputData[2] > ThresholdValue ? 0x04 : 0;
		Bits[3] = pInputData[3] > ThresholdValue ? 0x08 : 0;
		Bits[4] = pInputData[4] > ThresholdValue ? 0x10 : 0;
		Bits[5] = pInputData[5] > ThresholdValue ? 0x20 : 0;
		Bits[6] = pInputData[6] > ThresholdValue ? 0x40 : 0;
		Bits[7] = pInputData[7] > ThresholdValue ? 0x80 : 0;

		this->outputValues.get()[i] = Bits[0] | Bits[1] | Bits[2] | Bits[3] | Bits[4] | Bits[5] | Bits[6] | Bits[7];
	}

	if(numFullBytes < numBytes)
	{
		uint8_t Bits2[8] = {0};
		auto RestW = arrayLength & 7;
		auto pInputData = inputValues.get() + numFullBytes * 8;

		for(unsigned long long i = 0; i < RestW; ++i)
		{
			Bits2[i] = *pInputData > ThresholdValue ? 1 << i : 0;
			pInputData++;
		}

		this->outputValues.get()[numFullBytes] = Bits2[0] | Bits2[1] | Bits2[2] | Bits2[3] | Bits2[4] | Bits2[5] | Bits2[6];
	}
}

class SimdVersionFixture : public CompressBoolsFixture
{
public:
	void setUp(const celero::TestFixture::ExperimentValue& experimentValue) override
	{
		CompressBoolsFixture::setUp(experimentValue);

		this->numBytes = static_cast<unsigned int>((experimentValue.Value + 7) / 8);
		this->numFullBytes = static_cast<unsigned int>((experimentValue.Value) / 8);
		this->alignedOutputValues = (uint8_t*)_aligned_malloc(numBytes, 16);
		this->signedInputValues.reset(new int8_t[arrayLength]);

		for(size_t i = 0; i < this->arrayLength; ++i)
		{
			this->signedInputValues[i] = static_cast<int8_t>(this->inputValues[i] - 128);
		}
	}

	void tearDown() override
	{
		for(size_t i = 0; i < this->arrayLength; ++i)
		{
			Checker(GetBoolAt(i, this->alignedOutputValues), this->referenceValues[i], i);
		}

		if(this->alignedOutputValues)
		{
			_aligned_free(this->alignedOutputValues);
			this->alignedOutputValues = nullptr;
		}
	}

	unsigned int numBytes{0};
	unsigned int numFullBytes{0};
	uint8_t* alignedOutputValues{nullptr};
	std::unique_ptr<int8_t[]> signedInputValues;
};

BENCHMARK_F(CompressBoolsTest, SimdVersion, SimdVersionFixture, SamplesCount, IterationsCount)
{
	const size_t lenDiv16y16 = (arrayLength / 16) * 16; // full packs of 16 values...

	const __m128i sse127 = _mm_set1_epi8(127);
	const int8_t ConvertedThreshold = ThresholdValue - 128;
	const __m128i sseThresholds = _mm_set1_epi8(ConvertedThreshold);

	auto pInputData = signedInputValues.get();
	auto pOutputByte = alignedOutputValues;

	for(size_t j = 0; j < lenDiv16y16; j += 16)
	{
		const auto in16Values =
			_mm_set_epi8(pInputData[15], pInputData[14], pInputData[13], pInputData[12], pInputData[11], pInputData[10], pInputData[9], pInputData[8],
						 pInputData[7], pInputData[6], pInputData[5], pInputData[4], pInputData[3], pInputData[2], pInputData[1], pInputData[0]);
		const auto cmpRes = _mm_cmpgt_epi8(in16Values, sseThresholds);
		const auto packed = _mm_movemask_epi8(cmpRes);
		*((uint16_t*)pOutputByte) = static_cast<uint16_t>(packed);
		pOutputByte += 2;
		pInputData += 16;
	}

	if(arrayLength & 15)
	{
		auto RestW = arrayLength & 15;

		std::array<uint16_t, 16> Bits;

		for(size_t i = 0; i < RestW; ++i)
		{
			Bits[i] = *pInputData > ConvertedThreshold ? 1 << i : 0;
			pInputData++;
		}

		*pOutputByte++ = static_cast<uint8_t>(Bits[0] | Bits[1] | Bits[2] | Bits[3] | Bits[4] | Bits[5] | Bits[6] | Bits[7]);

		if(RestW > 8)
		{
			*pOutputByte++ = (Bits[8] | Bits[9] | Bits[10] | Bits[11] | Bits[12] | Bits[13] | Bits[14] | Bits[15]) >> 8;
		}
	}
}


/*

   This file is part of KeeperRL.

   */

#include "stdafx.h"
#include "unique_entity.h"
#include "level.h"
#include "item.h"
#include "creature.h"
#include "task.h"
#include "player_message.h"
#include "furniture.h"

template<typename T>
void UniqueEntity<T>::offsetForSerialization(GenericId o) {
  CHECK(o != 0);
  offset = o;
}

template<typename T>
void UniqueEntity<T>::clearOffset() {
  offset = 0;
}

template<typename T>
long long UniqueEntity<T>::offset = 0;

template<typename T>
UniqueEntity<T>::Id::Id() {
  key = Random.getLL();
  hash = int(key);
}

template<typename T>
UniqueEntity<T>::Id::Id(GenericId id) {
  key = id;
  hash = int(key);
}

template<typename T>
bool UniqueEntity<T>::Id::operator == (const Id& id) const {
  return key == id.key;
}

template<typename T>
bool UniqueEntity<T>::Id::operator < (const Id& id) const {
  return key < id.key;
}

template<typename T>
bool UniqueEntity<T>::Id::operator > (const Id& id) const {
  return key > id.key;
}

template<typename T>
bool UniqueEntity<T>::Id::operator != (const Id& id) const {
  return !(*this == id);
}

template<typename T>

{
  return hash;
}

template<typename T>
template <class Archive>
void UniqueEntity<T>::Id::serialize(Archive& ar, const unsigned int version) {
  if (key != 0)
    key += offset;
  ar(key, hash);
  if (key != 0)
    key -= offset;
}

template<typename T>
auto UniqueEntity<T>::getUniqueId() const -> Id {
  return id;
}

template<typename T>
void UniqueEntity<T>::setUniqueId(UniqueEntity::Id i) {
  id = i;
}

template<typename T>
GenericId UniqueEntity<T>::Id::getGenericId() const {
  if (hash == 0) // this workaround can be removed in alpha 34
    return 0;
  return key;
}

template<typename T>
template <class Archive>
void UniqueEntity<T>::serialize(Archive& ar, const unsigned int version) {
  ar(id);
}

SERIALIZABLE_TMPL(UniqueEntity, Level);
SERIALIZABLE_TMPL(UniqueEntity, Item);
SERIALIZABLE_TMPL(UniqueEntity, Creature);
SERIALIZABLE_TMPL(UniqueEntity, Task);
SERIALIZABLE_TMPL(UniqueEntity, PlayerMessage);
SERIALIZABLE_TMPL(UniqueEntity, Furniture);
SERIALIZABLE_TMPL(UniqueEntity, Collective);

SERIALIZABLE(UniqueEntity<Creature>::Id);
SERIALIZABLE(UniqueEntity<Item>::Id);
SERIALIZABLE(UniqueEntity<PlayerMessage>::Id);
SERIALIZABLE(UniqueEntity<Task>::Id);
SERIALIZABLE(UniqueEntity<Furniture>::Id);
SERIALIZABLE(UniqueEntity<Collective>::Id);


/*

   This file is part of KeeperRL.

   */

#include "stdafx.h"
#include "unique_entity.h"
#include "level.h"
#include "item.h"
#include "creature.h"
#include "task.h"
#include "player_message.h"
#include "furniture.h"

template<typename T>
void UniqueEntity<T>::offsetForSerialization(GenericId o) {
  CHECK(o != 0);
  offset = o;
}

template<typename T>
void UniqueEntity<T>::clearOffset() {
  offset = 0;
}

template<typename T>
long long UniqueEntity<T>::offset = 0;

template<typename T>
UniqueEntity<T>::Id::Id() {
  key = Random.getLL();
  hash = int(key);
}

template<typename T>
UniqueEntity<T>::Id::Id(GenericId id) {
  key = id;
  hash = int(key);
}

template<typename T>
bool UniqueEntity<T>::Id::operator == (const Id& id) const {
  return key == id.key;
}

template<typename T>
bool UniqueEntity<T>::Id::operator < (const Id& id) const {
  return key < id.key;
}

template<typename T>
bool UniqueEntity<T>::Id::operator > (const Id& id) const {
  return key > id.key;
}

template<typename T>
bool UniqueEntity<T>::Id::operator != (const Id& id) const

template<typename T>
int UniqueEntity<T>::Id::getHash() const {
  return hash;
}

template<typename T>
template <class Archive>
void UniqueEntity<T>::Id::serialize(Archive& ar, const unsigned int version) {
  if (key != 0)
    key += offset;
  ar(key, hash);
  if (key != 0)
    key -= offset;
}

template<typename T>
auto UniqueEntity<T>::getUniqueId() const -> Id {
  return id;
}

template<typename T>
void UniqueEntity<T>::setUniqueId(UniqueEntity::Id i) {
  id = i;
}

template<typename T>
GenericId UniqueEntity<T>::Id::getGenericId() const {
  if (hash == 0) // this workaround can be removed in alpha 34
    return 0;
  return key;
}

template<typename T>
template <class Archive>
void UniqueEntity<T>::serialize(Archive& ar, const unsigned int version) {
  ar(id);
}

SERIALIZABLE_TMPL(UniqueEntity, Level);
SERIALIZABLE_TMPL(UniqueEntity, Item);
SERIALIZABLE_TMPL(UniqueEntity, Creature);
SERIALIZABLE_TMPL(UniqueEntity, Task);
SERIALIZABLE_TMPL(UniqueEntity, PlayerMessage);
SERIALIZABLE_TMPL(UniqueEntity, Furniture);
SERIALIZABLE_TMPL(UniqueEntity, Collective);

SERIALIZABLE(UniqueEntity<Creature>::Id);
SERIALIZABLE(UniqueEntity<Item>::Id);
SERIALIZABLE(UniqueEntity<PlayerMessage>::Id);
SERIALIZABLE(UniqueEntity<Task>::Id);
SERIALIZABLE(UniqueEntity<Furniture>::Id);
SERIALIZABLE(UniqueEntity<Collective>::Id);


/*

   This file is part of KeeperRL.

   */

#include "stdafx.h"
#include "unique_entity.h"
#include "level.h"
#include "item.h"
#include "creature.h"
#include "task.h"
#include "player_message.h"
#include "furniture.h"

template<typename T>
void UniqueEntity<T>::offsetForSerialization(GenericId o) {
  CHECK(o != 0);
  offset = o;
}

template<typename T>
void UniqueEntity<T>::clearOffset() {
  offset = 0;
}

template<typename T>
long long UniqueEntity<T>::offset = 0;

template<typename T>
UniqueEntity<T>::Id::Id() {
  key = Random.getLL();
  hash = int(key);
}

template<typename T>
UniqueEntity<T>::Id::Id(GenericId id) {
  key = id;
  hash = int(key);
}

template<typename T>
bool UniqueEntity<T>::Id::operator == (const Id& id) const {
  return key == id.key;
}

template<typename T>
bool UniqueEntity<T>::Id::operator < (const Id& id) const {
  return key < id.key;
}

template<typename T>
bool UniqueEntity<T>::Id::operator > (const Id& id) const {
  return key > id.key;
}

template<typename T>
bool UniqueEntity<T>::Id::operator != (const Id& id) const {
  return !(*this == id);
}

template<typename T>
int UniqueEntity<T>::Id::getHash() const {
  return hash;
}

template<typename T>
template <class Archive>
void UniqueEntity<T>::Id::serialize(Archive& ar, const unsigned int version) {
  if (key != 0)
    key += offset;
  ar(key, hash);
  if (key != 0)
    key -= offset;
}

template<typename T>
auto UniqueEntity<T>::getUniqueId() const -> Id {
  retu

}

template<typename T>
void UniqueEntity<T>::setUniqueId(UniqueEntity::Id i) {
  id = i;
}

template<typename T>
GenericId UniqueEntity<T>::Id::getGenericId() const {
  if (hash == 0) // this workaround can be removed in alpha 34
    return 0;
  return key;
}

template<typename T>
template <class Archive>
void UniqueEntity<T>::serialize(Archive& ar, const unsigned int version) {
  ar(id);
}

SERIALIZABLE_TMPL(UniqueEntity, Level);
SERIALIZABLE_TMPL(UniqueEntity, Item);
SERIALIZABLE_TMPL(UniqueEntity, Creature);
SERIALIZABLE_TMPL(UniqueEntity, Task);
SERIALIZABLE_TMPL(UniqueEntity, PlayerMessage);
SERIALIZABLE_TMPL(UniqueEntity, Furniture);
SERIALIZABLE_TMPL(UniqueEntity, Collective);

SERIALIZABLE(UniqueEntity<Creature>::Id);
SERIALIZABLE(UniqueEntity<Item>::Id);
SERIALIZABLE(UniqueEntity<PlayerMessage>::Id);
SERIALIZABLE(UniqueEntity<Task>::Id);
SERIALIZABLE(UniqueEntity<Furniture>::Id);
SERIALIZABLE(UniqueEntity<Collective>::Id);


#include <gtest/gtest.h>
#include <cmath>
#include <tuple>
#include <string>
#include <algorithm>

#include "rclcpp/rclcpp.hpp"

#include "wait_recovery_tester.hpp"

using namespace std::chrono_literals;

using nav2_system_tests::WaitRecoveryTester;

std::string testNameGenerator(const testing::TestParamInfo<std::tuple<float, float>> & param)
{
  std::string name = std::to_string(std::abs(std::get<0>(param.param))) + "_" + std::to_string(
    std::get<1>(param.param));
  name.erase(std::remove(name.begin(), name.end(), '.'), name.end());
  return name;
}

class WaitRecoveryTestFixture
  : public ::testing::TestWithParam<std::tuple<float, float>>
{
public:
  static void SetUpTestCase()
  {
    wait_recovery_tester = new WaitRecoveryTester();
    if (!wait_recovery_tester->isActive()) {
      wait_recovery_tester->activate();
    }
  }

  static void TearDownTestCase()
  {
    delete wait_recovery_tester;
    wait_recovery_tester = nullptr;
  }

protected:
  static WaitRecoveryTester * wait_recovery_tester;
};

WaitRecoveryTester * WaitRecoveryTestFixture::wait_recovery_tester = nullptr;

TEST_P(WaitRecoveryTestFixture, testSWaitRecovery)
{
  float wait_time = std::get<0>(GetParam());
  float cancel = std::get<1>(GetParam());

  bool success = false;
  int num_tries = 3;
  for (int i = 0; i != num_tries; i++) {
    if (cancel == 1.0) {
      success = success || wait_recovery_tester->recoveryTestCancel(wait_time);
    } else {
      success = success || wait_recovery_tester->recoveryTest(wait_time);
    }
    if (success) {
      break;
    }
  }

  EXPECT_EQ(true, success);
}

INSTANTIATE_TEST_SUITE_P(
  WaitRecoveryTests,
  WaitRecoveryTestFixture,
  ::testing::Values(
    std::make_tuple(1.0, 0.0),
    std::make_tuple(2.0, 0.0),
    std::make_tuple(5.0, 0.0),
    std::make_tuple(10.0, 1.0)),
  testNameGenerator);

{
  ::testing::InitGoogleTest(&argc, argv);

  // initialize ROS
  rclcpp::init(argc, argv);

  bool all_successful = RUN_ALL_TESTS();

  // shutdown ROS
  rclcpp::shutdown();

  return all_successful;
}


{
  float wait_time = std::get<0>(GetParam());
  float cancel = std::get<1>(GetParam());

  bool success = false;
  int num_tries = 3;
  for (int i = 0; i != num_tries; i++) {
    if (cancel == 1.0) {
      success = success || wait_recovery_tester->recoveryTestCancel(wait_time);
    } else {
      success = success || wait_recovery_tester->recoveryTest(wait_time);
    }
    if (success) {
      break;
    }
  }

  EXPECT_EQ(true, success);
}

#include <gtest/gtest.h>
#include <cmath>
#include <tuple>
#include <string>
#include <algorithm>

#include "rclcpp/rclcpp.hpp"

#include "wait_recovery_tester.hpp"

using namespace std::chrono_literals;

using nav2_system_tests::WaitRecoveryTester;

std::string testNameGenerator(const testing::TestParamInfo<std::tuple<float, float>> & param)
{
  std::string name = std::to_string(std::abs(std::get<0>(param.param))) + "_" + std::to_string(
    std::get<1>(param.param));
  name.erase(std::remove(name.begin(), name.end(), '.'), name.end());
  return name;
}

class WaitRecoveryTestFixture
  : public ::testing::TestWithParam<std::tuple<float, float>>
{
public:
  static void SetUpTestCase()
  {
    wait_recovery_tester = new WaitRecoveryTester();
    if (!wait_recovery_tester->isActive()) {
      wait_recovery_tester->activate();
    }
  }

  static void TearDownTestCase()
  {
    delete wait_recovery_tester;
    wait_recovery_tester = nullptr;
  }

protected:
  static WaitRecoveryTester * wait_recovery_tester;
};

WaitRecoveryTester * WaitRecoveryTestFixture::wait_recovery_tester = nullptr;

TEST_P(WaitRecoveryTestFixture, testSWaitRecovery)

INSTANTIATE_TEST_SUITE_P(
  WaitRecoveryTests,
  WaitRecoveryTestFixture,
  ::testing::Values(
    std::make_tuple(1.0, 0.0),
    std::make_tuple(2.0, 0.0),
    std::make_tuple(5.0, 0.0),
    std::make_tuple(10.0, 1.0)),
  testNameGenerator);

int main(int argc, char ** argv)
{
  ::testing::InitGoogleTest(&argc, argv);

  // initialize ROS
  rclcpp::init(argc, argv);

  bool all_successful = RUN_ALL_TESTS();

  // shutdown ROS
  rclcpp::shutdown();

  return all_successful;
}


#include <gtest/gtest.h>
#include <cmath>
#include <tuple>
#include <string>
#include <algorithm>

#include "rclcpp/rclcpp.hpp"

#include "wait_recovery_tester.hpp"

using namespace std::chrono_literals;

using nav2_system_tests::WaitRecoveryTester;

std::string testNameGenerator(const testing::TestParamInfo<std::tuple<float, float>> & param)
{
  std::string name = std::to_string(std::abs(std::get<0>(param.param))) + "_" + std::to_string(
    std::get<1>(param.param));
  name.erase(std::remove(name.begin(), name.end(), '.'), name.end());
  return name;
}

class WaitRecoveryTestFixture
  : public ::testing::TestWithParam<std::tuple<float, float>>
{
public:
  static void SetUpTestCase()
  {
    wait_recovery_tester = new WaitRecoveryTester();
    if (!wait_recovery_tester->isActive()) {
      wait_recovery_tester->activate();
    }
  }

  static void TearDownTestCase()
  {
    delete wait_recovery_tester;
    wait_recovery_tester = nullptr;
  }

protected:
  static WaitRecoveryTester * wait_recovery_tester;
};

WaitRecoveryTester * WaitRecoveryTestFixture::wait_recovery_tester = nullptr;

TEST_P(WaitRecoveryTestFixture, testSWaitRecovery)
{
  float wait_time

  float cancel = std::get<1>(GetParam());

  bool success = false;
  int num_tries = 3;
  for (int i = 0; i != num_tries; i++) {
    if (cancel == 1.0) {
      success = success || wait_recovery_tester->recoveryTestCancel(wait_time);
    } else {
      success = success || wait_recovery_tester->recoveryTest(wait_time);
    }
    if (success) {
      break;
    }
  }

  EXPECT_EQ(true, success);
}

INSTANTIATE_TEST_SUITE_P(
  WaitRecoveryTests,
  WaitRecoveryTestFixture,
  ::testing::Values(
    std::make_tuple(1.0, 0.0),
    std::make_tuple(2.0, 0.0),
    std::make_tuple(5.0, 0.0),
    std::make_tuple(10.0, 1.0)),
  testNameGenerator);

int main(int argc, char ** argv)
{
  ::testing::InitGoogleTest(&argc, argv);

  // initialize ROS
  rclcpp::init(argc, argv);

  bool all_successful = RUN_ALL_TESTS();

  // shutdown ROS
  rclcpp::shutdown();

  return all_successful;
}