/*
* Copyright (C) 2017 Incognito (Edited by ProMetheus)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include <streamer/config.hpp>
#include "../natives.h"

#include "../core.h"
#include "../utility.h"

#include <streamer/objects.hpp>

#include <boost/chrono.hpp>
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/geometries.hpp>
#include <boost/intrusive_ptr.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/unordered_map.hpp>

#include <Eigen/Core>

#include <limits>
#include <string>

#include <a_objects.h>
#include <a_players.h>
#include <a_actor.h>
#include <sampgdk/interop.h>

STREAMER_BEGIN_NS

int CreateDynamicObject( int modelid, float x, float y,  float z,
	float rx, float ry, float rz, int worldid, int interiorid, int playerid,
	float streamDistance, float drawDistance, int areaid,  int priority) {
	if (core->getData()->getGlobalMaxItems(STREAMER_TYPE_OBJECT) == core->getData()->objects.size()) {
		return 0;
	}
	int objectId = Item::Object::identifier.get();
	Item::SharedObject object(new Item::Object);
	//object->amx = amx;
	object->objectId = objectId;
	object->inverseAreaChecking = false;
	object->noCameraCollision = false;
	object->inverseAreaChecking = false;
	object->originalComparableStreamDistance = -1.0f;
	object->positionOffset = Eigen::Vector3f::Zero();
	object->streamCallbacks = false;
	object->modelId = modelid;
	object->position = Eigen::Vector3f(x, y, z);
	object->rotation = Eigen::Vector3f(rx, ry, rz);

	Utility::addToContainer(object->worlds, worldid);
	Utility::addToContainer(object->interiors, interiorid);
	Utility::addToContainer(object->players, playerid);

	object->comparableStreamDistance = streamDistance < STREAMER_STATIC_DISTANCE_CUTOFF ? streamDistance : streamDistance * streamDistance;
	object->streamDistance = streamDistance;
	object->drawDistance = drawDistance;

	Utility::addToContainer(object->areas, areaid);

	object->priority = priority;
	core->getGrid()->addObject(object);
	core->getData()->objects.insert(std::make_pair(objectId, object));
	return objectId;
}

int DestroyDynamicObject(int id) {
	boost::unordered_map<int, Item::SharedObject>::iterator o = core->getData()->objects.find(id);
	if (o != core->getData()->objects.end()) {
		Utility::destroyObject(o);
		return 1;
	}
	return 0;
}

int IsValidDynamicObject(int id) {
	boost::unordered_map<int, Item::SharedObject>::iterator o = core->getData()->objects.find(id);
	if (o != core->getData()->objects.end()) {
		return 1;
	}
	return 0;
}

int SetDynamicObjectPos(int id, float x, float y, float z) {
	boost::unordered_map<int, Item::SharedObject>::iterator o = core->getData()->objects.find(id);
	if (o != core->getData()->objects.end()) {
		Eigen::Vector3f position = o->second->position;
		o->second->position = Eigen::Vector3f(x, y, z);
		for (boost::unordered_map<int, Player>::iterator p = core->getData()->players.begin(); p != core->getData()->players.end(); ++p) {
			boost::unordered_map<int, int>::iterator i = p->second.internalObjects.find(o->first);
			if (i != p->second.internalObjects.end())
			{
				sampgdk::SetPlayerObjectPos(p->first, i->second, o->second->position[0], o->second->position[1], o->second->position[2]);
			}
		}
		if (position[0] != o->second->position[0] || position[1] != o->second->position[1]) {
			if (o->second->cell)
			{
				core->getGrid()->removeObject(o->second, true);
			}
		}
		if (o->second->move) {
			o->second->move->duration = static_cast<int>((static_cast<float>(boost::geometry::distance(o->second->move->position.get<0>(), o->second->position) / o->second->move->speed) * 1000.0f));
			o->second->move->position.get<1>() = o->second->position;
			o->second->move->position.get<2>() = (o->second->move->position.get<0>() - o->second->position) / static_cast<float>(o->second->move->duration);
			if ((o->second->move->rotation.get<0>().maxCoeff() + 1000.0f) > std::numeric_limits<float>::epsilon())
			{
				o->second->move->rotation.get<1>() = o->second->rotation;
				o->second->move->rotation.get<2>() = (o->second->move->rotation.get<0>() - o->second->rotation) / static_cast<float>(o->second->move->duration);
			}
			o->second->move->time = boost::chrono::steady_clock::now();
		}
		return 1;
	}
	return 0;
}

/*int GetDynamicObjectPos(int id, float &x, float &y, float &z) {
	boost::unordered_map<int, Item::SharedObject>::iterator o = core->getData()->objects.find(id);
	if (o != core->getData()->objects.end()) {
		if (o->second->move) {
			core->getStreamer()->processActiveItems();
		}
		x = o->second->position[0];
		y = o->second->position[1];
		z = o->second->position[2];
		return 1;
	}
	return 0;
}*/

int SetDynamicObjectRot(int id, float rx, float ry, float rz) {
	boost::unordered_map<int, Item::SharedObject>::iterator o = core->getData()->objects.find(id);
	if (o != core->getData()->objects.end()) {
		o->second->rotation = Eigen::Vector3f(rx, ry, rz);
		for (boost::unordered_map<int, Player>::iterator p = core->getData()->players.begin(); p != core->getData()->players.end(); ++p) {
			boost::unordered_map<int, int>::iterator i = p->second.internalObjects.find(o->first);
			if (i != p->second.internalObjects.end())
			{
				sampgdk::SetPlayerObjectRot(p->first, i->second, o->second->rotation[0], o->second->rotation[1], o->second->rotation[2]);
			}
		}
		if (o->second->move) {
			if ((o->second->move->rotation.get<0>().maxCoeff() + 1000.0f) > std::numeric_limits<float>::epsilon()) {
				o->second->move->rotation.get<1>() = o->second->rotation;
				o->second->move->rotation.get<2>() = (o->second->move->rotation.get<0>() - o->second->rotation) / static_cast<float>(o->second->move->duration);
			}
		}
		return 1;
	}
	return 0;
}

/*int GetDynamicObjectRot(int id, float &rx, float &ry, float &rz) {
	boost::unordered_map<int, Item::SharedObject>::iterator o = core->getData()->objects.find(id);
	if (o != core->getData()->objects.end()) {
		if (o->second->move) {
			core->getStreamer()->processActiveItems();
		}
		rx = o->second->rotation[0];
		ry = o->second->rotation[1];
		rz = o->second->rotation[2];
		return 1;
	}
	return 0;
}*/

int SetDynamicObjectNoCameraCol(int id) {
	boost::unordered_map<int, Item::SharedObject>::iterator o = core->getData()->objects.find(id);
	if (o != core->getData()->objects.end()) {
		o->second->noCameraCollision = true;
		for (boost::unordered_map<int, Player>::iterator p = core->getData()->players.begin(); p != core->getData()->players.end(); ++p) {
			boost::unordered_map<int, int>::iterator i = p->second.internalObjects.find(o->first);
			if (i != p->second.internalObjects.end()) {
				sampgdk::SetPlayerObjectNoCameraCol(p->first, i->second);
			}
		}
		return 1;
	}
	return 0;
}

int GetDynamicObjectNoCameraCol(int id) {
	boost::unordered_map<int, Item::SharedObject>::iterator o = core->getData()->objects.find(id);
	if (o != core->getData()->objects.end()) {
		return o->second->noCameraCollision != 0;
	}
	return 0;
}

int MoveDynamicObject(int id, float x, float y, float z, float speed, float rx, float ry, float rz) {
	if (!rx) {
		return 0;
	}

	boost::unordered_map<int, Item::SharedObject>::iterator o = core->getData()->objects.find(id);
	if (o != core->getData()->objects.end()) {
		if (o->second->attach) {
			Utility::logError("MoveDynamicObject: Object is currently attached and cannot be moved.");
			return 0;
		}
		Eigen::Vector3f position(x, y, z);
		Eigen::Vector3f rotation(rx, ry, rz);
		o->second->move = boost::intrusive_ptr<Item::Object::Move>(new Item::Object::Move);
		o->second->move->duration = static_cast<int>((static_cast<float>(boost::geometry::distance(position, o->second->position) / speed * 1000.0f)));
		o->second->move->position.get<0>() = position;
		o->second->move->position.get<1>() = o->second->position;
		o->second->move->position.get<2>() = (position - o->second->position) / static_cast<float>(o->second->move->duration);
		o->second->move->rotation.get<0>() = rotation;
		if ((o->second->move->rotation.get<0>().maxCoeff() + 1000.0f) > std::numeric_limits<float>::epsilon())
		{
			o->second->move->rotation.get<1>() = o->second->rotation;
			o->second->move->rotation.get<2>() = (rotation - o->second->rotation) / static_cast<float>(o->second->move->duration);
		}
		o->second->move->speed = speed;
		o->second->move->time = boost::chrono::steady_clock::now();
		for (boost::unordered_map<int, Player>::iterator p = core->getData()->players.begin(); p != core->getData()->players.end(); ++p)
		{
			boost::unordered_map<int, int>::iterator i = p->second.internalObjects.find(o->first);
			if (i != p->second.internalObjects.end())
			{
				sampgdk::StopPlayerObject(p->first, i->second);
				sampgdk::MovePlayerObject(p->first, i->second, o->second->move->position.get<0>()[0], o->second->move->position.get<0>()[1], o->second->move->position.get<0>()[2], o->second->move->speed, o->second->move->rotation.get<0>()[0], o->second->move->rotation.get<0>()[1], o->second->move->rotation.get<0>()[2]);
			}
		}
		core->getStreamer()->movingObjects.insert(o->second);
		return static_cast<cell>(o->second->move->duration);
	}
	return 0;
}

int StopDynamicObject(int id) {
	boost::unordered_map<int, Item::SharedObject>::iterator o = core->getData()->objects.find(id);
	if (o != core->getData()->objects.end()) {
		if (o->second->move) {
			for (boost::unordered_map<int, Player>::iterator p = core->getData()->players.begin(); p != core->getData()->players.end(); ++p) {
				boost::unordered_map<int, int>::iterator i = p->second.internalObjects.find(o->first);
				if (i != p->second.internalObjects.end()) {
					sampgdk::StopPlayerObject(p->first, i->second);
				}
			}
			o->second->move.reset();
			core->getStreamer()->movingObjects.erase(o->second);
			return 1;
		}
	}
	return 0;
}

int IsDynamicObjectMoving(int id) {
	boost::unordered_map<int, Item::SharedObject>::iterator o = core->getData()->objects.find(id);
	if (o != core->getData()->objects.end()) {
		if (o->second->move) {
			return 1;
		}
	}
	return 0;
}

int AttachCameraToDynamicObject(int playerid, int objectid) {
	boost::unordered_map<int, Player>::iterator p = core->getData()->players.find(playerid);
	if (p != core->getData()->players.end()) {
		int internalID = INVALID_OBJECT_ID;
		boost::unordered_map<int, int>::iterator i = p->second.internalObjects.find(objectid);
		if (i == p->second.internalObjects.end()) {
			boost::unordered_map<int, Item::SharedObject>::iterator o = core->getData()->objects.find(objectid);
			if (o != core->getData()->objects.end())
			{
				p->second.position = Eigen::Vector3f(o->second->position[0], o->second->position[1], o->second->position[2]);
				core->getStreamer()->startManualUpdate(p->second, STREAMER_TYPE_OBJECT);
			}
			boost::unordered_map<int, int>::iterator j = p->second.internalObjects.find(objectid);
			if (j != p->second.internalObjects.end())
			{
				internalID = j->second;
			}
		} else {
			internalID = i->second;
		}
		if (internalID != INVALID_OBJECT_ID) {
			sampgdk::AttachCameraToPlayerObject(p->first, internalID);
			return 1;
		}
	}
	return 0;
}

int AttachDynamicObjectToObject(int objectid, int attachtoid, float x, float y, float z, float rx, float ry, float rz, bool sync) {
	if (sampgdk::FindNative("SetPlayerGravity") == NULL) {
		Utility::logError("AttachDynamicObjectToObject: YSF plugin must be loaded to attach objects to objects.");
		return 0;
	}
	boost::unordered_map<int, Item::SharedObject>::iterator o = core->getData()->objects.find(objectid);
	if (o != core->getData()->objects.end()) {
		if (o->second->move) {
			Utility::logError("AttachDynamicObjectToObject: Object is currently moving and must be stopped first.");
			return 0;
		}
		o->second->attach = boost::intrusive_ptr<Item::Object::Attach>(new Item::Object::Attach);
		o->second->attach->player = INVALID_OBJECT_ID;
		o->second->attach->vehicle = INVALID_OBJECT_ID;
		o->second->attach->object = attachtoid;
		o->second->attach->positionOffset = Eigen::Vector3f(x, y, z);
		o->second->attach->rotation = Eigen::Vector3f(rx, ry, rz);
		o->second->attach->syncRotation = sync;
		for (boost::unordered_map<int, Player>::iterator p = core->getData()->players.begin(); p != core->getData()->players.end(); ++p) {
			boost::unordered_map<int, int>::iterator i = p->second.internalObjects.find(o->first);
			if (i != p->second.internalObjects.end()) {
				boost::unordered_map<int, int>::iterator j = p->second.internalObjects.find(o->second->attach->object);
				if (j != p->second.internalObjects.end()) {
					AMX_NATIVE native = sampgdk::FindNative("AttachPlayerObjectToObject");
					if (native != NULL) {
						sampgdk::InvokeNative(native, "dddffffffb", p->first, i->second, j->second, o->second->attach->positionOffset[0], o->second->attach->positionOffset[1], o->second->attach->positionOffset[2], o->second->attach->rotation[0], o->second->attach->rotation[1], o->second->attach->rotation[2], o->second->attach->syncRotation);
					}
					for (boost::unordered_map<int, Item::Object::Material>::iterator m = o->second->materials.begin(); m != o->second->materials.end(); ++m) {
						if (m->second.main) {
							sampgdk::SetPlayerObjectMaterial(p->first, i->second, m->first, m->second.main->modelId, m->second.main->txdFileName.c_str(), m->second.main->textureName.c_str(), m->second.main->materialColor);
						} else if (m->second.text) {
							sampgdk::SetPlayerObjectMaterialText(p->first, i->second, m->second.text->materialText.c_str(), m->first, m->second.text->materialSize, m->second.text->fontFace.c_str(), m->second.text->fontSize, m->second.text->bold, m->second.text->fontColor, m->second.text->backColor, m->second.text->textAlignment);
						}
					}
				}
			}
		}
		if (attachtoid != INVALID_STREAMER_ID) {
			boost::unordered_map<int, Item::SharedObject>::iterator p = core->getData()->objects.find(attachtoid);
			if (p != core->getData()->objects.end())
			{
				if (o->second->comparableStreamDistance > STREAMER_STATIC_DISTANCE_CUTOFF && p->second->comparableStreamDistance > STREAMER_STATIC_DISTANCE_CUTOFF)
				{
					o->second->originalComparableStreamDistance = o->second->comparableStreamDistance;
					o->second->comparableStreamDistance = p->second->comparableStreamDistance + static_cast<float>(boost::geometry::comparable_distance(o->second->position, p->second->position));
				}
			}
			core->getStreamer()->attachedObjects.insert(o->second);
		} else {
			if (o->second->originalComparableStreamDistance > STREAMER_STATIC_DISTANCE_CUTOFF && o->second->comparableStreamDistance > STREAMER_STATIC_DISTANCE_CUTOFF) {
				o->second->comparableStreamDistance = o->second->originalComparableStreamDistance;
			}
			o->second->attach.reset();
			core->getStreamer()->attachedObjects.erase(o->second);
			core->getGrid()->removeObject(o->second, true);
		}
		return 1;
	}
	return 0;
}

int AttachDynamicObjectToPlayer(int objectid, int playerid, float x, float y, float z, float rx, float ry, float rz) {
	if (sampgdk::FindNative("SetPlayerGravity") == NULL) {
		Utility::logError("AttachDynamicObjectToPlayer: YSF plugin must be loaded to attach objects to players.");
		return 0;
	}
	boost::unordered_map<int, Item::SharedObject>::iterator o = core->getData()->objects.find(objectid);
	if (o != core->getData()->objects.end()) {
		if (o->second->move) {
			Utility::logError("AttachDynamicObjectToPlayer: Object is currently moving and must be stopped first.");
			return 0;
		}
		o->second->attach = boost::intrusive_ptr<Item::Object::Attach>(new Item::Object::Attach);
		o->second->attach->object = INVALID_STREAMER_ID;
		o->second->attach->vehicle = INVALID_OBJECT_ID;
		o->second->attach->player = playerid;
		o->second->attach->positionOffset = Eigen::Vector3f(x,y,z);
		o->second->attach->rotation = Eigen::Vector3f(rx,ry,rz);
		for (boost::unordered_map<int, Player>::iterator p = core->getData()->players.begin(); p != core->getData()->players.end(); ++p) {
			boost::unordered_map<int, int>::iterator i = p->second.internalObjects.find(o->first);
			if (i != p->second.internalObjects.end()) {
				AMX_NATIVE native = sampgdk::FindNative("AttachPlayerObjectToPlayer");
				if (native != NULL) {
					sampgdk::InvokeNative(native, "dddffffffd", p->first, i->second, o->second->attach->player, o->second->attach->positionOffset[0], o->second->attach->positionOffset[1], o->second->attach->positionOffset[2], o->second->attach->rotation[0], o->second->attach->rotation[1], o->second->attach->rotation[2], 0);
				}
				for (boost::unordered_map<int, Item::Object::Material>::iterator m = o->second->materials.begin(); m != o->second->materials.end(); ++m) {
					if (m->second.main) {
						sampgdk::SetPlayerObjectMaterial(p->first, i->second, m->first, m->second.main->modelId, m->second.main->txdFileName.c_str(), m->second.main->textureName.c_str(), m->second.main->materialColor);
					} else if (m->second.text) {
						sampgdk::SetPlayerObjectMaterialText(p->first, i->second, m->second.text->materialText.c_str(), m->first, m->second.text->materialSize, m->second.text->fontFace.c_str(), m->second.text->fontSize, m->second.text->bold, m->second.text->fontColor, m->second.text->backColor, m->second.text->textAlignment);
					}
				}
			}
		}
		if (playerid != INVALID_OBJECT_ID) {
			core->getStreamer()->attachedObjects.insert(o->second);
		} else {
			o->second->attach.reset();
			core->getStreamer()->attachedObjects.erase(o->second);
			core->getGrid()->removeObject(o->second, true);
		}
		return 1;
	}
	return 0;
}

int AttachDynamicObjectToVehicle(int objectid, int vehicleid, float x, float y, float z, float rx, float ry, float rz) {
	boost::unordered_map<int, Item::SharedObject>::iterator o = core->getData()->objects.find(objectid);
	if (o != core->getData()->objects.end()) {
		if (o->second->move) {
			Utility::logError("AttachDynamicObjectToVehicle: Object is currently moving and must be stopped first.");
			return 0;
		}
		o->second->attach = boost::intrusive_ptr<Item::Object::Attach>(new Item::Object::Attach);
		o->second->attach->object = INVALID_STREAMER_ID;
		o->second->attach->player = INVALID_OBJECT_ID;
		o->second->attach->vehicle = vehicleid;
		o->second->attach->positionOffset = Eigen::Vector3f(x, y, z);
		o->second->attach->rotation = Eigen::Vector3f(rx, ry, rz);
		for (boost::unordered_map<int, Player>::iterator p = core->getData()->players.begin(); p != core->getData()->players.end(); ++p) {
			boost::unordered_map<int, int>::iterator i = p->second.internalObjects.find(o->first);
			if (i != p->second.internalObjects.end()) {
				sampgdk::AttachPlayerObjectToVehicle(p->first, i->second, o->second->attach->vehicle, o->second->attach->positionOffset[0], o->second->attach->positionOffset[1], o->second->attach->positionOffset[2], o->second->attach->rotation[0], o->second->attach->rotation[1], o->second->attach->rotation[2]);
				for (boost::unordered_map<int, Item::Object::Material>::iterator m = o->second->materials.begin(); m != o->second->materials.end(); ++m) {
					if (m->second.main) {
						sampgdk::SetPlayerObjectMaterial(p->first, i->second, m->first, m->second.main->modelId, m->second.main->txdFileName.c_str(), m->second.main->textureName.c_str(), m->second.main->materialColor);
					} else if (m->second.text) {
						sampgdk::SetPlayerObjectMaterialText(p->first, i->second, m->second.text->materialText.c_str(), m->first, m->second.text->materialSize, m->second.text->fontFace.c_str(), m->second.text->fontSize, m->second.text->bold, m->second.text->fontColor, m->second.text->backColor, m->second.text->textAlignment);
					}
				}
			}
		}
		if (vehicleid != INVALID_OBJECT_ID) {
			core->getStreamer()->attachedObjects.insert(o->second);
		} else {
			o->second->attach.reset();
			core->getStreamer()->attachedObjects.erase(o->second);
			core->getGrid()->removeObject(o->second, true);
		}
		return 1;
	}
	return 0;
}

int EditDynamicObject(int playerid, int objectid) {
	boost::unordered_map<int, Player>::iterator p = core->getData()->players.find(playerid);
	if (p != core->getData()->players.end()) {
		int internalID = INVALID_OBJECT_ID;
		boost::unordered_map<int, int>::iterator i = p->second.internalObjects.find(objectid);
		if (i == p->second.internalObjects.end()) {
			boost::unordered_map<int, Item::SharedObject>::iterator o = core->getData()->objects.find(objectid);
			if (o != core->getData()->objects.end()) {
				if (o->second->comparableStreamDistance > STREAMER_STATIC_DISTANCE_CUTOFF && o->second->originalComparableStreamDistance < STREAMER_STATIC_DISTANCE_CUTOFF) {
					o->second->originalComparableStreamDistance = o->second->comparableStreamDistance;
					o->second->comparableStreamDistance = -1.0f;
				}

				p->second.position = Eigen::Vector3f(o->second->position[0], o->second->position[1], o->second->position[2]);
				core->getStreamer()->startManualUpdate(p->second, STREAMER_TYPE_OBJECT);
			}
			boost::unordered_map<int, int>::iterator j = p->second.internalObjects.find(objectid);
			if (j != p->second.internalObjects.end()) {
				internalID = j->second;
			}
		} else {
			internalID = i->second;
		}
		if (internalID != INVALID_OBJECT_ID) {
			sampgdk::EditPlayerObject(p->first, internalID);
			return 1;
		}
	}
	return 0;
}

int IsDynamicObjectMaterialUsed(int objectid, int materialindex) {
	boost::unordered_map<int, Item::SharedObject>::iterator o = core->getData()->objects.find(objectid);
	if (o != core->getData()->objects.end()) {
		boost::unordered_map<int, Item::Object::Material>::iterator m = o->second->materials.find(materialindex);
		if (m != o->second->materials.end()) {
			if (m->second.main) {
				return 1;
			}
		}
	}
	return 0;
}

int GetDynamicObjectMaterial(int objectid, int materialindex, int &modelid, std::string &txdname, std::string &texturename, int &materialcolor) {
	boost::unordered_map<int, Item::SharedObject>::iterator o = core->getData()->objects.find(objectid);
	if (o != core->getData()->objects.end()) {
		boost::unordered_map<int, Item::Object::Material>::iterator m = o->second->materials.find(materialindex);
		if (m != o->second->materials.end()) {
			if (m->second.main) {
				modelid = m->second.main->modelId;
				txdname = m->second.main->txdFileName;
				texturename = m->second.main->textureName;
				materialcolor = m->second.main->materialColor;
				return 1;
			}
		}
	}
	return 0;
}

int SetDynamicObjectMaterial(int id, int materialindex, int modelid,  std::string txdname, std::string texturename, int materialcolor)  {
	boost::unordered_map<int, Item::SharedObject>::iterator o = core->getData()->objects.find(id);
	if (o != core->getData()->objects.end()) {
		int index = materialindex;
		o->second->materials[index].main = boost::intrusive_ptr<Item::Object::Material::Main>(new Item::Object::Material::Main);
		o->second->materials[index].main->modelId = modelid;
		o->second->materials[index].main->txdFileName = txdname;
		o->second->materials[index].main->textureName = texturename;
		o->second->materials[index].main->materialColor = materialcolor;
		for (boost::unordered_map<int, Player>::iterator p = core->getData()->players.begin(); p != core->getData()->players.end(); ++p)
		{
			boost::unordered_map<int, int>::iterator i = p->second.internalObjects.find(o->first);
			if (i != p->second.internalObjects.end())
			{
				sampgdk::SetPlayerObjectMaterial(p->first, i->second, index, o->second->materials[index].main->modelId, o->second->materials[index].main->txdFileName.c_str(), o->second->materials[index].main->textureName.c_str(), o->second->materials[index].main->materialColor);
			}
		}
		o->second->materials[index].text.reset();
		return 1;
	}
	return 0;
}

int IsDynamicObjectMaterialTextUsed(int id, int materialindex) {
	boost::unordered_map<int, Item::SharedObject>::iterator o = core->getData()->objects.find(id);
	if (o != core->getData()->objects.end()) {
		boost::unordered_map<int, Item::Object::Material>::iterator m = o->second->materials.find(materialindex);
		if (m != o->second->materials.end()) {
			if (m->second.text) {
				return 1;
			}
		}
	}
	return 0;
}

int GetDynamicObjectMaterialText(int id, int materialindex, std::string &text, int &materialSize, std::string &fontface, int &fontsize, bool &bold, int &fontcolor, int &backcolor, int &textalignment) {
	boost::unordered_map<int, Item::SharedObject>::iterator o = core->getData()->objects.find(id);
	if (o != core->getData()->objects.end()) {
		boost::unordered_map<int, Item::Object::Material>::iterator m = o->second->materials.find(materialindex);
		if (m != o->second->materials.end()) {
			if (m->second.text) {
				text = m->second.text->materialText;
				materialSize = m->second.text->materialSize;
				fontface = m->second.text->fontFace;
				fontsize = m->second.text->fontSize;
				bold = m->second.text->bold;
				fontcolor = m->second.text->fontColor;
				backcolor = m->second.text->backColor;
				textalignment = m->second.text->textAlignment;
				return 1;
			}
		}
	}
	return 0;
}

int SetDynamicObjectMaterialText(int id, int materialindex, std::string text, int materialsize, std::string fontface, int fontsize, bool bold, int fontcolor, int backcolor, int textalignment) {
	boost::unordered_map<int, Item::SharedObject>::iterator o = core->getData()->objects.find(id);
	if (o != core->getData()->objects.end()) {
		int index = materialindex;
		o->second->materials[index].text = boost::intrusive_ptr<Item::Object::Material::Text>(new Item::Object::Material::Text);
		o->second->materials[index].text->materialText = text;
		o->second->materials[index].text->materialSize = materialsize;
		o->second->materials[index].text->fontFace = fontface;
		o->second->materials[index].text->fontSize = fontsize;
		o->second->materials[index].text->bold = bold;
		o->second->materials[index].text->fontColor = fontcolor;
		o->second->materials[index].text->backColor = backcolor;
		o->second->materials[index].text->textAlignment = textalignment;
		for (boost::unordered_map<int, Player>::iterator p = core->getData()->players.begin(); p != core->getData()->players.end(); ++p) {
			boost::unordered_map<int, int>::iterator i = p->second.internalObjects.find(o->first);
			if (i != p->second.internalObjects.end()) {
				sampgdk::SetPlayerObjectMaterialText(p->first, i->second, o->second->materials[index].text->materialText.c_str(), index, o->second->materials[index].text->materialSize, o->second->materials[index].text->fontFace.c_str(), o->second->materials[index].text->fontSize, o->second->materials[index].text->bold, o->second->materials[index].text->fontColor, o->second->materials[index].text->backColor, o->second->materials[index].text->textAlignment);
			}
		}
		o->second->materials[index].main.reset();
		return 1;
	}
	return 0;
}

int GetPlayerCameraTargetDynObject(int playerrid) {
	boost::unordered_map<int, Player>::iterator p = core->getData()->players.find(playerrid);
	if (p != core->getData()->players.end()) {
		int objectid = sampgdk::GetPlayerCameraTargetObject(p->second.playerId);
		if (objectid != INVALID_OBJECT_ID) {
			for (boost::unordered_map<int, int>::iterator i = p->second.internalObjects.begin(); i != p->second.internalObjects.end(); ++i) {
				if (i->second == objectid) {
					return i->first;
				}
			}
		}
	}
	return 0;
}

int GetDynamicObjectPos(int objectid, float &x, float &y, float &z) {
	boost::unordered_map<int, Item::SharedObject>::iterator o = core->getData()->objects.find(objectid);
	if (o != core->getData()->objects.end()) {
		if (o->second->move) {
			core->getStreamer()->processActiveItems();
		}
		x = o->second->position[0];
		y = o->second->position[1];
		z = o->second->position[2];
		return 1;
	}
	return 0;
}

int GetDynamicObjectRot(int objectid, float &rx, float &ry, float &rz) {
	boost::unordered_map<int, Item::SharedObject>::iterator o = core->getData()->objects.find(objectid);
	if (o != core->getData()->objects.end()) {
		if (o->second->move) {
			core->getStreamer()->processActiveItems();
		}
		rx = o->second->rotation[0];
		ry = o->second->rotation[1];
		rz = o->second->rotation[2];
		return 1;
	}
	return 0;
}

STREAMER_END_NS


/*
 * @file pppBayesTree.cpp
 * @brief Iterative GPS Range/Phase Estimator with collected data
 * @author Ryan Watson & Jason Gross
 */

// GTSAM related includes.
#include <gtsam/slam/dataset.h>
#include <gtsam/nonlinear/ISAM2.h>
#include <gtsam/inference/Symbol.h>
#include <gtsam/slam/PriorFactor.h>
#include <gtsam/slam/BetweenFactor.h>
#include <gtsam/gnssNavigation/GnssData.h>
#include <gtsam/gnssNavigation/GnssTools.h>
#include <gtsam/gnssNavigation/PhaseFactor.h>
#include <gtsam/gnssNavigation/nonBiasStates.h>
#include <gtsam/configReader/ConfDataReader.hpp>
#include <gtsam/nonlinear/NonlinearFactorGraph.h>
#include <gtsam/gnssNavigation/PseudorangeFactor.h>
#include <gtsam/nonlinear/LevenbergMarquardtOptimizer.h>

// GPSTK
#include <gtsam/gpstk/MJD.hpp>
#include <gtsam/gpstk/PowerSum.hpp>
#include <gtsam/gpstk/Decimate.hpp>
#include <gtsam/gpstk/SolidTides.hpp>
#include <gtsam/gpstk/PoleTides.hpp>
#include <gtsam/gpstk/TropModel.hpp>
#include <gtsam/gpstk/BasicModel.hpp>
#include <gtsam/gpstk/CommonTime.hpp>
#include <gtsam/gpstk/PCSmoother.hpp>
#include <gtsam/gpstk/OceanLoading.hpp>
#include <gtsam/gpstk/CodeSmoother.hpp>
#include <gtsam/gpstk/SimpleFilter.hpp>
#include <gtsam/gpstk/MWCSDetector.hpp>
#include <gtsam/gpstk/SatArcMarker.hpp>
#include <gtsam/gpstk/DCBDataReader.hpp>
#include <gtsam/gpstk/ComputeWindUp.hpp>
#include <gtsam/gpstk/Rinex3NavData.hpp>
#include <gtsam/gpstk/GNSSconstants.hpp>
#include <gtsam/gpstk/ComputeLinear.hpp>
#include <gtsam/gpstk/GPSWeekSecond.hpp>
#include <gtsam/gpstk/LICSDetector2.hpp>
#include <gtsam/gpstk/DataStructures.hpp>
#include <gtsam/gpstk/RinexObsStream.hpp>
#include <gtsam/gpstk/Rinex3ObsStream.hpp>
#include <gtsam/gpstk/Rinex3NavStream.hpp>
#include <gtsam/gpstk/ComputeTropModel.hpp>
#include <gtsam/gpstk/SP3EphemerisStore.hpp>
#include <gtsam/gpstk/ComputeSatPCenter.hpp>
#include <gtsam/gpstk/EclipsedSatFilter.hpp>
#include <gtsam/gpstk/GPSEphemerisStore.hpp>
#include <gtsam/gpstk/CorrectCodeBiases.hpp>
#include <gtsam/gpstk/ComputeSatPCenter.hpp>
#include <gtsam/gpstk/RequireObservables.hpp>
#include <gtsam/gpstk/CorrectObservables.hpp>
#include <gtsam/gpstk/LinearCombinations.hpp>
#include <gtsam/gpstk/GravitationalDelay.hpp>
#include <gtsam/gpstk/PhaseCodeAlignment.hpp>

// BOOST
#include <boost/filesystem.hpp>
#include <boost/program_options.hpp>
#include <boost/serialization/export.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>

// STD
#include <chrono>
#include <iomanip>
#include <fstream>
#include <iostream>
#include <unistd.h>
#include <algorithm>

using namespace std;
using namespace gtsam;
using namespace gpstk;
using namespace boost;
using namespace std::chrono;
namespace NM = gtsam::noiseModel;
namespace po = boost::program_options;
typedef noiseModel::Diagonal diagNoise;

// Intel Threading Building Block
#ifdef GTSAM_USE_TBB
  #include <tbb/tbb.h>
  #undef max // TBB seems to include windows.h and we don't want these macros
  #undef min
#endif

using symbol_shorthand::X; // nonBiasStates ( dx, dy, dz, trop, cb )
using symbol_shorthand::G;   // bias states ( Phase Biases )

int main(int argc, char* argv[])
{
        // define std out print color
        vector<int> prn_vec;
        vector<rnxData> data;
        const string red("\033[0;31m");
        const string green("\033[0;32m");
        string confFile, gnssFile, station, sp3File, p1p2File, p1c1File, antennaModel;
        string rnx_file, nav_file, sp3_file, out_file, antexFile;
        double xn, yn, zn, xp, yp, range, phase, rho, minElev, weightFactor;
        double antennaOffSetH, antennaOffSetE, antennaOffSetN;
        int startKey(0), currKey, startEpoch(0), svn, doy;
        int nThreads(-1), phase_break, break_count(0), nextKey, dec_int, itsBelowThree=0, count=0;
        bool printECEF, printENU, printAmb, printUpdateRate, first_ob(true), usingP1(false);

        cout.precision(12);

        po::options_description desc("Available options");
        desc.add_options()
                ("help,h", "Print help message")
                ("confFile,c", po::value<string>(&confFile)->default_value(""),
                "Input config file" )
                ("out", po::value<string>(&out_file)->default_value(""),
                "output file.")
                ("usingP1", "Are you using P1 instead of C1?");
        po::variables_map vm;
        po::store(po::command_line_parser(argc, argv).options(desc).run(), vm);
        po::notify(vm);

        ConfDataReader confReader;
        confReader.open(confFile);

        if (confFile.empty() ) {
                cout << red << "\n\n Currently, you need to provide a conf file \n"
                     << "\n\n"  << green << desc << endl;
        }

        while ( (station = confReader.getEachSection()) != "" )
        {
                // Fetch nominal station location [m]
                xn = confReader.fetchListValueAsDouble("nominalECEF",station);
                yn = confReader.fetchListValueAsDouble("nominalECEF",station);
                zn = confReader.fetchListValueAsDouble("nominalECEF",station);
                // day of year ( used for Niell Trop model)
                doy = confReader.getValueAsInt("DOY", station);
                // Elevation cut-off
                minElev = confReader.getValueAsDouble("minElev", station);
                // Code/carrier ratio
                weightFactor = confReader.getValueAsDouble("weightFactor", station);
                // Data file names
                gnssFile = confReader("rnxFile", station);
                sp3File = confReader("sp3File", station);
                p1p2File = confReader("p1p2", station);
                p1c1File = confReader("p1c1", station);
                // Print statements
                printENU = confReader.getValueAsBoolean("printENU", station);
                printAmb = confReader.getValueAsBoolean("printAmb", station);
                printECEF = confReader.getValueAsBoolean("printECEF", station);
                printUpdateRate = confReader.getValueAsBoolean("printUpdateRate", station);
        }

        usingP1 = (vm.count("usingP1")>0);

        Point3 nomXYZ(xn, yn, zn);
        Point3 prop_xyz = nomXYZ;

        #ifdef GTSAM_USE_TBB
        std::auto_ptr<tbb::task_scheduler_init> init;
        if(nThreads > 0) {
                init.reset(new tbb::task_scheduler_init(nThreads));
        }
        else
                cout << green << " \n\n Using threads for all processors" << endl;
        #else
        if(nThreads > 0) {
                cout << red <<" \n\n GTSAM is not compiled with TBB, so threading is"
                     << " disabled and the --threads option cannot be used."
                     << endl;
                exit(1);
        }
        #endif

        ISAM2DoglegParams doglegParams;
        ISAM2Params parameters;
        parameters.relinearizeThreshold = 0.1;
        parameters.relinearizeSkip = 100;
        ISAM2 isam(parameters);

        double output_time = 0.0;
        double rw = 2.5;
        double rangeWeight = pow(rw,2);
        double phaseWeight = pow(rw*1/weightFactor,2);

        string value;

        nonBiasStates prior_nonBias = (gtsam::Vector(5) << 0.0, 0.0, 0.0, 0.0, 0.0).finished();

        phaseBias bias_state(Z_1x1);
        gnssStateVector phase_arc(Z_34x1);
        gnssStateVector bias_counter(Z_34x1);
        for (int i=1; i<34; i++) {bias_counter(i) = bias_counter(i-1) + 10000; }

        nonBiasStates initEst(Z_5x1);
        nonBiasStates between_nonBias_State(Z_5x1);

        Values initial_values;
        Values result;

        noiseModel::Diagonal::shared_ptr nonBias_InitNoise = noiseModel::Diagonal::Variances((gtsam::Vector(5) << 10.0, 10.0, 10.0, 3e8, 1e-1).finished());

        noiseModel::Diagonal::shared_ptr nonBias_ProcessNoise = noiseModel::Diagonal::Variances((gtsam::Vector(5) << 0.1, 0.1, 0.1, 3e6, 3e-5).finished());

        noiseModel::Diagonal::shared_ptr initNoise = noiseModel::Diagonal::Variances((gtsam::Vector(1) << 100).finished());

        string obs_path = findExampleDataFile(gnssFile);
        string p1p2_path = findExampleDataFile(p1p2File);
        string p1c1_path = findExampleDataFile(p1c1File);

        if ( obs_path.empty() )
        {
                cout << " Must pass in obs file !!! " << desc << endl;
                exit(1);
        }
        if ( sp3File.empty() )
        {
                cout << " Must pass in ephemeris file !!! " << desc << endl;
                exit(1);
        }
        // Declare a "SP3EphemerisStore" object to handle precise ephemeris
        SP3EphemerisStore SP3EphList;

        size_t pos = 0;
        string path, token;
        string delimiter = " ";
        while ((pos = sp3File.find(delimiter)) != string::npos) {
                path = findExampleDataFile(sp3File.substr(0,pos));
                SP3EphList.loadFile(path);
                sp3File.erase(0, pos + delimiter.length());
        }
        path = findExampleDataFile(sp3File);
        SP3EphList.loadFile(path);

        // Set flags to reject satellites with bad or absent positional
        // values or clocks
        SP3EphList.rejectBadPositions(true);
        SP3EphList.rejectBadClocks(true);

        // Create the input observation file stream
        Rinex3ObsStream rin(obs_path);

        // station nominal position
        Position nominalPos(xn, yn, zn);

        CorrectCodeBiases corrCode;
        corrCode.setDCBFile(p1p2_path, p1c1_path);

        if (!usingP1) {
                corrCode.setUsingC1(true);
        }

        // This is the GNSS data structure that will hold all the
        // GNSS-related information
        gnssRinex gRin;

        RequireObservables requireObs;
        requireObs.addRequiredType(TypeID::L1);
        requireObs.addRequiredType(TypeID::L2);

        SimpleFilter pObsFilter;
        pObsFilter.setFilteredType(TypeID::C1);

        if ( usingP1 )
        {
                requireObs.addRequiredType(TypeID::P1);
                pObsFilter.addFilteredType(TypeID::P1);
                requireObs.addRequiredType(TypeID::P2);
                pObsFilter.addFilteredType(TypeID::P2);
        }
        else
        {
                requireObs.addRequiredType(TypeID::C1);
                pObsFilter.addFilteredType(TypeID::C1);
                requireObs.addRequiredType(TypeID::P2);
                pObsFilter.addFilteredType(TypeID::P2);
        }

        // Declare a couple of basic modelers
        BasicModel basic(nominalPos, SP3EphList);
        basic.setMinElev(minElev);

        // Object to correct for SP3 Sat Phase-center offset
        ComputeSatPCenter svPcenter(SP3EphList, nominalPos);

        // Objects to mark cycle slips
        MWCSDetector markCSMW;  // Checks Merbourne-Wubbena cycle slip

        // object def several linear combinations
        LinearCombinations comb;

        // Object to compute linear combinations for cycle slip detection
        ComputeLinear linear1;
        if ( usingP1 )
        {
                linear1.addLinear(comb.pdeltaCombination);
                linear1.addLinear(comb.mwubbenaCombination);
        }
        else
        {
                linear1.addLinear(comb.pdeltaCombWithC1);
                linear1.addLinear(comb.mwubbenaCombWithC1);
        }
        linear1.addLinear(comb.ldeltaCombination);
        linear1.addLinear(comb.liCombination);

        ComputeLinear linear2;

        // Read if we should use C1 instead of P1
        if ( usingP1 ) { linear2.addLinear(comb.pcCombination); }
        else {  linear2.addLinear(comb.pcCombWithC1); }
        linear2.addLinear(comb.lcCombination);

        LICSDetector2 markCSLI2;       // Checks LI cycle slips

        // Object to keep track of satellite arcs
        SatArcMarker markArc;
        markArc.setDeleteUnstableSats(true);

        // Objects to compute gravitational delay effects
        GravitationalDelay grDelay(nominalPos);

        // Object to remove eclipsed satellites
        EclipsedSatFilter eclipsedSV;

        //Object to compute wind-up effect
        ComputeWindUp windup( SP3EphList, nominalPos );

        // Object to compute prefit-residuals
        ComputeLinear linear3(comb.pcPrefit);
        linear3.addLinear(comb.lcPrefit);

        TypeIDSet tset;
        tset.insert(TypeID::prefitC);
        tset.insert(TypeID::prefitL);

        // Declare a NeillTropModel object, setting the defaults
        NeillTropModel neillTM( nominalPos.getAltitude(),
                                nominalPos.getGeodeticLatitude(),
                                doy);

        // Objects to compute the tropospheric data
        ComputeTropModel computeTropo(neillTM);

        // initialize factor graph
        NonlinearFactorGraph *graph = new NonlinearFactorGraph();

        auto start = std::chrono::steady_clock::now();
        auto end = std::chrono::steady_clock::now();
        // Loop over all data epochs
        while(rin >> gRin)
        {
                TimeSystem sys;
                sys.fromString("GPS");
                CommonTime time(gRin.header.epoch);
                time.setTimeSystem(sys);
                GPSWeekSecond gpstime( time );

                // update nominal ECEF with propogated pos.
                NeillTropModel neillTM( nominalPos.getAltitude(),
                                        nominalPos.getGeodeticLatitude(),
                                        doy);
                try
                {
                        gRin >> requireObs // Check if required observations are present
                        >> pObsFilter // Filter out spurious data
                        >> linear1 // Compute linear combinations to detect CS
                        >> markCSLI2 // Mark cycle slips
                        >> markArc // Keep track of satellite arcs
                        >> basic // Compute the basic components of model
                        >> eclipsedSV // Remove satellites in eclipse
                        >> grDelay // Compute gravitational delay
                        >> svPcenter // Computer delta for sat. phase center
                        >> corrCode // Correct for differential code biases
                        >> windup // phase windup correction
                        >> computeTropo // neill trop function
                        >> linear2  // Compute ionosphere-free combinations
                        >> linear3;   // Compute prefit residuals
                }
                catch(Exception& e) {continue; }
                catch(...)
                {
                        cerr << "Unknown exception at epoch: " << time << endl;
                        continue;
                }

                // Iterate through the GNSS Data Structure
                satTypeValueMap::const_iterator it;
                typeValueMap::const_iterator itObs;
                if ( itsBelowThree > 0 )
                {
                        itsBelowThree = 0;
                        continue;
                }

                if (gRin.body.size() == 0) { continue; }

                // Loop over all observed sats at current epoch
                for (it = gRin.body.begin(); it!= gRin.body.end(); it++)
                {

                        start = std::chrono::steady_clock::now();
                        svn = ((*it).first).id;
                        double satX, satY, satZ;
                        satX = (*it).second.getValue(TypeID::satX);
                        satY = (*it).second.getValue(TypeID::satY);
                        satZ = (*it).second.getValue(TypeID::satZ);
                        Point3 satXYZ = Point3(satX,satY,satZ);
                        double range, rangeRes;
                        range = (*it).second.getValue(TypeID::PC);
                        rangeRes = (*it).second.getValue(TypeID::prefitC);
                        double phase, phaseRes;
                        phase = (*it).second.getValue(TypeID::LC);
                        phaseRes = (*it).second.getValue(TypeID::prefitL);
                        int phase_break;
                        phase_break = (*it).second.getValue(TypeID::satArc);

                        if (first_ob) {
                                startKey = count; first_ob=false;
                                graph->add(PriorFactor<nonBiasStates>(X(count), initEst,  nonBias_InitNoise));
                                initial_values.insert(X(count), initEst);
                        }

                        if (phase_arc[svn]!=phase_break)
                        {
                                bias_state[0] = phase - range;
                                if (count > startKey) { bias_counter[svn] = bias_counter[svn] +1; }
                                graph->add(PriorFactor<phaseBias>(G(bias_counter[svn]), bias_state,  initNoise));
                                initial_values.insert(G(bias_counter[svn]), bias_state);
                                phase_arc[svn] = phase_break;
                        }
                        // Generate pseudorange factor
                        PseudorangeFactor gpsRangeFactor(X(count), rangeRes, satXYZ, nomXYZ, diagNoise::Variances( (gtsam::Vector(1) << elDepWeight(satXYZ, nomXYZ, rangeWeight)).finished()));

                        graph->add(gpsRangeFactor);

                        // Generate phase factor
                        PhaseFactor gpsPhaseFactor(X(count), G(bias_counter[svn]), phaseRes, satXYZ, nomXYZ, diagNoise::Variances( (gtsam::Vector(1) << elDepWeight(satXYZ, nomXYZ, phaseWeight)).finished() ));

                        graph->add(gpsPhaseFactor);
                        prn_vec.push_back(svn);
                }
                if (count > startKey ) {
                        graph->add(BetweenFactor<nonBiasStates>(X(count), X(count-1), between_nonBias_State, nonBias_ProcessNoise));
                }
                isam.update(*graph, initial_values);
                result = isam.calculateEstimate();

                end = std::chrono::steady_clock::now();

                prior_nonBias = result.at<nonBiasStates>(X(count));
                Point3 delta_xyz = (gtsam::Vector(3) << prior_nonBias.x(), prior_nonBias.y(), prior_nonBias.z()).finished();
                Position deltaPos(prior_nonBias.x(), prior_nonBias.y(), prior_nonBias.z());
                prop_xyz = nomXYZ - delta_xyz;
                nominalPos -= deltaPos;

                if (printECEF) {
                        cout << "xyz " << gpstime.week << " " << gpstime.sow << " " << prop_xyz.x() << " " << prop_xyz.y() << " " << prop_xyz.z() << endl;
                }

                if (printENU) {
                        Point3 enu = xyz2enu(prop_xyz, nomXYZ);
                        cout << "enu " << gpstime.week << " " << gpstime.sow << " " << enu.x() << " " << enu.y() << " " << enu.z() << endl;
                }

                if (printAmb) {
                        for (int k=0; k<prn_vec.size(); k++) {
                                cout << "amb. " << gpstime.week << " " << gpstime.sow << " ";
                                cout << prn_vec[k] << " ";
                                cout << result.at<phaseBias>(G(bias_counter[prn_vec[k]])) << endl;
                        }
                }

                if (printUpdateRate) {
                        cout << "Elapsed time "
                             << std::chrono::duration_cast<std::chrono::microseconds>(end - start).count()
                             << " µs" << endl;
                }

                output_time = output_time +1;
                graph->resize(0);
                initial_values.clear();
                prn_vec.clear();
                count++;
                initial_values.insert(X(count), prior_nonBias);
        }
        isam.saveGraph("gnss.tree");
        return 0;
}


/**
 * Copyright Soramitsu Co., Ltd. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 */

#include "consensus/yac/impl/supermajority_checker_cft.hpp"

#include <boost/range/algorithm/max_element.hpp>
#include <boost/range/numeric.hpp>
#include "consensus/yac/impl/supermajority_checker_kf1.hpp"

namespace iroha {
  namespace consensus {
    namespace yac {

      bool SupermajorityCheckerCft::hasSupermajority(
          PeersNumberType agreed, PeersNumberType all) const {
        return checkKfPlus1Supermajority(
            agreed, all, detail::kSupermajorityCheckerKfPlus1Cft);
      }

      bool SupermajorityCheckerCft::canHaveSupermajority(
          const VoteGroups &votes, PeersNumberType all) const {
        const PeersNumberType largest_group =
            boost::empty(votes) ? 0 : *boost::max_element(votes);
        const PeersNumberType voted = boost::accumulate(votes, 0);
        const PeersNumberType not_voted = all - voted;

        return hasSupermajority(largest_group + not_voted, all);
      }

    }  // namespace yac
  }    // namespace consensus
}  // namespace iroha


/*
	NOTE: This is not compiled as part of the Visual Studio project.
		  This file is the source of the compiled custom_object_detector.exe included in this repo.

		  Compiling it requires a machine with dlib installed, see the file this was
		  created from for more info and how to train: http://dlib.net/fhog_object_detector_ex.cpp.html
*/

#include <dlib/svm_threaded.h>
#include <dlib/string.h>
#include <dlib/image_processing.h>
#include <dlib/data_io.h>
#include <dlib/cmd_line_parser.h>

#include <iostream>
#include <fstream>

using namespace std;
using namespace dlib;

int main(int argc, char** argv)
{
    try
    {
        command_line_parser parser;
        parser.add_option("h","Display this help message.");
        parser.parse(argc, argv);

        // Turning this 3 into a 2 will disable the last layer, which is the basketball.
        typedef scan_fhog_pyramid<pyramid_down<3> > image_scanner_type;

        const char* one_time_opts[] = {"h"};
        parser.check_one_time_options(one_time_opts);

        if (parser.option("h"))
        {
            cout << "Usage: train_object_detector [options] <path to detector files> <image file>\n";
            parser.print_options();

            return EXIT_SUCCESS;
        }

        if (parser.number_of_arguments() != 2)
        {
            cout << "You must supply a training dataset generated by the imglab tool alongside an image to analyze." << endl;
            cout << "\nTry the -h option for more information." << endl;

            return EXIT_FAILURE;
        }

        // load a previously trained object detector and try it out on some data
        ifstream ball_fin(parser[0] + "/ball_detector.svm", ios::binary);
        ifstream net_fin(parser[0] + "/net_detector.svm", ios::binary);
        if (!ball_fin || !net_fin)
        {
            cout << "Can't find ball_detector.svm and net_detector.svm" << endl;
            cout << "in path " << parser[0] << "/." << endl;
            cout << "\nTry the -h option for more information." << endl;

            return EXIT_FAILURE;
        }

        image_scanner_type scanner;

        object_detector<image_scanner_type> ball_detector;
        object_detector<image_scanner_type> net_detector;
        deserialize(ball_detector, ball_fin);
        deserialize(net_detector, net_fin);

        dlib::array<array2d<unsigned char> > images;
        // Leaving this as a loop in case we want to handle multiple images at once later
        //images.resize(parser.number_of_arguments());
        images.resize(1);
        for (unsigned long i = 0; i < images.size(); ++i)
            load_image(images[i], parser[i + 1]); // parser[i + 1] since parser[0] is the detector path

        for (unsigned long i = 0; i < images.size(); ++i)
        {
            // Run the detector on images[i]
            const std::vector<rectangle> ball_rects = ball_detector(images[i]);
            const std::vector<rectangle> net_rects = net_detector(images[i]);

            if (ball_rects.size() == 1 && net_rects.size() == 1) {
                cout << "{\"net\":" << " {\"left\": " << net_rects[0].left() << ", \"top\": " << net_rects[0].top() << ", \"right\": " << net_rects[0].right() << ", \"bottom\": " << net_rects[0].bottom() << "}," << endl;
                cout << "\"ball\":" << " {\"left\": " << ball_rects[0].left() << ", \"top\": " << ball_rects[0].top() << ", \"right\": " << ball_rects[0].right() << ", \"bottom\": " << ball_rects[0].bottom() << "}}" << endl;
            }
        }
    }
    catch (exception& e)
    {
        cout << "\nexception thrown!" << endl;
        cout << e.what() << endl;
        cout << "\nTry the -h option for more information." << endl;
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}


#include <Eigen/Core>
#include <Eigen/Dense>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <iostream>
#include <ros/ros.h>
#include <sensor_msgs/Imu.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <sys/types.h>
#include <termios.h>
#include <time.h>
#include <unistd.h>

static int ret;
static int fd;

ros::Publisher pubImu;
#define BAUD 115200 // 115200 for JY61 ,9600 for others

template <typename Derived>
static Eigen::Matrix<typename Derived::Scalar, 3, 3>

ypr2R(const Eigen::MatrixBase<Derived> &ypr) {
  typedef typename Derived::Scalar Scalar_t;

  Scalar_t y = ypr(0) / 180.0 * M_PI;
  Scalar_t p = ypr(1) / 180.0 * M_PI;
  Scalar_t r = ypr(2) / 180.0 * M_PI;

  Eigen::Matrix<Scalar_t, 3, 3> Rz;
  Rz << cos(y), -sin(y), 0, sin(y), cos(y), 0, 0, 0, 1;

  Eigen::Matrix<Scalar_t, 3, 3> Ry;
  Ry << cos(p), 0., sin(p), 0., 1., 0., -sin(p), 0., cos(p);

  Eigen::Matrix<Scalar_t, 3, 3> Rx;
  Rx << 1., 0., 0., 0., cos(r), -sin(r), 0., sin(r), cos(r);

  return Rz * Ry * Rx;
}

int uart_open(int fd, const char *pathname) {
  fd = open(pathname, O_RDWR | O_NOCTTY);
  if (-1 == fd) {
    perror("Can't Open Serial Port");
    return (-1);
  } else
    printf("open %s success!\n", pathname);
  if (isatty(STDIN_FILENO) == 0)
    printf("standard input is not a terminal device\n");
  else
    printf("isatty success!\n");
  return fd;
}

int uart_set(int fd, int nSpeed, int nBits, char nEvent, int nStop) {
  struct termios newtio, oldtio;
  if (tcgetattr(fd, &oldtio) != 0) {
    perror("SetupSerial 1");
    printf("tcgetattr( fd,&oldtio) -> %d\n", tcgetattr(fd, &oldtio));
    return -1;
  }
  bzero(&newtio, sizeof(newtio));
  newtio.c_cflag |= CLOCAL | CREAD;
  newtio.c_cflag &= ~CSIZE;
  switch (nBits) {
  case 7:
    newtio.c_cflag |= CS7;
    break;
  case 8:
    newtio.c_cflag |= CS8;
    break;
  }
  switch (nEvent) {
  case 'o':
  case 'O':
    newtio.c_cflag |= PARENB;
    newtio.c_cflag |= PARODD;
    newtio.c_iflag |= (INPCK | ISTRIP);
    break;
  case 'e':
  case 'E':
    newtio.c_iflag |= (INPCK | ISTRIP);
    newtio.c_cflag |= PARENB;
    newtio.c_cflag &= ~PARODD;
    break;
  case 'n':
  case 'N':
    newtio.c_cflag &= ~PARENB;
    break;
  default:
    break;
  }

  /*设置波特率*/

  switch (nSpeed) {
  case 2400:
    cfsetispeed(&newtio, B2400);
    cfsetospeed(&newtio, B2400);
    break;
  case 4800:
    cfsetispeed(&newtio, B4800);
    cfsetospeed(&newtio, B4800);
    break;
  case 9600:
    cfsetispeed(&newtio, B9600);
    cfsetospeed(&newtio, B9600);
    break;
  case 115200:
    cfsetispeed(&newtio, B115200);
    cfsetospeed(&newtio, B115200);
    break;
  case 460800:
    cfsetispeed(&newtio, B460800);
    cfsetospeed(&newtio, B460800);
    break;
  default:
    cfsetispeed(&newtio, B9600);
    cfsetospeed(&newtio, B9600);
    break;
  }
  if (nStop == 1)
    newtio.c_cflag &= ~CSTOPB;
  else if (nStop == 2)
    newtio.c_cflag |= CSTOPB;
  newtio.c_cc[VTIME] = 0;
  newtio.c_cc[VMIN] = 0;
  tcflush(fd, TCIFLUSH);

  if ((tcsetattr(fd, TCSANOW, &newtio)) != 0) {
    perror("com set error");
    return -1;
  }
  printf("set done!\n");
  return 0;
}

int uart_close(int fd) {
  assert(fd);
  close(fd);

  return 0;
}
int send_data(int fd, char *send_buffer, int length) {
  length = write(fd, send_buffer, length * sizeof(unsigned char));
  return length;
}

int recv_data(int fd, char *recv_buffer, int length) {
  int has_receive_length = 0;
  while (length - has_receive_length > 0) {

    has_receive_length +=
        read(fd, recv_buffer + has_receive_length, length - has_receive_length);
    usleep(1000);
    //        std::cout << "has_receive_length: " << has_receive_length <<
    //        std::endl;
  }
  return length;
}

float a[3], w[3], Angle[3], h[3];
void ParseData(char *buffer) {
  char chrBuf[100];
  signed short sData[4];

  for (int i = 0; i < 4; i++) {
    memcpy(&chrBuf[0], buffer + 11 * i, 11);

    //        std::cout << "i: " << i << std::endl;
    //        for(int m =0; m< 11; m++)
    //        {
    //            printf("%x  ", chrBuf[m]);
    //        }
    memcpy(&sData[0], &chrBuf[2], 8);
    char cTemp = 0;
    time_t now;
    for (int j = 0; j < 10; j++) {
      cTemp += chrBuf[j];
    }

    if ((chrBuf[0] != 0x55) || ((chrBuf[1] & 0x50) != 0x50) ||
        (cTemp != chrBuf[10])) {
      printf("Error:%x  %x\r\n", chrBuf[0], chrBuf[1]);
      return;
    }
    switch (chrBuf[1]) {
    case 0x51:
      for (int k = 0; k < 3; k++) {
        a[k] = (float)sData[k] / 32768.0 * 16.0 * 9.81;
      }
      time(&now);
      printf("\r\nT:%s a:%6.3f %6.3f %6.3f ", asctime(localtime(&now)), a[0],
             a[1], a[2]);

      break;
    case 0x52:
      for (int k = 0; k < 3; k++) {
        w[k] = (float)sData[k] / 32768.0 * 2000.0/180*M_PI;
      }
      printf("w:%7.3f %7.3f %7.3f ", w[0], w[1], w[2]);
      break;
    case 0x53:
      for (int k = 0; k < 3; k++) {
        Angle[k] = (float)sData[k] / 32768.0 * 180.0;
      }
      printf("A:%7.3f %7.3f %7.3f ", Angle[0], Angle[1], Angle[2]);

      break;

    case 0x54:
      for (int k = 0; k < 3; k++) {
        h[k] = (float)sData[k];
      }
      printf("h:%4.0f %4.0f %4.0f ", h[0], h[1], h[2]);

      break;
    }
  }

  sensor_msgs::Imu imu_msg;

  imu_msg.header.stamp = ros::Time::now();
  imu_msg.header.frame_id = "/imu";
  imu_msg.angular_velocity.x = w[0];
  imu_msg.angular_velocity.y = w[1];
  imu_msg.angular_velocity.z = w[2];

  Eigen::Vector3d ypr;
  ypr << Angle[2], Angle[1], Angle[0];
  Eigen::Matrix R = ypr2R(ypr);
  std::cout << "R:\n " << R << std::endl;
  Eigen::Quaterniond q(R);
  q.normalize();

  imu_msg.orientation.w = q.w();
  imu_msg.orientation.x = q.x();
  imu_msg.orientation.y = q.y();
  imu_msg.orientation.z = q.z();

  imu_msg.linear_acceleration.x = a[0];
  imu_msg.linear_acceleration.y = a[1];
  imu_msg.linear_acceleration.z = a[2];

  pubImu.publish(imu_msg);
}

int main(int argc, char **argv) {
  ros::init(argc, argv, "xense_driver");
  ros::NodeHandle n_private("~");

  char r_buf[1024];
  bzero(r_buf, 1024);

  fd = uart_open(fd, "/dev/ttyUSB0"); /*串口号/dev/ttySn,USB口号/dev/ttyUSBn */
  if (fd == -1) {
    fprintf(stderr, "uart_open error\n");
    exit(EXIT_FAILURE);
  }

  if (uart_set(fd, BAUD, 8, 'N', 1) == -1) {
    fprintf(stderr, "uart set failed!\n");
    exit(EXIT_FAILURE);
  }

  pubImu = n_private.advertise<sensor_msgs::Imu>("/imu/data", 3000);

  while (ros::ok()) {
    ret = recv_data(fd, r_buf, 1);

    if (r_buf[0] != 0x55)
      continue;
    ret = recv_data(fd, r_buf + 1, 1);
    if (r_buf[1] != 0x51) {
      continue;
    }

    ret = recv_data(fd, r_buf + 2, 44 - 2);

    //        for(int i =0; i< 44; i++)
    //        {
    //            printf("%x  ", r_buf[i]);
    //        }
    std::cout << std::endl;
    ParseData(r_buf);
    usleep(1000);
  }

  ret = uart_close(fd);
  if (ret == -1) {
    fprintf(stderr, "uart_close error\n");
    exit(EXIT_FAILURE);
  }

  exit(EXIT_SUCCESS);
}


// This file is part of DM-HEOM (https://github.com/noma/dm-heom)
//
// Copyright (c) 2015-2019 Matthias Noack, Zuse Institute Berlin
//
// Licensed under the 3-clause BSD License, see accompanying LICENSE,
// CONTRIBUTORS.md, and README.md for further information.

#include <exception>
#include <fstream>
#include <iostream>

#include <boost/format.hpp>
#include <noma/num/meta_stepper.hpp>

#include "heom/command_line.hpp"
#include "heom/common.hpp"
#include "heom/dipole_matrix.hpp"
#include "heom/dipole_pseudo_pathway.hpp"
#include "heom/handle_main_exception.hpp"
#include "heom/hierarchy_mask.hpp"
#include "heom/hierarchy_norm.hpp"
#include "heom/instance.hpp"
#include "heom/make_file_observer_list.hpp"
#include "heom/ocl_config.hpp"
#include "heom/ode.hpp"
#include "heom/circular_dichroism_config.hpp"
#include "heom/population_dynamics_solver.hpp"
#include "heom/sites_to_states.hpp"

namespace bmt = ::heom::bmt;
namespace num = ::heom::num;
namespace ocl = ::heom::ocl;
using num::int_t;
using num::real_t;

int main(int argc, char* argv[])
{
	// start runtime measurement
	bmt::timer app_timer;

	// output compile time configuration
	std::cout << "-------------------- Compile-Time Configuration ------------" << std::endl;
	heom::write_compile_config(std::cout);
	std::cout << "------------------------------------------------------------" << std::endl;

	std::exception_ptr eptr;
	try {
		// command line parsing
		heom::command_line command_line { argc, argv };

		std::cout << "Parsing OpenCL configuration from file: " << command_line.ocl_config_filename() << std::endl;
		heom::ocl_config ocl_config(command_line.ocl_config_filename());
		std::cout << "Parsing HEOM configuration from file: " << command_line.heom_config_filename() << std::endl;
		heom::circular_dichroism_config heom_config(command_line.heom_config_filename());

		heom::hierarchy_graph complete_graph(heom_config.baths_number(), heom_config.baths_matsubaras(), heom_config.system_ado_depth());

		using stepper_t = num::meta_stepper; // uses solver_stepper_type from config
		using solver_t = heom::population_dynamics_solver<heom::ode, stepper_t, heom::hierarchy_norm>;

		// prepare output file (so we run into possible file errors before starting the computation)
		auto& observation_filename = heom_config.observations().get().front().get().second; // filename of first specified observation, guaranteed to exist by config checks
		std::ofstream observation_file(observation_filename);
		if (observation_file.fail())
			throw std::runtime_error("Error: could not open output file: " + observation_filename);

		int_t iterations = heom_config.solver_steps() / heom_config.program_observe_steps();
		int_t steps_per_iteration = heom_config.program_observe_steps();
		int_t total_steps = iterations * heom_config.program_observe_steps();

		//output data structure for traces
		const size_t num_dipole_matrices = heom_config.dipole_tensor_prefactors().size();
		const int_t num_observations = iterations + 1;
		std::vector<heom::matrix_trace_observation_view> observations(num_observations);

		// circular dichroism pseudo pathway with ground state
		const auto& pathway_spec = heom::dipole_pseudo_pathway_wgs_spec;
		const auto sts_mode = pathway_spec.sites_to_states_mode();

		// for progress estimate
		const size_t solver_runs = num_dipole_matrices;
		size_t solver_run = 0;

		// iterate over all dipoles and compute population dynamics
		for (size_t tensor_index = 0; tensor_index < heom_config.dipole_tensor_prefactors().size(); ++tensor_index) {
			heom::instance heom_instance(heom_config, sts_mode, complete_graph);
			heom_instance.set_hierarchy_top(); // NOTE: hierarchy top is initialised to zero with first element being complex_t(1.0,0.0) here

			// OpenCL range
			ocl::nd_range range {
				{}, // offset
				{1, static_cast<std::uint64_t>(heom_instance.matrices())}, // global size
				{1, 1} // local size
			};

			// create a solver from configuration and instance
			solver_t solver(ocl_config, range, heom_config, heom_instance);
			std::cout << "-------------------- OpenCL Runtime Configuration ----------" << std::endl;
			solver.write_ocl_runtime_config(std::cout);
			std::cout << "------------------------------------------------------------" << std::endl;
			++solver_run; // for progress estimate

			// compute dipole matrices
			auto dipole_matrix_minus = heom::get_dipole_matrix_for_pathway_circular(pathway_spec, 0, heom_config.system_sites(), heom_config, tensor_index);
			auto dipole_matrix_plus  = heom::get_dipole_matrix_for_pathway(pathway_spec, 1, heom_config.system_sites(), heom_config, tensor_index);

			// pre-scale D- for observer
			dipole_matrix_minus.scale(heom::get_dipole_tensor_prefactor(pathway_spec, heom_config, tensor_index));

			// multiply D+ from left
			solver.hierarchy_mmult_left(dipole_matrix_plus.data());

			// setup observer with pre-scaled D-
			heom::matrix_trace_observer<solver_t> trace_observer(complete_graph, solver, dipole_matrix_minus.data()); // D- from left via observer

			// generate first observation with initial value
			observations[0] += trace_observer.observe_trace(0.0);

			// write header once
			if (tensor_index == 0)
				trace_observer.write_header(observation_file, true);

			for (int_t i = 0; i < iterations; ++i) {
				// propagate
				solver.step_forward(steps_per_iteration);

				// after propagation, we are at:
				const auto current_step = (i + 1) * steps_per_iteration;
				const real_t current_time = current_step * heom_config.solver_step_size();

				// observe
				observations[i + 1] += trace_observer.observe_trace(current_time);

				// update status
				heom::write_progress(current_step - 1, total_steps, solver_run, solver_runs, "Calculation circular dichroism: ", std::cout);
			}

			// write_complex_matrix(result_buffer_top, heom_instance.states(), std::cout);
			std::cout << "-------------------- Solver Runtime Summary ----------------" << std::endl;
			solver.write_runtime_summary(std::cout);
			std::cout << "------------------------------------------------------------" << std::endl;

		} // for tensor_index

		// write averaged traces
		for (auto& obs : observations) {
			obs.avg(num_dipole_matrices);
			observation_file << obs << '\n';
		}
		observation_file << std::flush; // tell the OS to write to disk

		std::cout   << "-------------------- Memory Summary ------------------------" << std::endl;
		std::cout   << "max. heap via aligned::allocate(..):    "
		            << boost::format("%11.2f MiB") % (heom::memory::aligned::instance().allocated_byte_max() / 1024.0 / 1024.0)
		            << " ("
		            << boost::format("count: %6i") % heom::memory::aligned::instance().allocations_max()
		            << ')' << std::endl;
//		std::cout   << "max. OpenCL buffer allocations:         "
//		            << boost::format("%11.2f MiB") % (solver.ocl_helper().allocated_byte_max() / 1024.0 / 1024.0)
//		            << " ("
//		            << boost::format("count: %6i") % solver.ocl_helper().allocations_max()
//		            << ')' << std::endl;
		std::cout << "------------------------------------------------------------" << std::endl;

	} catch (...) {
		eptr = std::current_exception();
	}
	int ret = heom::handle_main_exception(eptr);

	// print application runtime
	std::cout << "time in main():\t" << boost::format("%11.2f") % std::chrono::duration_cast<bmt::seconds>(bmt::duration(app_timer.elapsed())).count() << " s" << std::endl;

	return ret;
}


#include <iostream>
#include <fstream>
#include <boost/cstdint.hpp>
#include "WeightedQuickUnion_UF_v2.hpp"

template <class T>
using UF = nxwheels::dyn_conn::WQU_UF_v2<T>;

int main(int argc, char *argv[]) {
    if (argc != 2)
        return -1;

    std::ifstream in{argv[1]};
    UF<boost::uint64_t> uf{};

    uf.fromStream(in);
    std::cout << uf.Count() << std::endl;
}


/**
 * @example types/test/quadtree_test.cc
 */
#include <boost/test/unit_test.hpp>
#include <usml/types/quadtree.h>
#include <usml/types/test/quadtree_test_support.h>
#include <iostream>
#include <list>
#include <cstdlib>

BOOST_AUTO_TEST_SUITE(quadtree_test)

using namespace boost::unit_test ;
using namespace usml::types ;
using namespace std ;

/**
 * @ingroup types_test
 * Test the construction, insertion and querying of the
 * quadtree class using points.
 */
BOOST_AUTO_TEST_CASE( quadtree_points ) {

    cout << "=== quadtree_test: quadtree_points ===" << endl;

    cout << "Creating a quadtree..." ;
    quadtree_type<point,1000>::points point_tree(-5000,-5000,10000,10000) ;
    cout << "done." << endl ;

    point p ;
//    box b( 22, 71, 70, 10 ) ;
    box b( -100, -37, 45, 20 ) ;
    list<point> truth ;
    srand(1) ;
    size_t N = 1000000 ;
    cout << "Populating the quadtree..." ;
    for(int i=0; i<N; ++i) {
        p.x = rand() % 5000 * std::pow( -1.0, i ) ;
        p.y = rand() % 5000 * std::pow( -1.0, i ) ;
        if( b.x <= p.x && p.x <= (b.x+b.width) ) {
            if( b.y <= p.y && p.y <= (b.y+b.height) ) {
                truth.push_back( p ) ;
            }
        }
        point_tree.insert(p) ;
    }
    cout << "complete." << endl ;
    truth.sort( sort_by_x<point> ) ;
//    point_tree.print() ;

    // query test
    list<point> result ;
    cout << "Querying the quadtree..." ;
    point_tree.query( b, &result ) ;
    cout << "finished, size of list is: " << result.size() << endl ;
    result.sort( sort_by_x<point> ) ;

    BOOST_CHECK_EQUAL( truth.size(), result.size() ) ;
    bool good = compare_list(truth,result) ;
    cout << "Query was " << ( good ? "successful" : "unsuccessful" ) << endl ;
    if( !good ) {
        cout << "Result list: " << endl ;
        print_list( result ) ;
        cout << "Truth list: " << endl ;
        print_list( truth ) ;
    }
}

BOOST_AUTO_TEST_SUITE_END()


/*
 * Copyright 2017 Facebook, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <folly/Checksum.h>
#include <boost/crc.hpp>
#include <folly/CpuId.h>
#include <folly/detail/ChecksumDetail.h>
#include <algorithm>
#include <stdexcept>

#if FOLLY_SSE_PREREQ(4, 2)
#include <nmmintrin.h>
#endif

namespace folly {

namespace detail {

uint32_t
crc32c_sw(const uint8_t* data, size_t nbytes, uint32_t startingChecksum);
#if FOLLY_SSE_PREREQ(4, 2)

// Fast SIMD implementation of CRC-32C for x86 with SSE 4.2
FOLLY_TARGET_ATTRIBUTE("sse4.2")
uint32_t crc32c_hw(const uint8_t *data, size_t nbytes,
    uint32_t startingChecksum) {
  uint32_t sum = startingChecksum;
  size_t offset = 0;

  // Process bytes one at a time until we reach an 8-byte boundary and can
  // start doing aligned 64-bit reads.
  static uintptr_t ALIGN_MASK = sizeof(uint64_t) - 1;
  size_t mask = (size_t)((uintptr_t)data & ALIGN_MASK);
  if (mask != 0) {
    size_t limit = std::min(nbytes, sizeof(uint64_t) - mask);
    while (offset < limit) {
      sum = (uint32_t)_mm_crc32_u8(sum, data[offset]);
      offset++;
    }
  }

  // Process 8 bytes at a time until we have fewer than 8 bytes left.
  while (offset + sizeof(uint64_t) <= nbytes) {
    const uint64_t* src = (const uint64_t*)(data + offset);
    sum = uint32_t(_mm_crc32_u64(sum, *src));
    offset += sizeof(uint64_t);
  }

  // Process any bytes remaining after the last aligned 8-byte block.
  while (offset < nbytes) {
    sum = (uint32_t)_mm_crc32_u8(sum, data[offset]);
    offset++;
  }
  return sum;
}

uint32_t
crc32_sw(const uint8_t* data, size_t nbytes, uint32_t startingChecksum);

// Fast SIMD implementation of CRC-32 for x86 with pclmul
uint32_t
crc32_hw(const uint8_t* data, size_t nbytes, uint32_t startingChecksum) {
  uint32_t sum = startingChecksum;
  size_t offset = 0;

  // Process unaligned bytes
  if ((uintptr_t)data & 15) {
    size_t limit = std::min(nbytes, -(uintptr_t)data & 15);
    sum = crc32_sw(data, limit, sum);
    offset += limit;
    nbytes -= limit;
  }

  if (nbytes >= 16) {
    sum = crc32_hw_aligned(sum, (const __m128i*)(data + offset), nbytes / 16);
    offset += nbytes & ~15;
    nbytes &= 15;
  }

  // Remaining unaligned bytes
  return crc32_sw(data + offset, nbytes, sum);
}

bool crc32c_hw_supported() {
  static folly::CpuId id;
  return id.sse42();
}

bool crc32_hw_supported() {
  static folly::CpuId id;
  return id.sse42();
}

#else

uint32_t crc32c_hw(const uint8_t *data, size_t nbytes,
    uint32_t startingChecksum) {
  throw std::runtime_error("crc32_hw is not implemented on this platform");
}

uint32_t crc32_hw(const uint8_t *data, size_t nbytes,
    uint32_t startingChecksum) {
  throw std::runtime_error("crc32_hw is not implemented on this platform");
}

bool crc32c_hw_supported() {
  return false;
}

bool crc32_hw_supported() {
  return false;
}
#endif

template <uint32_t CRC_POLYNOMIAL>
uint32_t crc_sw(const uint8_t* data, size_t nbytes, uint32_t startingChecksum) {
  // Reverse the bits in the starting checksum so they'll be in the
  // right internal format for Boost's CRC engine.
  //     O(1)-time, branchless bit reversal algorithm from
  //     http://graphics.stanford.edu/~seander/bithacks.html
  startingChecksum = ((startingChecksum >> 1) & 0x55555555) |
      ((startingChecksum & 0x55555555) << 1);
  startingChecksum = ((startingChecksum >> 2) & 0x33333333) |
      ((startingChecksum & 0x33333333) << 2);
  startingChecksum = ((startingChecksum >> 4) & 0x0f0f0f0f) |
      ((startingChecksum & 0x0f0f0f0f) << 4);
  startingChecksum = ((startingChecksum >> 8) & 0x00ff00ff) |
      ((startingChecksum & 0x00ff00ff) << 8);
  startingChecksum = (startingChecksum >> 16) |
      (startingChecksum << 16);

  boost::crc_optimal<32, CRC_POLYNOMIAL, ~0U, 0, true, true> sum(
      startingChecksum);
  sum.process_bytes(data, nbytes);
  return sum.checksum();
}

uint32_t
crc32c_sw(const uint8_t* data, size_t nbytes, uint32_t startingChecksum) {
  constexpr uint32_t CRC32C_POLYNOMIAL = 0x1EDC6F41;
  return crc_sw<CRC32C_POLYNOMIAL>(data, nbytes, startingChecksum);
}

uint32_t
crc32_sw(const uint8_t* data, size_t nbytes, uint32_t startingChecksum) {
  constexpr uint32_t CRC32_POLYNOMIAL = 0x04C11DB7;
  return crc_sw<CRC32_POLYNOMIAL>(data, nbytes, startingChecksum);
}

} // folly::detail

uint32_t crc32c(const uint8_t *data, size_t nbytes,
    uint32_t startingChecksum) {
  if (detail::crc32c_hw_supported()) {
    return detail::crc32c_hw(data, nbytes, startingChecksum);
  } else {
    return detail::crc32c_sw(data, nbytes, startingChecksum);
  }
}

uint32_t crc32(const uint8_t* data, size_t nbytes, uint32_t startingChecksum) {
  if (detail::crc32_hw_supported()) {
    return detail::crc32_hw(data, nbytes, startingChecksum);
  } else {
    return detail::crc32_sw(data, nbytes, startingChecksum);
  }
}

} // folly


#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <algorithm>
#include <chrono>
#include <memory>
#include <cmath>

#include <SmurffCpp/DataMatrices/Data.h>
#include <SmurffCpp/ConstVMatrixIterator.hpp>

#include <Eigen/Dense>
#include <Eigen/Sparse>

#include <SmurffCpp/Model.h>
#include <SmurffCpp/result.h>

#include <SmurffCpp/Utils/Error.h>
#include <SmurffCpp/Utils/StepFile.h>
#include <SmurffCpp/Utils/StringUtils.h>

#include <SmurffCpp/IO/GenericIO.h>
#include <SmurffCpp/IO/MatrixIO.h>

#define GLOBAL_TAG "global"
#define RMSE_AVG_TAG "rmse_avg"
#define RMSE_1SAMPLE_TAG "rmse_1sample"
#define AUC_AVG_TAG "auc_avg"
#define AUC_1SAMPLE_TAG "auc_1sample"
#define SAMPLE_ITER_TAG "sample_iter"
#define BURNIN_ITER_TAG "burnin_iter"

using namespace std;

using namespace smurff;

Result::Result() {}

//Y - test sparse matrix
Result::Result(std::shared_ptr<TensorConfig> Y, int nsamples)
    : m_dims(Y->getDims())
{
   if (!Y)
   {
      THROWERROR("test data is not initialized");
   }

   if(Y->isDense())
   {
      THROWERROR("test data should be sparse");
   }

   for(std::uint64_t i = 0; i < Y->getNNZ(); i++)
   {
      const auto p = Y->get(i);
      m_predictions.push_back(ResultItem(p.first, p.second, nsamples));
   }
}

Result::Result(PVec<> lo, PVec<> hi, double value, int nsamples)
    : m_dims(hi - lo)
{

   for(auto it = PVecIterator(lo, hi); !it.done(); ++it)
   {
      m_predictions.push_back(ResultItem(*it, value, nsamples));
   }
}

void Result::init()
{
   total_pos = 0;
   if (classify)
   {
         for (const auto &p : m_predictions)
         {
               int is_positive = p.val > threshold;
               total_pos += is_positive;
         }
   }
}

//--- output model to files
void Result::save(std::shared_ptr<const StepFile> sf) const
{
   savePred(sf);
   savePredState(sf);
}

template<typename Accessor>
std::shared_ptr<const MatrixConfig> Result::toMatrixConfig(const Accessor &acc) const
{
   std::vector<std::uint32_t> rows;
   std::vector<std::uint32_t> cols;
   std::vector<double> values;

   for (const auto &p : m_predictions)
   {
      rows.push_back(p.coords.at(0));
      cols.push_back(p.coords.at(1));
      values.push_back(acc(p));
   }

   return std::make_shared<MatrixConfig>(m_dims.at(0), m_dims.at(1), rows, cols, values, NoiseConfig(), false);
}

void Result::savePred(std::shared_ptr<const StepFile> sf) const
{
   if (isEmpty())
      return;

   std::string fname_pred = sf->makePredFileName();
   std::ofstream predFile;

   if (sf->isBinary())
   {
      predFile.open(fname_pred, std::ios::out | std::ios::binary);
      THROWERROR_ASSERT_MSG(predFile.is_open(), "Error opening file: " + fname_pred);
      predFile.write((const char *)(&m_predictions[0]), m_predictions.size() * sizeof(m_predictions[0]));

      if (m_dims.size() == 2)
      {
         std::string pred_avg_path = sf->makePredAvgFileName();
         auto pred_avg = toMatrixConfig([](const ResultItem &p) { return p.pred_avg; });
         smurff::matrix_io::write_matrix(pred_avg_path, pred_avg);

         std::string pred_var_path = sf->makePredVarFileName();
         auto pred_var = toMatrixConfig([](const ResultItem &p) { return p.var; });
         smurff::matrix_io::write_matrix(pred_var_path, pred_var);
      }
   }
   else
   {

      predFile.open(fname_pred, std::ios::out);
      THROWERROR_ASSERT_MSG(predFile.is_open(), "Error opening file: " + fname_pred);

      for (std::size_t d = 0; d < m_dims.size(); d++)
         predFile << "coord" << d << ",";

      predFile << "y,pred_1samp,pred_avg,var" << std::endl;

      for (std::vector<ResultItem>::const_iterator it = m_predictions.begin(); it != m_predictions.end(); it++)
      {
         it->coords.save(predFile)
             << "," << to_string(it->val)
             << "," << to_string(it->pred_1sample)
             << "," << to_string(it->pred_avg)
             << "," << to_string(it->var)
             << std::endl;
      }
   }

   predFile.close();

}

void Result::savePredState(std::shared_ptr<const StepFile> sf) const
{
   if (isEmpty())
      return;

   std::string predStateName = sf->makePredStateFileName();

   INIFile predStatefile;
   predStatefile.create(predStateName);

   predStatefile.startSection(GLOBAL_TAG);
   predStatefile.appendItem(GLOBAL_TAG, RMSE_AVG_TAG, std::to_string(rmse_avg));
   predStatefile.appendItem(GLOBAL_TAG, RMSE_1SAMPLE_TAG, std::to_string(rmse_1sample));
   predStatefile.appendItem(GLOBAL_TAG, AUC_AVG_TAG, std::to_string(auc_avg));
   predStatefile.appendItem(GLOBAL_TAG, AUC_1SAMPLE_TAG, std::to_string(auc_1sample));
   predStatefile.appendItem(GLOBAL_TAG, SAMPLE_ITER_TAG, std::to_string(sample_iter));
   predStatefile.appendItem(GLOBAL_TAG, BURNIN_ITER_TAG, std::to_string(burnin_iter));

   predStatefile.flush();
}

void Result::restore(std::shared_ptr<const StepFile> sf)
{
   restorePred(sf);
   restoreState(sf);
}

void Result::restorePred(std::shared_ptr<const StepFile> sf)
{
   std::string fname_pred = sf->getPredFileName();

   THROWERROR_FILE_NOT_EXIST(fname_pred);

   //open file with predictions
   std::ifstream predFile;

   std::string fname_ext = fname_pred.substr(fname_pred.find_last_of("."));

   if (fname_ext == ".bin") {
      predFile.open(fname_pred, std::ios::in | std::ios::binary);
      THROWERROR_ASSERT_MSG(predFile.is_open(), "Error opening file: " + fname_pred);
      predFile.read((char *)(&(m_predictions)[0]), m_predictions.size() * sizeof((m_predictions)[0]));
   }
   else if (fname_ext == ".csv")
   {
      //since predictions were set in set method - clear them
      std::size_t oldSize = m_predictions.size();
      m_predictions.clear();

      predFile.open(fname_pred);
      THROWERROR_ASSERT_MSG(predFile.is_open(), "Error opening file: " + fname_pred);

      //parse header
      std::string header;
      getline(predFile, header);

      std::vector<std::string> headerTokens;
      smurff::split(header, headerTokens, ',');

      //parse all lines
      std::vector<std::string> tokens;
      std::vector<int> coords;
      std::string line;

      while (getline(predFile, line))
      {
         //split line
         smurff::split(line, tokens, ',');

         //construct coordinates
         coords.clear();

         std::size_t nCoords = m_dims.size();

         for (std::size_t c = 0; c < nCoords; c++)
            coords.push_back(stoi(tokens[c].c_str()));

         //parse other values
         double val = stod(tokens.at(nCoords).c_str());
         double pred_1sample = stod(tokens.at(nCoords + 1).c_str());
         double pred_avg = stod(tokens.at(nCoords + 2).c_str());
         double var = stod(tokens.at(nCoords + 3).c_str());

         //construct result item
         m_predictions.push_back(ResultItem(smurff::PVec<>(coords), val, pred_1sample, pred_avg, var, sample_iter));
      }

      //just a sanity check, not sure if it is needed
      THROWERROR_ASSERT_MSG(oldSize == m_predictions.size(), "Incorrect predictions size after restore");
   }
   else
   {
      THROWERROR("Unknown extension: " + fname_pred);
   }

   predFile.close();
}

void Result::restoreState(std::shared_ptr<const StepFile> sf)
{
   std::string predStateName = sf->getPredStateFileName();

   INIFile iniReader;
   iniReader.open(predStateName);

   auto value = iniReader.get(GLOBAL_TAG, RMSE_AVG_TAG);
   rmse_avg = stod(value.c_str());

   value = iniReader.get(GLOBAL_TAG, RMSE_1SAMPLE_TAG);
   rmse_1sample = stod(value.c_str());

   value = iniReader.get(GLOBAL_TAG, AUC_AVG_TAG);
   auc_avg = stod(value.c_str());

   value = iniReader.get(GLOBAL_TAG, AUC_1SAMPLE_TAG);
   auc_1sample = stod(value.c_str());

   value = iniReader.get(GLOBAL_TAG, SAMPLE_ITER_TAG);
   sample_iter = stoi(value.c_str());

   value = iniReader.get(GLOBAL_TAG, BURNIN_ITER_TAG);
   burnin_iter = stoi(value.c_str());
}

//--- update RMSE and AUC

//model - holds samples (U matrices)
void Result::update(std::shared_ptr<const Model> model, bool burnin)
{
   if (m_predictions.empty())
      return;

   const size_t NNZ = m_predictions.size();

   if (burnin)
   {
      double se_1sample = 0.0;

      #pragma omp parallel for schedule(guided) reduction(+:se_1sample)
      for(size_t k = 0; k < m_predictions.size(); ++k)
      {
         auto &t = m_predictions.operator[](k);
         t.pred_1sample = model->predict(t.coords); //dot product of i'th columns in each U matrix
         se_1sample += std::pow(t.val - t.pred_1sample, 2);
      }

      burnin_iter++;
      rmse_1sample = std::sqrt(se_1sample / NNZ);

      if (classify)
      {
         auc_1sample = calc_auc(m_predictions, threshold,
               [](const ResultItem &a, const ResultItem &b) { return a.pred_1sample < b.pred_1sample;});
      }
   }
   else
   {
      double se_1sample = 0.0;
      double se_avg = 0.0;

      #pragma omp parallel for schedule(guided) reduction(+:se_1sample, se_avg)
      for(size_t k = 0; k < m_predictions.size(); ++k)
      {
         auto &t = m_predictions.operator[](k);
         const double pred = model->predict(t.coords); //dot product of i'th columns in each U matrix
         t.update(pred);

         se_1sample += std::pow(t.val - pred, 2);
         se_avg += std::pow(t.val - t.pred_avg, 2);
      }

      sample_iter++;
      rmse_1sample = std::sqrt(se_1sample / NNZ);
      rmse_avg = std::sqrt(se_avg / NNZ);

      if (classify)
      {
         auc_1sample = calc_auc(m_predictions, threshold,
               [](const ResultItem &a, const ResultItem &b) { return a.pred_1sample < b.pred_1sample;});

         auc_avg = calc_auc(m_predictions, threshold,
               [](const ResultItem &a, const ResultItem &b) { return a.pred_avg < b.pred_avg;});
      }
   }
}

std::ostream &Result::info(std::ostream &os, std::string indent)
{
   if (!m_predictions.empty())
   {
      std::uint64_t dtotal = 1;
      for(size_t d = 0; d < m_dims.size(); d++)
         dtotal *= m_dims[d];

      double test_fill_rate = 100. * m_predictions.size() / dtotal;

      os << indent << "Test data: " << m_predictions.size();

      os << " [";
      for(size_t d = 0; d < m_dims.size(); d++)
      {
         if(d == m_dims.size() - 1)
            os << m_dims[d];
         else
            os << m_dims[d] << " x ";
      }
      os << "]";

      os << " (" << test_fill_rate << "%)" << std::endl;

      if (classify)
      {
         double pos = 100. * (double)total_pos / (double)m_predictions.size();
         os << indent << "Binary classification threshold: " << threshold << std::endl;
         os << indent << "  " << pos << "% positives in test data" << std::endl;
      }
   }
   else
   {
      os << indent << "Test data: -" << std::endl;

      if (classify)
      {
         os << indent << "Binary classification threshold: " << threshold << std::endl;
         os << indent << "  " << "-" << "% positives in test data" << std::endl;
      }
   }

   return os;
}

bool Result::isEmpty() const
{
   return m_predictions.empty();
}


// Copyright (c) 2015-2018 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <chain.h>
#include <chainparams.h>
#include <pow.h>
#include <poc.h>
#include <random.h>
#include <util/system.h>
#include <test/test_bitcoin.h>

#include <boost/test/unit_test.hpp>

BOOST_FIXTURE_TEST_SUITE(poc_tests, BasicTestingSetup)

/* Test initial basetarget */
BOOST_AUTO_TEST_CASE(get_initial_basetarget)
{
    const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
    int64_t nLastRetargetTime = 0;
    CBlockIndex pindexLast;
    pindexLast.nHeight = 1;
    BOOST_CHECK_EQUAL(AdjustBaseTarget(&pindexLast, nLastRetargetTime), 18325193796L);
}

/* Test basetarget lower than 2700 */
BOOST_AUTO_TEST_CASE(get_middle_basetarget)
{
    const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
    int64_t nLastRetargetTime = 160960;

    std::vector<CBlockIndex> blocks(4);
    for (int i = 0; i < 4; i++) {
        blocks[i].pprev = i ? &blocks[i - 1] : nullptr;
        blocks[i].nHeight = 2000 + i;
        blocks[i].nTime = 160000 + 240*i;
        blocks[i].nBaseTarget = 10000;
    }

    BOOST_CHECK_EQUAL(AdjustBaseTarget(&blocks[3], nLastRetargetTime), 10000);
}

/* Test basetarget higher than 2700 */
BOOST_AUTO_TEST_CASE(get_last_basetarget)
{
    const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
    int64_t nLastRetargetTime = 165760;

    std::vector<CBlockIndex> blocks(24);
    for (int i = 0; i < 24; i++) {
        blocks[i].pprev = i ? &blocks[i - 1] : nullptr;
        blocks[i].nHeight = 2000 + i;
        blocks[i].nTime = 160000 + 240*i;
        blocks[i].nBaseTarget = 10000;
    }

    BOOST_CHECK_EQUAL(AdjustBaseTarget(&blocks[23], nLastRetargetTime), 10000);
}

//BOOST_AUTO_TEST_CASE(GetBlockProofEquivalentTime_test)
//{
//    const auto chainParams = CreateChainParams(CBaseChainParams::MAIN);
//    std::vector<CBlockIndex> blocks(10000);
//    for (int i = 0; i < 10000; i++) {
//        blocks[i].pprev = i ? &blocks[i - 1] : nullptr;
//        blocks[i].nHeight = i;
//        blocks[i].nTime = 1269211443 + i * chainParams->GetConsensus().nPowTargetSpacing;
//        blocks[i].nBaseTarget = 10000 + 1000*i;
//    }
//
//    for (int j = 0; j < 1000; j++) {
//        CBlockIndex *p1 = &blocks[InsecureRandRange(10000)];
//        CBlockIndex *p2 = &blocks[InsecureRandRange(10000)];
//        CBlockIndex *p3 = &blocks[InsecureRandRange(10000)];
//
//        int64_t tdiff = GetBlockProofEquivalentTime(*p1, *p2, *p3, chainParams->GetConsensus());
//        BOOST_CHECK_EQUAL(tdiff, p1->GetBlockTime() - p2->GetBlockTime());
//    }
//}

BOOST_AUTO_TEST_SUITE_END()


//==================================================================================================
/**
  Copyright 2016 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
**/
//==================================================================================================
#ifndef BOOST_SIMD_ARCH_X86_SSE2_SIMD_FUNCTION_SPLIT_HIGH_HPP_INCLUDED
#define BOOST_SIMD_ARCH_X86_SSE2_SIMD_FUNCTION_SPLIT_HIGH_HPP_INCLUDED

#include <boost/simd/detail/overload.hpp>
#include <boost/simd/function/is_ltz.hpp>
#include <boost/simd/constant/zero.hpp>
#include <boost/simd/detail/dispatch/meta/upgrade.hpp>

namespace boost { namespace simd { namespace ext
{
  BOOST_DISPATCH_OVERLOAD ( split_high_, (typename A0), bs::sse2_
                          , bs::pack_<bd::single_<A0>, bs::sse_>
                          )
  {
    BOOST_FORCEINLINE bd::upgrade_t<A0> operator()(const A0& a0) const BOOST_NOEXCEPT
    {
      return _mm_cvtps_pd(_mm_movehl_ps(a0, a0));
    }
  };

  BOOST_DISPATCH_OVERLOAD ( split_high_, (typename A0), bs::sse2_
                          , bs::pack_<bd::int8_<A0>, bs::sse_>
                          )
  {
    BOOST_FORCEINLINE bd::upgrade_t<A0> operator()(const A0& a0) const BOOST_NOEXCEPT
    {
      return _mm_unpackhi_epi8(a0, is_ltz(a0));
    }
  };

  BOOST_DISPATCH_OVERLOAD ( split_high_, (typename A0), bs::sse2_
                          , bs::pack_<bd::uint8_<A0>, bs::sse_>
                          )
  {
    BOOST_FORCEINLINE bd::upgrade_t<A0> operator()(const A0& a0) const BOOST_NOEXCEPT
    {
      return _mm_unpackhi_epi8(a0, Zero<A0>());
    }
  };

  BOOST_DISPATCH_OVERLOAD ( split_high_, (typename A0), bs::sse2_
                          , bs::pack_<bd::int16_<A0>, bs::sse_>
                          )
  {
    BOOST_FORCEINLINE bd::upgrade_t<A0> operator()(const A0& a0) const BOOST_NOEXCEPT
    {
      return _mm_unpackhi_epi16(a0, is_ltz(a0));
    }
  };

  BOOST_DISPATCH_OVERLOAD ( split_high_, (typename A0), bs::sse2_
                          , bs::pack_<bd::uint16_<A0>, bs::sse_>
                          )
  {
    BOOST_FORCEINLINE bd::upgrade_t<A0> operator()(const A0& a0) const BOOST_NOEXCEPT
    {
      return _mm_unpackhi_epi16(a0, Zero<A0>());
    }
  };

  BOOST_DISPATCH_OVERLOAD ( split_high_, (typename A0), bs::sse2_
                          , bs::pack_<bd::int32_<A0>, bs::sse_>
                          )
  {
    BOOST_FORCEINLINE bd::upgrade_t<A0> operator()(const A0& a0) const BOOST_NOEXCEPT
    {
      return _mm_unpackhi_epi32(a0, is_ltz(a0));
    }
  };

  BOOST_DISPATCH_OVERLOAD ( split_high_, (typename A0), bs::sse2_
                          , bs::pack_<bd::uint32_<A0>, bs::sse_>
                          )
  {
    BOOST_FORCEINLINE bd::upgrade_t<A0> operator()(const A0& a0) const BOOST_NOEXCEPT
    {
      return _mm_unpackhi_epi32(a0, Zero<A0>());
    }
  };
} } }

#endif


/** eval_sql_test.cc
    Jeremy Barnes, 3 August 2016
    Copyright (c) 2016 mldb.ai inc.  All rights reserved.

    This file is part of MLDB. Copyright 2016 mldb.ai inc. All rights reserved.

    Test of sql evaluation.
*/

#include "mldb/sql/binding_contexts.h"
#include "mldb/sql/eval_sql.h"

#define BOOST_TEST_MAIN
#define BOOST_TEST_DYN_LINK

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

using namespace std;

using namespace MLDB;

BOOST_AUTO_TEST_CASE(test_element_compare)
{
    SqlBindingScope scope;

    SqlRowScope rowScope;

    BOOST_CHECK_EQUAL(evalSql(scope, "$1 + $2", rowScope, 1, 2).getAtom(), 3);
    BOOST_CHECK_EQUAL(evalSql(scope, "$1 * $2", rowScope, 5, 20).getAtom(), 100);
    BOOST_CHECK_EQUAL(evalSql(scope, "[1, 2] + [3, 4]", rowScope)
                      .extractJson().toString(),
                      "[4,6]\n");
}