// ImGui - standalone example application for GLFW + OpenGL 3, using programmable pipeline
// If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp.
// (GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.)
// (GL3W is a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc.)

#include <stdio.h>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "Shader_m.h"
#include "Camera.h"
#include "Model.h"
#include <iostream>
#include <tuple>
#define CGAL_EIGEN3_ENABLED
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/Polyhedron_items_with_id_3.h>
#include <CGAL/extract_mean_curvature_flow_skeleton.h>
#include <CGAL/boost/graph/split_graph_into_polylines.h>
#include <fstream>
#include <boost/foreach.hpp>
#include <CGAL\Surface_mesh_deformation.h>
#include <CGAL/Polyhedron_items_with_id_3.h>
#include <CGAL/boost/graph/copy_face_graph.h>
#include <CGAL\Surface_mesh.h>
#include <CGAL/mesh_segmentation.h>
#include <CGAL/boost/graph/Face_filtered_graph.h>
#include <CGAL/property_map.h>
#include <CGAL/Polygon_mesh_processing/transform.h>
#include <CGAL/Aff_transformation_3.h>
#include <boost/variant/get.hpp>
#include <CGAL/aff_transformation_tags.h>
#include <CGAL/Vector_3.h>
#include <CGAL\IO\read_off_points.h>
#include <CGAL\IO\write_off_points.h>
#include <CGAL/Polygon_mesh_processing/triangulate_faces.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Polygon_mesh_processing/triangulate_hole.h>
#include <CGAL/Polygon_mesh_processing/compute_normal.h>
#include "ImportOBJ.h"
#include "TriMeshBuilder.h"
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#include "imgui.h"
#include "imgui_impl_glfw_gl3.h"

typedef Eigen::Vector3d													Vector3d;
typedef CGAL::Simple_cartesian<double>									Kernel;
typedef Kernel::Point_3													Point;
typedef Kernel::FT														pointFT;
typedef CGAL::Polyhedron_3<Kernel, CGAL::Polyhedron_items_with_id_3>    Polyhedron;
typedef boost::graph_traits<Polyhedron>::vertex_descriptor				vertex_descriptor;
typedef boost::graph_traits<Polyhedron>::vertex_iterator				vertex_iterator;
typedef boost::graph_traits<Polyhedron>::halfedge_descriptor			halfedge_descriptor;
typedef boost::graph_traits<Polyhedron>::out_edge_iterator				out_edge_iterator;
typedef boost::graph_traits<Polyhedron>::face_descriptor				face_descriptor;
typedef CGAL::Mean_curvature_flow_skeletonization<Polyhedron>			Skeletonization;
typedef CGAL::Surface_mesh<Point>										SM;
typedef Skeletonization::Skeleton										Skeleton;
typedef Skeleton::vertex_descriptor										Skeleton_vertex;
typedef Skeleton::edge_descriptor										Skeleton_edge;
/// Define the maps
typedef std::map<vertex_descriptor, std::size_t>						Vertex_id_map;
typedef std::map<halfedge_descriptor, std::size_t>						Hedge_id_map;
typedef boost::associative_property_map<Vertex_id_map>					Vertex_id_pmap;
typedef boost::associative_property_map<Hedge_id_map>					Hedge_id_pmap;
typedef CGAL::Surface_mesh_deformation<Polyhedron, CGAL::Default, CGAL::Default, CGAL::SRE_ARAP>	Surface_mesh_deformation;
//typedef CGAL::Polyhedron_3<Kernel, CGAL::Polyhedron_items_3, CGAL::HalfedgeDS_list>       CgalPolyhedron;
//typedef CGAL::Surface_mesh<Kernel::Point_3> SM;

/// Property map associating a facet with an integer as id to an
/// element in a vector stored internally
template<class ValueType>
struct Facet_with_id_pmap
	: public boost::put_get_helper<ValueType&,
	Facet_with_id_pmap<ValueType> >
{
	typedef face_descriptor key_type;
	typedef ValueType value_type;
	typedef value_type& reference;
	typedef boost::lvalue_property_map_tag category;
	Facet_with_id_pmap(
		std::vector<ValueType>& internal_vector
	) : internal_vector(internal_vector) { }
	reference operator[](key_type key) const
	{
		return internal_vector[key->id()];
	}
private:
	std::vector<ValueType>& internal_vector;
};

/// only needed for the display of the skeleton as maximal polylines
struct Display_polylines
{
	const Skeleton& skeleton;
	std::ofstream& out;
	int polyline_size;
	std::stringstream sstr;

	Display_polylines(const Skeleton& skeleton, std::ofstream& out)
		: skeleton(skeleton), out(out)
	{}
	void start_new_polyline() {
		polyline_size = 0;
		sstr.str("");
		sstr.clear();
	}
	void add_node(Skeleton_vertex v) {
		++polyline_size;
		sstr << " " << skeleton[v].point;

	}
	void end_polyline()
	{
		out << polyline_size << sstr.str() << "\n";
	}
};

/// Collect the vertices which are at distance less or equal to k
/// from the vertex v in the graph of vertices connected by the edges of P
std::vector<vertex_descriptor> extract_k_ring(const Polyhedron &P, vertex_descriptor v, int k)
{
	std::map<vertex_descriptor, int>  D;
	std::vector<vertex_descriptor>    Q;
	Q.push_back(v); D[v] = 0;
	std::size_t current_index = 0;
	int dist_v;
	while (current_index < Q.size() && (dist_v = D[Q[current_index]]) < k) {
		v = Q[current_index++];
		out_edge_iterator e, e_end;
		for (boost::tie(e, e_end) = out_edges(v, P); e != e_end; e++)
		{
			halfedge_descriptor he = halfedge(*e, P);
			vertex_descriptor new_v = target(he, P);
			if (D.insert(std::make_pair(new_v, dist_v + 1)).second) {
				Q.push_back(new_v);
			}
		}
	}
	return Q;
}

/// Simple helper function to load an image into a OpenGL texture with common settings
bool LoadTextureFromFile(const char* filename, GLuint* out_texture, int* out_width, int* out_height)
{
	/// Load from file
	int image_width = 0;
	int image_height = 0;
	unsigned char* image_data = stbi_load(filename, &image_width, &image_height, NULL, 4);
	if (image_data == NULL)
		return false;

	/// Create a OpenGL texture identifier
	GLuint image_texture;
	glGenTextures(1, &image_texture);
	glBindTexture(GL_TEXTURE_2D, image_texture);

	/// Setup filtering parameters for display
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

	/// Upload pixels into texture
	glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image_width, image_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data);
	stbi_image_free(image_data);

	*out_texture = image_texture;
	*out_width = image_width;
	*out_height = image_height;

	return true;
}

void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void processInput(GLFWwindow *window);

/// settings
const unsigned int SCR_WIDTH = 1900;
const unsigned int SCR_HEIGHT = 1000;
bool blinn = false;
bool blinnKeyPressed = false;

/// camera
Camera camera(glm::vec3(0.0f, 4.0f, 20.0f));
float lastX = SCR_WIDTH / 2.0f;
float lastY = SCR_HEIGHT / 2.0f;
bool firstMouse = true;

/// timing
float deltaTime = 0.0f;
float lastFrame = 0.0f;

/// lighting
glm::vec3 lightPos(1.2f, 1.0f, 2.0f);

bool skeletonWindowFlag = false;

std::vector<GLfloat> vectorOfEdges;
std::vector<Polyhedron> vectorOfSegments;
std::vector<Model> vectorOfModelsSegments;

GLuint textureTree = 0;
GLuint imageModelTreeSimple = 0;
GLuint imageModelPinus = 0;
GLuint imageModelOak = 0;
Model* modelLoaded;
int numberOfModelLoaded = 0;
Polyhedron tmesh;
Skeleton skeleton;

void loadModelImages()
{
	int imageModelTreeSimpleWidth = 0;
	int imageModelTreeSimpleHeight = 0;

	int imageModelTreePinusWidth = 0;
	int imageModelTreePinusHeight = 0;

	int imageModelTreeOakWidth = 0;
	int imageModelTreeOakHeight = 0;

	bool result0 = LoadTextureFromFile("resources/images/treegrow.png", &imageModelTreeSimple, &imageModelTreeSimpleWidth, &imageModelTreeSimpleHeight);
	IM_ASSERT(result0);
	bool result1 = LoadTextureFromFile("resources/images/pinus.png", &imageModelPinus, &imageModelTreePinusWidth, &imageModelTreePinusHeight);
	IM_ASSERT(result1);
	bool result2 = LoadTextureFromFile("resources/images/oak.png", &imageModelOak, &imageModelTreeOakWidth, &imageModelTreeOakHeight);
	IM_ASSERT(result2);

	int textureTreeWidth = 0;
	int textureTreeHeight = 0;
	bool result3 = LoadTextureFromFile("resources/textures/worn/albedo.tif", &textureTree, &textureTreeWidth, &textureTreeHeight);
	IM_ASSERT(result3);
}

void loadModel(const int &numberModel)
{
	if (numberModel == 1)
	{
		modelLoaded = new Model("resources/treegrow_materials/treegrow_materials.obj");
	}
	else if (numberModel == 2)
	{
		modelLoaded = new Model("resources/treegrow_pinus/pinus_final.obj");
	}
	else if (numberModel == 3)
	{
		modelLoaded = new Model("resources/treegrow_oak/oak_tree.obj");
	}
}

bool generateSkeleton(const int &numberModel)
{
	/// modelos em off para utilizacao da CGAL
	std::ifstream* input;
	if (numberModel == 1)
	{
		input = new std::ifstream("resources/treegrow_materials/treegrow_materials.off");
		if (!*input || !(*input >> tmesh) || tmesh.is_empty())
		{
			std::cerr << "Cannot open file.off" << std::endl;
			return EXIT_FAILURE;
		}
	}
	if (numberModel == 2)
	{
		input = new std::ifstream("resources/treegrow_pinus/pinus_final.off");
		if (!*input || !(*input >> tmesh) || tmesh.is_empty())
		{
			std::cerr << "Cannot open file.off" << std::endl;
			return EXIT_FAILURE;
		}
	}
	if (numberModel == 3)
	{
		input = new std::ifstream("resources/treegrow_oak/oak_tree.off");
		if (!*input || !(*input >> tmesh) || tmesh.is_empty())
		{
			std::cerr << "Cannot open file.off" << std::endl;
			return EXIT_FAILURE;
		}
	}

	if (!CGAL::is_closed(tmesh))
	{
		return EXIT_FAILURE;
	}
	if (!CGAL::is_triangle_mesh(tmesh))
	{
		std::cout << "Input geometry is not triangulated." << std::endl;
		std::cout << "Tring triangulated Input geometry " << std::endl;
		CGAL::Polygon_mesh_processing::triangulate_faces(tmesh);
		if (!CGAL::is_triangle_mesh(tmesh))
		{
			return EXIT_FAILURE;
		}
	}

	/// Gera skeleto
	CGAL::extract_mean_curvature_flow_skeleton(tmesh, skeleton);
	//std::cout << "Number of vertices of the skeleton: " << boost::num_vertices(skeleton) << "\n";
	//std::cout << "Number of edges of the skeleton: " << boost::num_edges(skeleton) << "\n";

	// Output all the edges of the skeleton.
	//std::ofstream output("skel-poly.cgal");
	//Display_polylines display(skeleton, output);
	//CGAL::split_graph_into_polylines(skeleton, display);
	//output.close();
	// Output skeleton points and the corresponding surface points
	//output.open("correspondance-poly.cgal");
	//BOOST_FOREACH(Skeleton_vertex v, vertices(skeleton))
	//	BOOST_FOREACH(vertex_descriptor vd, skeleton[v].vertices)
	//	output << "2 " << skeleton[v].point << " "
	//	<< get(CGAL::vertex_point, tmesh, vd) << "\n";
	//output.close();
	// Output all the edges of the skeleton.
	//output.open("skel-sm.cgal");
	BOOST_FOREACH(Skeleton_edge e, edges(skeleton))
	{
		const Point& s = skeleton[source(e, skeleton)].point;
		const Point& t = skeleton[target(e, skeleton)].point;
		vectorOfEdges.push_back(s.x());
		vectorOfEdges.push_back(s.y());
		vectorOfEdges.push_back(s.z());
		vectorOfEdges.push_back(t.x());
		vectorOfEdges.push_back(t.y());
		vectorOfEdges.push_back(t.z());
		//output << "2 " << s << " " << t << "\n";
	}
	//output.close();
	skeletonWindowFlag = true;
	return true;
}

bool generateSegments(const int &numberModel)
{
	/// init the polyhedron simplex indices
	CGAL::set_halfedgeds_items_id(tmesh);
	//for each input vertex compute its distance to the skeleton
	std::vector<double> distances(num_vertices(tmesh));
	BOOST_FOREACH(Skeleton_vertex v, vertices(skeleton))
	{
		const Point& skel_pt = skeleton[v].point;
		BOOST_FOREACH(vertex_descriptor mesh_v, skeleton[v].vertices)
		{
			const Point& mesh_pt = mesh_v->point();
			distances[mesh_v->id()] = std::sqrt(CGAL::squared_distance(skel_pt, mesh_pt));
		}
	}

	/// create a property-map for sdf values
	std::vector<double> sdf_values(num_faces(tmesh));
	Facet_with_id_pmap<double> sdf_property_map(sdf_values);
	/// compute sdf values with skeleton
	BOOST_FOREACH(face_descriptor f, faces(tmesh))
	{
		double dist = 0;
		BOOST_FOREACH(halfedge_descriptor hd, halfedges_around_face(halfedge(f, tmesh), tmesh))
			dist += distances[target(hd, tmesh)->id()];
		sdf_property_map[f] = dist / 3.0;
	}
	/// post-process the sdf values
	CGAL::sdf_values_postprocessing(tmesh, sdf_property_map);
	/// create a property-map for segment-ids (it is an adaptor for this case)
	std::vector<std::size_t> segment_ids(num_faces(tmesh));
	Facet_with_id_pmap<std::size_t> segment_property_map(segment_ids);

	/// segment the mesh using default parameters for number of levels, and smoothing lambda
	/// Any other scalar values can be used instead of using SDF values computed using the CGAL function
	std::size_t number_of_segments = CGAL::segmentation_from_sdf_values(tmesh, sdf_property_map, segment_property_map);
	std::cout << "Number of segments: " << number_of_segments << "\n";
	typedef CGAL::Face_filtered_graph<Polyhedron> Filtered_graph;
	/// print area of each segment and then put it in a Mesh and print it in an OFF file
	Filtered_graph segment_mesh(tmesh, 0, segment_property_map);

	for (std::size_t id = 0; id < number_of_segments; ++id)
	{
		if (id > 0)
			segment_mesh.set_selected_faces(id, segment_property_map);
		Polyhedron mesh_out;
		CGAL::copy_face_graph(segment_mesh, mesh_out);
		std::ostringstream oss;
		oss << "resources/segments/Segment_" << id << ".off";
		std::ofstream os(oss.str().data());
		os << mesh_out;
		vectorOfSegments.push_back(mesh_out);
	}
	return true;
}

void loadSegments()
{
	for (size_t i = 0; i < vectorOfSegments.size(); ++i)
	{

	}
}

void setStyleImGui()
{
	ImGuiStyle& style = ImGui::GetStyle();
	style.WindowRounding = 5.3f;
	style.FrameRounding = 5.0f;
	style.ScrollbarRounding = 0;

	style.Colors[ImGuiCol_Text] = ImVec4(0.90f, 0.90f, 0.90f, 0.90f);
	style.Colors[ImGuiCol_TextDisabled] = ImVec4(0.60f, 0.60f, 0.60f, 1.00f);
	style.Colors[ImGuiCol_WindowBg] = ImVec4(0.09f, 0.09f, 0.15f, 1.00f);
	style.Colors[ImGuiCol_ChildWindowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
	style.Colors[ImGuiCol_PopupBg] = ImVec4(0.05f, 0.05f, 0.10f, 0.85f);
	style.Colors[ImGuiCol_Border] = ImVec4(0.70f, 0.70f, 0.70f, 0.65f);
	style.Colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
	style.Colors[ImGuiCol_FrameBg] = ImVec4(0.0f, 1.0f, 0.0f, 0.25f);
	style.Colors[ImGuiCol_FrameBgHovered] = ImVec4(0.0f, 1.0f, 0.0f, 0.50f);
	style.Colors[ImGuiCol_FrameBgActive] = ImVec4(0.0f, 1.0f, 0.0f, 0.75f);
	style.Colors[ImGuiCol_TitleBg] = ImVec4(0.0f, 1.0f, 0.0f, 0.50f);
	style.Colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.0f, 1.0f, 0.0f, 0.50f);
	style.Colors[ImGuiCol_TitleBgActive] = ImVec4(0.0f, 1.0f, 0.0f, 0.75f);
	style.Colors[ImGuiCol_MenuBarBg] = ImVec4(0.01f, 0.01f, 0.02f, 0.80f);
	style.Colors[ImGuiCol_ScrollbarBg] = ImVec4(0.20f, 0.25f, 0.30f, 0.60f);
	style.Colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.55f, 0.53f, 0.55f, 0.51f);
	style.Colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.56f, 0.56f, 0.56f, 1.00f);
	style.Colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.56f, 0.56f, 0.56f, 0.91f);
	//style.Colors[ImGuiCol_ComboBg] = ImVec4(0.1f, 0.1f, 0.1f, 0.99f);
	style.Colors[ImGuiCol_CheckMark] = ImVec4(0.0f, 1.0f, 0.0f, 0.75f);
	style.Colors[ImGuiCol_SliderGrab] = ImVec4(0.0f, 1.0f, 0.0f, 0.50f);
	style.Colors[ImGuiCol_SliderGrabActive] = ImVec4(0.0f, 1.0f, 0.0f, 0.75f);
	style.Colors[ImGuiCol_Button] = ImVec4(0.0f, 1.0f, 0.0f, 0.25f);
	style.Colors[ImGuiCol_ButtonHovered] = ImVec4(0.0f, 1.0f, 0.0f, 0.50f);
	style.Colors[ImGuiCol_ButtonActive] = ImVec4(0.0f, 1.0f, 0.0f, 0.75f);
	style.Colors[ImGuiCol_Header] = ImVec4(0.0f, 1.0f, 0.0f, 0.25f);
	style.Colors[ImGuiCol_HeaderHovered] = ImVec4(0.0f, 1.0f, 0.0f, 0.50f);
	style.Colors[ImGuiCol_HeaderActive] = ImVec4(0.0f, 1.0f, 0.0f, 0.75f);
	style.Colors[ImGuiCol_Column] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);
	style.Colors[ImGuiCol_ColumnHovered] = ImVec4(0.70f, 0.60f, 0.60f, 1.00f);
	style.Colors[ImGuiCol_ColumnActive] = ImVec4(0.90f, 0.70f, 0.70f, 1.00f);
	style.Colors[ImGuiCol_ResizeGrip] = ImVec4(1.00f, 1.00f, 1.00f, 0.85f);
	style.Colors[ImGuiCol_ResizeGripHovered] = ImVec4(1.00f, 1.00f, 1.00f, 0.60f);
	style.Colors[ImGuiCol_ResizeGripActive] = ImVec4(1.00f, 1.00f, 1.00f, 0.90f);
	//style.Colors[ImGuiCol_CloseButton] = ImVec4(0.50f, 0.50f, 0.90f, 0.50f);
	//style.Colors[ImGuiCol_CloseButtonHovered] = ImVec4(0.70f, 0.70f, 0.90f, 0.60f);
	//style.Colors[ImGuiCol_CloseButtonActive] = ImVec4(0.70f, 0.70f, 0.70f, 1.00f);
	style.Colors[ImGuiCol_PlotLines] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
	style.Colors[ImGuiCol_PlotLinesHovered] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
	style.Colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
	style.Colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);
	style.Colors[ImGuiCol_TextSelectedBg] = ImVec4(0.0f, 1.0f, 0.0f, 0.25f);
	style.Colors[ImGuiCol_ModalWindowDarkening] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f);
}

/// ------------------------------------------------------------------ INT MAIN ----------------------------------------------------------------- ///
int main()
{
	/// glfw: initialize and configure
	glfwInit();
	glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
	glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
	glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

	#ifdef __APPLE__
		glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // uncomment this statement to fix compilation on OS X
	#endif

	/// glfw window creation
	GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "TreeGrow", NULL, NULL);
	if (window == NULL)
	{
		std::cout << "Failed to create GLFW window" << std::endl;
		glfwTerminate();
		return -1;
	}

	glfwMakeContextCurrent(window);
	glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
	//glfwSetCursorPosCallback(window, mouse_callback);
	glfwSetScrollCallback(window, scroll_callback);

	/// tell GLFW to capture our mouse
	glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
	glewExperimental = true;
	glewInit();

	/// configure global opengl state
	glEnable(GL_DEPTH_TEST);

	/// build and compile shaders
	Shader lightShader("VertexShader.glsl", "FragmentShader.glsl");

	GLuint vertexArrayBones, vertexBufferBones;
	glLineWidth(2.0f);
	glGenVertexArrays(1, &vertexArrayBones);
	glGenBuffers(1, &vertexBufferBones);
	glBindVertexArray(vertexArrayBones);
	glBindBuffer(GL_ARRAY_BUFFER, vertexBufferBones);

	/// vetor com todos os vertices e valores de posicao
	//std::vector<pointFT> vectorOfVertices;
	//Model modelLoaded1("resources/segments/Segment_0.obj");
	//Model modelLoaded2("resources/segments/Segment_1.obj");
	//Model modelLoaded3("resources/segments/Segment_2.obj");
	//Model modelLoaded4("resources/segments/Segment_3.obj");
	//Model modelLoaded5("resources/segments/Segment_4.obj");
	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	//std::vector<Model> vectorOfModels;*/

	//vectorOfModels.push_back(modelLoaded1);
	//vectorOfModels.push_back(modelLoaded2);
	//vectorOfModels.push_back(modelLoaded3);
	//vectorOfModels.push_back(modelLoaded4);
	//vectorOfModels.push_back(modelLoaded5);
	/*
	// vetor de numero de vertices em cada mesh;
	std::vector<int> vectorOfNumberOfVertices;
	for (int i = 0, numberOfVertices = 0, numberOfMeshes = modelLoaded.meshes.size(); i < numberOfMeshes; ++i)
	{
		numberOfVertices = modelLoaded.meshes[i].vertices.size();
		std::cout << "Number of vertices of mesh : " << i << " is : " << numberOfVertices << std::endl;
		vectorOfNumberOfVertices.push_back(numberOfVertices);
	}
	for (int j = 0; j < vectorOfNumberOfVertices[0]; ++j)
	{
		vectorOfVertices.push_back(modelLoaded.meshes[0].vertices[j].Position.x);
		vectorOfVertices.push_back(modelLoaded.meshes[0].vertices[j].Position.y);
		vectorOfVertices.push_back(modelLoaded.meshes[0].vertices[j].Position.z);
	}
	*/

	/*
		Assimp::Importer importer;
		Assimp::Exporter exporter;
		const aiScene* pTest = importer.ReadFile("resources/treegrow_materials/treegrow_materials.off", aiProcess_ValidateDataStructure);
		//aiScene* scene;
		//scene->mMeshes = pTest->mMeshes;
		//scene->mNumMeshes = pTest->mNumMeshes;
		//scene->mRootNode = new aiNode();

		//scene->mMaterials = pTest->mMaterials;
		//scene->mNumMaterials = pTest->mNumMaterials;

		exporter.Export(pTest, "collada", "resources/collada/teste.dae");
	}

	*/
	///Initialize ImGUI
	ImGui::CreateContext();
	ImGuiIO& io = ImGui::GetIO(); (void)io;
	io.MouseDrawCursor = true;
	//io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;  // Enable Keyboard Controls
	//io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;   // Enable Gamepad Controls
	ImGui_ImplGlfwGL3_Init(window, true);

	/// Setup style
	ImGui::StyleColorsDark();

	/// shader configuration
	lightShader.use();
	lightShader.setInt("material.diffuse", 0);
	lightShader.setInt("material.specular", 0);

	/// draw in wireframe
	//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

	////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

	std::vector<glm::vec3> vectorOfVec3;
	glm::vec3 vec3Scale1(0.1, 0.1, 0.1);
	vectorOfVec3.push_back(vec3Scale1);
	glm::vec3 vec3Scale2(0.1, 0.1, 0.1);
	vectorOfVec3.push_back(vec3Scale2);
	glm::vec3 vec3Scale3(0.1, 0.1, 0.1);
	vectorOfVec3.push_back(vec3Scale3);
	glm::vec3 vec3Scale4(0.1, 0.1, 0.1);
	vectorOfVec3.push_back(vec3Scale4);
	glm::vec3 vec3Scale5(0.1, 0.1, 0.1);
	vectorOfVec3.push_back(vec3Scale5);

	int numberOfSegments = vectorOfSegments.size();
	float* valueOfScale = new float[numberOfSegments];
	for (int i = 0; i < vectorOfSegments.size(); ++i)
	{
		valueOfScale[i] = vec3Scale1.x;
	}

	bool showWindowModelsFlag = false;
	bool imageModelsLoadedFlag = false;
	bool generateSkeletonFlag = false;
	bool showModelFlag = false;
	bool showMenuSegments = false;
	bool generateSegmentsFlag = false;

	/// -----------------------------------------------------------------   RENDER LOOP -----------------------------------------------------------///
	while (!glfwWindowShouldClose(window))
	{
		glfwPollEvents();
		ImGui_ImplGlfwGL3_NewFrame();
		/// per-frame time logic
		float currentFrame = glfwGetTime();
		deltaTime = currentFrame - lastFrame;
		lastFrame = currentFrame;
		/// input
		processInput(window);
		/// render
		// ------
		glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		/// be sure to activate shader when setting uniforms/drawing objects
		lightShader.use();
		lightShader.setVec3("light.direction", -0.2f, -1.0f, -0.3f);
		lightShader.setVec3("viewPos", camera.Position);

		/// light properties
		lightShader.setVec3("light.ambient", 0.2f, 0.2f, 0.2f);
		lightShader.setVec3("light.diffuse", 0.5f, 0.5f, 0.5f);
		lightShader.setVec3("light.specular", 1.0f, 1.0f, 1.0f);

		/// material properties
		lightShader.setFloat("material.shininess", 32.0f);

		/// view/projection transformations
		glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
		glm::mat4 view = camera.GetViewMatrix();
		lightShader.setMat4("projection", projection);
		lightShader.setMat4("view", view);

		/// render the loaded model
		glm::mat4 model = glm::mat4(1.0f);
		model = glm::translate(model, glm::vec3(0.0f, 0.0f, 0.0f)); // translate it down so it's at the center of the scene
		model = glm::scale(model, glm::vec3(1.0, 1.0, 1.0));	// it's a bit too big for our scene, so scale it down
		lightShader.setMat4("model", model);

		/// draw model
		if (numberOfModelLoaded != 0 && showModelFlag)
		{
			modelLoaded->Draw(lightShader);
		}

		GLuint vertexArray, vertexBuffer;
		glLineWidth(1.0f);
		glGenVertexArrays(1, &vertexArray);
		glGenBuffers(1, &vertexBuffer);
		glBindVertexArray(vertexArray);
		glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);

		/*
		//Copies the Vertex data into the buffer
		glBufferData(GL_ARRAY_BUFFER, vectorOfEdges.size() * sizeof(GLfloat), vectorOfEdges.data(), GL_STATIC_DRAW);
		glVertexAttribPointer(
			0,                  // attribute 0. No particular reason for 0, but must match the layout in the shader.
			3,                  // size
			GL_FLOAT,           // type
			GL_FALSE,           // normalized?
			0, // stride
			(GLvoid*)0            // array buffer offset
		);
		//Sends the sprite's color information in the the shader
		lightShader.setVec4("uniformColor", 0.0f, 1.0f, 0.0f, 1.0f);
		//lightShader.use();
		//Activates Vertex Position Information
		glEnableVertexAttribArray(0);
		// Draw the line
		glDrawArrays(GL_LINES, 0, vectorOfEdges.size() * sizeof(GLfloat));
		glBindBuffer(GL_ARRAY_BUFFER, 0);
		glBindVertexArray(0);
		*/
		////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		bool treeGrowFlag = true;
		setStyleImGui();
		ImGui::Begin("TreeGrow", &treeGrowFlag, ImGuiWindowFlags_MenuBar);

		if (ImGui::BeginMenuBar())
		{
			if (ImGui::BeginMenu("Arquivo"))
			{
				if (ImGui::MenuItem("Abrir modelo 3D", "Ctrl+O"))
				{
					showWindowModelsFlag = true;
					showModelFlag = true;
				}
				ImGui::EndMenu();
			}
			ImGui::EndMenuBar();
		}
		if (showWindowModelsFlag)
		{
			if (!imageModelsLoadedFlag)
			{
				loadModelImages();
				imageModelsLoadedFlag = true;
			}

			ImGui::Begin("Modelos 3D disponiveis:", &showWindowModelsFlag, ImVec2(300, 100), 0.9f, ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove);
			if (ImGui::ImageButton((void*)(intptr_t)imageModelTreeSimple, ImVec2(200, 200)))
			{
				numberOfModelLoaded = 1;
				loadModel(numberOfModelLoaded);
				showWindowModelsFlag = false;
			}
			ImGui::SameLine();
			if (ImGui::ImageButton((void*)(intptr_t)imageModelPinus, ImVec2(200, 200)))
			{
				numberOfModelLoaded = 2;
				loadModel(numberOfModelLoaded);
				showWindowModelsFlag = false;
			}
			ImGui::SameLine();
			if (ImGui::ImageButton((void*)(intptr_t)imageModelOak, ImVec2(200, 200)))
			{
				numberOfModelLoaded = 3;
				loadModel(numberOfModelLoaded);
				showWindowModelsFlag = false;
			}
			ImGui::End();
		}

		ImGui::Text("Escolha um arquivo! ");
		ImGui::Text("Arquivo selecionado : ");

		if (numberOfModelLoaded == 1)
		{
			ImGui::SameLine();
			ImGui::Text("Arvore Simples");
		}
		else if (numberOfModelLoaded == 2)
		{
			ImGui::SameLine();
			ImGui::Text("Pinheiro");
		}
		else if (numberOfModelLoaded == 3)
		{
			ImGui::SameLine();
			ImGui::Text("Carvalho");
		}
		if (numberOfModelLoaded != 0)
		{

			if (ImGui::Button("Gerar o Esqueleto da Arvore"))
			{
				generateSkeletonFlag = true;
			}
		}
		if (generateSkeletonFlag)
		{
			if (generateSkeleton(numberOfModelLoaded))
			{
				generateSkeletonFlag = false;
			}
		}
		if (skeletonWindowFlag)
		{
			//ImGui::Begin("Esqueleto da Arvore", &generateSkeletonFlag, ImVec2(300, 100), 0.9f);
			showModelFlag = false;
			glBufferData(GL_ARRAY_BUFFER, vectorOfEdges.size() * sizeof(GLfloat), vectorOfEdges.data(), GL_STATIC_DRAW);
			glVertexAttribPointer(
				0,                  // attribute 0. No particular reason for 0, but must match the layout in the shader.
				3,                  // size
				GL_FLOAT,           // type
				GL_FALSE,           // normalized?
				0, // stride
				(GLvoid*)0            // array buffer offset
			);
			//Sends the sprite's color information in the the shader
			lightShader.setVec4("uniformColor", 0.5f, 0.5, 0.5, 1.0f);
			//lightShader.use();
			//Activates Vertex Position Information
			glEnableVertexAttribArray(0);
			// Draw the line
			glDrawArrays(GL_LINES, 0, vectorOfEdges.size() * sizeof(GLfloat));
			glBindBuffer(GL_ARRAY_BUFFER, 0);
			glBindVertexArray(0);
			//ImGui::End();
			showMenuSegments = true;
		}

		if (showMenuSegments)
		{
			if (ImGui::Button("Separar a malha em segmentos"))
			{
				lightShader.setVec4("uniformColor", 0.0f, 0.0f, 0.0f, 1.0f);
				skeletonWindowFlag = false;
				showModelFlag = true;
				generateSegments(numberOfModelLoaded);
				generateSegmentsFlag = true;
			}
		}
		if (generateSegmentsFlag)
		{
			loadSegments();
			/// draw segments
			for (int i = 0; i < vectorOfSegments.size(); ++i)
			{
				glm::mat4 model1 = glm::mat4(1.0f);
				model1 = glm::translate(model1, glm::vec3(-5.0f, 0.0f, 0.5f)); // translate it down so it's at the center of the scene
				vectorOfVec3[i].y = vectorOfVec3[i].x;
				vectorOfVec3[i].z = vectorOfVec3[i].x;
				model1 = glm::scale(model1, vectorOfVec3[i]);	// it's a bit too big for our scene, so scale it down
				lightShader.setMat4("model", model1);
				vectorOfModelsSegments[i].Draw(lightShader);
			}

			for (int i = 0; i < vectorOfSegments.size(); ++i)
			{
				std::ostringstream oss;
				oss << "segment: " << i;
				ImGui::SliderFloat(oss.str().data(), &vectorOfVec3[i].x, 0.0f, 1.0f);
			}
		}

		//ImGui::ShowDemoWindow();
		ImGui::End();

		/// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
		ImGui::Render();
		ImGui_ImplGlfwGL3_RenderDrawData(ImGui::GetDrawData());
		glfwSwapBuffers(window);
	}

	/// glfw: terminate, clearing all previously allocated GLFW resources.
	ImGui_ImplGlfwGL3_Shutdown();
	ImGui::DestroyContext();
	glfwTerminate();
	return 0;
}

/// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
void processInput(GLFWwindow *window)
{
	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
		glfwSetWindowShouldClose(window, true);

	if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
		camera.ProcessKeyboard(FORWARD, deltaTime);
	if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS)
		camera.ProcessKeyboard(BACKWARD, deltaTime);
	if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS)
		camera.ProcessKeyboard(LEFT, deltaTime);
	if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS)
		camera.ProcessKeyboard(RIGHT, deltaTime);
}

/// glfw: whenever the window size changed (by OS or user resize) this callback function executes
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
	/// make sure the viewport matches the new window dimensions; note that width and
	/// height will be significantly larger than specified on retina displays.
	glViewport(0, 0, width, height);
}

/// glfw: whenever the mouse moves, this callback is called
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
	if (firstMouse)
	{
		lastX = xpos;
		lastY = ypos;
		firstMouse = false;
	}
	float xoffset = xpos - lastX;
	float yoffset = lastY - ypos; // reversed since y-coordinates go from bottom to top
	lastX = xpos;
	lastY = ypos;
	camera.ProcessMouseMovement(xoffset, yoffset);
}

/// glfw: whenever the mouse scroll wheel scrolls, this callback is called
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
	camera.ProcessMouseScroll(yoffset);
}


#include <cmath>
#include <fstream>
#include <iostream>
#include <random>
#include <sstream>
#include <boost/algorithm/string.hpp>
#include "Instance.h"

using namespace std;

Instance::Instance(string code) : code_{code} {
    try {
        cout<<"Instance(): code: "<<code_<<endl;
        istringstream iss(code_);
        string part;
        getline(iss, part, '-');
        boost::to_lower(part);
        if (part.size()!=1||(part[0]!='e'&&part[0]!='v')) {
            cout<<"invalid network identifier"<<endl;
            exit(-1);
        }
        if (part[0]=='e') {
            network="eindhoven";
            cout<<"loading Eindhoven network ..."<<endl;
        } else if (part[0]=='v') {
            network="vienna";
            cout<<"loading Vienna network ..."<<endl;
        }
        loadRealWorld();
        part.erase();
        getline(iss, part, '-');
        Lambda=stof(part);
        cout<<"Lambda: "<<Lambda<<endl;
        part.erase();
        getline(iss, part, '-');
        boost::to_lower(part);
        if (part!="uti"&&part!="cti"&&part!="ctd") {
            cout<<"invalid space-time distribution"<<endl;
            exit(-1);
        }
        stDist=part=="uti"?UTI:part=="cti"?CTI:CTD;
        cout<<"space-time distribution: "<<part<<" ("<<stDist<<")"<<endl;
        part.erase();
        getline(iss, part, '-');
        K=stoi(part);
        if (K<2) {
            cout<<"invalid number of vehicles"<<endl;
            exit(-1);
        }
        cout<<"number of vehicles: "<<K<<endl;
        part.erase();
        getline(iss, part, '-');
        degDyn=stof(part)/100;
        if (degDyn<0||degDyn>1) {
            cout<<"invalid degree of dynamism"<<endl;
            exit(-1);
        }
        cout<<"length of the service period: "<<U<<" minutes"<<endl;
        setupClusters();
        setupDepot();
    } catch (...) {
        usage();
        exit(-1);
    }
}

bool Instance::belongs(int i, const tuple<double, double, int>& cluster) const {
    return distanceLL(lats[i], lons[i], get<0>(cluster), get<1>(cluster))
            <=get<2>(cluster);
}

double Instance::distanceLL(double lat1, double lon1, double lat2, double lon2)
        const {
    const double R=6371e3;          // Earth's radius in meters
    double x=(lon2-lon1)*cos((lat1+lat2)/2);
    double y=lat2-lat1;
    return sqrt(x*x+y*y)*R;
}

double Instance::distanceXY(double x1, double y1, double x2, double y2) const {
    double x=x2-x1;
    double y=y2-y1;
    return sqrt(x*x+y*y);
}

int Instance::findNearestNodeLL(double lat, double lon) const {
    pair<int, double> nearest {-1, numeric_limits<double>::infinity()};
    for (int i=0; i<V; ++i) {
        double dist=distanceLL(lat, lon, lats[i], lons[i]);
        if (dist<nearest.second) {
            nearest.first=i;
            nearest.second=dist;
        }
    }
    return nearest.first;
}

int Instance::findNearestNodeXY(double x, double y) const {
    pair<int, double> nearest {-1, numeric_limits<double>::infinity()};
    for (int i=0; i<V; ++i) {
        double dist=distanceXY(x, y, xcoords[i], ycoords[i]);
        if (dist<nearest.second) {
            nearest.first=i;
            nearest.second=dist;
        }
    }
    return nearest.first;
}

void Instance::generateRandomDynRequests() const {
    cout<<"generating random dynamic requests ..."<<endl;
    vector<Request> reqs=sampleDynRequests(0);
    for (const auto& req : reqs)
        cout<<req.u<<" "<<req.i<<" "<<req.d<<endl;
    cout<<reqs.size()<<" requests generated (expected: "<<Lambda*U<<")"<<endl;
}

vector<Request> Instance::genRandomDynRequestsCTD(double u) const {
    // indexes of clustered (both types) and non-clustered nodes
    vector<int> clust1, clust2, nonclust;
    for (int i=0; i<V; ++i)
        if (type[i]==C1)
            clust1.push_back(i);
        else if (type[i]==C2)
            clust2.push_back(i);
        else
            nonclust.push_back(i);
    mt19937 gen((random_device()()));
    /*
    static long long seed=0;
    mt19937 gen(seed++);
    */
    uniform_real_distribution<double> unifDist;
    uniform_int_distribution<int> nodeC1Dist(0, clust1.size()-1);
    uniform_int_distribution<int> nodeC2Dist(0, clust2.size()-1);
    uniform_int_distribution<int> nodeNCDist(0, nonclust.size()-1);
    normal_distribution<double> durDist(fDmean, fDstd);
    vector<Request> reqs;
    while (true) {
        double unif=unifDist(gen);
        u+=-(1/Lambda)*log(unif);
        if (u>U)
            break;
        unif=unifDist(gen);
        int i;
        if (unif>0.5) {     // request from a cluster
            unif=unifDist(gen);
            if (unif>((0.8-0.2)/600)*u+0.2) {       // C1 (early) cluster
                i=clust1[nodeC1Dist(gen)];
                while (i==dep)
                    i=clust1[nodeC1Dist(gen)];
            } else {                                // C2 (late) cluster
                i=clust2[nodeC2Dist(gen)];
                while (i==dep)
                    i=clust2[nodeC2Dist(gen)];
            }
        } else {            // request from a non-cluster
            i=nonclust[nodeNCDist(gen)];
            while (i==dep)
                i=nonclust[nodeNCDist(gen)];
        }
        double d=durDist(gen);
        while (d<=0)
            d=durDist(gen);
        reqs.emplace_back(u, i, d);
    }
    return reqs;
}

vector<Request> Instance::genRandomDynRequestsCTI(double u) const {
    // indexes of clustered and non-clustered nodes
    vector<int> clust, nonclust;
    for (int i=0; i<V; ++i)
        if (type[i]==C)
            clust.push_back(i);
        else
            nonclust.push_back(i);
    mt19937 gen((random_device()()));
    /*
    static long long seed=0;
    mt19937 gen(seed++);
    */
    uniform_real_distribution<double> unifDist;
    uniform_int_distribution<int> nodeCDist(0, clust.size()-1);
    uniform_int_distribution<int> nodeNCDist(0, nonclust.size()-1);
    normal_distribution<double> durDist(fDmean, fDstd);
    vector<Request> reqs;
    while (true) {
        double unif=unifDist(gen);
        u+=-(1/Lambda)*log(unif);
        if (u>U)
            break;
        unif=unifDist(gen);
        int i;
        if (unif>0.5) {     // request from a cluster
            i=clust[nodeCDist(gen)];
            while (i==dep)
                i=clust[nodeCDist(gen)];
        } else {            // request from a non-cluster
            i=nonclust[nodeNCDist(gen)];
            while (i==dep)
                i=nonclust[nodeNCDist(gen)];
        }
        double d=durDist(gen);
        while (d<=0)
            d=durDist(gen);
        reqs.emplace_back(u, i, d);
    }
    return reqs;
}

vector<Request> Instance::genRandomDynRequestsUTI(double u) const {
    mt19937 gen((random_device()()));
    /*
    static long long seed=7;
    mt19937 gen(seed++);
    */
    uniform_real_distribution<double> unifDist;
    uniform_int_distribution<int> nodeDist(0, V-1);
    normal_distribution<double> durDist(fDmean, fDstd);
    vector<Request> reqs;
    while (true) {
        double unif=unifDist(gen);
        u+=-(1/Lambda)*log(unif);
        if (u>U)
            break;
        int i=nodeDist(gen);
        while (i==dep)
            i=nodeDist(gen);
        double d=durDist(gen);
        while (d<=0)
            d=durDist(gen);
        reqs.emplace_back(u, i, d);
    }
    return reqs;
}

void Instance::generateRandomStaticRequests() const {
    cout<<"generating "<<staticRequests()<<" random static requests ..."<<endl;
    mt19937 gen((random_device()()));
    //mt19937 gen(std::hash<string>()(code_));
    uniform_int_distribution<int> nodeDist(0, V-1);
    normal_distribution<double> durDist(fDmean, fDstd);
    for (int j=0; j<staticRequests(); ++j) {
        int i=nodeDist(gen);
        while (i==dep)
            i=nodeDist(gen);
        double d=durDist(gen);
        while (d<=0)
            d=durDist(gen);
        cout<<"0 "<<i<<" "<<d<<endl;
    }
}

pair<double, double> Instance::getXYCoords(double lat, double lon) const {
    const double lat0=network=="vienna"?(48.2206/180)*PI:(51.438/180)*PI;
    const double offsetx=network=="vienna"?-0.185:-0.059;
    const double offsety=network=="vienna"?-0.841:-0.897;
    const double scaling=network=="vienna"?25000:25000;
    return {(lon*cos(lat0)+offsetx)*scaling, (lat+offsety)*scaling};
}

void Instance::loadRealWorld() {
    string filename="../in/coords/"+network+".xy";
    ifstream fcoords(filename);
    if (!fcoords.is_open()) {
        cout<<"loadRealWorld(): unable to open file "<<filename<<endl;
        exit(-1);
    }
    fcoords>>V;
    xcoords=vector<double>(V, 0);
    ycoords=vector<double>(V, 0);
    lats=vector<double>(V, 0);
    lons=vector<double>(V, 0);
    int n;
    for (int i=0; i<V; ++i) {
        fcoords>>n>>lats[i]>>lons[i];
        auto xy=getXYCoords(lats[i], lons[i]);
        xcoords[i]=xy.first;
        ycoords[i]=xy.second;
        if (n!=i) {
            cout<<"loadRealWorld(): inconsistency: n,i="<<n<<","<<i<<endl;
            exit(-1);
        }
    }
    filename="../in/dists/"+network+".d";
    ifstream fdists(filename);
    if (!fdists.is_open()) {
        cout<<"loadRealWorld(): unable to open file "<<filename<<endl;
        exit(-1);
    }
    linksFromV=vector<vector<Link>>(V, vector<Link>());
    linksToV=vector<vector<Link>>(V, vector<Link>());
    fdists>>A;
    for (int l=0; l<A; ++l) {
        int s, t;
        double d;
        fdists>>s>>t>>d;
        Link link(l, s, t, d);
        links_.push_back(link);
        linksFromV.at(link.s).push_back(link);
        linksToV.at(link.t).push_back(link);
    }
    // consistency check: every vertex need to have at least 1 link to/from
    for (int i=0; i<V; ++i)
        if (linksFromV[i].size()==0 || linksToV[i].size()==0) {
            cout<<"loadRealWorld(): inconsistency"<<endl;
            exit(-1);
        }
}

vector<Request> Instance::sampleDynRequests(double u) const {
    return stDist==UTI?genRandomDynRequestsUTI(u)
            :stDist==CTI?genRandomDynRequestsCTI(u)
            :genRandomDynRequestsCTD(u);
}

void Instance::setupClusters() {
    cout<<"setting up node types based on spatial dist. and clusters ..."<<endl;
    if (stDist!=UTI) {
        if (network=="eindhoven")
            clusters={{51.436879, 5.472860, 900},   // Center
                {51.445041, 5.454482, 600},         // Strijp-S
                {51.467228, 5.473936, 1000},        // Woensel-Zuid
                {51.423800, 5.492604, 750}};        // Heistraat
        else if (network=="vienna")
            clusters={{48.249999, 16.443487, 3000}, // Kagraner Platz (Early)
                {48.192414, 16.315842, 3000}};      // Westbahnhof (Late)
        /*
            clusters={{48.208153, 16.373294, 1400}, // Stephansplatz
                {48.192414, 16.315842, 1400},       // Westbahnhof
                {48.279066, 16.410855, 2100},       // Floridsdorf
                {48.176011, 16.377125, 1400},       // Favoriten
                {48.230560, 16.328330, 1000},       // Gersthof
                {48.165604, 16.315499, 1200}};      // Hetzendorf
        */
        // converting lat,lon to radians
        for (auto& cluster : clusters) {
            get<0>(cluster)=(get<0>(cluster)/180)*PI;
            get<1>(cluster)=(get<1>(cluster)/180)*PI;
        }
    }
    // determine node types based on spatial distribution and clusters
    type=vector<int>(V, 0);
    for (int i=0; i<V; ++i) {
        if (stDist==UTI)
            type[i]=NC;
        else if (stDist==CTI) {
            type[i]=NC;
            for (const auto& cluster : clusters)
                if (belongs(i, cluster)) {
                    type[i]=C;
                    break;
                }
        } else {        // CTD
            type[i]=NC;
            for (int j=0; j<clusters.size(); ++j)
                if (belongs(i, clusters[j])) {
                    if (j%2==0)
                        type[i]=C1;
                    else
                        type[i]=C2;
                    break;
                }
        }
    }
}

void Instance::setupDepot() {
    pair<double, double> ll=network=="eindhoven"
            ?pair<double,double>({51.434555,5.510250})
            :pair<double,double>({48.178808,16.438460});
    dep=findNearestNodeLL((ll.first/180)*PI, (ll.second/180)*PI);
}

void Instance::usage() const {
    cout<<"invalid instance code `"<<code_<<"'"<<endl;
    cout<<"code should be in the format `n-rate-dist-K-dd', where:"<<endl;
    cout<<"n\t network code (eg `e' for Eindhoven and `v' for Vienna)"<<endl;
    cout<<"rate\t rate of requests, per minute (eg 1.5)"<<endl;
    cout<<"dist\t space-time distribution of requests (`uti', `cti' or `ctd')"
            <<endl;
    cout<<"K\t number of vehicles"<<endl;
    cout<<"dd\t degree of dynamism"<<endl;
}

/*
void Instance::testDistances() const {
    pair<double, double> llorig {48.228249, 16.326659};     // Czartoryskig.6
    pair<double, double> lldest {48.269926, 16.426735};     // Giefingg.7
    int orig=findNearestNode((llorig.first/180)*PI, (llorig.second/180)*PI);
    int dest=findNearestNode((lldest.first/180)*PI, (lldest.second/180)*PI);
    Dijkstra dijk;
    cout<<"shortest-path cost: "<<dijk.solve(orig, dest).cost()<<endl;
    cin.get();
}
*/


#include "DFT.h"
#include <string>
#include <EigenSolver/EigenSolver.h>
int main(int argc, char* argv[])
{
  int refined_time = atoi(argv[1]);
  std::string fileName = argv[2];
  std::cout<<"reading parameter from terminal is done..."<<std::endl;
  //////////////////////////////////////////////////////////////
  // declare the class DFT
  DFT myDFT;
  std::cout<<"Declare class DFT is done..."<<std::endl;
  //////////////////////////////////////////////////////////////
  // setup mesh
  myDFT.readMesh(fileName, refined_time);
  std::cout<<"reading mesh is done..."<<std::endl;
  //////////////////////////////////////////////////////////////
  // initialize template element information
  myDFT.initialize();
  std::cout<< "initializing template elements is done..."<<std::endl;
  //////////////////////////////////////////////////////////////
  // build the finite element space.
  myDFT.buildspace();
  std::cout<<"get the index of the boundary..."<<std::endl;
  myDFT.getBoundaryIndex();

  //////////////////////////////////////////////////////////////
  // prepare has been done. It's time to assemble matrix and rhs
  // to get solution of the problem.
  // assemble the matrix for poisson equation of Hartree potential.
  myDFT.buildHartreeMatrix();

  //////////////////////////////////////////////////////////////
  // initialize the electric density to start self-consistent
  // iteration.
  std::cout<<"initialize the electric density...\n";
  myDFT.initializePhi();
  std::cout<<"Density finish initializing!\n";

  int iter = 0;
  double res = 0;
  double tol = 1.e-3;
  do{
    //////////////////////////////////////////////////////////////
    // get the right-hand side vector of the poisson equations via
    // electric density.
    myDFT.getval4rho();
    std::cout<<"get the rhs vector of the poisson equation of Hartree potential...\n";
    myDFT.getRHS_Hartree();
    std::cout<<"Apply boundary condition...\n";
    myDFT.addBoundaryCondition_Hartree();
    std::cout<<"Begin to solve the poisson equation for Hartree...\n";
    myDFT.solveHartree();
    std::cout<<"Begin to build the system of KS equation...\n";
    myDFT.buildMatrixA4KS();
    myDFT.buildMatrixM4KS();
    std::cout<<"Begin to apply the boundary condition into the matrices...\n";
    myDFT.addBoundaryCondition_KS();
    std::cout<<"solve the KS equation...\n";
    myDFT.solveKS();
    std::cout<<"normalize the solution...\n";
    myDFT.normalize_phi();
    std::cout<<"compute the res...\n";
    myDFT.get_res4psi(res);
    if(res < tol)
      {
	break;
      }
    std::cout<<"The res is "<<res<<"\n";
    std::cout << "Press ENTER to continue or CTRL+C to stop ..." << std::flush;
    getchar();

    std::cout<<"mix the wavefunction Psi...\n";
    myDFT.mixing();
    iter++;
  }while(1);
  myDFT.printMesh();
}


#include "geom.h"

#include <boost/geometry/geometries/segment.hpp>
#include <boost/geometry/index/rtree.hpp>

typedef boost::geometry::model::segment<Point> simplify_segment;
typedef boost::geometry::index::rtree<simplify_segment, boost::geometry::index::quadratic<16>> simplify_rtree;

struct simplify_rtree_counter
{
	using value_type = simplify_segment;
	std::size_t count = 0;
	void push_back(value_type const &) { ++count; }
	std::size_t size() const { return count; }
};

template<typename GeometryType>
void simplify(GeometryType const &input, GeometryType &output, double max_distance, simplify_rtree const &outer_rtree = simplify_rtree())
{
	simplify_rtree rtree;

    std::deque<std::size_t> nodes(input.size());
    for(std::size_t i = 0; i < input.size(); ++i)
        nodes[i] = i;
    for(std::size_t i = 0; i < input.size() - 1; ++i)
		rtree.insert({ input[i], input[i + 1] });

    std::priority_queue<std::size_t, std::vector<size_t>> pq;
    for(std::size_t i = 0; i < input.size() - 2; ++i)
        pq.push(i);

    while(!pq.empty()) {
        auto entry = pq.top();
        pq.pop();

        auto start = nodes[entry];
        auto middle = nodes[entry + 1];
        auto end = nodes[entry + 2];

        simplify_segment line(input[start], input[end]);
        double distance = 0.0;
        for(auto i = start + 1; i < end; ++i)
            distance = std::max(distance, boost::geometry::distance(line, input[i]));

        if(boost::geometry::distance(input[start], input[end]) < 2 * max_distance || distance < max_distance) {
            simplify_rtree_counter result;
            boost::geometry::index::query(rtree, boost::geometry::index::intersects(line), std::back_inserter(result));
            boost::geometry::index::query(outer_rtree, boost::geometry::index::intersects(line), std::back_inserter(result));

            std::size_t query_expected = ((start == 0 || end == input.size() - 1) ? 2 : 4);
            if(result.size() == query_expected) {
                nodes.erase(nodes.begin() + entry + 1);
                rtree.remove(simplify_segment(input[start], input[middle]));
                rtree.remove(simplify_segment(input[middle], input[end]));
                rtree.insert(line);

                if(entry + 2 < nodes.size()) {
                    pq.push(start);
                }
            }
        }
    }

    for(auto i: nodes)
        boost::geometry::append(output, input[i]);
}

Polygon simplify(Polygon const &p, double max_distance)
{
	simplify_rtree outer_rtree;
	for(std::size_t j = 0; j < p.outer().size() - 1; ++j)
		outer_rtree.insert({ p.outer()[j], p.outer()[j + 1] });

	std::vector<Ring> combined_inners;
	for(size_t i = 0; i < p.inners().size(); ++i) {
		Ring new_inner = p.inners()[i];
		if(boost::geometry::area(new_inner) < 0) {
			std::reverse(new_inner.begin(), new_inner.end());
			simplify_combine(combined_inners, std::move(new_inner));
		}
	}

	std::vector<Ring> new_inners;
	for(size_t i = 0; i < combined_inners.size(); ++i) {
		Ring new_inner;
		simplify(combined_inners[i], new_inner, max_distance, outer_rtree);

		if(boost::geometry::area(new_inner) > max_distance * max_distance) {
			simplify_combine(new_inners, std::move(new_inner));
		}
	}

	simplify_rtree inners_rtree;
	for(auto const &inner: new_inners) {
		for(std::size_t z = 0; z < inner.size() - 1; ++z)
			inners_rtree.insert({ inner[z], inner[z + 1] });
	}

	Polygon result;
	simplify(p.outer(), result.outer(), max_distance, inners_rtree);
	if(boost::geometry::area(result.outer()) < max_distance * max_distance) {
		return Polygon();
	}

	for(auto&& r: new_inners) {
		std::reverse(r.begin(), r.end());
		result.inners().push_back(r);
	}

	return result;
}

Linestring simplify(Linestring const &ls, double max_distance)
{
	Linestring result;
	boost::geometry::simplify(ls, result, max_distance);
	return result;
}

MultiPolygon simplify(MultiPolygon const &mp, double max_distance)
{
	MultiPolygon combined_mp;
	for(auto const &p: mp) {
    	if(!p.outer().empty()) {
			simplify_combine(combined_mp, Polygon(p));
		}
	}

	MultiPolygon result_mp;
	for(auto const &p: combined_mp) {
		Polygon new_p = simplify(p, max_distance);
    	if(!new_p.outer().empty()) {
			simplify_combine(result_mp, std::move(new_p));
		}
	}

	return result_mp;
}


#define BOOST_TEST_MODULE pcraster geo riks_neighbourhood
#include <boost/test/unit_test.hpp>
#include <boost/math/tr1.hpp>
#include "com_math.h"
#include "geo_riksneighbourhood.h"

class RiksNeighbourhoodWrapper : geo::RiksNeighbourhood {
public:
   RiksNeighbourhoodWrapper(double fromRadius, double toRadius) : RiksNeighbourhood(fromRadius, toRadius) {
   };

   RiksNeighbourhoodWrapper(double toRadius) : RiksNeighbourhood(toRadius) {
   };

   double sum() const {
     return RiksNeighbourhood::sum();
   }

   double cell(size_t a, size_t b){
     return RiksNeighbourhood::cell(a,b);
   }

   size_t radius() const {
     return RiksNeighbourhood::radius();
   }
};

BOOST_AUTO_TEST_CASE(test)
{
  using namespace geo;

  {
    // Nr 1.
    RiksNeighbourhoodWrapper neighbourhood(0.0);
    BOOST_CHECK(neighbourhood.cell(0, 0) == 1.0);
  }

  {
    // Nr 2.
    RiksNeighbourhoodWrapper neighbourhood(1.0, 1.0);
    BOOST_CHECK(neighbourhood.cell(0, 1) == 1.0);
    BOOST_CHECK(neighbourhood.cell(1, 0) == 1.0);
    BOOST_CHECK(neighbourhood.cell(1, 2) == 1.0);
    BOOST_CHECK(neighbourhood.cell(2, 1) == 1.0);
    BOOST_CHECK(neighbourhood.sum() == 4.0);
  }

  {
    // Nr 4.
    RiksNeighbourhoodWrapper neighbourhood(2.0, 2.0);
    BOOST_CHECK(neighbourhood.cell(0, 2) == 1.0);
    BOOST_CHECK(neighbourhood.cell(2, 0) == 1.0);
    BOOST_CHECK(neighbourhood.cell(2, 4) == 1.0);
    BOOST_CHECK(neighbourhood.cell(4, 2) == 1.0);
    BOOST_CHECK(neighbourhood.sum() == 4.0);
  }

  {
    // Nr 13.
    double radius = boost::math::tr1::hypot(4.0, 2.0);
    RiksNeighbourhoodWrapper neighbourhood(radius, radius);
    size_t offset = neighbourhood.radius();
    BOOST_CHECK(neighbourhood.cell(offset + 4, offset + 2) == 1.0);
    BOOST_CHECK(neighbourhood.cell(offset + 2, offset + 4) == 1.0);
    BOOST_CHECK(neighbourhood.cell(offset - 2, offset + 4) == 1.0);
    BOOST_CHECK(neighbourhood.cell(offset - 4, offset + 2) == 1.0);
    BOOST_CHECK(neighbourhood.cell(offset + 4, offset - 2) == 1.0);
    BOOST_CHECK(neighbourhood.cell(offset + 2, offset - 4) == 1.0);
    BOOST_CHECK(neighbourhood.cell(offset - 2, offset - 4) == 1.0);
    BOOST_CHECK(neighbourhood.cell(offset - 4, offset - 2) == 1.0);
    BOOST_CHECK(neighbourhood.sum() == 8.0);
  }

  {
    // Nr 30.
    RiksNeighbourhoodWrapper neighbourhood(8.0, 8.0);
    size_t offset = neighbourhood.radius();
    BOOST_CHECK(neighbourhood.cell(offset + 0, offset + 8) == 1.0);
    BOOST_CHECK(neighbourhood.cell(offset + 8, offset + 0) == 1.0);
    BOOST_CHECK(neighbourhood.cell(offset - 8, offset + 0) == 1.0);
    BOOST_CHECK(neighbourhood.cell(offset + 0, offset - 8) == 1.0);
    BOOST_CHECK(neighbourhood.sum() == 4.0);
  }
}


//=======================================================================
// Copyright (c) 2014 Karol Wegrzycki, Piotr Wygocki
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================
/**
 * @file lsh-regression.cpp
 * @brief lsh_regression binnary
 * @author Karol Wegrzycki, Andrzej Pacuk, Piotr Wygocki
 * @version 1.0
 * @date 2014-11-19
 */

#include "paal/data_structures/mapped_file.hpp"
#include "paal/regression/lsh_nearest_neighbors_regression.hpp"
#include "paal/utils/functors.hpp"
#include "paal/utils/performance_measures.hpp"
#include "paal/utils/read_svm.hpp"
#include "paal/utils/singleton_iterator.hpp"
#include "paal/utils/system_message.hpp"

#include <boost/algorithm/string.hpp>
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/function_output_iterator.hpp>
#include <boost/iostreams/device/mapped_file.hpp>
#include <boost/numeric/ublas/vector_sparse.hpp>
#include <boost/program_options.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include <boost/range/algorithm/copy.hpp>
#include <boost/range/algorithm/fill.hpp>
#include <boost/range/empty.hpp>
#include <boost/range/size.hpp>

#include <cstdlib>
#include <iostream>
#include <random>
#include <sstream>
#include <string>
#include <thread>
#include <utility>

using point_type_sparse = boost::numeric::ublas::compressed_vector<double>;
using point_type_dense =  boost::numeric::ublas::vector<double>;

using paal::utils::tuple_get;
namespace utils = paal::utils;
namespace po = boost::program_options;

enum Metric {HAMMING, L1, L2, JACCARD};
enum Vector_type {SPARSE, DENSE};

struct l1_tag{};
struct l2_tag{};
struct ham_tag{};
struct jaccard_tag{};

std::istream& operator>>(std::istream& in, Metric& metr) {
    std::string token;
    in >> token;
    boost::algorithm::to_lower(token);
    if (token == "hamming" || token == "h")
        metr = HAMMING;
    else if (token == "l1")
        metr = L1;
    else if (token == "l2")
        metr = L2;
    else if (token == "jaccard")
        metr = JACCARD;
    else
        assert(0 && "couldn't conclude metric name");
    return in;
}

std::istream& operator>>(std::istream& in, Vector_type & vec_type) {
    std::string token;
    in >> token;
    boost::algorithm::to_lower(token);
    if (token == "dense")
        vec_type = DENSE;
    else if (token == "sparse")
        vec_type = SPARSE;
    else
        assert(0 && "couldn't conclude sparse/dense representation");
    return in;
}

struct params {
    unsigned m_passes;
    unsigned m_nthread;
    std::size_t m_dimensions;
    std::size_t m_row_buffer_size;
    unsigned m_precision;
    int m_seed;
    double m_w;
    Vector_type m_dense;
    Metric m_metric;
};

auto get_function_generator(l1_tag, params const &p) {
    return paal::lsh::l_1_hash_function_generator<>{p.m_dimensions, p.m_w, std::default_random_engine(p.m_seed)};
}

auto get_function_generator(l2_tag, params const &p) {
    return paal::lsh::l_2_hash_function_generator<>{p.m_dimensions, p.m_w, std::default_random_engine(p.m_seed)};
}

auto get_function_generator(ham_tag, params const &p) {
    return paal::lsh::hamming_hash_function_generator(p.m_dimensions, std::default_random_engine(p.m_seed));
}

auto get_function_generator(jaccard_tag, params const &p) {
    return paal::lsh::jaccard_hash_function_generator(p.m_dimensions, std::default_random_engine(p.m_seed));
}

template <typename Row = point_type_sparse, typename LshFunctionTag>
void m_main(po::variables_map const &vm,
            params const &p,
            LshFunctionTag tag) {
    using lsh_fun = paal::pure_result_of_t<
                        paal::hash_function_tuple_generator<
                                    decltype(get_function_generator(tag, p))
                                                           >()
                                          >;
    using hash_result = typename std::remove_reference<
        typename std::result_of<lsh_fun(Row)>::type
        >::type;
    using model_t = paal::lsh_nearest_neighbors_regression<hash_result, lsh_fun>;
    using point_with_result_t = std::tuple<Row, int>;
    using boost::adaptors::transformed;
    constexpr paal::utils::tuple_get<0> get_coordinates{};
    constexpr paal::utils::tuple_get<1> get_result{};
    std::vector<point_with_result_t> points_buffer;

    model_t model;

    if (vm.count("model_in")) {
        Metric metric;
        std::size_t dimensions;
        std::ifstream ifs(vm["model_in"].as<std::string>());
        boost::archive::binary_iarchive ia(ifs);
        ia >> metric;
        ia >> dimensions;
        ia >> model;
        assert(metric == p.m_metric);
        assert(dimensions == p.m_dimensions);
    } else {
        model = paal::make_lsh_nearest_neighbors_regression_tuple_hash(
                        points_buffer | transformed(get_coordinates),
                        points_buffer | transformed(get_result),
                        p.m_passes,
                        get_function_generator(tag, p) , p.m_precision, p.m_nthread);
    }

    auto ignore_bad_row = [&](std::string const &bad_line) {
        utils::warning("following line will be ignored cause of bad format (typically more columns than passed dimensions): ", bad_line);
        return true;
    };

    if (vm.count("training_file")) {
        std::ifstream training_file_stream(vm["training_file"].as<std::string>());
        if (!training_file_stream.good()) {
            utils::failure("training file does not exist or is empty!");
        }

        points_buffer.reserve(p.m_row_buffer_size);
        while (training_file_stream.good()) {
            points_buffer.clear();
            auto dimensions = p.m_dimensions;
            paal::read_svm(training_file_stream, dimensions, points_buffer, p.m_row_buffer_size, ignore_bad_row);

            model.update(points_buffer | transformed(get_coordinates),
                         points_buffer | transformed(get_result),
                         p.m_nthread);
        }
    }

    if (vm.count("test_file")) {
        // Our algorithm returns range<tuple<Prediction,RealValue>>
        constexpr paal::utils::tuple_get<0> get_prediction{};
        constexpr paal::utils::tuple_get<1> get_real_value{};
        std::string const test_path = vm["test_file"].as<std::string>();

        // Lambda - for given line return tuple<LshPrediction,RealValue>>
        auto line_tester = [&](std::string const & line) {
            double test_result{};

            std::stringstream row_stream(line);
            using result_t = int;
            paal::detail::svm_row<Row, result_t> row{p.m_dimensions};
            if (!(row_stream >> row)) {
                ignore_bad_row(line);
                // prediction equal = result, should have the same impact on global result as ignoring the row
                return std::make_tuple(test_result, static_cast<result_t>(test_result));
            }

            auto test_point_iterator = paal::utils::make_singleton_range(row.get_coordinates());
            auto output_iterator = boost::make_function_output_iterator([&](double x){test_result = x;});

            model.test(test_point_iterator, output_iterator);

            return std::make_tuple(test_result,row.get_result());
        };

        // compute results using n threads and our lambda
        auto test_results = paal::data_structures::for_each_line(line_tester, test_path, p.m_nthread);

        auto loss = paal::log_loss<double>(test_results | transformed(get_prediction),
                                           test_results | transformed(get_real_value));

        utils::info("logloss on test set = ", loss, ", likelihood = ", paal::likelihood_from_log_loss(loss));

        if (vm.count("result_file") > 0) {
            std::ofstream result_file(vm["result_file"].as<std::string>());

            for (auto d: test_results | transformed(get_prediction)) {
                result_file << d << "\n";
            }
        }
    }

    if (vm.count("model_out")) {
        std::ofstream ofs(vm["model_out"].as<std::string>());
        boost::archive::binary_oarchive oa(ofs);
        oa << p.m_metric;
        oa << p.m_dimensions;
        oa << model;
    }
}

template <typename LshFunctionTag>
void choose_vector_type_main(po::variables_map const &vm,
            params const &p,
            LshFunctionTag tag) {
    if (p.m_dense == DENSE) {
        m_main<point_type_dense>(vm,p,tag);
    } else if( p.m_dense == SPARSE) {
        m_main<point_type_sparse>(vm,p,tag);
    }
}

void choose_vector_type_main(po::variables_map const &vm,
            params const &p,
            jaccard_tag tag) {
    assert(p.m_dense == SPARSE && "Jaccard metric supports only sparse rows representation.");
    m_main<point_type_sparse>(vm,p,tag);
}

int main(int argc, char** argv)
{
    params p{};

    po::options_description desc("LSH nearest neighbors regression - \n"\
            "suite for fast machine learning KNN algorithm which is using "\
            "locality sensitive hashing functions\n\nUsage:\n"\
            "This command will train on training file and output the predictions in test_file:\n"
            "\tlsh-regression --training_file path_to_training_file --test_file path_to_test_file --dimensions number_of_dimensions\n\n"\
            "If you want to use L1 metric, with 7 passes and 10 threads, and save model\n"\
            "you can use following command:\n"
            "\tlsh-regression -d training.svm -i 7 -n 10 -m L1 --dimensions number_of_dimensions --model_out model.lsh\n\n"\
            "Then if you want to use this model to make a prediction to result_file:\n"\
            "\tlsh-regression -t test.svm --model_in model.lsh -o results.txt\n\n"
            "Options description");

    desc.add_options()
        ("help,h", "help message")
        ("training_file,d", po::value<std::string>(), "training file path (in SVM format)")
        ("test_file,t", po::value<std::string>(), "test file path (in SVM format, it doesn't matter what label says)")
        ("model_in", po::value<std::string>(), "path to model, before doing any training or testing")
        ("model_out", po::value<std::string>(), "Write the model to this file when everything is done")
        ("result_file,o", po::value<std::string>(), "path to the file with prediction " \
                  "(float for every test in test set)")
        ("dimensions", po::value<std::size_t>(&p.m_dimensions), "number of dimensions")
        ("passes,i", po::value<unsigned>(&p.m_passes)->default_value(3), "number of iteration (default value = 3)")
        ("nthread,n", po::value<unsigned>(&p.m_nthread)->default_value(std::thread::hardware_concurrency()),
                 "number of threads (default = number of cores)")
        ("dense", po::value<Vector_type>(&p.m_dense)->default_value(SPARSE), "Dense/Sparse - Allows to use dense\
                  array representation (It might significantly speed up program for dense data)")
        ("metric,m", po::value<Metric>(&p.m_metric)->default_value(HAMMING), "Metric used for determining " \
                 "similarity between objects - [HAMMING/L1/L2/JACCARD] (default = Hamming)")
        ("precision,b", po::value<unsigned>(&p.m_precision)->default_value(10), "Number " \
                 "of hashing function that are encoding the object")
        ("parm_w,w", po::value<double>(&p.m_w)->default_value(1000.), "Parameter w " \
                 "should be essentially bigger than radius of expected test point neighborhood")
        ("row_buffer_size", po::value<std::size_t>(&p.m_row_buffer_size)->default_value(100000),
                 "size of row buffer (default value = 100000)")
        ("seed", po::value<int>(&p.m_seed)->default_value(0), "Seed of random number generator, (default = random)")
    ;

    po::variables_map vm;
    po::store(po::parse_command_line(argc, argv, desc), vm);
    po::notify(vm);

    auto param_is_set_explicitly = [&vm] (const std::string &param_name) {
        return vm.count(param_name) > 0 && !vm[param_name].defaulted();
    };

    if (vm.count("help")) {
        utils::info(desc);
        return EXIT_SUCCESS;
    }

    auto error_with_usage = [&] (const std::string &message) {
        utils::failure(message, "\n", desc);
    };

    if (vm.count("training_file") == 0 && vm.count("test_file") == 0) {
        error_with_usage("Neither training_file nor test_file were set");
    }

    if (vm.count("dimensions") == 0 && vm.count("model_in") == 0) {
        error_with_usage("Parameter dimensions was not set");
    }

    if (vm.count("training_file") == 0 && vm.count("model_in") == 0) {
        error_with_usage("If you don't set training file (training_file) you have to set input model (model_in)");
    }

    if (vm.count("model_in")) {
        Metric m;
        std::size_t dimensions;
        std::ifstream ifs(vm["model_in"].as<std::string>());
        boost::archive::binary_iarchive ia(ifs);

        ia >> m;
        ia >> dimensions;

        auto ignore_serializable_param = [&](std::string const &param_name, bool param_is_equal_to_serialized) {
            if (param_is_set_explicitly(param_name)) {
                if (param_is_equal_to_serialized) {
                    utils::warning("if input model is specified one does not have to specify param ", param_name);
                } else {
                    utils::warning("the specified param ", param_name, " is ignored, because it differs from the input model param ", param_name);
                }
            }
        };
        ignore_serializable_param("metric", m == p.m_metric);
        ignore_serializable_param("dimensions", dimensions == p.m_dimensions);

        auto ignored = [&](std::string const & param, std::string const & param_display) {
            if (param_is_set_explicitly(param)) {
                utils::warning("parameter ", param_display, " was set, but model_in is used, param ", param_display, " is discarded");
            }
        };
        ignored("parm_w", "w");
        ignored("precision", "precision");
        ignored("seed", "seed");
        ignored("passes", "passes");

        p.m_metric = m;
        p.m_dimensions = dimensions;
    } else {
        if (p.m_metric == HAMMING && param_is_set_explicitly("parm_w")) {
            utils::warning("parameter w was set, but hamming metric is used, param w is discarded");
        }
    }

    switch (p.m_metric) {
        case L1: choose_vector_type_main(vm, p, l1_tag{});
            break;

        case L2: choose_vector_type_main(vm, p, l2_tag{});
            break;

        case HAMMING: choose_vector_type_main(vm, p, ham_tag{});
            break;

        case JACCARD: choose_vector_type_main(vm, p, jaccard_tag{});
            break;

        default:
            assert(false);
    }
    return EXIT_SUCCESS;
}


//=======================================================================
// Copyright 1997-2001 University of Notre Dame.
// Authors: Jeremy G. Siek, Andrew Lumsdaine, Lie-Quan Lee
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//=======================================================================

#error \
    "This example appears to be incorrect; it uses edge weights that are smaller than 0 using the comparison operator passed to Dijkstra's algorithm, which is not allowed."

#include <boost/config.hpp>
#include <iostream>

#include <boost/graph/graph_traits.hpp>
#include <boost/graph/graph_utility.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/graph/visitors.hpp>
#include <boost/graph/transpose_graph.hpp>

/* Output:

  distances from start vertex:
  distance(a) = 0
  distance(b) = 3
  distance(c) = 1
  distance(d) = 3
  distance(e) = 3

  min-max paths tree
  a --> c
  b -->
  c --> d
  d --> e
  e --> b

*/

int main(int, char*[])
{
    using namespace boost;

    typedef adjacency_list< listS, vecS, directedS, no_property,
        property< edge_weight_t, int > >
        Graph;
    typedef graph_traits< Graph >::vertex_descriptor Vertex;

    typedef std::pair< int, int > E;

    const char name[] = "abcdef";

    const int num_nodes = 6;
    E edges[] = { E(0, 2), E(1, 1), E(1, 3), E(1, 4), E(2, 1), E(2, 3), E(3, 4),
        E(4, 0), E(4, 1) };
    int weights[] = { 1, 2, 1, 2, 7, 3, 1, 1, 1 };
    const int n_edges = sizeof(edges) / sizeof(E);
#if defined(BOOST_MSVC) && BOOST_MSVC <= 1300
    // VC++ can't handle iterator constructors
    Graph G(num_nodes);
    property_map< Graph, edge_weight_t >::type weightmap = get(edge_weight, G);
    for (std::size_t j = 0; j < sizeof(edges) / sizeof(E); ++j)
    {
        graph_traits< Graph >::edge_descriptor e;
        bool inserted;
        boost::tie(e, inserted) = add_edge(edges[j].first, edges[j].second, G);
        weightmap[e] = weights[j];
    }
#else
    Graph G(edges, edges + n_edges, weights, num_nodes);
    property_map< Graph, edge_weight_t >::type weightmap = get(edge_weight, G);
#endif

    std::vector< Vertex > p(num_vertices(G));
    std::vector< int > d(num_vertices(G));

    Vertex s = *(vertices(G).first);

#if defined(BOOST_MSVC) && BOOST_MSVC <= 1300
    dijkstra_shortest_paths(G, s, &p[0], &d[0], weightmap, get(vertex_index, G),
        std::greater< int >(), closed_plus< int >(),
        (std::numeric_limits< int >::max)(), 0, default_dijkstra_visitor());
#else
    dijkstra_shortest_paths(G, s,
        distance_map(&d[0]).predecessor_map(&p[0]).distance_compare(
            std::greater< int >()));
#endif

    std::cout << "distances from start vertex:" << std::endl;
    graph_traits< Graph >::vertex_iterator vi, vend;
    for (boost::tie(vi, vend) = vertices(G); vi != vend; ++vi)
        std::cout << "distance(" << name[*vi] << ") = " << d[*vi] << std::endl;
    std::cout << std::endl;

    std::cout << "min-max paths tree" << std::endl;
    adjacency_list<> tree(num_nodes);

    for (boost::tie(vi, vend) = vertices(G); vi != vend; ++vi)
        if (*vi != p[*vi])
            add_edge(p[*vi], *vi, tree);

    print_graph(tree, name);

    return 0;
}


#include <iostream>
#include <map>
#include <cstring>
#include <fstream>
#include <queue>
#include <boost/algorithm/string.hpp>
#include <list>
#include <algorithm>

using namespace std;

std::list<string> tokenizer()
{
	std::list<string> mylist;
	string filename;
	cout << "What file do you want to use for input? ";
	getline(cin, filename);
	ifstream fin;
	fin.open(filename.c_str());
	char * token;
	string str;
	while (fin >> str)
	{
		char *fileName = (char*)str.c_str();
		token = strtok (fileName," ,.-!;:\|'}{][)(");
		while (token != NULL)
		{
			boost::algorithm::to_lower(token);

			mylist.push_back(token);
			token = strtok (NULL, " ,.-!;:\|'}{][)(");
		}
	}
	fin.close();

	return mylist;
}

void print_tokens(std::list<string>mylist)
{
	for (std::list<string>::iterator it=mylist.begin(); it!=mylist.end(); ++it)
	{
		cout<<*it<<endl;
	}
}

std::list<string> compute_bigrams(std::list<string> mylist)
{
	std::list<string> bigrams;
	std::list<string>::iterator it_fast=mylist.begin();
	it_fast++;
	std::list<string>::iterator it=mylist.begin();
	while ( it_fast!=mylist.end())
	{
		bigrams.push_back(*it+*it_fast);
		it_fast++;
		it++;
	}
	return bigrams;
}

std::map<string,int> Freq_bigram(std::list<string> bigrams)
		{
			std::map<string,int> mymap;
			for (std::list<string>::iterator it=bigrams.begin(); it!=bigrams.end(); ++it)
			{
				mymap[*it]++;

			}
/*
			for (std::map<string,int>::iterator it=mymap.begin(); it!=mymap.end(); ++it)
					{
						//pq.push(std::make_pair(it->second, it->first)); ///push into heap
						cout<<it->second<<endl;
					}
					*/
			return mymap;

		}

int print_freq_sorted(std::map<std::string, int> myMap)
{
	int total=0;

	std::priority_queue<std::pair<int, std::string> > pq;
	for (std::map<string,int>::iterator it=myMap.begin(); it!=myMap.end(); ++it)
	{
		//cout<<it->second<<endl;
		pq.push(std::make_pair(it->second, it->first)); ///push into heap
		total=total+it->second;
	}
	int size=myMap.size();
	for(int i=1; i<=size;i++)
	{
		std::cout << pq.top().first << ", " << pq.top().second << std::endl;
		pq.pop();
	}
	cout<<"Total Number of words"<<":"<<total<<endl;
	return total;
}

int main()
{
	std::list<string> tokens=tokenizer();
	print_tokens(tokens);
	std::list<string> bigrams=compute_bigrams(tokens);
	print_tokens(bigrams);
	std::map<string,int> Frequency = Freq_bigram(bigrams);
	print_freq_sorted(Frequency);
	return 0;
}


#include <stan/math/rev/mat.hpp>
#include <gtest/gtest.h>
#include <test/unit/math/rev/mat/fun/util.hpp>
#include <test/unit/math/rev/mat/util.hpp>

#ifdef STAN_OPENCL
#include <boost/random/mersenne_twister.hpp>
#define EXPECT_MATRIX_NEAR(A, B, DELTA) \
  for (int i = 0; i < A.size(); i++)    \
    EXPECT_NEAR(stan::math::value_of(A(i)), stan::math::value_of(B(i)), DELTA);

boost::random::mt19937 rng;
#define MULTIPLY_OPENCL_OVERRIDE 0
#define MULTIPLY_CPU_OVERRIDE INT_MAX
TEST(AgradRevMatrix, multiply_val_vv_cl) {
  int temp = stan::math::opencl_context.tuning_opts()
                 .multiply_dim_prod_worth_transfer;
  using stan::math::matrix_d;
  using stan::math::matrix_v;
  using stan::math::multiply;
  int size = 234;
  matrix_v Av(size, size);
  matrix_v Bv(size, size);
  matrix_v C, C_cl;
  for (int i = 0; i < size; i++) {
    for (int j = 0; j < size; j++) {
      Av(i, j) = stan::math::uniform_rng(-5, 5, rng);
      Bv(i, j) = stan::math::uniform_rng(-5, 5, rng);
    }
  }
  stan::math::opencl_context.tuning_opts().multiply_dim_prod_worth_transfer
      = MULTIPLY_OPENCL_OVERRIDE;
  C_cl = multiply(Av, Bv);
  C_cl(0, 0).grad();
  stan::math::opencl_context.tuning_opts().multiply_dim_prod_worth_transfer
      = MULTIPLY_CPU_OVERRIDE;
  C = multiply(Av, Bv);
  C(0, 0).grad();
  EXPECT_MATRIX_NEAR(C, C_cl, 1.0E-12);
  EXPECT_MATRIX_NEAR(C.adj(), C_cl.adj(), 1.0E-12);
  stan::math::recover_memory();
  stan::math::opencl_context.tuning_opts().multiply_dim_prod_worth_transfer
      = temp;
}

TEST(AgradRevMatrix, multiply_val_vd_cl) {
  int temp = stan::math::opencl_context.tuning_opts()
                 .multiply_dim_prod_worth_transfer;
  using stan::math::matrix_d;
  using stan::math::matrix_v;
  using stan::math::multiply;
  int size = 256;
  matrix_v Av(size, size);
  matrix_v Bd(size, size);
  matrix_v C, C_cl;
  for (int i = 0; i < size; i++) {
    for (int j = 0; j < size; j++) {
      Av(i, j) = stan::math::uniform_rng(-5, 5, rng);
      Bd(i, j) = stan::math::uniform_rng(-5, 5, rng);
    }
  }

  stan::math::opencl_context.tuning_opts().multiply_dim_prod_worth_transfer
      = MULTIPLY_OPENCL_OVERRIDE;
  C_cl = multiply(Av, Bd);
  C_cl(0, 0).grad();
  stan::math::opencl_context.tuning_opts().multiply_dim_prod_worth_transfer
      = MULTIPLY_CPU_OVERRIDE;
  C = multiply(Av, Bd);
  C(0, 0).grad();
  EXPECT_MATRIX_NEAR(C, C_cl, 1.0E-12);
  EXPECT_MATRIX_NEAR(C.adj(), C_cl.adj(), 1.0E-12);
  stan::math::recover_memory();
  stan::math::opencl_context.tuning_opts().multiply_dim_prod_worth_transfer
      = temp;
}

TEST(AgradRevMatrix, multiply_val_dv_cl) {
  int temp = stan::math::opencl_context.tuning_opts()
                 .multiply_dim_prod_worth_transfer;
  using stan::math::matrix_d;
  using stan::math::matrix_v;
  using stan::math::multiply;
  int size = 321;
  matrix_v Ad(size, size);
  matrix_v Bv(size, size);
  matrix_v C, C_cl;
  for (int i = 0; i < size; i++) {
    for (int j = 0; j < size; j++) {
      Ad(i, j) = stan::math::uniform_rng(-5, 5, rng);
      Bv(i, j) = stan::math::uniform_rng(-5, 5, rng);
    }
  }
  stan::math::opencl_context.tuning_opts().multiply_dim_prod_worth_transfer
      = MULTIPLY_OPENCL_OVERRIDE;
  C_cl = multiply(Ad, Bv);
  C_cl(0, 0).grad();
  stan::math::opencl_context.tuning_opts().multiply_dim_prod_worth_transfer
      = MULTIPLY_CPU_OVERRIDE;
  C = multiply(Ad, Bv);
  C(0, 0).grad();
  EXPECT_MATRIX_NEAR(C, C_cl, 1.0E-12);
  EXPECT_MATRIX_NEAR(C.adj(), C_cl.adj(), 1.0E-12);
  stan::math::recover_memory();
  stan::math::opencl_context.tuning_opts().multiply_dim_prod_worth_transfer
      = temp;
}

#endif


/*
 * NormalVectorsFilter.hpp
 *
 *  Created on: May 05, 2015
 *      Author: Peter Fankhauser, Martin Wermelinger
 *   Institute: ETH Zurich, ANYbotics
 */

#pragma once

#include <filters/filter_base.h>
#include <grid_map_core/grid_map_core.hpp>

#include <Eigen/Core>
#include <string>

namespace grid_map {

/*!
 * Compute the normal vectors of a layer in a map.
 */
template<typename T>
class NormalVectorsFilter : public filters::FilterBase<T>
{

 public:
  /*!
   * Constructor
   */
  NormalVectorsFilter();

  /*!
   * Destructor.
   */
  virtual ~NormalVectorsFilter();

  /*!
   * Configures the filter from parameters on the Parameter Server
   */
  virtual bool configure();

  /*!
   * Compute the normal vectors of a layer in a map and
   * saves it as additional grid map layer.
   * @param mapIn grid map containing the layer for which the normal vectors are computed for.
   * @param mapOut grid map containing mapIn and the new layers for the normal vectors.
   */
  virtual bool update(const T& mapIn, T& mapOut);

 private:

  void computeWithArea(GridMap& map, const std::string& inputLayer, const std::string& outputLayersPrefix);
  void computeWithRaster(GridMap& map, const std::string& inputLayer, const std::string& outputLayersPrefix);

  enum class Method {
    Area,
    Raster
  };

  Method method_;

  //! Radius of submap for normal vector estimation.
  double estimationRadius_;

  //! Normal vector positive axis.
  Eigen::Vector3d normalVectorPositiveAxis_;

  //! Input layer name.
  std::string inputLayer_;

  //! Output layer name.
  std::string outputLayersPrefix_;
};

} /* namespace */


#pragma once

// system includes -------------------------------------------------------------
#include <iostream>
#include <type_traits>

// own includes ----------------------------------------------------------------
#include "spectral/basis/spectral_basis_factory_ks.hpp"
#include "traits/spectral_traits.hpp"

#include "aux/eigen2hdf.hpp"
#include "collision_tensor/collision_tensor.hpp"

// system includes -------------------------------------------------------------
#include <boost/filesystem.hpp>

namespace boltzmann {

template <typename ELEM>
class CollisionTensorFactory
{
 public:
  typedef CollisionTensor collision_tensor_t;

 private:
  typedef Eigen::SparseMatrix<double> matrix_t;

 private:
  typedef typename SpectralFactoryTraits<ELEM>::basis_factory_t basis_factory_t;

 public:
  /**
   * @brief read collision tensor from file
   *
   * Note: new_basis must be a subset of the original basis in which the
   *       tensor was computed.
   *
   * @param tensor         destination
   * @param trial_basis    destination trial basis
   * @param tensor_file    path/to/collision/tensor/hdf5-File
   * @param tensor_basis_descriptor path/to/collsion/tensor/basis/descriptor
   *                                (trial basis)
   */
  template <typename BASIS>
  static void load_from_file(collision_tensor_t& tensor,
                             const BASIS& trial_basis,
                             std::string tensor_file = "collision_tensor.h5",
                             std::string tensor_basis_descriptor = "ct_basis.desc");
};

template <typename ELEM>
template <typename BASIS>
void
CollisionTensorFactory<ELEM>::load_from_file(collision_tensor_t& tensor,
                                             const BASIS& trial_basis,
                                             std::string tensor_file,
                                             std::string tensor_basis_descriptor)
{
  // !! Attention: all (from tensor and new ones) basis descriptors need to have the same ordering
  // in k,l
  static_assert(std::is_same<typename BASIS::elem_t, ELEM>::value, "type mismatch");

  hid_t file = H5Fopen(tensor_file.c_str(), H5F_ACC_RDONLY, H5P_DEFAULT);

  if (!boost::filesystem::exists(tensor_basis_descriptor)) {
    std::cerr << "cannot find file " << tensor_basis_descriptor << std::endl;
  }
  // read the reference basis from file
  BASIS ref_basis;
  basis_factory_t::create(ref_basis, tensor_basis_descriptor);

  unsigned int Nref = ref_basis.n_dofs();
  unsigned int N = trial_basis.n_dofs();

  //  - check: test_basis is a subset of ref_basis
  //  - create mapping jref2jnew index old -> index new
  std::vector<int> jref2jnew(Nref, -1);
  for (auto it = trial_basis.begin(); it != trial_basis.end(); ++it) {
    unsigned int jnew = it - trial_basis.begin();
    auto refit = ref_basis.get_iter(it->get_id());
    if (refit == ref_basis.end()) {
      // new basis contains element that are not
      // contained in old basis, => cannot proceed
      std::cerr << "Error! Basis elem not found!\n" << it->get_id().to_string() << std::endl;
      exit(-1);
    } else {
      unsigned int jref = refit - ref_basis.begin();
      jref2jnew[jref] = jnew;
    }
  }

  // fill new tensor with entries from old tensor
  for (auto it_master = trial_basis.begin(); it_master != trial_basis.end(); ++it_master) {
    // get corresponding index in ref_basis
    unsigned int jref = ref_basis.get_dof_index(it_master->get_id());
    int jnew = jref2jnew[jref];
    if (jnew != -1) {  // read slice into ref_slice
      matrix_t ref_slice(Nref, Nref);
      eigen2hdf::load_sparse(file, boost::lexical_cast<std::string>(jref), ref_slice);
      // allocate new slice
      typedef std::shared_ptr<matrix_t> ptr_t;
      ptr_t slice = ptr_t(new matrix_t(N, N));
      // copy relevant entries from ref_slice into slice
      for (int k = 0; k < ref_slice.outerSize(); ++k)
        for (matrix_t::InnerIterator it(ref_slice, k); it; ++it) {
          it.value();
          int iref = it.row();  // row index
          int jref = it.col();  // col index (here it is equal to k)
          it.index();           // inner index, here it is equal to it.row()
          if ((jref2jnew[jref] != -1) && (jref2jnew[iref] != -1)) {
            slice->insert(jref2jnew[iref], jref2jnew[jref]) = it.value();
          }
        }
      tensor.add(slice, jnew);
    }
  }

  matrix_t mass_matrix(N, N);
  matrix_t ref_mass_matrix(Nref, Nref);
  eigen2hdf::load_sparse(file, "mass_matrix", ref_mass_matrix);

  for (int k = 0; k < ref_mass_matrix.outerSize(); ++k) {
    for (matrix_t::InnerIterator it(ref_mass_matrix, k); it; ++it) {
      it.value();
      int iref = it.row();  // row index
      int jref = it.col();  // col index (here it is equal to k)
      it.index();           // inner index, here it is equal to it.row()
      if ((jref2jnew[jref] != -1) && (jref2jnew[iref] != -1)) {
        mass_matrix.insert(jref2jnew[iref], jref2jnew[jref]) = it.value();
      }
    }
  }

  tensor.set_mass_matrix(mass_matrix);

  H5Fclose(file);
}
}  // end namespace boltzmann


// Boost.Geometry Index
// Additional tests

// Copyright (c) 2011-2015 Adam Wulkiewicz, Lodz, Poland.

// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

#include <iostream>
#include <vector>
#include <algorithm>

#include <boost/chrono.hpp>
#include <boost/foreach.hpp>
#include <boost/random.hpp>

#include <boost/geometry.hpp>
#include <boost/geometry/index/rtree.hpp>
#include <boost/geometry/geometries/geometries.hpp>

#include <boost/geometry/index/detail/rtree/utilities/are_boxes_ok.hpp>
#include <boost/geometry/index/detail/rtree/utilities/are_counts_ok.hpp>
#include <boost/geometry/index/detail/rtree/utilities/are_levels_ok.hpp>

namespace bg = boost::geometry;
namespace bgi = bg::index;

typedef bg::model::point<double, 2, bg::cs::cartesian> P;
typedef bg::model::box<P> B;
typedef bg::model::segment<P> S;
typedef P V;
//typedef B V;
//typedef S V;

template <typename V>
struct generate_value {};

template <>
struct generate_value<B>
{
    static inline B apply(float x, float y) { return B(P(x - 0.5f, y - 0.5f), P(x + 0.5f, y + 0.5f)); }
};

template <>
struct generate_value<S>
{
    static inline S apply(float x, float y) { return S(P(x - 0.5f, y - 0.5f), P(x + 0.5f, y + 0.5f)); }
};

template <>
struct generate_value<P>
{
    static inline P apply(float x, float y) { return P(x, y); }
};

template <typename RT>
void test_queries(RT const& t, std::vector< std::pair<float, float> > const& coords, size_t queries_count)
{
    typedef boost::chrono::thread_clock clock_t;
    typedef boost::chrono::duration<float> dur_t;

    std::vector<V> result;
    result.reserve(100);

    clock_t::time_point start = clock_t::now();
    size_t temp = 0;
    for (size_t i = 0 ; i < queries_count ; ++i )
    {
        float x = coords[i].first;
        float y = coords[i].second;
        result.clear();
        t.query(bgi::intersects(B(P(x - 10, y - 10), P(x + 10, y + 10))), std::back_inserter(result));
        temp += result.size();
    }
    dur_t time = clock_t::now() - start;
    std::cout << time.count() << " " << temp << '\n';
}

//#define BOOST_GEOMETRY_INDEX_BENCHMARK_DEBUG

int main()
{
    //typedef bgi::rtree<V, bgi::linear<4, 2> > RT;
    //typedef bgi::rtree<V, bgi::linear<16, 4> > RT;
    //typedef bgi::rtree<V, bgi::quadratic<4, 2> > RT;
    typedef bgi::rtree<V, bgi::rstar<8, 2> > RT;

    typedef boost::chrono::thread_clock clock_t;
    typedef boost::chrono::duration<float> dur_t;

#ifndef BOOST_GEOMETRY_INDEX_BENCHMARK_DEBUG
    size_t values_count = 1000000;
    size_t queries_count = 100000;
    size_t nearest_queries_count = 20000;
    unsigned neighbours_count = 10;
    size_t max_range_inserts = 10;
#else
    size_t values_count = 10000;
    size_t queries_count = 1000;
    size_t nearest_queries_count = 100;
    unsigned neighbours_count = 10;
    size_t max_range_inserts = 10;
#endif

    float max_val = static_cast<float>(values_count / 2);
    std::vector< std::pair<float, float> > coords;
    std::vector<V> values;

    //randomize values
    {
        boost::mt19937 rng;
        //rng.seed(static_cast<unsigned int>(std::time(0)));
        boost::uniform_real<float> range(-max_val, max_val);
        boost::variate_generator<boost::mt19937&, boost::uniform_real<float> > rnd(rng, range);

        coords.reserve(values_count);

        std::cout << "randomizing data\n";
        for ( size_t i = 0 ; i < values_count ; ++i )
        {
            float x = rnd();
            float y = rnd();
            coords.push_back(std::make_pair(x, y));
            values.push_back(generate_value<V>::apply(x, y));
        }
        std::cout << "randomized\n";
    }

    for (;;)
    {
        // packing test
        {
            clock_t::time_point start = clock_t::now();

            RT t(values.begin(), values.end());

            BOOST_ASSERT(bgi::detail::rtree::utilities::are_boxes_ok(t));
            BOOST_ASSERT(bgi::detail::rtree::utilities::are_counts_ok(t));
            BOOST_ASSERT(bgi::detail::rtree::utilities::are_levels_ok(t));

            dur_t time = clock_t::now() - start;
            std::cout << "pack(" << values_count << ") - " << time.count() << ", ";

            test_queries(t, coords, queries_count);
        }

        {
            size_t n_per_max = values_count / max_range_inserts;

            for ( size_t j = 0 ; j < max_range_inserts ; ++j )
            {
                clock_t::time_point start = clock_t::now();

                RT t;

                // perform j range-inserts
                for ( size_t i = 0 ; i < j ; ++i )
                {
                    t.insert(values.begin() + n_per_max * i,
                             values.begin() + (std::min)(n_per_max * (i + 1), values_count));
                }

                if ( !t.empty() )
                {
                    BOOST_ASSERT(bgi::detail::rtree::utilities::are_boxes_ok(t));
                    BOOST_ASSERT(bgi::detail::rtree::utilities::are_counts_ok(t));
                    BOOST_ASSERT(bgi::detail::rtree::utilities::are_levels_ok(t));
                }

                // perform n-n/max_inserts*j inserts
                size_t inserted_count = (std::min)(n_per_max*j, values_count);
                for ( size_t i = inserted_count ; i < values_count ; ++i )
                {
                    t.insert(values[i]);
                }

                if ( !t.empty() )
                {
                    BOOST_ASSERT(bgi::detail::rtree::utilities::are_boxes_ok(t));
                    BOOST_ASSERT(bgi::detail::rtree::utilities::are_counts_ok(t));
                    BOOST_ASSERT(bgi::detail::rtree::utilities::are_levels_ok(t));
                }

                dur_t time = clock_t::now() - start;
                std::cout << j << "*insert(N/" << max_range_inserts << ")+insert(" << (values_count - inserted_count) << ") - " << time.count() << ", ";

                test_queries(t, coords, queries_count);
            }
        }

        std::cout << "------------------------------------------------\n";
    }

    return 0;
}


// Software License for MTL
//
// Copyright (c) 2007 The Trustees of Indiana University.
//               2008 Dresden University of Technology and the Trustees of Indiana University.
//               2010 SimuNova UG (haftungsbeschränkt), www.simunova.com.
// All rights reserved.
// Authors: Peter Gottschling and Andrew Lumsdaine
//
// This file is part of the Matrix Template Library
//
// See also license.mtl.txt in the distribution.

#ifndef MTL_ASSIGN_MODE_INCLUDE
#define MTL_ASSIGN_MODE_INCLUDE

#include <boost/numeric/linear_algebra/identity.hpp>
#include <boost/numeric/mtl/utility/exception.hpp>

namespace mtl { namespace assign {

/// Functor for assigning the result of a calculation (e.g. a sum) to a typically scalar value
struct assign_sum
{
    static const bool init_to_zero= true;

    template <typename T>
    static void init(T& v)
    {
	using math::zero;
	v= zero(v);
    }

    // Sets x with y when empty or not initialized
    template <typename T, typename U>
    static void set_empty(T& x, const U& y)
    {
	x= y;
    }

    // The first update sets the value and avoids the zeroing
    template <typename T, typename U>
    static void first_update(T& x, const U& y)
    {
	x= y;
    }

    template <typename T, typename U>
    static void update(T& x, const U& y)
    {
	x+= y;
    }

    // To be used like sfunctor
    template <typename T, typename U>
    static T& apply(T& x, const U& y)
    {
	return x= y;
    }

};

/// Functor for incrementing a typically scalar value with the result of a calculation (e.g. a sum)
struct plus_sum
{
    static const bool init_to_zero= false;

    template <typename T>
    static void init(T&) {}

    // Sets x with y when empty or not initialized
    template <typename T, typename U>
    static void set_empty(T& x, const U& y)
    {
	x= y;
    }

    template <typename T, typename U>
    static void first_update(T& x, const U& y)
    {
	x+= y;
    }

    template <typename T, typename U>
    static void update(T& x, const U& y)
    {
	x+= y;
    }

    // To be used like sfunctor
    template <typename T, typename U>
    static T& apply(T& x, const U& y)
    {
	return x+= y;
    }
};

/// Functor for incrementing a typically scalar value with the result of a calculation (e.g. a sum)
struct minus_sum
{
    static const bool init_to_zero= false;

    template <typename T>
    static void init(T&) {}

    // Sets x with y when empty or not initialized
    template <typename T, typename U>
    static void set_empty(T& x, const U& y)
    {
	x= -y;
    }

    template <typename T, typename U>
    static void first_update(T& x, const U& y)
    {
	x-= y;
    }

    template <typename T, typename U>
    static void update(T& x, const U& y)
    {
	x-= y;
    }

    // To be used like sfunctor
    template <typename T, typename U>
    static T& apply(T& x, const U& y)
    {
	return x-= y;
    }
};

/// Functor for multiplying a typically scalar value with the result of a calculation (e.g. a sum)
struct times_sum
{
    static const bool init_to_zero= false;

    template <typename T>
    static void init(T&) {}

    // Sets x with y when empty or not initialized
    template <typename T, typename U>
    static void set_empty(T& x, const U&)
    {
	x= T(0);
    }

    template <typename T, typename U>
    static void first_update(T& x, const U& y)
    {
	x*= y;
    }

    template <typename T, typename U>
    static void update(T& x, const U& y)
    {
	x*= y;
    }

    // To be used like sfunctor
    template <typename T, typename U>
    static T& apply(T& x, const U& y)
    {
	return x*= y;
    }
};

/// Functor for dividing a typically scalar value with the result of a calculation (e.g. a sum)
struct divide_sum
{
    static const bool init_to_zero= false;

    template <typename T>
    static void init(T&) {}

    // Sets x with y when empty or not initialized
    template <typename T, typename U>
    static void set_empty(T& x, const U& MTL_DEBUG_ARG( y ))
    {
        MTL_DEBUG_THROW_IF(y == U(0), division_by_zero());
	x= T(0);
    }

    template <typename T, typename U>
    static void first_update(T& x, const U& y)
    {
        MTL_DEBUG_THROW_IF(y == U(0), division_by_zero());
	x/= y;
    }

    template <typename T, typename U>
    static void update(T& x, const U& y)
    {
        MTL_DEBUG_THROW_IF(y == U(0), division_by_zero());
	x/= y;
    }

    // To be used like sfunctor
    template <typename T, typename U>
    static T& apply(T& x, const U& y)
    {
	return x/= y;
    }
};

}} // namespace mtl::assign

#endif // MTL_ASSIGN_MODE_INCLUDE