text:
#include "cpu/image_proc.h"

#include <Eigen/Dense>
#include <map>
#include <string>
#include <cmath>
#include <stdexcept>

namespace helper {

bool in_bounds(Eigen::Vector2f p, int h, int w) {
	return p.x() >= 0.0 && p.x() < static_cast<float>(w) && p.y() >= 0.0 && p.y() < static_cast<float>(h);
}

bool valid_flow_at(const Eigen::Vector2f& p, int h, int w, py::array_t<float>& flow_image, Eigen::Vector2f& flow) {
	if (!in_bounds(p, h, w)) {
		return false;
	}

	flow.x() = *flow_image.data(p.y(), p.x(), 0);
	flow.y() = *flow_image.data(p.y(), p.x(), 1);

	if (!flow.allFinite()) {
		return false;
	}

	return true;
}
} // namespace helper

namespace image_proc {

using Vec2f = Eigen::Vector2f;

template<class V, class L = std::less<std::string>,
		class A = Eigen::aligned_allocator<std::pair<const std::string, V>>>
using aligned_dict = std::map<std::string, V, L, A>;

py::array_t<float> compute_augmented_flow_from_rotation(py::array_t<float>& flow_image_rot_sa2so,
                                                        py::array_t<float>& flow_image_so2to,
                                                        py::array_t<float>& flow_image_rot_to2ta,
                                                        const int height, const int width) {
	// TODO: change to runtime asserts
	// assert(flow_image_rot_sa2so.ndim() == 3);
	// assert(flow_image_rot_sa2so.shape(0) == 2);
	// assert(flow_image_rot_sa2so.shape(1) == height);
	// assert(flow_image_rot_sa2so.shape(2) == width);

	// assert(flow_image_so2to.ndim() == 3);
	// assert(flow_image_so2to.shape(0) == 2);
	// assert(flow_image_so2to.shape(1) == height);
	// assert(flow_image_so2to.shape(2) == width);

	// assert(flow_image_rot_to2ta.ndim() == 3);
	// assert(flow_image_rot_to2ta.shape(0) == 2);
	// assert(flow_image_rot_to2ta.shape(1) == height);
	// assert(flow_image_rot_to2ta.shape(2) == width);

	// allocate memory for output array
	py::array_t<float> flow_image_rot_sa2ta = py::array_t<float>(flow_image_rot_sa2so.request().size);

	// reshape array to match input shape
	flow_image_rot_sa2ta.resize({height, width, 2});

	for (int y = 0; y < height; y++) {
		for (int x = 0; x < width; x++) {

			// update output flow image
			*flow_image_rot_sa2ta.mutable_data(y, x, 0) = -std::numeric_limits<float>::infinity();
			*flow_image_rot_sa2ta.mutable_data(y, x, 1) = -std::numeric_limits<float>::infinity();

			Vec2f p_sa(x, y);

			/////////////////////////////////////////////////////////////////////////////////
			// 1. SOURCE AUGMENTED TO SOURCE ORIGINAL
			/////////////////////////////////////////////////////////////////////////////////

			// flow from source augmented to source original
			Vec2f flow_sa2so(*flow_image_rot_sa2so.data(y, x, 0), *flow_image_rot_sa2so.data(y, x, 1));

			// flow_sa2so should be dense and w/o any invalid value
			if (!flow_sa2so.allFinite()) {
				throw std::runtime_error("flow_sa2so should be dense and w/o any invalid residuals!");
			}

			// compute warped location on source original (so we're going from source augmented to source original)
			Vec2f p_so = p_sa + flow_sa2so;

			// init flow_sa2ta with the first contribution, i.e, flow_sa2so
			Vec2f flow_sa2ta = flow_sa2so;

			/////////////////////////////////////////////////////////////////////////////////
			// 2. SOURCE ORIGINAL TO TARGET ORIGINAL
			/////////////////////////////////////////////////////////////////////////////////
			int u0 = std::floor(p_so.x());
			int u1 = u0 + 1;
			int v0 = std::floor(p_so.y());
			int v1 = v0 + 1;

			Vec2f p00(u0, v0);
			Vec2f p01(u0, v1);
			Vec2f p10(u1, v0);
			Vec2f p11(u1, v1);

			aligned_dict<Vec2f> valid_coords;
			aligned_dict<Vec2f> valid_flows;

			Vec2f flow_00_so2to;
			if (helper::valid_flow_at(p00, height, width, flow_image_so2to, flow_00_so2to)) {
				valid_coords["p00"] = p00;
				valid_flows["p00"] = flow_00_so2to;
			}

			Vec2f flow_01_so2to;
			if (helper::valid_flow_at(p01, height, width, flow_image_so2to, flow_01_so2to)) {
				valid_coords["p01"] = p01;
				valid_flows["p01"] = flow_01_so2to;
			}

			Vec2f flow_10_so2to;
			if (helper::valid_flow_at(p10, height, width, flow_image_so2to, flow_10_so2to)) {
				valid_coords["p10"] = p10;
				valid_flows["p10"] = flow_10_so2to;
			}

			Vec2f flow_11_so2to;
			if (helper::valid_flow_at(p11, height, width, flow_image_so2to, flow_11_so2to)) {
				valid_coords["p11"] = p11;
				valid_flows["p11"] = flow_11_so2to;
			}

			// Depending on how many valid flows we have, do bilinear interpolation or nearest neighbor:
			Vec2f flow_so2to;

			if (valid_coords.empty()) {
				continue;
			} else if (valid_coords.size() == 4) {
				// Bilinear interpolation
				float du = p_so.x() - static_cast<float>(u0);
				float dv = p_so.y() - static_cast<float>(v0);

				float w00 = (1 - du) * (1 - dv);
				float w01 = (1 - du) * dv;
				float w10 = du * (1 - dv);
				float w11 = du * dv;

				flow_so2to = w00 * valid_flows["p00"] +
				             w01 * valid_flows["p01"] +
				             w10 * valid_flows["p10"] +
				             w11 * valid_flows["p11"];
			} else {
				// Nearest Neighbor
				std::string nn = "None";
				float min_dist = std::numeric_limits<float>::max();

				for (const auto& valid_coord : valid_coords) {
					const std::string k = valid_coord.first;
					const Vec2f& p = valid_coord.second;

					float dist = (p_so - p).norm();
					if (dist < min_dist) {
						min_dist = dist;
						nn = k;
					}
				}

				if (nn == "None") {
					throw std::runtime_error("Neighrest Neighbor 'nn' was not assigned...");
				}

				flow_so2to = valid_flows[nn];
			}

			// compute warped location on target original (so we're going from source original to target original)
			Vec2f p_to = p_so + flow_so2to;

			// add flow_so2to to flow_sa2ta
			flow_sa2ta += flow_so2to;

			/////////////////////////////////////////////////////////////////////////////////
			// 3. TARGET ORIGINAL TO TARGET AUGMENTED
			/////////////////////////////////////////////////////////////////////////////////
			u0 = std::floor(p_to.x());
			u1 = u0 + 1;
			v0 = std::floor(p_to.y());
			v1 = v0 + 1;

			p00 = Vec2f(u0, v0);
			p01 = Vec2f(u0, v1);
			p10 = Vec2f(u1, v0);
			p11 = Vec2f(u1, v1);

			valid_coords.clear();
			valid_flows.clear();

			Vec2f flow_00_to2ta;
			if (helper::valid_flow_at(p00, height, width, flow_image_rot_to2ta, flow_00_to2ta)) {
				valid_coords["p00"] = p00;
				valid_flows["p00"] = flow_00_to2ta;
			}

			Vec2f flow_01_to2ta;
			if (helper::valid_flow_at(p01, height, width, flow_image_rot_to2ta, flow_01_to2ta)) {
				valid_coords["p01"] = p01;
				valid_flows["p01"] = flow_01_to2ta;
			}

			Vec2f flow_10_to2ta;
			if (helper::valid_flow_at(p10, height, width, flow_image_rot_to2ta, flow_10_to2ta)) {
				valid_coords["p10"] = p10;
				valid_flows["p10"] = flow_10_to2ta;
			}

			Vec2f flow_11_to2ta;
			if (helper::valid_flow_at(p11, height, width, flow_image_rot_to2ta, flow_11_to2ta)) {
				valid_coords["p11"] = p11;
				valid_flows["p11"] = flow_11_to2ta;
			}

			// Depending on how many valid flows we have, do bilinear interpolation or nearest neighbor:
			Vec2f flow_to2ta;

			if (valid_coords.empty()) {
				continue;
			} else if (valid_coords.size() == 4) {
				// Bilinear interpolation
				float du = p_to.x() - static_cast<float>(u0);
				float dv = p_to.y() - static_cast<float>(v0);

				float w00 = (1 - du) * (1 - dv);
				float w01 = (1 - du) * dv;
				float w10 = du * (1 - dv);
				float w11 = du * dv;

				flow_to2ta = w00 * valid_flows["p00"] +
				             w01 * valid_flows["p01"] +
				             w10 * valid_flows["p10"] +
				             w11 * valid_flows["p11"];
			} else {
				// Nearest Neighbor
				std::string nn = "None";
				float min_dist = std::numeric_limits<float>::max();

				for (const auto& valid_coord : valid_coords) {
					const std::string k = valid_coord.first;
					const Vec2f& p = valid_coord.second;

					float dist = (p_to - p).norm();
					if (dist < min_dist) {
						min_dist = dist;
						nn = k;
					}
				}

				if (nn == "None") {
					throw std::runtime_error("Neighrest Neighbor 'nn' was not assigned...");
				}

				flow_to2ta = valid_flows[nn];
			}

			// add flow_to2ta to flow_sa2ta
			flow_sa2ta += flow_to2ta;

			// update output flow image
			*flow_image_rot_sa2ta.mutable_data(y, x, 0) = flow_sa2ta.x();
			*flow_image_rot_sa2ta.mutable_data(y, x, 1) = flow_sa2ta.y();
		}
	}

	return flow_image_rot_sa2ta;
}

static inline bool has_immediate_true_z_neighbors(const int& index, const int& z, const int& z_bound, const py::array_t<bool>& array) {
	auto z_in_range = [&z_bound](int neighbor_z) { return neighbor_z > 0 && neighbor_z < z_bound; };
	auto value_if_in_range_or_zero = [&z, &index, &z_in_range, &array](int delta_z) {
		return z_in_range(z + delta_z) ? *array.data(index, z + delta_z) : 0;
	};
	return value_if_in_range_or_zero(-1) || value_if_in_range_or_zero(0) || value_if_in_range_or_zero(1);
}

int count_tp1(py::array_t<bool>& p, py::array_t<bool>& gt) {
	assert(p.ndim() == 2);
	assert(gt.ndim() == 2);
	const int n_batch = p.shape(0);
	const int dimz = p.shape(1);

	auto& ptr = p;
	int counter = 0;
	for (int i = 0; i < n_batch; i++) {
		for (int z = 0; z < dimz; z++) {
			if (*gt.data(i, z)) {
				counter += has_immediate_true_z_neighbors(i, z, dimz, p);
			}
		}
	}

	return counter;
}

int count_tp2(py::array_t<bool>& p, py::array_t<bool>& gt) {
	assert(p.ndim() == 4);
	assert(gt.ndim() == 4);
	const int n_batch = p.shape(0);
	assert(p.shape(1) == 1);
	const int height = p.shape(2);
	const int width = p.shape(3);

	auto& ptr = p;
	int counter = 0;
	for (int i = 0; i < n_batch; i++)
		for (int y = 0; y < height; y++)
			for (int x = 0; x < width; x++) {
				if (*gt.data(i, 0, y, x)) {
					counter += CHECK2();
				}
			}
	return counter;
}

int count_tp3(py::array_t<bool>& p, py::array_t<bool>& gt) {
	assert(p.ndim() == 5);
	assert(gt.ndim() == 5);
	const int n_batch = p.shape(0);
	assert(p.shape(1) == 1);
	const int dimz = p.shape(2);
	const int dimy = p.shape(3);
	const int dimx = p.shape(4);

	auto& ptr = p;
	int counter = 0;
	for (int i = 0; i < n_batch; i++)
		for (int z = 0; z < dimz; z++)
			for (int y = 0; y < dimy; y++)
				for (int x = 0; x < dimx; x++) {
					if (*gt.data(i, 0, z, y, x)) {
						counter += CHECK3();
						//printf("i %d x %d y %d z %d in %d\n", i, x, y, z, res);
					}
				}
	return counter;
}

void extend3(py::array_t<bool>& in, py::array_t<bool>& out) {
	assert(in.ndim() == 5);
	assert(out.ndim() == 5);
	int n_batch = in.shape(0);
	assert(in.shape(1) == 1);
	int dimz = in.shape(2);
	int dimy = in.shape(3);
	int dimx = in.shape(4);

	auto& ptr = in;
	for (int i = 0; i < n_batch; i++) {
		for (int z = 1; z < dimz - 1; z++) {
			for (int y = 1; y < dimy - 1; y++) {
				for (int x = 1; x < dimx - 1; x++) {
					*out.mutable_data(i, 0, z, y, x) = CHECK3();
				}
			}
		}
	}
}

void backproject_depth_ushort(py::array_t<unsigned short>& image_in, py::array_t<float>& point_image_out,
                              const float fx, const float fy, const float cx, const float cy, const float normalizer) {
	assert(image_in.ndim() == 2);
	assert(point_image_out.ndim() == 3);

	const int width = image_in.shape(1);
	const int height = image_in.shape(0);

	assert(point_image_out.shape(0) == height);
	assert(point_image_out.shape(1) == width);
	assert(point_image_out.shape(2) == 3);

#pragma omp parallel for default(none) shared(image_in, point_image_out) firstprivate(height, width, fx, fy, cx, cy, normalizer)
	for (int y = 0; y < height; y++) {
		for (int x = 0; x < width; x++) {
			float depth = float(*image_in.data(y, x)) / normalizer;

			if (depth > 0) {
				float pos_x = depth * (static_cast<float>(x) - cx) / fx;
				float pos_y = depth * (static_cast<float>(y) - cy) / fy;
				float pos_z = depth;

				*point_image_out.mutable_data(y, x, 0) = pos_x;
				*point_image_out.mutable_data(y, x, 1) = pos_y;
				*point_image_out.mutable_data(y, x, 2) = pos_z;
			}
		}
	}
}

py::array_t<float> backproject_depth_ushort(py::array_t<unsigned short>& image_in, float fx, float fy, float cx, float cy, float normalizer) {
	py::array_t<float> point_image_out({image_in.shape(0), image_in.shape(1), static_cast<ssize_t>(3)});
	memset(point_image_out.mutable_data(0, 0, 0), 0, point_image_out.size() * sizeof(float));
	backproject_depth_ushort(image_in, point_image_out, fx, fy, cx, cy, normalizer);
	return point_image_out;
}

void backproject_depth_float(py::array_t<float>& image_in, py::array_t<float>& point_image_out,
                             float fx, float fy, float cx, float cy) {
	assert(image_in.ndim() == 2);
	assert(point_image_out.ndim() == 3);

	int width = image_in.shape(1);
	int height = image_in.shape(0);
	assert(point_image_out.shape(0) == 3);
	assert(point_image_out.shape(1) == height);
	assert(point_image_out.shape(2) == width);

#pragma omp parallel for default(none) shared(image_in, point_image_out) firstprivate(height, width, fx, fy, cx, cy)
	for (int y = 0; y < height; y++) {
		for (int x = 0; x < width; x++) {
			float depth = *image_in.data(y, x);

			if (depth > 0) {
				float pos_x = depth * (static_cast<float>(x) - cx) / fx;
				float pos_y = depth * (static_cast<float>(y) - cy) / fy;
				float pos_z = depth;

				*point_image_out.mutable_data(y, x, 0) = pos_x;
				*point_image_out.mutable_data(y, x, 1) = pos_y;
				*point_image_out.mutable_data(y, x, 2) = pos_z;
			}
		}
	}
}

void compute_mesh_from_depth(const py::array_t<float>& point_image_in, float max_triangle_edge_distance,
                             py::array_t<float>& vertex_positions_out, py::array_t<int>& vertex_pixels_out,
                             py::array_t<int>& face_indices_out) {
	assert(point_image_in.ndim() == 3);
	assert(point_image_in.shape(2) == 3);

	int width = static_cast<int>(point_image_in.shape(1));
	int height = static_cast<int>(point_image_in.shape(0));

	// Compute valid pixel vertices and faces.
	// We also need to compute the pixel -> vertex index mapping for
	// computation of faces.
	// We connect neighboring pixels on the square into two triangles.
	// We only select valid triangles, i.e. with all valid vertices and
	// not too far apart.
	// Important: The triangle orientation is set such that the normals
	// point towards the camera.
	std::vector<Eigen::Vector3f> vertices;
	std::vector<Eigen::Vector3i> faces;
	std::vector<Eigen::Vector2i> pixels;

	int vertexIdx = 0;
	std::vector<int> mapPixelToVertexIdx(width * height, -1);

	for (int y = 0; y < height - 1; y++) {
		for (int x = 0; x < width - 1; x++) {
			Eigen::Vector3f obs00(*point_image_in.data(y + 0, x + 0, 0), *point_image_in.data(y + 0, x + 0, 1), *point_image_in.data(y + 0, x + 0, 2));
			Eigen::Vector3f obs01(*point_image_in.data(y + 1, x + 0, 0), *point_image_in.data(y + 1, x + 0, 1), *point_image_in.data(y + 1, x + 0, 2));
			Eigen::Vector3f obs10(*point_image_in.data(y + 0, x + 1, 0), *point_image_in.data(y + 0, x + 1, 1), *point_image_in.data(y + 0, x + 1, 2));
			Eigen::Vector3f obs11(*point_image_in.data(y + 1, x + 1, 0), *point_image_in.data(y + 1, x + 1, 1), *point_image_in.data(y + 1, x + 1, 2));

			int idx00 = y * width + x;
			int idx01 = (y + 1) * width + x;
			int idx10 = y * width + (x + 1);
			int idx11 = (y + 1) * width + (x + 1);

			bool valid00 = obs00.z() > 0;
			bool valid01 = obs01.z() > 0;
			bool valid10 = obs10.z() > 0;
			bool valid11 = obs11.z() > 0;

			if (valid00 && valid01 && valid10) {
				float d0 = (obs00 - obs01).norm();
				float d1 = (obs00 - obs10).norm();
				float d2 = (obs01 - obs10).norm();

				if (d0 <= max_triangle_edge_distance && d1 <= max_triangle_edge_distance && d2 <= max_triangle_edge_distance) {
					int vIdx0 = mapPixelToVertexIdx[idx00];
					int vIdx1 = mapPixelToVertexIdx[idx01];
					int vIdx2 = mapPixelToVertexIdx[idx10];

					if (vIdx0 == -1) {
						vIdx0 = vertexIdx;
						mapPixelToVertexIdx[idx00] = vertexIdx;
						vertices.push_back(obs00);
						vertexIdx++;
						pixels.emplace_back(x, y);
					}
					if (vIdx1 == -1) {
						vIdx1 = vertexIdx;
						mapPixelToVertexIdx[idx01] = vertexIdx;
						vertices.push_back(obs01);
						vertexIdx++;
						pixels.emplace_back(x, y + 1);
					}
					if (vIdx2 == -1) {
						vIdx2 = vertexIdx;
						mapPixelToVertexIdx[idx10] = vertexIdx;
						vertices.push_back(obs10);
						vertexIdx++;
						pixels.emplace_back(x + 1, y);
					}

					faces.emplace_back(vIdx0, vIdx1, vIdx2);
				}
			}

			if (valid01 && valid10 && valid11) {
				float d0 = (obs10 - obs01).norm();
				float d1 = (obs10 - obs11).norm();
				float d2 = (obs01 - obs11).norm();

				if (d0 <= max_triangle_edge_distance && d1 <= max_triangle_edge_distance && d2 <= max_triangle_edge_distance) {
					int vIdx0 = mapPixelToVertexIdx[idx11];
					int vIdx1 = mapPixelToVertexIdx[idx10];
					int vIdx2 = mapPixelToVertexIdx[idx01];

					if (vIdx0 == -1) {
						vIdx0 = vertexIdx;
						mapPixelToVertexIdx[idx11] = vertexIdx;
						vertices.push_back(obs11);
						vertexIdx++;
						pixels.emplace_back(x + 1, y + 1);
					}
					if (vIdx1 == -1) {
						vIdx1 = vertexIdx;
						mapPixelToVertexIdx[idx10] = vertexIdx;
						vertices.push_back(obs10);
						vertexIdx++;
						pixels.emplace_back(x + 1, y);
					}
					if (vIdx2 == -1) {
						vIdx2 = vertexIdx;
						mapPixelToVertexIdx[idx01] = vertexIdx;
						vertices.push_back(obs01);
						vertexIdx++;
						pixels.emplace_back(x, y + 1);
					}

					faces.emplace_back(vIdx0, vIdx1, vIdx2);
				}
			}
		}
	}

	// Convert to numpy array.
	int vertex_count = vertices.size();
	int face_count = faces.size();

	if (vertex_count > 0 && face_count > 0) {
		// Reference check should be set to false otherwise there is a runtime
		// error. Check why that is the case.
		vertex_positions_out.resize({vertex_count, 3}, false);
		face_indices_out.resize({face_count, 3}, false);
		vertex_pixels_out.resize({vertex_count, 2}, false);

		for (int i = 0; i < vertex_count; i++) {
			*vertex_positions_out.mutable_data(i, 0) = vertices[i].x();
			*vertex_positions_out.mutable_data(i, 1) = vertices[i].y();
			*vertex_positions_out.mutable_data(i, 2) = vertices[i].z();

			*vertex_pixels_out.mutable_data(i, 0) = pixels[i].x();
			*vertex_pixels_out.mutable_data(i, 1) = pixels[i].y();
		}

		for (int i = 0; i < face_count; i++) {
			*face_indices_out.mutable_data(i, 0) = faces[i].x();
			*face_indices_out.mutable_data(i, 1) = faces[i].y();
			*face_indices_out.mutable_data(i, 2) = faces[i].z();
		}
	}
}

void compute_mesh_from_depth(
		const py::array_t<float>& point_image_in, float max_triangle_edge_distance,
		py::array_t<float>& vertex_positions_out, py::array_t<int>& face_indices_out
) {
	py::array_t<int> vertex_pixels;
	compute_mesh_from_depth(point_image_in, max_triangle_edge_distance, vertex_positions_out, vertex_pixels, face_indices_out);
}

py::tuple compute_mesh_from_depth(const py::array_t<float>& point_image_in, float max_triangle_edge_distance) {
	py::array_t<float> vertex_positions_out;
	py::array_t<int> face_indices_out;
	py::array_t<int> vertex_pixels_out;
	compute_mesh_from_depth(point_image_in, max_triangle_edge_distance, vertex_positions_out, vertex_pixels_out, face_indices_out);
	return py::make_tuple(vertex_positions_out, vertex_pixels_out, face_indices_out);
}

void compute_mesh_from_depth_and_color(
		const py::array_t<float>& point_image, const py::array_t<int>& color_image, float max_triangle_edge_distance,
		py::array_t<float>& vertex_positions, py::array_t<int>& vertex_colors, py::array_t<int>& face_indices
) {
	int width = static_cast<int>(point_image.shape(1));
	int height = static_cast<int>(point_image.shape(0));

	// Compute valid pixel vertices and faces.
	// We also need to compute the pixel -> vertex index mapping for
	// computation of faces.
	// We connect neighboring pixels on the square into two triangles.
	// We only select valid triangles, i.e. with all valid vertices and
	// not too far apart.
	// Important: The triangle orientation is set such that the normals
	// point towards the camera.
	std::vector<Eigen::Vector3f> vertices;
	std::vector<Eigen::Vector3i> colors;
	std::vector<Eigen::Vector3i> faces;

	int vertexIdx = 0;
	std::vector<int> mapPixelToVertexIdx(width * height, -1);

	for (int y = 0; y < height - 1; y++) {
		for (int x = 0; x < width - 1; x++) {
			Eigen::Vector3f obs00(*point_image.data(y + 0, x + 0, 0), *point_image.data(y + 0, x + 0, 1), *point_image.data(y + 0, x + 0, 2));
			Eigen::Vector3f obs01(*point_image.data(y + 1, x + 0, 0), *point_image.data(y + 1, x + 0, 1), *point_image.data(y + 1, x + 0, 2));
			Eigen::Vector3f obs10(*point_image.data(y + 0, x + 1, 0), *point_image.data(y + 0, x + 1, 1), *point_image.data(y + 0, x + 1, 2));
			Eigen::Vector3f obs11(*point_image.data(y + 1, x + 1, 0), *point_image.data(y + 1, x + 1, 1), *point_image.data(y + 1, x + 1, 2));

			Eigen::Vector3i color00(*color_image.data(y + 0, x + 0, 0), *color_image.data(y + 0, x + 0, 1), *color_image.data(y + 0, x + 0, 2));
			Eigen::Vector3i color01(*color_image.data(y + 1, x + 0, 0), *color_image.data(y + 1, x + 0, 1), *color_image.data(y + 1, x + 0, 2));
			Eigen::Vector3i color10(*color_image.data(y + 0, x + 1, 0), *color_image.data(y + 0, x + 1, 1), *color_image.data(y + 0, x + 1, 2));
			Eigen::Vector3i color11(*color_image.data(y + 1, x + 1, 0), *color_image.data(y + 1, x + 1, 1), *color_image.data(y + 1, x + 1, 2));

			// find linear indices
			int idx00 = y * width + x;
			int idx01 = (y + 1) * width + x;
			int idx10 = y * width + (x + 1);
			int idx11 = (y + 1) * width + (x + 1);

			bool valid00 = obs00.z() > 0;
			bool valid01 = obs01.z() > 0;
			bool valid10 = obs10.z() > 0;
			bool valid11 = obs11.z() > 0;

			// region ======= LOWER LEFT TRIANGLE =======
			if (valid00 && valid01 && valid10) {
				float d0 = (obs00 - obs01).norm();
				float d1 = (obs00 - obs10).norm();
				float d2 = (obs01 - obs10).norm();

				if (d0 <= max_triangle_edge_distance && d1 <= max_triangle_edge_distance && d2 <= max_triangle_edge_distance) {
					int vIdx0 = mapPixelToVertexIdx[idx00];
					int vIdx1 = mapPixelToVertexIdx[idx01];
					int vIdx2 = mapPixelToVertexIdx[idx10];

					if (vIdx0 == -1) {
						vIdx0 = vertexIdx;
						mapPixelToVertexIdx[idx00] = vertexIdx;
						vertices.push_back(obs00);
						colors.push_back(color00);
						vertexIdx++;
					}
					if (vIdx1 == -1) {
						vIdx1 = vertexIdx;
						mapPixelToVertexIdx[idx01] = vertexIdx;
						vertices.push_back(obs01);
						colors.push_back(color01);
						vertexIdx++;
					}
					if (vIdx2 == -1) {
						vIdx2 = vertexIdx;
						mapPixelToVertexIdx[idx10] = vertexIdx;
						vertices.push_back(obs10);
						colors.push_back(color10);
						vertexIdx++;
					}

					faces.emplace_back(vIdx0, vIdx1, vIdx2);
				}
			}
			// endregion
			// region ======= UPPER RIGHT TRIANGLE =======
			if (valid01 && valid10 && valid11) {
				float d0 = (obs10 - obs01).norm();
				float d1 = (obs10 - obs11).norm();
				float d2 = (obs01 - obs11).norm();

				if (d0 <= max_triangle_edge_distance && d1 <= max_triangle_edge_distance && d2 <= max_triangle_edge_distance) {
					int vIdx0 = mapPixelToVertexIdx[idx11];
					int vIdx1 = mapPixelToVertexIdx[idx10];
					int vIdx2 = mapPixelToVertexIdx[idx01];

					if (vIdx0 == -1) {
						vIdx0 = vertexIdx;
						mapPixelToVertexIdx[idx11] = vertexIdx;
						vertices.push_back(obs11);
						colors.push_back(color11);
						vertexIdx++;
					}
					if (vIdx1 == -1) {
						vIdx1 = vertexIdx;
						mapPixelToVertexIdx[idx10] = vertexIdx;
						vertices.push_back(obs10);
						colors.push_back(color10);
						vertexIdx++;
					}
					if (vIdx2 == -1) {
						vIdx2 = vertexIdx;
						mapPixelToVertexIdx[idx01] = vertexIdx;
						vertices.push_back(obs01);
						colors.push_back(color01);
						vertexIdx++;
					}

					faces.emplace_back(vIdx0, vIdx1, vIdx2);
				}
			}
			// endregion
		}
	}

	// Convert to numpy array.
	int vertex_count = vertices.size();
	int face_count = faces.size();

	if (vertex_count > 0 && face_count > 0) {
		// Reference check should be set to false otherwise there is a runtime
		// error. Check why that is the case.
		vertex_positions.resize({vertex_count, 3}, false);
		vertex_colors.resize({vertex_count, 3}, false);
		face_indices.resize({face_count, 3}, false);

		for (int i = 0; i < vertex_count; i++) {
			*vertex_positions.mutable_data(i, 0) = vertices[i].x();
			*vertex_positions.mutable_data(i, 1) = vertices[i].y();
			*vertex_positions.mutable_data(i, 2) = vertices[i].z();

			*vertex_colors.mutable_data(i, 0) = colors[i].x();
			*vertex_colors.mutable_data(i, 1) = colors[i].y();
			*vertex_colors.mutable_data(i, 2) = colors[i].z();
		}

		for (int i = 0; i < face_count; i++) {
			*face_indices.mutable_data(i, 0) = faces[i].x();
			*face_indices.mutable_data(i, 1) = faces[i].y();
			*face_indices.mutable_data(i, 2) = faces[i].z();
		}
	}
}

void compute_mesh_from_depth_and_flow(
		const py::array_t<float>& point_image_in, const py::array_t<float>& flow_image_in,
		float max_triangle_edge_distance, py::array_t<float>& vertex_positions_out,
		py::array_t<float>& vertex_flows_out, py::array_t<int>& vertex_pixels_out, py::array_t<int>& face_indices_out
) {
	int width = point_image_in.shape(1);
	int height = point_image_in.shape(0);

	// Compute valid pixel vertices and faces
	// a pixel is considered valid if the corresponding point's z coordinate is greater than zero and x, y, and z of the flow vector are all finite

	// We also need to compute the pixel -> vertex index mapping for computation of faces.
	// TODO: why are there no vertex indices > 400, if the total image has 640x480=307200 pixels?

	// For every 2x2 pixel area, we connect neighboring pixels into two triangles.
	// [ ]-------[ ]
	//  | ╲       |
	//  |    ╲    |
	//  |       ╲ |
	// [ ]-------[ ]

	// We only select valid triangles, i.e. with all valid vertices, and vertices not too far apart.

	// Important: The triangle orientation is set such that the normals point towards the camera.

	std::vector<Eigen::Vector3f> vertices;
	std::vector<Eigen::Vector3f> flows;
	std::vector<Eigen::Vector2i> pixels;
	std::vector<Eigen::Vector3i> faces;

	int vertex_index = 0;
	std::vector<int> pixel_to_vertex_index_map(width * height, -1);

	for (int y = 0; y < height - 1; y++) {
		for (int x = 0; x < width - 1; x++) {
			Eigen::Vector3f obs00(*point_image_in.data(y, x, 0), *point_image_in.data(y, x, 1), *point_image_in.data(y, x, 2));
			Eigen::Vector3f obs01(*point_image_in.data(y + 1, x, 0), *point_image_in.data(y + 1, x, 1), *point_image_in.data(y + 1, x, 2));
			Eigen::Vector3f obs10(*point_image_in.data(y, x + 1, 0), *point_image_in.data(y, x + 1, 1), *point_image_in.data(y, x + 1, 2));
			Eigen::Vector3f obs11(*point_image_in.data(y + 1, x + 1, 0), *point_image_in.data(y + 1, x + 1, 1), *point_image_in.data(y + 1, x + 1, 2));

			Eigen::Vector3f flow00(*flow_image_in.data(y, x, 0), *flow_image_in.data(y, x, 1), *flow_image_in.data(y, x, 2));
			Eigen::Vector3f flow01(*flow_image_in.data(y + 1, x, 0), *flow_image_in.data(y + 1, x, 1), *flow_image_in.data(y + 1, x, 2));
			Eigen::Vector3f flow10(*flow_image_in.data(y, x + 1, 0), *flow_image_in.data(y, x + 1, 1), *flow_image_in.data(y, x + 1, 2));
			Eigen::Vector3f flow11(*flow_image_in.data(y + 1, x + 1, 0), *flow_image_in.data(y + 1, x + 1, 1), *flow_image_in.data(y + 1, x + 1, 2));

			// linear indices of the four pixels
			int idx00 = y * width + x;
			int idx01 = (y + 1) * width + x;
			int idx10 = y * width + (x + 1);
			int idx11 = (y + 1) * width + (x + 1);

			// determine pixel validity
			bool valid00 = obs00.z() > 0 && std::isfinite(flow00.x()) && std::isfinite(flow00.y()) && std::isfinite(flow00.z());
			bool valid01 = obs01.z() > 0 && std::isfinite(flow01.x()) && std::isfinite(flow01.y()) && std::isfinite(flow01.z());
			bool valid10 = obs10.z() > 0 && std::isfinite(flow10.x()) && std::isfinite(flow10.y()) && std::isfinite(flow10.z());
			bool valid11 = obs11.z() > 0 && std::isfinite(flow11.x()) && std::isfinite(flow11.y()) && std::isfinite(flow11.z());

			// region ======= LOWER LEFT TRIANGLE =============
			if (valid00 && valid01 && valid10) {
				float d0 = (obs00 - obs01).norm();
				float d1 = (obs00 - obs10).norm(); // hypotenuse when triangle projected to image plane
				float d2 = (obs01 - obs10).norm();

				if (d0 <= max_triangle_edge_distance && d1 <= max_triangle_edge_distance && d2 <= max_triangle_edge_distance) {
					int vertex_index_0 = pixel_to_vertex_index_map[idx00];
					int vertex_index_1 = pixel_to_vertex_index_map[idx01];
					int vertex_index_2 = pixel_to_vertex_index_map[idx10];

					if (vertex_index_0 == -1) {
						vertex_index_0 = vertex_index;
						pixel_to_vertex_index_map[idx00] = vertex_index;
						vertices.push_back(obs00);
						flows.push_back(flow00);
						pixels.emplace_back(x, y);
						vertex_index++;
					}
					if (vertex_index_1 == -1) {
						vertex_index_1 = vertex_index;
						pixel_to_vertex_index_map[idx01] = vertex_index;
						vertices.push_back(obs01);
						flows.push_back(flow01);
						pixels.emplace_back(x, y + 1);
						vertex_index++;
					}
					if (vertex_index_2 == -1) {
						vertex_index_2 = vertex_index;
						pixel_to_vertex_index_map[idx10] = vertex_index;
						vertices.push_back(obs10);
						flows.push_back(flow10);
						pixels.emplace_back(x + 1, y);
						vertex_index++;
					}

					faces.emplace_back(vertex_index_0, vertex_index_1, vertex_index_2);
				}
			}
			// endregion
			// region ======= UPPER RIGHT TRIANGLE ==================
			if (valid01 && valid10 && valid11) {
				float d0 = (obs10 - obs01).norm();
				float d1 = (obs10 - obs11).norm();
				float d2 = (obs01 - obs11).norm();

				if (d0 <= max_triangle_edge_distance && d1 <= max_triangle_edge_distance && d2 <= max_triangle_edge_distance) {
					int vertex_index_0 = pixel_to_vertex_index_map[idx11];
					int vertex_index_1 = pixel_to_vertex_index_map[idx10];
					int vertex_index_2 = pixel_to_vertex_index_map[idx01];

					if (vertex_index_0 == -1) {
						vertex_index_0 = vertex_index;
						pixel_to_vertex_index_map[idx11] = vertex_index;
						vertices.push_back(obs11);
						flows.push_back(flow11);
						pixels.emplace_back(x + 1, y + 1);
						vertex_index++;
					}
					if (vertex_index_1 == -1) {
						vertex_index_1 = vertex_index;
						pixel_to_vertex_index_map[idx10] = vertex_index;
						vertices.push_back(obs10);
						flows.push_back(flow10);
						pixels.emplace_back(x + 1, y);
						vertex_index++;
					}
					if (vertex_index_2 == -1) {
						vertex_index_2 = vertex_index;
						pixel_to_vertex_index_map[idx01] = vertex_index;
						vertices.push_back(obs01);
						flows.push_back(flow01);
						pixels.emplace_back(x, y + 1);
						vertex_index++;
					}

					faces.emplace_back(vertex_index_0, vertex_index_1, vertex_index_2);
				}
			}
			// endregion
		}
	}

	// Convert to numpy array.
	int vertex_count = vertices.size();
	int face_count = faces.size();

	if (vertex_count > 0 && face_count > 0) {
		// Reference check should be set to false otherwise there is a runtime
		// error. Check why that is the case.
		vertex_positions_out.resize({vertex_count, 3}, false);
		vertex_flows_out.resize({vertex_count, 3}, false);
		vertex_pixels_out.resize({vertex_count, 2}, false);
		face_indices_out.resize({face_count, 3}, false);

		for (int i = 0; i < vertex_count; i++) {
			*vertex_positions_out.mutable_data(i, 0) = vertices[i].x();
			*vertex_positions_out.mutable_data(i, 1) = vertices[i].y();
			*vertex_positions_out.mutable_data(i, 2) = vertices[i].z();

			*vertex_flows_out.mutable_data(i, 0) = flows[i].x();
			*vertex_flows_out.mutable_data(i, 1) = flows[i].y();
			*vertex_flows_out.mutable_data(i, 2) = flows[i].z();

			*vertex_pixels_out.mutable_data(i, 0) = pixels[i].x();
			*vertex_pixels_out.mutable_data(i, 1) = pixels[i].y();
		}

		for (int i = 0; i < face_count; i++) {
			*face_indices_out.mutable_data(i, 0) = faces[i].x();
			*face_indices_out.mutable_data(i, 1) = faces[i].y();
			*face_indices_out.mutable_data(i, 2) = faces[i].z();
		}
	}
}

py::tuple compute_mesh_from_depth_and_flow(const py::array_t<float>& point_image_in, const py::array_t<float>& flow_image_in,
                                           float max_triangle_edge_distance) {
	py::array_t<float> vertex_positions_out;
	py::array_t<float> vertex_flows_out;
	py::array_t<int> vertex_pixels_out;
	py::array_t<int> face_indices_out;
	compute_mesh_from_depth_and_flow(point_image_in, flow_image_in, max_triangle_edge_distance, vertex_positions_out, vertex_flows_out, vertex_pixels_out, face_indices_out);
	return py::make_tuple(vertex_positions_out, vertex_flows_out, vertex_pixels_out, face_indices_out);
}

void filter_depth(py::array_t<unsigned short>& depth_image_in, py::array_t<unsigned short>& depth_image_out, int radius) {
	assert(depth_image_in.ndim() == 2);
	assert(depth_image_out.ndim() == 2);
	unsigned kernel_size = 2 * radius + 1;
	unsigned window_size = kernel_size * kernel_size;

	int width = depth_image_in.shape(1);
	int height = depth_image_in.shape(0);
	assert(depth_image_out.shape(0) == height);
	assert(depth_image_out.shape(1) == width);

	// #pragma omp parallel for
	for (int y = 0; y < height; y++) {
		for (int x = 0; x < width; x++) {
			// Get all residuals in the median window.
			int x_min = std::max(x - radius, 0);
			int x_max = std::min(x + radius, int(width) - 1);
			int y_min = std::max(y - radius, 0);
			int y_max = std::min(y + radius, int(height) - 1);

			std::vector<unsigned short> window_values;
			window_values.reserve(window_size);

			for (int y_near = y_min; y_near <= y_max; y_near++) {
				for (int x_near = x_min; x_near <= x_max; x_near++) {
					unsigned short depth = *depth_image_in.data(y_near, x_near);
					if (depth > 0) {
						window_values.push_back(depth);
					}
				}
			}

			// Sort the residuals and pick the median as the middle element.
			unsigned element_count = window_values.size();
			std::sort(window_values.begin(), window_values.end());

			unsigned middle_index = std::floor(element_count / 2);
			unsigned short median = window_values[middle_index];

			// Write out the median value.
			*depth_image_out.mutable_data(y, x) = median;
		}
	}
}

py::array_t<unsigned short> filter_depth(py::array_t<unsigned short>& depth_image_in, int radius) {
	py::array_t<unsigned short> depth_image_out({depth_image_in.shape(0), depth_image_in.shape(1)});
	memset(depth_image_out.mutable_data(0, 0), 0, depth_image_out.size() * sizeof(unsigned short));
	filter_depth(depth_image_in, depth_image_out, radius);
	return depth_image_out;
}

py::array_t<float> warp_flow(const py::array_t<float>& image, const py::array_t<float>& flow, const py::array_t<float>& mask) {
	// We assume:
	//      image shape (3, h, w)
	//      flow shape  (2, h, w)
	//      mask shape  (2, h, w)

	int width = image.shape(2);
	int height = image.shape(1);

	py::array_t<float> imageWarped = py::array_t<float>({3, height, width});
	py::array_t<float> weightsWarped = py::array_t<float>({1, height, width});

	// Initialize to zero.
	for (int v = 0; v < height; v++) {
		for (int u = 0; u < width; u++) {
			*imageWarped.mutable_data(0, v, u) = 0.0;
			*imageWarped.mutable_data(1, v, u) = 0.0;
			*imageWarped.mutable_data(2, v, u) = 0.0;
			*weightsWarped.mutable_data(0, v, u) = 0.0;
		}
	}

	// Compute image residuals and interpolation weights.
	for (int v = 0; v < height; v++) {
		for (int u = 0; u < width; u++) {
			// Check if pixel is inside the mask.
			if (*mask.data(0, v, u) <= 0 || *mask.data(1, v, u) <= 0) continue;

			// Compute the warped pixel.
			float u_warped = static_cast<float>(u) + *flow.data(0, v, u);
			float v_warped = static_cast<float>(v) + *flow.data(1, v, u);

			int u0 = std::floor(u_warped);
			int u1 = u0 + 1;
			int v0 = std::floor(v_warped);
			int v1 = v0 + 1;

			if (u0 < 0 || u1 >= width || v0 < 0 || v1 >= height) continue;

			// Interpolate the color contributions.
			float du = u_warped - u0;
			float dv = v_warped - v0;

			float w00 = (1 - du) * (1 - dv);
			float w01 = (1 - du) * dv;
			float w10 = du * (1 - dv);
			float w11 = du * dv;

			float c0 = *image.data(0, v, u);
			float c1 = *image.data(1, v, u);
			float c2 = *image.data(2, v, u);

			*imageWarped.mutable_data(0, v0, u0) += w00 * c0;
			*imageWarped.mutable_data(1, v0, u0) += w00 * c1;
			*imageWarped.mutable_data(2, v0, u0) += w00 * c2;
			*imageWarped.mutable_data(0, v1, u0) += w01 * c0;
			*imageWarped.mutable_data(1, v1, u0) += w01 * c1;
			*imageWarped.mutable_data(2, v1, u0) += w01 * c2;
			*imageWarped.mutable_data(0, v0, u1) += w10 * c0;
			*imageWarped.mutable_data(1, v0, u1) += w10 * c1;
			*imageWarped.mutable_data(2, v0, u1) += w10 * c2;
			*imageWarped.mutable_data(0, v1, u1) += w11 * c0;
			*imageWarped.mutable_data(1, v1, u1) += w11 * c1;
			*imageWarped.mutable_data(2, v1, u1) += w11 * c2;

			*weightsWarped.mutable_data(0, v0, u0) += w00;
			*weightsWarped.mutable_data(0, v1, u0) += w01;
			*weightsWarped.mutable_data(0, v0, u1) += w10;
			*weightsWarped.mutable_data(0, v1, u1) += w11;
		}
	}

	// Normalize image.
	for (int v = 0; v < height; v++) {
		for (int u = 0; u < width; u++) {
			float w = *weightsWarped.data(0, v, u);
			if (w > 0) {
				*imageWarped.mutable_data(0, v, u) /= w;
				*imageWarped.mutable_data(1, v, u) /= w;
				*imageWarped.mutable_data(2, v, u) /= w;
			} else {
				*imageWarped.mutable_data(0, v, u) = 1.0;
				*imageWarped.mutable_data(1, v, u) = 1.0;
				*imageWarped.mutable_data(2, v, u) = 1.0;
			}
		}
	}

	return imageWarped;
}

py::array_t<float> warp_rigid(
		const py::array_t<float>& rgbxyz_image,
		const py::array_t<float>& rotation,
		const py::array_t<float>& translation,
		float fx, float fy, float cx, float cy
) {
	// We assume:
	//      rgbd shape (6, h, w)
	//      rotation shape  (9)
	//      translation shape  (2)

	int width = rgbxyz_image.shape(2);
	int height = rgbxyz_image.shape(1);

	float r00 = *rotation.data(0);
	float r01 = *rotation.data(1);
	float r02 = *rotation.data(2);
	float r10 = *rotation.data(3);
	float r11 = *rotation.data(4);
	float r12 = *rotation.data(5);
	float r20 = *rotation.data(6);
	float r21 = *rotation.data(7);
	float r22 = *rotation.data(8);
	float t0 = *translation.data(0);
	float t1 = *translation.data(1);
	float t2 = *translation.data(2);

	py::array_t<float> image_warped = py::array_t<float>({3, height, width});
	py::array_t<float> weights_warped = py::array_t<float>({1, height, width});

	// Initialize to zero.
	for (int v = 0; v < height; v++) {
		for (int u = 0; u < width; u++) {
			*image_warped.mutable_data(0, v, u) = 0.0;
			*image_warped.mutable_data(1, v, u) = 0.0;
			*image_warped.mutable_data(2, v, u) = 0.0;
			*weights_warped.mutable_data(0, v, u) = 0.0;
		}
	}

	// Compute image residuals and interpolation weights.
	for (int v = 0; v < height; v++) {
		for (int u = 0; u < width; u++) {
			// Compute the warped pixel.
			float x = *rgbxyz_image.data(3, v, u);
			float y = *rgbxyz_image.data(4, v, u);
			float z = *rgbxyz_image.data(5, v, u);
			if (z <= 0) continue;

			float x_def = r00 * x + r01 * y + r02 * z + t0;
			float y_def = r10 * x + r11 * y + r12 * z + t1;
			float z_def = r20 * x + r21 * y + r22 * z + t2;
			if (z_def <= 0) continue;

			float u_warped = fx * x_def / z_def + cx;
			float v_warped = fy * y_def / z_def + cy;

			int u0 = std::floor(u_warped);
			int u1 = u0 + 1;
			int v0 = std::floor(v_warped);
			int v1 = v0 + 1;

			if (u0 < 0 || u1 >= width || v0 < 0 || v1 >= height) continue;

			// Interpolate the color contributions.
			float du = u_warped - u0;
			float dv = v_warped - v0;

			float w00 = (1 - du) * (1 - dv);
			float w01 = (1 - du) * dv;
			float w10 = du * (1 - dv);
			float w11 = du * dv;

			float c0 = *rgbxyz_image.data(0, v, u);
			float c1 = *rgbxyz_image.data(1, v, u);
			float c2 = *rgbxyz_image.data(2, v, u);

			*image_warped.mutable_data(0, v0, u0) += w00 * c0;
			*image_warped.mutable_data(1, v0, u0) += w00 * c1;
			*image_warped.mutable_data(2, v0, u0) += w00 * c2;
			*image_warped.mutable_data(0, v1, u0) += w01 * c0;
			*image_warped.mutable_data(1, v1, u0) += w01 * c1;
			*image_warped.mutable_data(2, v1, u0) += w01 * c2;
			*image_warped.mutable_data(0, v0, u1) += w10 * c0;
			*image_warped.mutable_data(1, v0, u1) += w10 * c1;
			*image_warped.mutable_data(2, v0, u1) += w10 * c2;
			*image_warped.mutable_data(0, v1, u1) += w11 * c0;
			*image_warped.mutable_data(1, v1, u1) += w11 * c1;
			*image_warped.mutable_data(2, v1, u1) += w11 * c2;

			*weights_warped.mutable_data(0, v0, u0) += w00;
			*weights_warped.mutable_data(0, v1, u0) += w01;
			*weights_warped.mutable_data(0, v0, u1) += w10;
			*weights_warped.mutable_data(0, v1, u1) += w11;
		}
	}

	// Normalize image.
	for (int v = 0; v < height; v++) {
		for (int u = 0; u < width; u++) {
			float w = *weights_warped.data(0, v, u);
			if (w > 0) {
				*image_warped.mutable_data(0, v, u) /= w;
				*image_warped.mutable_data(1, v, u) /= w;
				*image_warped.mutable_data(2, v, u) /= w;
			} else {
				*image_warped.mutable_data(0, v, u) = 1.0;
				*image_warped.mutable_data(1, v, u) = 1.0;
				*image_warped.mutable_data(2, v, u) = 1.0;
			}
		}
	}

	return image_warped;
}

py::array_t<float>
warp_3d(const py::array_t<float>& rgbxyz_image, const py::array_t<float>& points,
        const py::array_t<int>& mask, float fx, float fy, float cx, float cy) {

	// We assume:
	//      image shape             (6, h, w)
	//      points shape            (3, h, w)
	//      mask shape    (h, w)

	int width = rgbxyz_image.shape(2);
	int height = rgbxyz_image.shape(1);

	py::array_t<float> image_warped = py::array_t<float>({3, height, width});
	py::array_t<float> weights_warped = py::array_t<float>({1, height, width});

	// Initialize to zero.
	for (int v = 0; v < height; v++) {
		for (int u = 0; u < width; u++) {
			*image_warped.mutable_data(0, v, u) = 0.0;
			*image_warped.mutable_data(1, v, u) = 0.0;
			*image_warped.mutable_data(2, v, u) = 0.0;
			*weights_warped.mutable_data(0, v, u) = 0.0;
		}
	}

	// Compute image residuals and interpolation weights.
	for (int v = 0; v < height; v++) {
		for (int u = 0; u < width; u++) {
			// Compute the warped pixel.
			if (*mask.data(v, u) <= 0) continue;

			float z = *rgbxyz_image.data(5, v, u);
			if (z <= 0) continue;

			float x_def = *points.data(0, v, u);
			float y_def = *points.data(1, v, u);
			float z_def = *points.data(2, v, u);
			if (z_def <= 0) continue;

			float u_warped = fx * x_def / z_def + cx;
			float v_warped = fy * y_def / z_def + cy;

			int u0 = std::floor(u_warped);
			int u1 = u0 + 1;
			int v0 = std::floor(v_warped);
			int v1 = v0 + 1;

			if (u0 < 0 || u1 >= width || v0 < 0 || v1 >= height) continue;

			// Interpolate the color contributions.
			float du = u_warped - u0;
			float dv = v_warped - v0;

			float w00 = (1 - du) * (1 - dv);
			float w01 = (1 - du) * dv;
			float w10 = du * (1 - dv);
			float w11 = du * dv;

			float c0 = *rgbxyz_image.data(0, v, u);
			float c1 = *rgbxyz_image.data(1, v, u);
			float c2 = *rgbxyz_image.data(2, v, u);

			*image_warped.mutable_data(0, v0, u0) += w00 * c0;
			*image_warped.mutable_data(1, v0, u0) += w00 * c1;
			*image_warped.mutable_data(2, v0, u0) += w00 * c2;
			*image_warped.mutable_data(0, v1, u0) += w01 * c0;
			*image_warped.mutable_data(1, v1, u0) += w01 * c1;
			*image_warped.mutable_data(2, v1, u0) += w01 * c2;
			*image_warped.mutable_data(0, v0, u1) += w10 * c0;
			*image_warped.mutable_data(1, v0, u1) += w10 * c1;
			*image_warped.mutable_data(2, v0, u1) += w10 * c2;
			*image_warped.mutable_data(0, v1, u1) += w11 * c0;
			*image_warped.mutable_data(1, v1, u1) += w11 * c1;
			*image_warped.mutable_data(2, v1, u1) += w11 * c2;

			*weights_warped.mutable_data(0, v0, u0) += w00;
			*weights_warped.mutable_data(0, v1, u0) += w01;
			*weights_warped.mutable_data(0, v0, u1) += w10;
			*weights_warped.mutable_data(0, v1, u1) += w11;
		}
	}

	// Normalize image.
	for (int v = 0; v < height; v++) {
		for (int u = 0; u < width; u++) {
			float w = *weights_warped.data(0, v, u);
			if (w > 0) {
				*image_warped.mutable_data(0, v, u) /= w;
				*image_warped.mutable_data(1, v, u) /= w;
				*image_warped.mutable_data(2, v, u) /= w;
			} else {
				*image_warped.mutable_data(0, v, u) = 1.0;
				*image_warped.mutable_data(1, v, u) = 1.0;
				*image_warped.mutable_data(2, v, u) = 1.0;
			}
		}
	}

	return image_warped;
}

} //namespace image_proc

max_stars_repo_name: Algomorph/NeuralTracking

max_issues_repo_name: Algomorph/NeuralTracking

max_forks_repo_name: Algomorph/NeuralTracking


text:
#define CATCH_CONFIG_MAIN
#include "../../vendor/catch/catch.hpp"

#include "filesystem.hpp"
#include "manipulator/details/basic.hpp"
#include "manipulator/details/types.hpp"
#include "thread_utility.hpp"
#include <boost/optional/optional_io.hpp>

namespace {
krbn::modifier_flag_manager::active_modifier_flag left_command_1(krbn::modifier_flag_manager::active_modifier_flag::type::increase,
                                                                 krbn::modifier_flag::left_command,
                                                                 krbn::device_id(1));
krbn::modifier_flag_manager::active_modifier_flag left_control_1(krbn::modifier_flag_manager::active_modifier_flag::type::increase,
                                                                 krbn::modifier_flag::left_control,
                                                                 krbn::device_id(1));
krbn::modifier_flag_manager::active_modifier_flag left_option_1(krbn::modifier_flag_manager::active_modifier_flag::type::increase,
                                                                krbn::modifier_flag::left_option,
                                                                krbn::device_id(1));
krbn::modifier_flag_manager::active_modifier_flag left_shift_1(krbn::modifier_flag_manager::active_modifier_flag::type::increase,
                                                               krbn::modifier_flag::left_shift,
                                                               krbn::device_id(1));
krbn::modifier_flag_manager::active_modifier_flag right_shift_1(krbn::modifier_flag_manager::active_modifier_flag::type::increase,
                                                                krbn::modifier_flag::right_shift,
                                                                krbn::device_id(1));

void set_file_logger(const std::string& file_path) {
  unlink(file_path.c_str());
  auto l = spdlog::rotating_logger_mt(file_path, file_path, 256 * 1024, 3);
  l->set_pattern("[%l] %v");
  krbn::logger::set_logger(l);
}

void set_null_logger(void) {
  static auto l = spdlog::stdout_logger_mt("null");
  l->flush_on(spdlog::level::off);
  krbn::logger::set_logger(l);
}
} // namespace

TEST_CASE("initialize") {
  krbn::thread_utility::register_main_thread();
}

TEST_CASE("modifier_definition.test_modifier") {
  using krbn::manipulator::details::basic;
  using krbn::manipulator::details::event_definition;
  using krbn::manipulator::details::modifier_definition;

  {
    krbn::modifier_flag_manager modifier_flag_manager;
    modifier_flag_manager.push_back_active_modifier_flag(left_command_1);

    {
      auto actual = basic::from_event_definition::test_modifier(modifier_flag_manager, modifier_definition::modifier::left_shift);
      auto expected = std::make_pair(false, krbn::modifier_flag::zero);
      REQUIRE(actual == expected);
    }
    {
      auto actual = basic::from_event_definition::test_modifier(modifier_flag_manager, modifier_definition::modifier::left_command);
      auto expected = std::make_pair(true, krbn::modifier_flag::left_command);
      REQUIRE(actual == expected);
    }
    {
      auto actual = basic::from_event_definition::test_modifier(modifier_flag_manager, modifier_definition::modifier::right_command);
      auto expected = std::make_pair(false, krbn::modifier_flag::zero);
      REQUIRE(actual == expected);
    }
    {
      auto actual = basic::from_event_definition::test_modifier(modifier_flag_manager, modifier_definition::modifier::command);
      auto expected = std::make_pair(true, krbn::modifier_flag::left_command);
      REQUIRE(actual == expected);
    }
    {
      auto actual = basic::from_event_definition::test_modifier(modifier_flag_manager, modifier_definition::modifier::shift);
      auto expected = std::make_pair(false, krbn::modifier_flag::zero);
      REQUIRE(actual == expected);
    }
  }
  {
    krbn::modifier_flag_manager modifier_flag_manager;
    modifier_flag_manager.push_back_active_modifier_flag(right_shift_1);

    {
      auto actual = basic::from_event_definition::test_modifier(modifier_flag_manager, modifier_definition::modifier::left_shift);
      auto expected = std::make_pair(false, krbn::modifier_flag::zero);
      REQUIRE(actual == expected);
    }
    {
      auto actual = basic::from_event_definition::test_modifier(modifier_flag_manager, modifier_definition::modifier::right_shift);
      auto expected = std::make_pair(true, krbn::modifier_flag::right_shift);
      REQUIRE(actual == expected);
    }
    {
      auto actual = basic::from_event_definition::test_modifier(modifier_flag_manager, modifier_definition::modifier::shift);
      auto expected = std::make_pair(true, krbn::modifier_flag::right_shift);
      REQUIRE(actual == expected);
    }
  }
}

TEST_CASE("from_event_definition.test_modifiers") {
  using krbn::manipulator::details::basic;
  using krbn::manipulator::details::event_definition;
  using krbn::manipulator::details::modifier_definition;

  // empty

  {
    basic::from_event_definition event_definition(nlohmann::json::object({
        {"key_code", "spacebar"},
    }));

    REQUIRE(event_definition.get_event_definitions().size() == 1);
    REQUIRE(event_definition.get_event_definitions().front().get_key_code() == krbn::key_code::spacebar);
    REQUIRE(event_definition.get_mandatory_modifiers() == std::unordered_set<modifier_definition::modifier>{});
    REQUIRE(event_definition.get_optional_modifiers() == std::unordered_set<modifier_definition::modifier>{});

    {
      krbn::modifier_flag_manager modifier_flag_manager;
      REQUIRE(event_definition.test_modifiers(modifier_flag_manager) == std::unordered_set<krbn::modifier_flag>({}));
    }
    {
      krbn::modifier_flag_manager modifier_flag_manager;
      modifier_flag_manager.push_back_active_modifier_flag(left_shift_1);
      REQUIRE(event_definition.test_modifiers(modifier_flag_manager) == boost::none);
    }
  }

  // empty (modifier key)

  {
    basic::from_event_definition event_definition(nlohmann::json::object({
        {"key_code", "left_shift"},
    }));

    REQUIRE(event_definition.get_event_definitions().size() == 1);
    REQUIRE(event_definition.get_event_definitions().front().get_key_code() == krbn::key_code::left_shift);
    REQUIRE(event_definition.get_mandatory_modifiers() == std::unordered_set<modifier_definition::modifier>{});
    REQUIRE(event_definition.get_optional_modifiers() == std::unordered_set<modifier_definition::modifier>{});

    {
      krbn::modifier_flag_manager modifier_flag_manager;
      REQUIRE(event_definition.test_modifiers(modifier_flag_manager) == std::unordered_set<krbn::modifier_flag>({}));
    }
    {
      krbn::modifier_flag_manager modifier_flag_manager;
      modifier_flag_manager.push_back_active_modifier_flag(left_shift_1);
      REQUIRE(event_definition.test_modifiers(modifier_flag_manager) == boost::none);
    }
  }

  // mandatory_modifiers any

  {
    basic::from_event_definition event_definition(nlohmann::json::object({
        {"key_code", "spacebar"},
        {"modifiers", nlohmann::json::object({
                          {"mandatory", nlohmann::json::array({"any"})},
                      })},
    }));

    REQUIRE(event_definition.get_event_definitions().size() == 1);
    REQUIRE(event_definition.get_event_definitions().front().get_key_code() == krbn::key_code::spacebar);
    REQUIRE(event_definition.get_mandatory_modifiers() == std::unordered_set<modifier_definition::modifier>{modifier_definition::modifier::any});
    REQUIRE(event_definition.get_optional_modifiers() == std::unordered_set<modifier_definition::modifier>{});

    {
      krbn::modifier_flag_manager modifier_flag_manager;
      REQUIRE(event_definition.test_modifiers(modifier_flag_manager) == std::unordered_set<krbn::modifier_flag>({}));
    }
    {
      krbn::modifier_flag_manager modifier_flag_manager;
      modifier_flag_manager.push_back_active_modifier_flag(left_shift_1);
      REQUIRE(event_definition.test_modifiers(modifier_flag_manager) == std::unordered_set<krbn::modifier_flag>({
                                                                            krbn::modifier_flag::left_shift,
                                                                        }));
    }
    {
      krbn::modifier_flag_manager modifier_flag_manager;
      modifier_flag_manager.push_back_active_modifier_flag(left_command_1);
      modifier_flag_manager.push_back_active_modifier_flag(left_shift_1);
      REQUIRE(event_definition.test_modifiers(modifier_flag_manager) == std::unordered_set<krbn::modifier_flag>({
                                                                            krbn::modifier_flag::left_command,
                                                                            krbn::modifier_flag::left_shift,
                                                                        }));
    }
  }

  // optional_modifiers any

  {
    basic::from_event_definition event_definition(nlohmann::json::object({
        {"key_code", "spacebar"},
        {"modifiers", nlohmann::json::object({
                          {"optional", nlohmann::json::array({"any"})},
                      })},
    }));

    REQUIRE(event_definition.get_event_definitions().size() == 1);
    REQUIRE(event_definition.get_event_definitions().front().get_key_code() == krbn::key_code::spacebar);
    REQUIRE(event_definition.get_mandatory_modifiers() == std::unordered_set<modifier_definition::modifier>{});
    REQUIRE(event_definition.get_optional_modifiers() == std::unordered_set<modifier_definition::modifier>{modifier_definition::modifier::any});

    {
      krbn::modifier_flag_manager modifier_flag_manager;
      REQUIRE(event_definition.test_modifiers(modifier_flag_manager) == std::unordered_set<krbn::modifier_flag>({}));
    }
    {
      krbn::modifier_flag_manager modifier_flag_manager;
      modifier_flag_manager.push_back_active_modifier_flag(left_shift_1);
      REQUIRE(event_definition.test_modifiers(modifier_flag_manager) == std::unordered_set<krbn::modifier_flag>({}));
    }
    {
      krbn::modifier_flag_manager modifier_flag_manager;
      modifier_flag_manager.push_back_active_modifier_flag(left_command_1);
      modifier_flag_manager.push_back_active_modifier_flag(left_shift_1);
      REQUIRE(event_definition.test_modifiers(modifier_flag_manager) == std::unordered_set<krbn::modifier_flag>({}));
    }
  }

  // mandatory_modifiers and optional_modifiers

  {
    basic::from_event_definition event_definition(nlohmann::json::object({
        {"key_code", "p"},
        {"modifiers", nlohmann::json::object({
                          {"mandatory", nlohmann::json::array({"control"})},
                          {"optional", nlohmann::json::array({"shift"})},
                      })},
    }));

    REQUIRE(event_definition.get_event_definitions().size() == 1);
    REQUIRE(event_definition.get_event_definitions().front().get_key_code() == krbn::key_code::p);
    REQUIRE(event_definition.get_mandatory_modifiers() == std::unordered_set<modifier_definition::modifier>{modifier_definition::modifier::control});
    REQUIRE(event_definition.get_optional_modifiers() == std::unordered_set<modifier_definition::modifier>{modifier_definition::modifier::shift});

    {
      krbn::modifier_flag_manager modifier_flag_manager;
      REQUIRE(event_definition.test_modifiers(modifier_flag_manager) == boost::none);
    }
    {
      krbn::modifier_flag_manager modifier_flag_manager;
      modifier_flag_manager.push_back_active_modifier_flag(left_control_1);
      REQUIRE(event_definition.test_modifiers(modifier_flag_manager) == std::unordered_set<krbn::modifier_flag>({
                                                                            krbn::modifier_flag::left_control,
                                                                        }));
    }
    {
      krbn::modifier_flag_manager modifier_flag_manager;
      modifier_flag_manager.push_back_active_modifier_flag(left_control_1);
      modifier_flag_manager.push_back_active_modifier_flag(left_shift_1);
      REQUIRE(event_definition.test_modifiers(modifier_flag_manager) == std::unordered_set<krbn::modifier_flag>({
                                                                            krbn::modifier_flag::left_control,
                                                                        }));
    }
    {
      krbn::modifier_flag_manager modifier_flag_manager;
      modifier_flag_manager.push_back_active_modifier_flag(left_control_1);
      modifier_flag_manager.push_back_active_modifier_flag(left_option_1);
      REQUIRE(event_definition.test_modifiers(modifier_flag_manager) == boost::none);
    }
  }

  // optional_modifiers any with mandatory_modifiers

  {
    basic::from_event_definition event_definition(nlohmann::json::object({
        {"key_code", "spacebar"},
        {"modifiers", nlohmann::json::object({
                          {"mandatory", nlohmann::json::array({"left_shift"})},
                          {"optional", nlohmann::json::array({"any"})},
                      })},
    }));

    REQUIRE(event_definition.get_event_definitions().size() == 1);
    REQUIRE(event_definition.get_event_definitions().front().get_key_code() == krbn::key_code::spacebar);
    REQUIRE(event_definition.get_mandatory_modifiers() == std::unordered_set<modifier_definition::modifier>{modifier_definition::modifier::left_shift});
    REQUIRE(event_definition.get_optional_modifiers() == std::unordered_set<modifier_definition::modifier>{modifier_definition::modifier::any});

    {
      krbn::modifier_flag_manager modifier_flag_manager;
      REQUIRE(event_definition.test_modifiers(modifier_flag_manager) == boost::none);
    }
    {
      krbn::modifier_flag_manager modifier_flag_manager;
      modifier_flag_manager.push_back_active_modifier_flag(left_shift_1);
      REQUIRE(event_definition.test_modifiers(modifier_flag_manager) == std::unordered_set<krbn::modifier_flag>({
                                                                            krbn::modifier_flag::left_shift,
                                                                        }));
    }
    {
      krbn::modifier_flag_manager modifier_flag_manager;
      modifier_flag_manager.push_back_active_modifier_flag(left_command_1);
      modifier_flag_manager.push_back_active_modifier_flag(left_shift_1);
      REQUIRE(event_definition.test_modifiers(modifier_flag_manager) == std::unordered_set<krbn::modifier_flag>({
                                                                            krbn::modifier_flag::left_shift,
                                                                        }));
    }
  }

  // mandatory_modifiers strict matching

  {
    basic::from_event_definition event_definition(nlohmann::json::object({
        {"key_code", "spacebar"},
        {"modifiers", nlohmann::json::object({
                          {"mandatory", nlohmann::json::array({"left_shift"})},
                      })},
    }));

    REQUIRE(event_definition.get_event_definitions().size() == 1);
    REQUIRE(event_definition.get_event_definitions().front().get_key_code() == krbn::key_code::spacebar);
    REQUIRE(event_definition.get_mandatory_modifiers() == std::unordered_set<modifier_definition::modifier>{modifier_definition::modifier::left_shift});
    REQUIRE(event_definition.get_optional_modifiers() == std::unordered_set<modifier_definition::modifier>{});

    {
      krbn::modifier_flag_manager modifier_flag_manager;
      modifier_flag_manager.push_back_active_modifier_flag(left_command_1);
      REQUIRE(event_definition.test_modifiers(modifier_flag_manager) == boost::none);
    }
    {
      krbn::modifier_flag_manager modifier_flag_manager;
      modifier_flag_manager.push_back_active_modifier_flag(left_shift_1);
      REQUIRE(event_definition.test_modifiers(modifier_flag_manager) == std::unordered_set<krbn::modifier_flag>({
                                                                            krbn::modifier_flag::left_shift,
                                                                        }));
    }
    {
      krbn::modifier_flag_manager modifier_flag_manager;
      modifier_flag_manager.push_back_active_modifier_flag(left_shift_1);
      modifier_flag_manager.push_back_active_modifier_flag(right_shift_1);
      REQUIRE(event_definition.test_modifiers(modifier_flag_manager) == boost::none);
    }
  }

  // mandatory_modifiers (modifier::shift)

  {
    basic::from_event_definition event_definition(nlohmann::json::object({
        {"key_code", "spacebar"},
        {"modifiers", nlohmann::json::object({
                          {"mandatory", nlohmann::json::array({"shift"})},
                          {"optional", nlohmann::json::array({"any"})},
                      })},
    }));

    REQUIRE(event_definition.get_event_definitions().size() == 1);
    REQUIRE(event_definition.get_event_definitions().front().get_key_code() == krbn::key_code::spacebar);
    REQUIRE(event_definition.get_mandatory_modifiers() == std::unordered_set<modifier_definition::modifier>{modifier_definition::modifier::shift});
    REQUIRE(event_definition.get_optional_modifiers() == std::unordered_set<modifier_definition::modifier>{modifier_definition::modifier::any});

    {
      krbn::modifier_flag_manager modifier_flag_manager;
      modifier_flag_manager.push_back_active_modifier_flag(left_command_1);
      REQUIRE(event_definition.test_modifiers(modifier_flag_manager) == boost::none);
    }
    {
      krbn::modifier_flag_manager modifier_flag_manager;
      modifier_flag_manager.push_back_active_modifier_flag(left_shift_1);
      REQUIRE(event_definition.test_modifiers(modifier_flag_manager) == std::unordered_set<krbn::modifier_flag>({
                                                                            krbn::modifier_flag::left_shift,
                                                                        }));
    }
    {
      krbn::modifier_flag_manager modifier_flag_manager;
      modifier_flag_manager.push_back_active_modifier_flag(right_shift_1);
      REQUIRE(event_definition.test_modifiers(modifier_flag_manager) == std::unordered_set<krbn::modifier_flag>({
                                                                            krbn::modifier_flag::right_shift,
                                                                        }));
    }
    {
      krbn::modifier_flag_manager modifier_flag_manager;
      modifier_flag_manager.push_back_active_modifier_flag(left_shift_1);
      modifier_flag_manager.push_back_active_modifier_flag(right_shift_1);
      REQUIRE(event_definition.test_modifiers(modifier_flag_manager) == std::unordered_set<krbn::modifier_flag>({
                                                                            krbn::modifier_flag::left_shift,
                                                                        }));
    }
    {
      krbn::modifier_flag_manager modifier_flag_manager;
      modifier_flag_manager.push_back_active_modifier_flag(left_command_1);
      modifier_flag_manager.push_back_active_modifier_flag(left_shift_1);
      modifier_flag_manager.push_back_active_modifier_flag(right_shift_1);
      REQUIRE(event_definition.test_modifiers(modifier_flag_manager) == std::unordered_set<krbn::modifier_flag>({
                                                                            krbn::modifier_flag::left_shift,
                                                                        }));
    }
  }

  // mandatory_modifiers strict matching (modifier::shift)

  {
    basic::from_event_definition event_definition(nlohmann::json::object({
        {"key_code", "spacebar"},
        {"modifiers", nlohmann::json::object({
                          {"mandatory", nlohmann::json::array({"shift"})},
                      })},
    }));

    REQUIRE(event_definition.get_event_definitions().size() == 1);
    REQUIRE(event_definition.get_event_definitions().front().get_key_code() == krbn::key_code::spacebar);
    REQUIRE(event_definition.get_mandatory_modifiers() == std::unordered_set<modifier_definition::modifier>{modifier_definition::modifier::shift});
    REQUIRE(event_definition.get_optional_modifiers() == std::unordered_set<modifier_definition::modifier>{});

    {
      krbn::modifier_flag_manager modifier_flag_manager;
      modifier_flag_manager.push_back_active_modifier_flag(left_command_1);
      REQUIRE(event_definition.test_modifiers(modifier_flag_manager) == boost::none);
    }
    {
      krbn::modifier_flag_manager modifier_flag_manager;
      modifier_flag_manager.push_back_active_modifier_flag(left_shift_1);
      REQUIRE(event_definition.test_modifiers(modifier_flag_manager) == std::unordered_set<krbn::modifier_flag>({
                                                                            krbn::modifier_flag::left_shift,
                                                                        }));
    }
    {
      krbn::modifier_flag_manager modifier_flag_manager;
      modifier_flag_manager.push_back_active_modifier_flag(right_shift_1);
      REQUIRE(event_definition.test_modifiers(modifier_flag_manager) == std::unordered_set<krbn::modifier_flag>({
                                                                            krbn::modifier_flag::right_shift,
                                                                        }));
    }
    {
      krbn::modifier_flag_manager modifier_flag_manager;
      modifier_flag_manager.push_back_active_modifier_flag(left_shift_1);
      modifier_flag_manager.push_back_active_modifier_flag(right_shift_1);
      REQUIRE(event_definition.test_modifiers(modifier_flag_manager) == std::unordered_set<krbn::modifier_flag>({
                                                                            krbn::modifier_flag::left_shift,
                                                                        }));
    }
    {
      krbn::modifier_flag_manager modifier_flag_manager;
      modifier_flag_manager.push_back_active_modifier_flag(left_command_1);
      modifier_flag_manager.push_back_active_modifier_flag(left_shift_1);
      modifier_flag_manager.push_back_active_modifier_flag(right_shift_1);
      REQUIRE(event_definition.test_modifiers(modifier_flag_manager) == boost::none);
    }
  }
}

TEST_CASE("manipulator.details.basic::from_event_definition") {
  using krbn::manipulator::details::basic;

  {
    nlohmann::json json;
    basic::from_event_definition event_definition(json);
    REQUIRE(event_definition.get_event_definitions().size() == 0);
    REQUIRE(event_definition.get_mandatory_modifiers().size() == 0);
    REQUIRE(event_definition.get_optional_modifiers().size() == 0);
  }
  {
    nlohmann::json json({
        {"key_code", "spacebar"},
        {"modifiers", {
                          {"mandatory", {
                                            "shift",
                                            "left_command",
                                        }},
                          {"optional", {
                                           "any",
                                       }},
                      }},
    });
    basic::from_event_definition event_definition(json);
    REQUIRE(event_definition.get_event_definitions().size() == 1);
    REQUIRE(event_definition.get_event_definitions().front().get_type() == krbn::manipulator::details::event_definition::type::key_code);
    REQUIRE(event_definition.get_event_definitions().front().get_key_code() == krbn::key_code::spacebar);
    REQUIRE(event_definition.get_event_definitions().front().get_pointing_button() == boost::none);
    REQUIRE(event_definition.get_mandatory_modifiers() == std::unordered_set<krbn::manipulator::details::modifier_definition::modifier>({
                                                              krbn::manipulator::details::modifier_definition::modifier::shift,
                                                              krbn::manipulator::details::modifier_definition::modifier::left_command,
                                                          }));
    REQUIRE(event_definition.get_optional_modifiers() == std::unordered_set<krbn::manipulator::details::modifier_definition::modifier>({
                                                             krbn::manipulator::details::modifier_definition::modifier::any,
                                                         }));
    REQUIRE(event_definition.get_event_definitions().front().to_event() == krbn::event_queue::queued_event::event(krbn::key_code::spacebar));
  }
  {
    nlohmann::json json({
        {"key_code", "right_option"},
        {"modifiers", {
                          {"mandatory", {
                                            "shift",
                                            "left_command",
                                            // duplicated
                                            "shift",
                                            "left_command",
                                        }},
                          {"optional", {
                                           "any",
                                           // duplicated
                                           "any",
                                       }},
                      }},
    });
    basic::from_event_definition event_definition(json);
    REQUIRE(event_definition.get_event_definitions().size() == 1);
    REQUIRE(event_definition.get_event_definitions().front().get_type() == krbn::manipulator::details::event_definition::type::key_code);
    REQUIRE(event_definition.get_event_definitions().front().get_key_code() == krbn::key_code::right_option);
    REQUIRE(event_definition.get_event_definitions().front().get_pointing_button() == boost::none);
    REQUIRE(event_definition.get_mandatory_modifiers() == std::unordered_set<krbn::manipulator::details::modifier_definition::modifier>({
                                                              krbn::manipulator::details::modifier_definition::modifier::shift,
                                                              krbn::manipulator::details::modifier_definition::modifier::left_command,
                                                          }));
    REQUIRE(event_definition.get_optional_modifiers() == std::unordered_set<krbn::manipulator::details::modifier_definition::modifier>({
                                                             krbn::manipulator::details::modifier_definition::modifier::any,
                                                         }));
  }
  {
    nlohmann::json json({
        // The top level event_definition is ignored if `simultaneous` exists.
        {"key_code", "spacebar"},

        {"simultaneous", nlohmann::json::array({
                             nlohmann::json::object({
                                 {"key_code", "left_shift"},
                             }),
                             nlohmann::json::object({
                                 {"key_code", "right_shift"},
                             }),
                         })},
    });
    basic::from_event_definition event_definition(json);
    REQUIRE(event_definition.get_event_definitions().size() == 2);
    REQUIRE(event_definition.get_event_definitions()[0].get_key_code() == krbn::key_code::left_shift);
    REQUIRE(event_definition.get_event_definitions()[1].get_key_code() == krbn::key_code::right_shift);
  }
  {
    nlohmann::json json({
        {"key_code", nlohmann::json::array()},
        {"modifiers", {
                          {"mandatory", {
                                            "dummy",
                                        }},
                      }},
    });
    basic::from_event_definition event_definition(json);
    REQUIRE(event_definition.get_event_definitions().size() == 0);
    REQUIRE(event_definition.get_mandatory_modifiers().size() == 0);
  }
}

TEST_CASE("event_definition.error_messages") {
  using krbn::manipulator::details::basic;

  set_file_logger("tmp/error_messages.log");

  {
    std::ifstream json_file("json/error_messages.json");
    auto json = nlohmann::json::parse(json_file);
    for (const auto& j : json["from_event_definition"]) {
      basic::from_event_definition from_event_definition(j);
    }
  }

  set_null_logger();
}

TEST_CASE("basic::from_event_definition.test_event") {
  using krbn::manipulator::details::basic;

  {
    basic::from_event_definition d(nlohmann::json::object({}));

    REQUIRE(!basic::from_event_definition::test_event(krbn::event_queue::queued_event::event(krbn::key_code::a), d));
    REQUIRE(!basic::from_event_definition::test_event(krbn::event_queue::queued_event::event(krbn::consumer_key_code::mute), d));
    REQUIRE(!basic::from_event_definition::test_event(krbn::event_queue::queued_event::event(krbn::pointing_button::button1), d));
  }

  {
    basic::from_event_definition d(nlohmann::json::object({
        {"key_code", "spacebar"},
    }));

    REQUIRE(basic::from_event_definition::test_event(krbn::event_queue::queued_event::event(krbn::key_code::spacebar), d));

    REQUIRE(!basic::from_event_definition::test_event(krbn::event_queue::queued_event::event(krbn::key_code::a), d));
    REQUIRE(!basic::from_event_definition::test_event(krbn::event_queue::queued_event::event(krbn::consumer_key_code::mute), d));
    REQUIRE(!basic::from_event_definition::test_event(krbn::event_queue::queued_event::event(krbn::pointing_button::button1), d));
  }

  {
    basic::from_event_definition d(nlohmann::json::object({
        {"consumer_key_code", "rewind"},
    }));

    REQUIRE(basic::from_event_definition::test_event(krbn::event_queue::queued_event::event(krbn::consumer_key_code::rewind), d));

    REQUIRE(!basic::from_event_definition::test_event(krbn::event_queue::queued_event::event(krbn::key_code::a), d));
    REQUIRE(!basic::from_event_definition::test_event(krbn::event_queue::queued_event::event(krbn::consumer_key_code::mute), d));
    REQUIRE(!basic::from_event_definition::test_event(krbn::event_queue::queued_event::event(krbn::pointing_button::button1), d));
  }

  {
    basic::from_event_definition d(nlohmann::json::object({
        {"pointing_button", "button2"},
    }));

    REQUIRE(basic::from_event_definition::test_event(krbn::event_queue::queued_event::event(krbn::pointing_button::button2), d));

    REQUIRE(!basic::from_event_definition::test_event(krbn::event_queue::queued_event::event(krbn::key_code::a), d));
    REQUIRE(!basic::from_event_definition::test_event(krbn::event_queue::queued_event::event(krbn::consumer_key_code::mute), d));
    REQUIRE(!basic::from_event_definition::test_event(krbn::event_queue::queued_event::event(krbn::pointing_button::button1), d));
  }

  {
    basic::from_event_definition d(nlohmann::json::object({
        {"any", "key_code"},
    }));

    REQUIRE(basic::from_event_definition::test_event(krbn::event_queue::queued_event::event(krbn::key_code::a), d));
    REQUIRE(!basic::from_event_definition::test_event(krbn::event_queue::queued_event::event(krbn::consumer_key_code::mute), d));
    REQUIRE(!basic::from_event_definition::test_event(krbn::event_queue::queued_event::event(krbn::pointing_button::button1), d));
  }

  {
    basic::from_event_definition d(nlohmann::json::object({
        {"any", "consumer_key_code"},
    }));

    REQUIRE(!basic::from_event_definition::test_event(krbn::event_queue::queued_event::event(krbn::key_code::a), d));
    REQUIRE(basic::from_event_definition::test_event(krbn::event_queue::queued_event::event(krbn::consumer_key_code::mute), d));
    REQUIRE(!basic::from_event_definition::test_event(krbn::event_queue::queued_event::event(krbn::pointing_button::button1), d));
  }

  {
    basic::from_event_definition d(nlohmann::json::object({
        {"any", "pointing_button"},
    }));

    REQUIRE(!basic::from_event_definition::test_event(krbn::event_queue::queued_event::event(krbn::key_code::a), d));
    REQUIRE(!basic::from_event_definition::test_event(krbn::event_queue::queued_event::event(krbn::consumer_key_code::mute), d));
    REQUIRE(basic::from_event_definition::test_event(krbn::event_queue::queued_event::event(krbn::pointing_button::button1), d));
  }
}

TEST_CASE("simultaneous_options") {
  using krbn::manipulator::details::basic;

  {
    basic::from_event_definition event_definition(nlohmann::json::object({
        {"simultaneous_options", nlohmann::json::object({
                                     {"key_down_order", "strict"},
                                     {"key_up_order", "strict_inverse"},
                                 })},
    }));

    REQUIRE(event_definition.get_simultaneous_options().get_key_down_order() == basic::from_event_definition::simultaneous_options::key_order::strict);
    REQUIRE(event_definition.get_simultaneous_options().get_key_up_order() == basic::from_event_definition::simultaneous_options::key_order::strict_inverse);
  }

  {
    basic::from_event_definition event_definition(nlohmann::json::object({
        {"simultaneous_options", nlohmann::json::object({
                                     {"key_down_order", "unknown"},
                                     {"key_up_order", nlohmann::json::array()},
                                     {"to_after_key_up", "unknown"},
                                 })},
    }));

    REQUIRE(event_definition.get_simultaneous_options().get_key_down_order() == basic::from_event_definition::simultaneous_options::key_order::insensitive);
    REQUIRE(event_definition.get_simultaneous_options().get_key_up_order() == basic::from_event_definition::simultaneous_options::key_order::insensitive);
  }

  {
    basic::from_event_definition event_definition(nlohmann::json::object({
        {"simultaneous_options", "unknown"},
    }));

    REQUIRE(event_definition.get_simultaneous_options().get_key_down_order() == basic::from_event_definition::simultaneous_options::key_order::insensitive);
    REQUIRE(event_definition.get_simultaneous_options().get_key_up_order() == basic::from_event_definition::simultaneous_options::key_order::insensitive);
  }
}

ext: cpp


text:
/*
Copyright 2017-2018 Glen Joseph Fernandes
(glenjofe@gmail.com)

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)
*/

#ifndef BOOST_TT_DETECTED_OR_HPP_INCLUDED
#define BOOST_TT_DETECTED_OR_HPP_INCLUDED

#include <boost/type_traits/detail/detector.hpp>

namespace boost {

template<class Default, template<class...> class Op, class... Args>
using detected_or = detail::detector<Default, void, Op, Args...>;

template<class Default, template<class...> class Op, class... Args>
using detected_or_t = typename detected_or<Default, Op, Args...>::type;

} /* boost */

#endif

ext: hpp

max_stars_repo_name: ZhouGuixin/CicadaPlayer

max_issues_repo_name: ZhouGuixin/CicadaPlayer

max_forks_repo_name: ZhouGuixin/CicadaPlayer


text:
/*
 * Copyright (c) 2010-2015 OTClient <https://github.com/edubart/otclient>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include "otmlparser.h"
#include "otmldocument.h"
#include "otmlexception.h"
#include <boost/tokenizer.hpp>

OTMLParser::OTMLParser(OTMLDocumentPtr doc, std::istream& in) :
    currentDepth(0), currentLine(0),
    doc(doc), currentParent(doc), previousNode(0),
    in(in)
{
}

void OTMLParser::parse()
{
    if(!in.good())
        throw OTMLException(doc, "cannot read from input stream");

    while(!in.eof())
        parseLine(getNextLine());
}

std::string OTMLParser::getNextLine()
{
    currentLine++;
    std::string line;
    std::getline(in, line);
    return line;
}

int OTMLParser::getLineDepth(const std::string& line, bool multilining)
{
    // count number of spaces at the line beginning
    std::size_t spaces = 0;
    while(line[spaces] == ' ')
        spaces++;

    // pre calculate depth
    int depth = spaces / 2;

    if(!multilining || depth <= currentDepth) {
        // check the next character is a tab
        if(line[spaces] == '\t')
            throw OTMLException(doc, "indentation with tabs are not allowed", currentLine);

        // must indent every 2 spaces
        if(spaces % 2 != 0)
            throw OTMLException(doc, "must indent every 2 spaces", currentLine);
    }

    return depth;
}

void OTMLParser::parseLine(std::string line)
{
    int depth = getLineDepth(line);

    if(depth == -1)
        return;

    // remove line sides spaces
    stdext::trim(line);

    // skip empty lines
    if(line.empty())
        return;

    // skip comments
    if(stdext::starts_with(line, "//"))
        return;

    // a depth above, change current parent to the previous added node
    if(depth == currentDepth+1) {
        currentParent = previousNode;
    // a depth below, change parent to previous parent
    } else if(depth < currentDepth) {
        for(int i=0;i<currentDepth-depth;++i)
            currentParent = parentMap[currentParent];
    // if it isn't the current depth, it's a syntax error
    } else if(depth != currentDepth)
        throw OTMLException(doc, "invalid indentation depth, are you indenting correctly?", currentLine);

    // sets current depth
    currentDepth = depth;

    // alright, new depth is set, the line is not empty and it isn't a comment
    // then it must be a node, so we parse it
    parseNode(line);
}

void OTMLParser::parseNode(const std::string& data)
{
    std::string tag;
    std::string value;
    std::size_t dotsPos = data.find_first_of(':');
    int nodeLine = currentLine;

    // node that has no tag and may have a value
    if(!data.empty() && data[0] == '-') {
        value = data.substr(1);
        stdext::trim(value);
    // node that has tag and possible a value
    } else if(dotsPos != std::string::npos) {
        tag = data.substr(0, dotsPos);
        if(data.size() > dotsPos+1)
            value = data.substr(dotsPos+1);
    // node that has only a tag
    } else {
        tag = data;
    }

    stdext::trim(tag);
    stdext::trim(value);

    // process multitine values
    if(value == "|" || value == "|-" || value == "|+") {
        // reads next lines until we can a value below the same depth
        std::string multiLineData;
        do {
            size_t lastPos = in.tellg();
            std::string line = getNextLine();
            int depth = getLineDepth(line, true);

            // depth above current depth, add the text to the multiline
            if(depth > currentDepth) {
                multiLineData += line.substr((currentDepth+1)*2);
            // it has contents below the current depth
            } else {
                // if not empty, its a node
                stdext::trim(line);
                if(!line.empty()) {
                    // rewind and break
                    in.seekg(lastPos, std::ios::beg);
                    currentLine--;
                    break;
                }
            }
            multiLineData += "\n";
        } while(!in.eof());

        /* determine how to treat new lines at the end
         * | strip all new lines at the end and add just a new one
         * |- strip all new lines at the end
         * |+ keep all the new lines at the end (the new lines until next node)
         */
        if(value == "|" || value == "|-") {
            // remove all new lines at the end
            int lastPos = multiLineData.length();
            while(multiLineData[--lastPos] == '\n')
                multiLineData.erase(lastPos, 1);

            if(value == "|")
                multiLineData.append("\n");
        } // else it's |+

        value = multiLineData;
    }

    // create the node
    OTMLNodePtr node = OTMLNode::create(tag);

    node->setUnique(dotsPos != std::string::npos);
    node->setTag(tag);
    node->setSource(doc->source() + ":" + stdext::unsafe_cast<std::string>(nodeLine));

    // ~ is considered the null value
    if(value == "~")
        node->setNull(true);
    else {
        if(stdext::starts_with(value, "[") && stdext::ends_with(value, "]")) {
            std::string tmp = value.substr(1, value.length()-2);
            boost::tokenizer<boost::escaped_list_separator<char>> tokens(tmp);
            for(std::string v : tokens) {
                stdext::trim(v);
                node->writeIn(v);
            }
        } else
            node->setValue(value);
    }

    currentParent->addChild(node);
    parentMap[node] = currentParent;
    previousNode = node;
}

max_stars_repo_name: dalkon/otclient

max_issues_repo_name: dalkon/otclient

max_forks_repo_name: dalkon/otclient


text:
#include "murk/web/form.hpp"

#include "murk/web/uri.hpp"

#include "murk/flows/string.hpp"

#include <boost/beast.hpp>

#include <boost/algorithm/string.hpp>

namespace murk::web {
  std::string multipart_formdata_encode(const multiform_t& form, std::string_view boundary) {
    std::string ret;

    for (auto& i : form.base) {
      ret.append(fmt::format("--{}\r\nContent-Disposition: form-data; name=\"{}\"\r\n\r\n",
                             boundary, escape_double_quotes(i.first)));
      ret.append(i.second);
      ret.append("\r\n");
    }
    for (auto& i : form.files) {
      ret.append(fmt::format("--{}\r\nContent-Disposition: form-data; name=\"{}\"; filename=\"{}\"\r\nContent-Type: {}\r\n\r\n",
                             boundary, escape_double_quotes(i.first), escape_double_quotes(i.second.filename), escape_double_quotes(i.second.mime)));
      ret.append(reinterpret_cast<const char*>(i.second.dat.data()), i.second.dat.size());
      ret.append("\r\n");
    }
//    ret.erase(ret.end() - 2, ret.end());
    ret.append(fmt::format("--{}--\r\n", boundary));
    return ret;
  }
  multiform_t multipart_formdata_decode(std::string_view form, std::string_view boundary);

  std::string form_url_encode_dodgy(const form_t& form) {
    std::string ret;

    for (auto& i : form) {
      ret.append(fmt::format("{}={}&", i.first, i.second));
    }
    ret.pop_back();

    return ret;
  }

  std::string form_url_encode(const form_t& form) {
    std::string ret;

    for (auto& i : form) {
      ret.append(fmt::format("{}={}&", uri::encode(i.first), uri::encode(i.second)));
    }
    ret.pop_back();

    return ret;
  }
  form_t form_url_decode(std::string_view s) {
    // BUG: does not escape
    form_t ret;

    std::vector<std::string> elems;
    boost::split(elems, s, [](char c) { return c == '&'; });

    for (auto& i : elems) {
      auto split_pt = i.find('=');
      if (split_pt == std::string_view::npos)
        throw std::invalid_argument("No value in url encoded form");
      auto iter = i.begin() + split_pt;
      ret.emplace(std::string{i.begin(), iter}, std::string{iter + 1, i.end()});
    }

    return ret;
  }
}


text:
/**
 * Copyright (c) 2020 Melown Technologies SE
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * *  Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * *  Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/filter/gzip.hpp>

#include "meshop.hpp"

namespace bio = boost::iostreams;

namespace geometry {

void saveAsGzippedObj(const Mesh &mesh, const boost::filesystem::path &filepath
                      , const ObjMaterial &mtl
                      , const ObjStreamSetup &streamSetup)
{
    LOG(info2) << "Saving gzipped mesh to file <" << filepath << ">.";

    std::ofstream f;
    f.exceptions(std::ios::badbit | std::ios::failbit);
    try {
        f.open(filepath.string(), std::ios_base::out | std::ios_base::trunc);
    } catch (const std::exception&) {
        LOGTHROW(err3, std::runtime_error)
            << "Unable to save mesh to <" << filepath << ">.";
    }

    bio::filtering_ostream gzipped;
    gzipped.push(bio::gzip_compressor(bio::gzip_params(9), 1 << 16));
    gzipped.push(f);

    {
        bool setFormat(true);
        if (streamSetup) { setFormat = !streamSetup(f); }
        saveAsObj(mesh, gzipped, mtl, filepath, setFormat);
    }

    gzipped.flush();
}

} // namespace geometry


text:
#pragma once

#include "boost_defs.hpp"

#include "gcd_utility.hpp"
#include <boost/signals2.hpp>
#include <deque>
#include <mach/mach_time.h>

namespace krbn {
namespace manipulator {
class manipulator_timer final {
public:
  enum class timer_id : uint64_t {
    zero = 0,
  };

  class entry final {
  public:
    entry(uint64_t when) : when_(when) {
      static std::mutex mutex;
      std::lock_guard<std::mutex> guard(mutex);

      static timer_id id;
      id = timer_id(static_cast<uint64_t>(id) + 1);
      timer_id_ = id;
    }

    timer_id get_timer_id(void) const {
      return timer_id_;
    }

    uint64_t get_when(void) const {
      return when_;
    }

    bool compare(const entry& other) {
      if (when_ != other.when_) {
        return when_ < other.when_;
      } else {
        return timer_id_ < other.timer_id_;
      }
    }

  private:
    timer_id timer_id_;
    uint64_t when_;
  };

  class core final {
  public:
    boost::signals2::signal<void(timer_id, uint64_t)> timer_invoked;

    core(void) : enabled_(false) {
    }

    // For unit testing
    const std::deque<entry>& get_entries(void) const {
      return entries_;
    }

    void enable(void) {
      std::lock_guard<std::mutex> guard(mutex_);

      enabled_ = true;
    }

    void disable(void) {
      std::lock_guard<std::mutex> guard(mutex_);

      enabled_ = false;
    }

    timer_id add_entry(uint64_t when) {
      std::lock_guard<std::mutex> guard(mutex_);

      entries_.emplace_back(when);
      auto result = entries_.back().get_timer_id();

      std::sort(std::begin(entries_),
                std::end(entries_),
                [](auto& a, auto& b) {
                  return a.compare(b);
                });

      set_timer();

      return result;
    }

    void signal(uint64_t now) {
      for (;;) {
        boost::optional<timer_id> id;

        {
          std::lock_guard<std::mutex> guard(mutex_);

          if (!entries_.empty() && entries_.front().get_when() <= now) {
            id = entries_.front().get_timer_id();
            entries_.pop_front();
          }
        }

        if (id) {
          timer_invoked(*id, now);
        } else {
          break;
        }
      }

      set_timer();
    }

  private:
    void set_timer(void) {
      if (!enabled_) {
        timer_ = nullptr;
        return;
      }

      if (entries_.empty()) {
        timer_ = nullptr;
        return;
      }

      timer_ = std::make_unique<gcd_utility::main_queue_after_timer>(entries_.front().get_when(),
                                                                     true,
                                                                     ^{
                                                                       uint64_t now = mach_absolute_time();
                                                                       signal(now);
                                                                     });
    }

    std::mutex mutex_;
    bool enabled_;
    std::deque<entry> entries_;
    std::unique_ptr<gcd_utility::main_queue_after_timer> timer_;
  };

  static core& get_instance(void) {
    static std::mutex mutex;
    std::lock_guard<std::mutex> guard(mutex);

    static std::unique_ptr<core> core_;
    if (!core_) {
      core_ = std::make_unique<core>();
    }

    return *core_;
  }
};
} // namespace manipulator
} // namespace krbn

max_stars_repo_name: rolandwalker/Karabiner-Elements

max_issues_repo_name: rolandwalker/Karabiner-Elements

max_forks_repo_name: rolandwalker/Karabiner-Elements


text:
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.  See the License for the
specific language governing permissions and limitations
under the License.
*/

#ifndef CONTAINERHASH_HPP
#define CONTAINERHASH_HPP

#include <boost/functional/hash.hpp>

namespace common
{

// Taken from <http://stackoverflow.com/a/10405129/1391325>
template <typename Container> // we can make this generic for any container [1]
struct ContainerHash
{
    typedef Container argument_type;
    typedef std::size_t result_type;

    result_type operator()(const argument_type& c) const
    {
        return boost::hash_range(c.begin(), c.end());
    }
};

}

#endif // CONTAINERHASH_HPP