#include <chrono>
#include <iostream>
#include <iomanip>
#include <numeric>
#include <boost/program_options.hpp>
#include <boost/filesystem/operations.hpp>
#include "cu_helper.h"
#include "io_service_pool.hpp"
#include "safe_class.hpp"
#include "random_forest.hpp"
#include "receptor.hpp"
#include "ligand.hpp"
#include "log.hpp"
#include "source.hpp"

//! Represents a data wrapper for kernel callback.
template <typename T>
class callback_data
{
public:
	callback_data(io_service_pool& io, const path& output_folder_path, const size_t max_conformations, const size_t num_tasks, const receptor& rec, const forest& f, const scoring_function& sf, const T dev, const float* const cnfh, ligand&& lig_, safe_function& safe_print, log_engine& log, safe_vector<T>& idle) : io(io), output_folder_path(output_folder_path), max_conformations(max_conformations), num_tasks(num_tasks), rec(rec), f(f), sf(sf), dev(dev), cnfh(cnfh), lig(move(lig_)), safe_print(safe_print), log(log), idle(idle) {}
	io_service_pool& io;
	const path& output_folder_path;
	const size_t max_conformations;
	const size_t num_tasks;
	const receptor& rec;
	const forest& f;
	const scoring_function& sf;
	const T dev;
	const float* const cnfh;
	ligand lig;
	safe_function& safe_print;
	log_engine& log;
	safe_vector<T>& idle;
};

int main(int argc, char* argv[])
{
	path receptor_path, input_folder_path, output_folder_path, log_path;
	array<float, 3> center, size;
	size_t seed, num_threads, num_trees, num_tasks, num_bfgs_iterations, max_conformations;
	float granularity;

	// Parse program options in a try/catch block.
	try
	{
		// Initialize the default values of optional arguments.
		const path default_output_folder_path = "output";
		const path default_log_path = "log.csv";
		const size_t default_seed = chrono::system_clock::now().time_since_epoch().count();
		const size_t default_num_threads = thread::hardware_concurrency();
		const size_t default_num_trees = 128;
		const size_t default_num_tasks = 256;
		const size_t default_num_bfgs_iterations = 300;
		const size_t default_max_conformations = 9;
		const  float default_granularity = 0.15625f;

		// Set up options description.
		using namespace boost::program_options;
		options_description input_options("input (required)");
		input_options.add_options()
			("receptor", value<path>(&receptor_path)->required(), "receptor in PDBQT format")
			("input_folder", value<path>(&input_folder_path)->required(), "folder of input ligands in PDBQT format")
			("center_x", value<float>(&center[0])->required(), "x coordinate of the search space center")
			("center_y", value<float>(&center[1])->required(), "y coordinate of the search space center")
			("center_z", value<float>(&center[2])->required(), "z coordinate of the search space center")
			("size_x", value<float>(&size[0])->required(), "size in the x dimension in Angstrom")
			("size_y", value<float>(&size[1])->required(), "size in the y dimension in Angstrom")
			("size_z", value<float>(&size[2])->required(), "size in the z dimension in Angstrom")
			;
		options_description output_options("output (optional)");
		output_options.add_options()
			("output_folder", value<path>(&output_folder_path)->default_value(default_output_folder_path), "folder of output ligands in PDBQT format")
			("log", value<path>(&log_path)->default_value(default_log_path), "log file in csv format")
			;
		options_description miscellaneous_options("options (optional)");
		miscellaneous_options.add_options()
			("seed", value<size_t>(&seed)->default_value(default_seed), "explicit non-negative random seed")
			("threads", value<size_t>(&num_threads)->default_value(default_num_threads), "worker threads to use")
			("trees", value<size_t>(&num_trees)->default_value(default_num_trees), "trees in random forest")
			("tasks", value<size_t>(&num_tasks)->default_value(default_num_tasks), "Monte Carlo tasks for global search")
			("generations", value<size_t>(&num_bfgs_iterations)->default_value(default_num_bfgs_iterations), "generations in BFGS")
			("max_conformations", value<size_t>(&max_conformations)->default_value(default_max_conformations), "maximum binding conformations to write")
			("granularity", value<float>(&granularity)->default_value(default_granularity), "density of probe atoms of grid maps")
			("help", "help information")
			("version", "version information")
			("config", value<path>(), "configuration file to load options from")
			;
		options_description all_options;
		all_options.add(input_options).add(output_options).add(miscellaneous_options);

		// Parse command line arguments.
		variables_map vm;
		store(parse_command_line(argc, argv, all_options), vm);

		// If no command line argument is supplied or help is requested, print the usage and exit.
		if (argc == 1 || vm.count("help"))
		{
			cout << all_options;
			return 0;
		}

		// If version is requested, print the version and exit.
		if (vm.count("version"))
		{
			cout << "3.0.0" << endl;
			return 0;
		}

		// If a configuration file is presented, parse it.
		if (vm.count("config"))
		{
			boost::filesystem::ifstream config_file(vm["config"].as<path>());
			store(parse_config_file(config_file, all_options), vm);
		}

		// Notify the user of parsing errors, if any.
		vm.notify();

		// Validate receptor.
		if (!is_regular_file(receptor_path))
		{
			cerr << "Receptor " << receptor_path << " does not exist or is not a regular file" << endl;
			return 1;
		}

		// Validate input_folder.
		if (!is_directory(input_folder_path))
		{
			cerr << "Input folder " << input_folder_path << " does not exist or is not a directory" << endl;
			return 1;
		}

		// Validate output_folder.
		if (exists(output_folder_path))
		{
			if (!is_directory(output_folder_path))
			{
				cerr << "Output folder " << output_folder_path << " is not a directory" << endl;
				return 1;
			}
		}
		else
		{
			if (!create_directories(output_folder_path))
			{
				cerr << "Failed to create output folder " << output_folder_path << endl;
				return 1;
			}
		}
	}
	catch (const exception& e)
	{
		cerr << e.what() << endl;
		return 1;
	}

	// Initialize a Mersenne Twister random number generator.
	cout << "Using random seed " << seed << endl;

	cout << "Creating an io service pool of " << num_threads << " worker threads" << endl;
	io_service_pool io(num_threads);
	safe_counter<size_t> cnt;
	safe_function safe_print;

	cout << "Precalculating a scoring function of " << scoring_function::n << " atom types in parallel" << endl;
	scoring_function sf;
	cnt.init((sf.n + 1) * sf.n >> 1);
	for (size_t t1 = 0; t1 < sf.n; ++t1)
	for (size_t t0 = 0; t0 <=  t1; ++t0)
	{
		io.post([&, t0, t1]()
		{
			sf.precalculate(t0, t1);
			cnt.increment();
		});
	}
	cnt.wait();

	cout << "Parsing receptor " << receptor_path << endl;
	receptor rec(receptor_path, center, size, granularity);

	cout << "Detecting CUDA devices with compute capability 1.1 or greater" << endl;
	checkCudaErrors(cuInit(0));
	int num_devices;
	checkCudaErrors(cuDeviceGetCount(&num_devices));
	cout << "D               Name  CC SM GMEM(MB) SMEM(KB) CMEM(KB) MAPHOST ECC TIMEOUT MODE" << endl;
	vector<CUdevice> devices;
	devices.reserve(num_devices);
	for (int dev = 0; dev < num_devices; ++dev)
	{
		// Get a device handle from an ordinal.
		CUdevice device;
		checkCudaErrors(cuDeviceGet(&device, dev));

		// Filter devices with compute capability 1.1 or greater, which is required by cuMemHostGetDevicePointer and cuStreamAddCallback.
		int major;
		int minor;
		checkCudaErrors(cuDeviceGetAttribute(&major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, device));
		checkCudaErrors(cuDeviceGetAttribute(&minor, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR, device));
		if (major == 1 && minor == 0) continue;

		// Save the device handle.
		devices.push_back(device);

		// Get and print device attributes.
		char name[256];
		size_t totalGlobalMem;
		int multiProcessorCount;
		int sharedMemPerBlock;
		int totalConstMem;
		int canMapHostMemory;
		int ECCEnabled;
		int kernelExecTimeoutEnabled;
		int computeMode;
		checkCudaErrors(cuDeviceGetName(name, sizeof(name), device));
		checkCudaErrors(cuDeviceTotalMem(&totalGlobalMem, device));
		checkCudaErrors(cuDeviceGetAttribute(&multiProcessorCount, CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT, device));
		checkCudaErrors(cuDeviceGetAttribute(&sharedMemPerBlock, CU_DEVICE_ATTRIBUTE_MAX_SHARED_MEMORY_PER_BLOCK, device));
		checkCudaErrors(cuDeviceGetAttribute(&totalConstMem, CU_DEVICE_ATTRIBUTE_TOTAL_CONSTANT_MEMORY, device));
		checkCudaErrors(cuDeviceGetAttribute(&canMapHostMemory, CU_DEVICE_ATTRIBUTE_CAN_MAP_HOST_MEMORY, device));
		checkCudaErrors(cuDeviceGetAttribute(&ECCEnabled, CU_DEVICE_ATTRIBUTE_ECC_ENABLED, device));
		checkCudaErrors(cuDeviceGetAttribute(&kernelExecTimeoutEnabled, CU_DEVICE_ATTRIBUTE_KERNEL_EXEC_TIMEOUT, device));
		checkCudaErrors(cuDeviceGetAttribute(&computeMode, CU_DEVICE_ATTRIBUTE_COMPUTE_MODE, device));
		cout << dev << setw(19) << name << setw(2) << major << '.' << minor << setw(3) << multiProcessorCount << setw(9) << totalGlobalMem / 1048576 << setw(9) << sharedMemPerBlock / 1024 << setw(9) << totalConstMem / 1024 << setw(8) << canMapHostMemory << setw(4) << ECCEnabled << setw(8) << kernelExecTimeoutEnabled << setw(5) << computeMode << endl;
	}
	num_devices = devices.size();
	if (!num_devices)
	{
		cerr << "No CUDA devices with compute capability 1.1 or greater detected" << endl;
		return 2;
	}

	cout << "Creating contexts and compiling kernel source for " << num_devices << " devices" << endl;
	source src;
	vector<CUcontext> contexts(num_devices);
	vector<CUfunction> functions(num_devices);
	vector<array<CUdeviceptr, sf.n>> mpsd(num_devices);
	vector<CUdeviceptr> mpsv(num_devices);
	vector<CUdeviceptr> slnv(num_devices);
	vector<CUdeviceptr> ligv(num_devices);
	vector<int*> ligh(num_devices);
	vector<CUdeviceptr> ligd(num_devices);
	vector<CUdeviceptr> slnd(num_devices);
	vector<float*> cnfh(num_devices);
	vector<size_t> lig_elems(num_devices, 2601);
	vector<size_t> sln_elems(num_devices, 3438);
	vector<size_t> cnf_elems(num_devices,   43);
	for (int dev = 0; dev < num_devices; ++dev)
	{
		// Create a context for the current device.
		checkCudaErrors(cuCtxCreate(&contexts[dev], CU_CTX_SCHED_AUTO/*CU_CTX_SCHED_YIELD*/ | CU_CTX_MAP_HOST, devices[dev]));
//		checkCudaErrors(cuCtxSetCacheConfig(CU_FUNC_CACHE_PREFER_L1));
//		checkCudaErrors(cuCtxSetSharedMemConfig(CU_SHARED_MEM_CONFIG_EIGHT_BYTE_BANK_SIZE));

		// Initialize just-in-time compilation options.
		const unsigned int num_jit_options = 2;
		array<CUjit_option, num_jit_options> jit_keys =
		{
			CU_JIT_MAX_REGISTERS,
			CU_JIT_CACHE_MODE
		};
		array<void*, num_jit_options> jit_vals =
		{
			(void*)32,
			(void*)CU_JIT_CACHE_OPTION_NONE // CU_JIT_CACHE_OPTION_CG, CU_JIT_CACHE_OPTION_CA
		};

		// Load the module into the current context.
		CUmodule module;
		checkCudaErrors(cuModuleLoadDataEx(&module, src.data(), num_jit_options, jit_keys.data(), jit_vals.data()));

		// Get functions from module.
		checkCudaErrors(cuModuleGetFunction(&functions[dev], module, "monte_carlo"));

		// Get symbols from module.
		CUdeviceptr sfec;
		CUdeviceptr sfdc;
		CUdeviceptr sfsc;
		CUdeviceptr cr0c;
		CUdeviceptr cr1c;
		CUdeviceptr nprc;
		CUdeviceptr gric;
		CUdeviceptr mpsc;
		CUdeviceptr nbic;
		CUdeviceptr sedc;
		CUdeviceptr slnc;
		CUdeviceptr ligc;
		size_t sfes;
		size_t sfds;
		size_t sfss;
		size_t cr0s;
		size_t cr1s;
		size_t nprs;
		size_t gris;
		size_t mpss;
		size_t nbis;
		size_t seds;
		size_t slns;
		size_t ligs;
		checkCudaErrors(cuModuleGetGlobal(&sfec, &sfes, module, "sfe")); //   8 const float*
		checkCudaErrors(cuModuleGetGlobal(&sfdc, &sfds, module, "sfd")); //   8 const float*
		checkCudaErrors(cuModuleGetGlobal(&sfsc, &sfss, module, "sfs")); //   4 int
		checkCudaErrors(cuModuleGetGlobal(&cr0c, &cr0s, module, "cr0")); //  12 float3
		checkCudaErrors(cuModuleGetGlobal(&cr1c, &cr1s, module, "cr1")); //  12 float3
		checkCudaErrors(cuModuleGetGlobal(&nprc, &nprs, module, "npr")); //  12 int3
		checkCudaErrors(cuModuleGetGlobal(&gric, &gris, module, "gri")); //   4 float
		checkCudaErrors(cuModuleGetGlobal(&mpsc, &mpss, module, "mps")); // 120 conat float* [15]
		checkCudaErrors(cuModuleGetGlobal(&nbic, &nbis, module, "nbi")); //   4 int
		checkCudaErrors(cuModuleGetGlobal(&sedc, &seds, module, "sed")); //   8 unsigned long
		checkCudaErrors(cuModuleGetGlobal(&slnc, &slns, module, "s0e")); //   8 float*
		checkCudaErrors(cuModuleGetGlobal(&ligc, &ligs, module, "lig")); //   8 const int*

		// Initialize symbols for scoring function.
		CUdeviceptr sfed;
		CUdeviceptr sfdd;
		const int sfsh = sf.ns;
		assert(sfes == sizeof(sfed));
		assert(sfds == sizeof(sfdd));
		assert(sfss == sizeof(sfsh));
		const size_t sfe_bytes = sizeof(float) * sf.e.size();
		const size_t sfd_bytes = sizeof(float) * sf.d.size();
		checkCudaErrors(cuMemAlloc(&sfed, sfe_bytes));
		checkCudaErrors(cuMemAlloc(&sfdd, sfd_bytes));
		checkCudaErrors(cuMemcpyHtoD(sfed, sf.e.data(), sfe_bytes));
		checkCudaErrors(cuMemcpyHtoD(sfdd, sf.d.data(), sfd_bytes));
		checkCudaErrors(cuMemcpyHtoD(sfec, &sfed, sfes));
		checkCudaErrors(cuMemcpyHtoD(sfdc, &sfdd, sfds));
		checkCudaErrors(cuMemcpyHtoD(sfsc, &sfsh, sfss));

		// Initialize symbols for receptor.
		assert(cr0s == sizeof(rec.corner0));
		assert(cr1s == sizeof(rec.corner1));
		assert(nprs == sizeof(rec.num_probes));
		assert(gris == sizeof(rec.granularity_inverse));
		assert(mpss == sizeof(float*) * sf.n);
		checkCudaErrors(cuMemcpyHtoD(cr0c, rec.corner0.data(), cr0s));
		checkCudaErrors(cuMemcpyHtoD(cr1c, rec.corner1.data(), cr1s));
		checkCudaErrors(cuMemcpyHtoD(nprc, rec.num_probes.data(), nprs));
		checkCudaErrors(cuMemcpyHtoD(gric, &rec.granularity_inverse, gris));
		mpsv[dev] = mpsc;

		// Initialize symbols for program control.
		const int nbih = num_bfgs_iterations;
		assert(nbis == sizeof(nbih));
		assert(seds == sizeof(seed));
		checkCudaErrors(cuMemcpyHtoD(nbic, &nbih, nbis));
		checkCudaErrors(cuMemcpyHtoD(sedc, &seed, seds));

		// Allocate ligh, ligd, slnd and cnfh.
		checkCudaErrors(cuMemHostAlloc((void**)&ligh[dev], sizeof(int) * lig_elems[dev], CU_MEMHOSTALLOC_DEVICEMAP));
		checkCudaErrors(cuMemHostGetDevicePointer(&ligd[dev], ligh[dev], 0));
		checkCudaErrors(cuMemAlloc(&slnd[dev], sizeof(float) * sln_elems[dev] * num_tasks));
		checkCudaErrors(cuMemHostAlloc((void**)&cnfh[dev], sizeof(float) * cnf_elems[dev] * num_tasks, 0));

		// Initialize symbols for sln and lig.
		assert(slns == sizeof(slnd[dev]));
		assert(ligs == sizeof(ligd[dev]));
		checkCudaErrors(cuMemcpyHtoD(slnc, &slnd[dev], slns));
		checkCudaErrors(cuMemcpyHtoD(ligc, &ligd[dev], ligs));
		slnv[dev] = slnc;
		ligv[dev] = ligc;

		// Pop the current context.
		checkCudaErrors(cuCtxPopCurrent(NULL));
	}
	src.clear();
	sf.clear();

	// Initialize a vector of idle devices.
	safe_vector<int> idle(num_devices);
	iota(idle.begin(), idle.end(), 0);

	cout << "Training a random forest of " << num_trees << " trees in parallel" << endl;
	forest f(num_trees, seed);
	cnt.init(num_trees);
	for (size_t i = 0; i < num_trees; ++i)
	{
		io.post([&, i]()
		{
			f[i].train(4, f.u01_s);
			cnt.increment();
		});
	}
	cnt.wait();
	f.clear();

	// Perform docking for each ligand in the input folder.
	log_engine log;
	cout.setf(ios::fixed, ios::floatfield);
	cout << "Executing " << num_tasks << " optimization runs of " << num_bfgs_iterations << " BFGS iterations in parallel" << endl
	     << "   Index        Ligand D  pKd 1     2     3     4     5     6     7     8     9" << endl << setprecision(2);
	for (directory_iterator dir_iter(input_folder_path), const_dir_iter; dir_iter != const_dir_iter; ++dir_iter)
	{
		// Filter files with .pdbqt extension name.
		const path& input_ligand_path = dir_iter->path();
		if (input_ligand_path.extension() != ".pdbqt") continue;

		// Parse the ligand. Don't declare it const as it will be moved to the callback data wrapper.
		ligand lig(input_ligand_path);

		// Find atom types that are presented in the current ligand but not presented in the grid maps.
		vector<size_t> xs;
		for (size_t t = 0; t < sf.n; ++t)
		{
			if (lig.xs[t] && rec.maps[t].empty())
			{
				rec.maps[t].resize(rec.num_probes_product);
				xs.push_back(t);
			}
		}

		// Create grid maps on the fly if necessary.
		if (xs.size())
		{
			// Precalculate p_offset.
			rec.precalculate(sf, xs);

			// Create grid maps in parallel.
			cnt.init(rec.num_probes[2]);
			for (size_t z = 0; z < rec.num_probes[2]; ++z)
			{
				io.post([&,z]()
				{
					rec.populate(xs, z, sf);
					cnt.increment();
				});
			}
			cnt.wait();
		}

		// Wait until a device is ready for execution.
		const int dev = idle.safe_pop_back();

		// Push the context of the chosen device.
		checkCudaErrors(cuCtxPushCurrent(contexts[dev]));

		// Copy grid maps from host memory to device memory if necessary.
		for (size_t t = 0; t < sf.n; ++t)
		{
			if (lig.xs[t] && !mpsd[dev][t])
			{
				checkCudaErrors(cuMemAlloc(&mpsd[dev][t], rec.map_bytes));
				checkCudaErrors(cuMemcpyHtoD(mpsd[dev][t], rec.maps[t].data(), rec.map_bytes));
				checkCudaErrors(cuMemcpyHtoD(mpsv[dev] + sizeof(CUdeviceptr) * t, &mpsd[dev][t], sizeof(CUdeviceptr)));
			}
		}

		// Reallocate ligh and ligd should the current ligand elements exceed the default size.
		const size_t this_lig_elems = lig.get_lig_elems();
		if (this_lig_elems > lig_elems[dev])
		{
			checkCudaErrors(cuMemFreeHost(ligh[dev]));
			lig_elems[dev] = this_lig_elems;
			checkCudaErrors(cuMemHostAlloc((void**)&ligh[dev], sizeof(int) * lig_elems[dev], CU_MEMHOSTALLOC_DEVICEMAP));
			checkCudaErrors(cuMemHostGetDevicePointer(&ligd[dev], ligh[dev], 0));
			checkCudaErrors(cuMemcpyHtoD(ligv[dev], &ligd[dev], sizeof(ligv[dev])));
		}

		// Compute the number of shared memory bytes.
		const size_t lig_bytes = sizeof(int) * lig_elems[dev];

		// Encode the current ligand.
		lig.encode(ligh[dev]);

		// Reallocate slnd should the current solution elements exceed the default size.
		const size_t this_sln_elems = lig.get_sln_elems();
		if (this_sln_elems > sln_elems[dev])
		{
			checkCudaErrors(cuMemFree(slnd[dev]));
			sln_elems[dev] = this_sln_elems;
			checkCudaErrors(cuMemAlloc(&slnd[dev], sizeof(float) * sln_elems[dev] * num_tasks));
			checkCudaErrors(cuMemcpyHtoD(slnv[dev], &slnd[dev], sizeof(slnv[dev])));
		}

		// Clear the solution buffer.
		checkCudaErrors(cuMemsetD32Async(slnd[dev], 0, sln_elems[dev] * num_tasks, NULL));

		// Launch kernel.
		void* params[] = { &lig.nv, &lig.nf, &lig.na, &lig.np };
		checkCudaErrors(cuLaunchKernel(functions[dev], (num_tasks - 1) / 32 + 1, 1, 1, 32, 1, 1, lig_bytes, NULL, params, NULL));

		// Reallocate cnfh should the current conformation elements exceed the default size.
		const size_t this_cnf_elems = lig.get_cnf_elems();
		if (this_cnf_elems > cnf_elems[dev])
		{
			checkCudaErrors(cuMemFreeHost(cnfh[dev]));
			cnf_elems[dev] = this_cnf_elems;
			checkCudaErrors(cuMemHostAlloc((void**)&cnfh[dev], sizeof(float) * cnf_elems[dev] * num_tasks, 0));
		}

		// Copy conformations from device memory to host memory.
		checkCudaErrors(cuMemcpyDtoHAsync(cnfh[dev], slnd[dev], sizeof(float) * cnf_elems[dev] * num_tasks, NULL));

		// Add a callback to the compute stream.
		checkCudaErrors(cuStreamAddCallback(NULL, [](CUstream stream, CUresult error, void* data)
		{
			checkCudaErrors(error);
			const shared_ptr<callback_data<int>> cbd(reinterpret_cast<callback_data<int>*>(data));
			cbd->io.post([=]()
			{
				const auto& output_folder_path = cbd->output_folder_path;
				const auto  max_conformations = cbd->max_conformations;
				const auto  num_tasks = cbd->num_tasks;
				const auto& rec = cbd->rec;
				const auto& f = cbd->f;
				const auto& sf = cbd->sf;
				const auto  dev = cbd->dev;
				const auto cnfh = cbd->cnfh;
				auto& lig = cbd->lig;
				auto& safe_print = cbd->safe_print;
				auto& log = cbd->log;
				auto& idle = cbd->idle;

				// Write conformations.
				lig.write(cnfh, output_folder_path, max_conformations, num_tasks, rec, f, sf);

				// Output and save ligand stem and predicted affinities.
				safe_print([&]()
				{
					string stem = lig.filename.stem().string();
					cout << setw(8) << log.size() + 1 << setw(14) << stem << setw(2) << dev << ' ';
					for_each(lig.affinities.cbegin(), lig.affinities.cbegin() + min<size_t>(lig.affinities.size(), 9), [](const float a)
					{
						cout << setw(6) << a;
					});
					cout << endl;
					log.push_back(new log_record(move(stem), move(lig.affinities)));
				});

				// Signal the main thread to post another task.
				idle.safe_push_back(dev);
			});
		}, new callback_data<int>(io, output_folder_path, max_conformations, num_tasks, rec, f, sf, dev, cnfh[dev], move(lig), safe_print, log, idle), 0));

		// Pop the context after use.
		checkCudaErrors(cuCtxPopCurrent(NULL));
	}

	// Synchronize contexts.
	for (auto& context : contexts)
	{
		checkCudaErrors(cuCtxPushCurrent(context));
		checkCudaErrors(cuCtxSynchronize());
		checkCudaErrors(cuCtxPopCurrent(NULL));
	}

	// Wait until the io service pool has finished all its tasks.
	io.wait();
	assert(idle.size() == num_devices);

	// Destroy contexts.
	for (auto& context : contexts)
	{
		checkCudaErrors(cuCtxDestroy(context));
	}

	// Sort and write ligand log records to the log file.
	if (log.empty()) return 0;
	cout << "Writing log records of " << log.size() << " ligands to " << log_path << endl;
	log.sort();
	log.write(log_path);
}


#include <GLFW/glfw3.h>

#include <memory>
#include <iostream>
#include <algorithm>

#include <merely3d/app.hpp>
#include <merely3d/window.hpp>
#include <merely3d/camera_controller.hpp>

#include <Eigen/Geometry>

#include "example_model.hpp"

using merely3d::Window;
using merely3d::WindowBuilder;
using merely3d::Frame;
using merely3d::Key;
using merely3d::Action;
using merely3d::EventHandler;
using merely3d::Material;
using merely3d::Color;
using merely3d::CameraController;
using merely3d::Line;

using merely3d::red;
using merely3d::green;

using merely3d::renderable;
using merely3d::Rectangle;
using merely3d::Box;
using merely3d::Sphere;

using Eigen::Vector2f;
using Eigen::Vector3f;
using Eigen::Quaternionf;
using Eigen::AngleAxisf;

/// A simple click event handler, showcasing how to handle click events,
/// as well as how to map screen coordinates to a ray moving
/// out from the camera, which can be used in e.g. mouse picking applications.
///
/// Here we just render the corresponding ray and let it stay in the simulation for
/// a few seconds before removing it (e.g. the lifetime variable below).
class ClickEventHandler : public merely3d::EventHandler
{
public:
    ClickEventHandler()
        : line(Vector3f::Zero(), Vector3f::Zero()),
          lifetime(0.0)
    {
        line.color = merely3d::red();
    }

    bool mouse_button_press(merely3d::Window &window,
                            merely3d::MouseButton button,
                            merely3d::Action action,
                            int) override
    {
        using merely3d::MouseButton;
        using merely3d::Action;

        if (button == MouseButton::Right && action == Action::Press)
        {
            const auto cursor_pos = window.get_current_cursor_position();
            const auto view_pos = window.unproject_screen_coordinates(cursor_pos);

            const Vector3f world_pos = window.camera().transform() * view_pos;
            const Vector3f direction = (world_pos - window.camera().position()).normalized();

            const float ray_length = 100.0f;

            line.from = window.camera().position() + 0.3 * direction;
            line.to = window.camera().position() + ray_length * direction;
            lifetime = 10.0f;

            return true;
        }

        return false;
    }

    void before_frame(Window &, Frame & frame) override
    {
        if (lifetime > 0.0f)
        {
            frame.draw_line(line);
            frame.draw(renderable(Sphere(0.001))
                        .with_position(line.from)
                        .with_material(Material().with_color(merely3d::red())
                                                 .with_pattern_grid_size(0.0f)));
        }

        lifetime = std::max(0.0, lifetime - frame.time_since_prev_frame());
    }

private:
    merely3d::Line line;
    double lifetime;
};

merely3d::StaticMesh load_example_model()
{
    // Create a static mesh which we can use when rendering
    const auto & vn = merely3d::example_model::vertices_and_normals;
    const auto & idx = merely3d::example_model::indices;
    const auto model_vertices_normals = std::vector<float>(vn.begin(), vn.end());
    const auto model_indices = std::vector<unsigned int>(idx.begin(), idx.end());
    return merely3d::StaticMesh(model_vertices_normals, model_indices);
}

int main()
{
    // Constructing the app first is essential: it makes sure that
    // GLFW is set up properly. Note that as an alternative, you can call
    // glfw init/terminate yourself directly, but you must be careful that
    // any windows are destroyed before calling terminate(). App automatically
    // takes care of this as long as it outlives any windows.
    merely3d::App app;

    auto window = WindowBuilder()
                   .dimensions(1024, 768)
                   .title("Hello merely3d!")
                   .multisampling(8)
                   .build();

    window.camera().look_in(Vector3f(1.0, 0.0, -1), Vector3f(0.0, 0.0, 1.0));
    window.camera().set_position(Vector3f(-1.0, 0.0, 3.0));

    window.add_event_handler(std::shared_ptr<EventHandler>(new CameraController));
    window.add_event_handler(std::shared_ptr<EventHandler>(new ClickEventHandler));

    // Create a static mesh which we can use when rendering.
    // Even though we draw the model in every frame in the code below, merely3d
    // internally manages the data transfer so that it typically only happens on the first
    // frame the model is used. Moreover, if several instances of the same model data
    // is rendered in the same frame, merely3d will similarly avoid unnecessary memory transfers
    // and internally reuse the OpenGL resources. Hence you may simply load your models
    // outside your rendering loop, and then afterwards not worry about the overhead
    // of repeatedly drawing your models.
    const auto model = load_example_model();

    while (!window.should_close())
    {
        window.render_frame([&model] (Frame & frame)
        {
            frame.draw(renderable(Rectangle(0.5, 0.5))
                        .with_position(1.0, 0.0, 0.5)
                        .with_orientation(AngleAxisf(0.78, Vector3f(1.0f, 0.0f, 0.0f)))
                        .with_material(Material().with_color(Color(0.5, 0.3, 0.3))));

            frame.draw(renderable(Box(1.0, 1.0, 1.0))
                        .with_position(4.0, 0.0, 1.1));

            frame.draw(renderable(Box(0.2, 5.0, 1.0))
                        .with_position(0.0, 0.0, 1.0));

            frame.draw(renderable(Box(0.2, 1.0, 1.0))
                        .with_position(0.0, 0.0, 5.0)
                        .with_orientation(AngleAxisf(0.5, Vector3f(1.0, 1.0, 1.0)))
                        .with_material(Material().with_color(red())));

            frame.draw(renderable(Sphere(1.0))
                        .with_position(3.0, 3.0, 3.0));

            frame.draw(renderable(Sphere(1.0))
                        .with_position(3.0, 3.0, 6.0)
                        .with_material(Material().with_wireframe(true)));

            frame.draw(renderable(Box(1.0, 2.0, 1.0))
                        .with_position(-3.0, 3.0, 9.0)
                        .with_material(Material().with_wireframe(true)));

            frame.draw(renderable(Rectangle(10.0, 2.0))
                        .with_position(0.0, 0.0, 10.0)
                        .with_material(Material().with_pattern_grid_size(0.0f)));

            frame.draw(renderable(Rectangle(10.0, 2.0))
                        .with_position(0.0, 8.0, 10.0)
                        .with_material(Material().with_wireframe(true)));

            const auto yrot = AngleAxisf(1.57, Vector3f(0.0, 1.0, 0.0));
            const auto zrot = AngleAxisf(1.57, Vector3f(0.0, 0.0, 1.0));
            const auto model_color = Color(0.0, 0.6, 0.0);

            // Render a few instances of our example model. Note: It looks like the connectivity
            // of our model may be a little messed up, and as a result attempts to render the model
            // as a wireframe may give strange results.
            frame.draw(renderable(model)
                        .with_position(8.0, 8.0, 0.0)
                        .with_orientation(zrot * zrot * yrot * zrot)
                        .with_material(Material().with_pattern_grid_size(0.0f)
                                                 .with_color(model_color)));

            frame.draw(renderable(model)
                               .with_position(4.0, 4.0, 0.0)
                               .with_uniform_scale(0.3)
                               .with_orientation(zrot * zrot * zrot * yrot * zrot)
                               .with_material(Material().with_pattern_grid_size(0.0f)
                                                      .with_color(model_color)));

            frame.draw_line(Line(Vector3f(0.0, 0.0, 0.0), Vector3f(10.0, -5.0, 10.0)));

            const auto floor_color = Color(0.5f, 0.35f, 0.35f);
            frame.draw(renderable(Rectangle(20.0f, 20.0f))
                               .with_position(0.0f, 0.0f, 0.0f)
                               .with_material(Material().with_color(floor_color)));
        });
    }

    return 0;
}


#include <ros/ros.h>
#include <tuw_serialization/serializer.h>
#include <memory>
#include <opencv/cv.hpp>
#include <queue>
#include <string>

#include <boost/archive/xml_iarchive.hpp>
#include <boost/archive/xml_oarchive.hpp>

namespace tuw_graph
{
#define GRAPH_INFO_NAME     "graphInfo"
#define TREE_INFO_NAME      "treeInfo"
#define DATA_NAME           "graphData"
#define MAP_NAME            "map.png"

    Serializer::Serializer()
    {

    }

    std::size_t Serializer::getHash(const std::vector<signed char> &_map, const std::vector<double> &_parameters) const
    {
        std::size_t seed = 0;

        for(const double & val : _parameters)
        {
            boost::hash_combine(seed, val);
        }

        for(const signed char & val : _map)
        {
            boost::hash_combine(seed, val);
        }

        return seed;
    }

    bool Serializer::load(const std::string &_mapPath, std::vector<Segment> &_segs, Eigen::Vector2d &_origin, float &_resolution)
    {
        {
            boost::filesystem::path graf(_mapPath + GRAPH_INFO_NAME);
            boost::filesystem::path tree(_mapPath + TREE_INFO_NAME);
            boost::filesystem::path data(_mapPath + DATA_NAME);

            if(!boost::filesystem::exists(graf) | !boost::filesystem::exists(tree) | !boost::filesystem::exists(data) )
            {
                return false;
            }
        }

        GraphInfo g;

        std::ifstream ifs(_mapPath + GRAPH_INFO_NAME);
        assert(ifs.good());
        boost::archive::xml_iarchive xml(ifs);
        xml >> boost::serialization::make_nvp("GraphInfo", g);

        TreeInfo t(g.SegmentLength);

        std::ifstream ifti(_mapPath + TREE_INFO_NAME);
        assert(ifti.good());
        boost::archive::xml_iarchive xmlti(ifti);
        xmlti >> boost::serialization::make_nvp("TreeInfo", t);

        _origin[0] = g.Origin.x;
        _origin[1] = g.Origin.y;
        _resolution = g.Resolution;

        std::vector<SegmentSerializer> segs;

        int *pred = t.Predecessors.get();
        int *succ = t.Successors.get();
        int *pts = t.Points.get();

        for(int i = 0; i < t.Length; i++)
        {

            segs.emplace_back(pred[i], succ[i], pts[i]);
        }

        GraphSerializer graph(segs);
        std::ifstream ifsDist(_mapPath + DATA_NAME);
        boost::archive::xml_iarchive iaDist(ifsDist);
        iaDist >> boost::serialization::make_nvp("graph",graph);

        _segs.clear();

        //Generate Segment List
        for(int i = 0; i < graph.Length; i++)
        {
            std::vector<Eigen::Vector2d> pts;

            for(int j = 0; j < graph.segments_[i].pointLength; j++)
            {
                PointSerializer *ptPtr = graph.segments_[i].points.get();
                pts.emplace_back(ptPtr[j].x, ptPtr[j].y);
            }

            Segment s(pts, graph.segments_[i].minDistance);
            _segs.push_back(s);//std::make_shared<Segment>(pts, graph.segments_[i].minDistance));
        }

        //Add Dependancies
        for(int i = 0; i < graph.Length; i++)
        {
            std::vector<uint32_t> predecessors;
            std::vector<uint32_t> successors;

            for(int j = 0; j < graph.segments_[i].predecessorLength; j++)
            {
                int *predPtr = graph.segments_[i].predecessors.get();

                if(!_segs[i].containsPredecessor(predPtr[j]))
                    _segs[i].addPredecessor(predPtr[j]);
            }

            for(int j = 0; j < graph.segments_[i].successorLength; j++)
            {
                int *succPtr = graph.segments_[i].successors.get();

                if(!_segs[i].containsSuccessor(succPtr[j]))
                    _segs[i].addSuccessor(succPtr[j]);
            }

        }

        return true;
    }
    bool Serializer::load(const std::string &_mapPath, std::vector<Segment> &_segs, Eigen::Vector2d &_origin, float &_resolution, cv::Mat &_map){
        if(load(_mapPath, _segs, _origin, _resolution) && boost::filesystem::exists(boost::filesystem::path(_mapPath + MAP_NAME))){
             _map = cv::imread(_mapPath + MAP_NAME, cv::IMREAD_GRAYSCALE);
            return true;
        } else {
            return false;
        }
    }

    /**
     * @brief saves the map in the given location
     * @param mapInfo the name of the mapInfo file
     **/
    void Serializer::save(const std::string &_mapPath, const std::vector<Segment> &_segs, const Eigen::Vector2d &_origin, const float &_resolution)
    {
        if(!boost::filesystem::exists(_mapPath))
            boost::filesystem::create_directories(_mapPath);

        //Save map info (Length of segments)
        GraphInfo info(_origin, _resolution, _segs.size());
        std::ofstream ofs(_mapPath + GRAPH_INFO_NAME);
        assert(ofs.good());
        boost::archive::xml_oarchive oa(ofs);
        oa << boost::serialization::make_nvp("GraphInfo", info);
        ofs.close();

        //Save data strucutre info (Length pred, succ, points)
        TreeInfo tInfo(_segs);
        std::ofstream ofsTree(_mapPath + TREE_INFO_NAME);
        assert(ofsTree.good());
        boost::archive::xml_oarchive ot(ofsTree);
        ot << boost::serialization::make_nvp("TreeInfo", tInfo);
        ofsTree.close();

        //Save data
        std::vector<SegmentSerializer> segs;

        for(const auto & seg : _segs)
        {
            segs.emplace_back(seg);
        }

        GraphSerializer graph(segs);

        std::ofstream ofsGraph(_mapPath + DATA_NAME);
        boost::archive::xml_oarchive oaGraph(ofsGraph);
        oaGraph <<  boost::serialization::make_nvp("graph", graph);
        ofsGraph.close();

    }
    void Serializer::save(const std::string &_mapPath, const std::vector<Segment> &_segs, const Eigen::Vector2d &_origin, const float &_resolution, const cv::Mat &_map){
        save(_mapPath, _segs, _origin, _resolution);
        if(!boost::filesystem::exists(_mapPath))
            boost::filesystem::create_directories(_mapPath);
        if(_map.size != 0){
            cv::imwrite(_mapPath + MAP_NAME,  _map);
        }
    }

}


#ifndef _ZCJOINSPLIT_H_
#define _ZCJOINSPLIT_H_

#include "Address.hpp"
#include "IncrementalMerkleTree.hpp"
#include "Note.hpp"
#include "NoteEncryption.hpp"
#include "Proof.hpp"
#include "Zcash.h"

#include "uint252.h"
#include "uint256.h"

#include <boost/array.hpp>

namespace libzcash {

class JSInput {
public:
  ZCIncrementalWitness witness;
  Note note;
  SpendingKey key;

  JSInput();
  JSInput(ZCIncrementalWitness witness, Note note, SpendingKey key)
      : witness(witness), note(note), key(key) {}

  uint256 nullifier() const { return note.nullifier(key); }
};

class JSOutput {
public:
  PaymentAddress addr;
  uint64_t value;
  boost::array<unsigned char, ZC_MEMO_SIZE> memo = {
      {0xF6}}; // 0xF6 is invalid UTF8 as per spec, rest of array is 0x00

  JSOutput();
  JSOutput(PaymentAddress addr, uint64_t value) : addr(addr), value(value) {}

  Note note(const uint252 &phi, const uint256 &r, size_t i,
            const uint256 &h_sig) const;
};

template <size_t NumInputs, size_t NumOutputs> class JoinSplit {
public:
  virtual ~JoinSplit() {}

  static JoinSplit<NumInputs, NumOutputs> *Generate();
  static JoinSplit<NumInputs, NumOutputs> *Unopened();
  static uint256 h_sig(const uint256 &randomSeed,
                       const boost::array<uint256, NumInputs> &nullifiers,
                       const uint256 &pubKeyHash);

  // TODO: #789
  virtual void setProvingKeyPath(std::string) = 0;
  virtual void loadProvingKey() = 0;

  virtual void saveProvingKey(std::string path) = 0;
  virtual void loadVerifyingKey(std::string path) = 0;
  virtual void saveVerifyingKey(std::string path) = 0;
  virtual void saveR1CS(std::string path) = 0;

  virtual ZCProof
  prove(const boost::array<JSInput, NumInputs> &inputs,
        const boost::array<JSOutput, NumOutputs> &outputs,
        boost::array<Note, NumOutputs> &out_notes,
        boost::array<ZCNoteEncryption::Ciphertext, NumOutputs> &out_ciphertexts,
        uint256 &out_ephemeralKey, const uint256 &pubKeyHash,
        uint256 &out_randomSeed, boost::array<uint256, NumInputs> &out_hmacs,
        boost::array<uint256, NumInputs> &out_nullifiers,
        boost::array<uint256, NumOutputs> &out_commitments, uint64_t vpub_old,
        uint64_t vpub_new, const uint256 &rt, bool computeProof = true) = 0;

  virtual bool verify(const ZCProof &proof, ProofVerifier &verifier,
                      const uint256 &pubKeyHash, const uint256 &randomSeed,
                      const boost::array<uint256, NumInputs> &hmacs,
                      const boost::array<uint256, NumInputs> &nullifiers,
                      const boost::array<uint256, NumOutputs> &commitments,
                      uint64_t vpub_old, uint64_t vpub_new,
                      const uint256 &rt) = 0;

protected:
  JoinSplit() {}
};
}

typedef libzcash::JoinSplit<ZC_NUM_JS_INPUTS, ZC_NUM_JS_OUTPUTS> ZCJoinSplit;

#endif // _ZCJOINSPLIT_H_


#include <iostream>
#include <string>
#include <sstream>
#include <iomanip>
#include <Player.h>
#include <RationalValue.h>

#if 0
#include <boost/regex.hpp>

template <typename T>
int to_integer(const T &x){
	int ret = 0;
	for (auto &i : x){
		ret *= 10;
		ret += i - '0';
	}
	return ret;
}

template <typename T>
double to_double(const T &x){
	double ret = 0;
	double multiplier = 1;
	for (auto &i : x){
		multiplier /= 10;
		ret += (i - '0') * multiplier;
	}
	return ret;
}

double parse_time(const std::string &time_string){
	static boost::regex s   ("^(\\d+)$");
	static boost::regex sd  ("^(\\d+)\\.(\\d+)$");
	static boost::regex ms  ("^(\\d+)\\:(\\d{2})$");
	static boost::regex msd ("^(\\d+)\\:(\\d{2})\\.(\\d+)$");
	static boost::regex hms ("^(\\d+)\\:(\\d{2})\\:(\\d{2})$");
	static boost::regex hmsd("^(\\d+)\\:(\\d{2})\\:(\\d{2})\\.(\\d+)$");

	boost::smatch match;
	if (boost::regex_search(time_string, match, s))
		return to_integer(match[1]);
	if (boost::regex_search(time_string, match, sd))
		return to_integer(match[1]) + to_double(match[2]);
	if (boost::regex_search(time_string, match, ms))
		return to_integer(match[1]) * 60 + to_integer(match[2]);
	if (boost::regex_search(time_string, match, msd))
		return to_integer(match[1]) * 60 + to_integer(match[2]) + to_double(match[3]);
	if (boost::regex_search(time_string, match, hms))
		return (to_integer(match[1]) * 60 + to_integer(match[2])) * 60 + to_integer(match[3]);
	if (boost::regex_search(time_string, match, hmsd))
		return (to_integer(match[1]) * 60 + to_integer(match[2])) * 60 + to_integer(match[3]) + to_double(match[4]);
	return std::nan("");
}
#else
double parse_time(const char *time_string){
	const static double bad = std::nan("");

	double ret = 0;
	double multiplier = 1;
	int fragment = 0;
	int colons = 0;
	int fragment_length = 0;
	for (auto p = time_string; *p; p++){
		auto c = *p;
		switch (c){
			case '0':
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
			case '8':
			case '9':
				if (multiplier == 1){
					fragment *= 10;
					fragment += c - '0';
				}else{
					ret += (c - '0') * multiplier;
					multiplier /= 10;
				}
				fragment_length++;
				break;
			case ':':
				if (colons == 2 || multiplier != 1 || colons && fragment_length != 2)
					return bad;
				colons++;
				ret += fragment;
				ret *= 60;
				fragment = 0;
				fragment_length = 0;
				break;
			case '.':
				if (multiplier != 1)
					return bad;
				ret += fragment;
				multiplier /= 10;
				fragment_length = 0;
				break;
		}
	}
	if (!fragment_length)
		return bad;
	if (multiplier == 1){
		if (colons && fragment_length != 2)
			return bad;
		ret += fragment;
	}
	return ret;
}

double parse_time(const std::string &time_string){
	return parse_time(time_string.c_str());
}
#endif

int main(){
	try{
		Player player;
		while (true){
			std::cout << "> ";
			std::string line;
			std::getline(std::cin, line);

			if (line == "exit" || line == "quit")
				break;
			if (line == "help"){
				std::cout <<
					"Commands:\n"
					"output\n"
					"load\n"
					"play\n"
					"pause\n"
					"seek\n"
					"stop\n"
					"time\n";
			}else if (line == "output"){
				auto outputs = player.get_output_devices();
				if (!outputs.size()){
					std::cout << "There are no output devices available!\n";
					return 0;
				}
				size_t i = 1;
				size_t j = 0;
				do{
					for (auto &o : outputs){
						std::stringstream stream;
						stream << std::setw(2) << i++ << ". " << o.name;
						std::cout << stream.str() << std::endl;
					}
					std::getline(std::cin, line);
					std::stringstream stream(line);
					stream >> j;
				}while (j < 1 || j > outputs.size());
				player.open_output_device(outputs[j - 1].unique_id);
			}else if (line == "load"){
				std::cout << "Path: ";
				std::getline(std::cin, line);
				bool result = player.load_file(line);
				std::cout << (result ? "OK.\n" : "Failed.\n");
			}else if (line == "play"){
				player.play();
			}else if (line == "pause"){
				player.pause();
			}else if (line == "stop"){
				player.stop();
			}else if (line == "seek"){
				std::cout << "Time: ";
				std::getline(std::cin, line);
				auto time = parse_time(line);
				if (isnan(time)){
					std::cout << "Invalid time string.\n";
					continue;
				}
				player.seek(to_rational<std::int64_t, (std::int64_t)1 << 32>(time));
			}else if (line == "time"){
				auto t = player.current_time();
				std::cout << "Current time: " << absolute_format_time(t) << std::endl;
			}
		}
	}catch (std::exception &e){
		std::cerr << "Error: " << e.what() << std::endl;
		return -1;
	}
	return 0;
}


// For conditions of distribution and use, see copyright notice in license.txt

#include "StableHeaders.h"

#include "DebugOperatorNew.h"
#include <boost/algorithm/string.hpp>
#include <QList>
#include "MemoryLeakCheck.h"
#include "XMLUtilities.h"
#include "CoreStringUtils.h"

#include <QDomElement>

float ParseReal(const std::string& text, float default_value)
{
    float ret_value = default_value;
    try
    {
        ret_value = ParseString<float>(text);
    }
    catch(boost::bad_lexical_cast)
    {
    }
    return ret_value;
}

int ParseInt(const std::string& text, int default_value)
{
    int ret_value = default_value;
    try
    {
        ret_value = ParseString<int>(text);
    }
    catch(boost::bad_lexical_cast)
    {
    }
    return ret_value;
}
/*
Vector3df ParseVector3(const std::string& text)
{
    Vector3df vec(0.0f, 0.0f, 0.0f);

    StringVector components = SplitString(text, ' ');
    if (components.size() == 3)
    {
        try
        {
            vec.x = ParseString<float>(components[0]);
            vec.y = ParseString<float>(components[1]);
            vec.z = ParseString<float>(components[2]);
        }
        catch(boost::bad_lexical_cast)
        {
        }
    }
    return vec;
}
*/
Color ParseColor(const std::string& text)
{
    Color color(0.0f, 0.0f, 0.0f);

    StringVector components = SplitString(text, ' ');
    if (components.size() == 3)
    {
        try
        {
            color.r = ParseString<float>(components[0]);
            color.g = ParseString<float>(components[1]);
            color.b = ParseString<float>(components[2]);
        }
        catch(boost::bad_lexical_cast)
        {
        }
    }
    if (components.size() == 4)
    {
        try
        {
            color.r = ParseString<float>(components[0]);
            color.g = ParseString<float>(components[1]);
            color.b = ParseString<float>(components[2]);
            color.a = ParseString<float>(components[3]);
        }
        catch(boost::bad_lexical_cast)
        {
        }
    }
    return color;
}
/*
Quaternion ParseEulerAngles(const std::string& text)
{
    Quaternion quat;

    StringVector components = SplitString(text, ' ');
    if (components.size() == 3)
    {
        try
        {
            float xrad = degToRad(ParseString<float>(components[0]));
            float yrad = degToRad(ParseString<float>(components[1]));
            float zrad = degToRad(ParseString<float>(components[2]));

            float angle = yrad * 0.5;
            double cx = cos(angle);
            double sx = sin(angle);

            angle = zrad * 0.5;
            double cy = cos(angle);
            double sy = sin(angle);

            angle = xrad * 0.5;
            double cz = cos(angle);
            double sz = sin(angle);

            quat.x = sx * sy * cz + cx * cy * sz;
            quat.y = sx * cy * cz + cx * sy * sz;
            quat.z = cx * sy * cz - sx * cy * sz;
            quat.w = cx * cy * cz - sx * sy * sz;

            quat.normalize();
        }
        catch(boost::bad_lexical_cast)
        {
        }
    }

    return quat;
}
*/
std::string WriteBool(bool value)
{
    if (value)
        return "true";
    else
        return "false";
}

std::string WriteReal(float value)
{
    return ToString<float>(value);
}

std::string WriteInt(int value)
{
    return ToString<int>(value);
}
/*
std::string WriteVector3(const Vector3df& vector)
{
    return ToString<float>(vector.x) + " " +
        ToString<float>(vector.y) + " " +
        ToString<float>(vector.z);
}
*/
std::string WriteColor(const Color& color)
{
    return ToString<float>(color.r) + " " +
        ToString<float>(color.g) + " " +
        ToString<float>(color.b) + " " +
        ToString<float>(color.a);
}
/*
std::string WriteEulerAngles(const Quaternion& quat)
{
    Vector3df radians;
    quat.toEuler(radians);

    return ToString<float>(radians.x * RADTODEG) + " " +
        ToString<float>(radians.y * RADTODEG) + " " +
        ToString<float>(radians.z * RADTODEG);

    //Ogre::Matrix3 rotMatrix;
    //Ogre::Quaternion value = OgreRenderer::ToOgreQuaternion(quat);
    //value.ToRotationMatrix(rotMatrix);
    //Ogre::Radian anglex;
    //Ogre::Radian angley;
    //Ogre::Radian anglez;
    //rotMatrix.ToEulerAnglesXYZ(anglex, angley, anglez);

    //float angles[3];
    //angles[0] = anglex.valueDegrees();
    //angles[1] = angley.valueDegrees();
    //angles[2] = anglez.valueDegrees();
    //
    //return ToString<float>(angles[0]) + " " +
    //    ToString<float>(angles[1]) + " " +
    //    ToString<float>(angles[2]);
}
*/
void SetAttribute(QDomElement& elem, const std::string& name, const char* value)
{
    elem.setAttribute(QString::fromStdString(name), value);
}

void SetAttribute(QDomElement& elem, const std::string& name, const std::string& value)
{
    elem.setAttribute(QString::fromStdString(name), QString::fromStdString(value));
}

void SetAttribute(QDomElement& elem, const std::string& name, float value)
{
    elem.setAttribute(QString::fromStdString(name), QString::fromStdString(ToString<float>(value)));
}

void SetAttribute(QDomElement& elem, const std::string& name, bool value)
{
    elem.setAttribute(QString::fromStdString(name), QString::fromStdString(WriteBool(value)));
}

void SetAttribute(QDomElement& elem, const std::string& name, int value)
{
    elem.setAttribute(QString::fromStdString(name), QString::fromStdString(ToString<int>(value)));
}


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

/**
 * @file logic.hpp
 *
 * @brief Logic operations
 *
 * @author Mathias Soeken
 * @since  2.2
 */

#ifndef LOGIC_HPP
#define LOGIC_HPP

#include <boost/assign/std/vector.hpp>
#include <boost/range/adaptors.hpp>
#include <boost/range/algorithm_ext/push_back.hpp>

#include <classical/sat/sat_solver.hpp>

using namespace boost::assign;

namespace cirkit
{

template<class S>
inline void blocking_and( S& solver, int sel, int a, int b, int c )
{
  add_clause( solver )( {-sel, a, -c} );
  add_clause( solver )( {-sel, b, -c} );
  add_clause( solver )( {-sel, -a, -b, c} );
}

template<class S>
void blocking_and( S& solver, int sel, const clause_t& x, int c )
{
  using boost::adaptors::transformed;

  for ( auto l : x )
  {
    add_clause( solver )( {-sel, l, -c} );
  }
  clause_t clause = { -sel };
  boost::push_back( clause, x | transformed( []( int l ) { return -l; } ) );
  clause += c;
  add_clause( solver )( clause );
}

template<class S>
inline void blocking_or( S& solver, int sel, int a, int b, int c )
{
  add_clause( solver )( {-sel, -a, c} );
  add_clause( solver )( {-sel, -b, c} );
  add_clause( solver )( {-sel, a, b, -c} );
}

template<class S>
void blocking_or( S& solver, int sel, const clause_t& x, int c )
{
  for ( auto l : x )
  {
    add_clause( solver )( {-sel, -l, c} );
  }
  clause_t clause( x.begin(), x.end() );
  clause += -sel;
  clause += -c;
  add_clause( solver )( clause );
}

template<class S>
inline void blocking_xor( S& solver, int sel, int a, int b, int c )
{
  add_clause( solver )( {-sel, -a, b, c} );
  add_clause( solver )( {-sel, a, -b, c} );
  add_clause( solver )( {-sel, a, b, -c} );
  add_clause( solver )( {-sel, -a, -b, -c} );
}

template<class S>
inline void blocking_xnor( S& solver, int sel, int a, int b, int c )
{
  add_clause( solver )( {-sel, -a, b, -c} );
  add_clause( solver )( {-sel, a, -b, -c} );
  add_clause( solver )( {-sel, a, b, c} );
  add_clause( solver )( {-sel, -a, -b, c} );
}

template<class S>
inline void logic_and( S& solver, int a, int b, int c )
{
  add_clause( solver )( {a, -c} );
  add_clause( solver )( {b, -c} );
  add_clause( solver )( {-a, -b, c} );
}

template<class S>
void logic_and( S& solver, const clause_t& x, int c )
{
  using boost::adaptors::transformed;

  for ( auto l : x )
  {
    add_clause( solver )( {l, -c} );
  }
  clause_t clause;
  boost::push_back( clause, x | transformed( []( int l ) { return -l; } ) );
  clause += c;
  add_clause( solver )( clause );
}

template<class S>
inline void logic_or( S& solver, int a, int b, int c )
{
  add_clause( solver )( {-a, c} );
  add_clause( solver )( {-b, c} );
  add_clause( solver )( {a, b, -c} );
}

template<class S>
void logic_or( S& solver, const clause_t& x, int c )
{
  for ( auto l : x )
  {
    add_clause( solver )( {-l, c} );
  }
  clause_t clause( x.begin(), x.end() );
  clause += -c;
  add_clause( solver )( clause );
}

template<class S>
inline void logic_xor( S& solver, int a, int b, int c )
{
  add_clause( solver )( {-a, b, c} );
  add_clause( solver )( {a, -b, c} );
  add_clause( solver )( {a, b, -c} );
  add_clause( solver )( {-a, -b, -c} );
}

template<class S>
inline void logic_xnor( S& solver, int a, int b, int c )
{
  add_clause( solver )( {-a, b, -c} );
  add_clause( solver )( {a, -b, -c} );
  add_clause( solver )( {a, b, c} );
  add_clause( solver )( {-a, -b, c} );
}

}

#endif

// Local Variables:
// c-basic-offset: 2
// eval: (c-set-offset 'substatement-open 0)
// eval: (c-set-offset 'innamespace 0)
// End:


//==============================================================================
//         Copyright 2003 - 2011 LASMEA UMR 6602 CNRS/Univ. Clermont II
//         Copyright 2009 - 2011 LRI    UMR 8623 CNRS/Univ Paris Sud XI
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================
#ifndef BOOST_SIMD_TOOLBOX_PREDICATES_FUNCTIONS_SCALAR_SB2B_HPP_INCLUDED
#define BOOST_SIMD_TOOLBOX_PREDICATES_FUNCTIONS_SCALAR_SB2B_HPP_INCLUDED

#include <boost/simd/toolbox/predicates/functions/sb2b.hpp>
#include <boost/simd/include/constants/zero.hpp>
#include <boost/simd/include/constants/false.hpp>
#include <boost/simd/sdk/simd/logical.hpp>

/////////////////////////////////////////////////////////////////////////////
// Implementation when type  is fundamental_
/////////////////////////////////////////////////////////////////////////////
namespace boost { namespace simd { namespace ext
{
  BOOST_SIMD_FUNCTOR_IMPLEMENTATION( boost::simd::tag::sb2b_, tag::cpu_
                            , (A0)
                            , (scalar_< fundamental_<A0> >)
                            )
  {

    typedef A0 result_type;

    BOOST_SIMD_FUNCTOR_CALL(1)
    {
      return result_type(a0!= boost::simd::Zero<A0>());
    }
  };
  BOOST_SIMD_FUNCTOR_IMPLEMENTATION( boost::simd::tag::sb2b_, tag::cpu_
                            , (A0)
                            , (scalar_<logical_<A0> >)
                            )
  {

    typedef typename A0::value_type result_type;

    BOOST_SIMD_FUNCTOR_CALL(1)
    {
      return result_type(a0!= boost::simd::False<A0>());
    }
  };

} } }

#endif


/**
 * @author J. Santos <jamillo@gmail.com>
 * @date October 13, 2016
 */

#include <gtest/gtest.h>
#include <boost/filesystem/path.hpp>

#include "../../src/configuration/configuration.h"
#include "../resources.h"

GTEST_TEST(configuration_configuration, loadFromFile)
{
	mote::walking::Configuration configuration;
	configuration.loadFromFile((Resources::resources() / "config.json").string());

	ASSERT_EQ("Arash", configuration.robot);

	ASSERT_EQ(1000, configuration.walking.head.panSpeed);
	ASSERT_EQ(1000, configuration.walking.head.tiltSpeed);

	ASSERT_EQ(100, configuration.walking.minVoltage);

	ASSERT_EQ(471, configuration.walking.legLength);

	ASSERT_EQ(-0.07, configuration.walking.velocityOffset.x);
	ASSERT_EQ(0.0, configuration.walking.velocityOffset.y);
	ASSERT_EQ(0.001, configuration.walking.velocityOffset.theta);

	ASSERT_EQ(0.13, configuration.walking.engine.motionResolution);
	ASSERT_EQ(0.1, configuration.walking.engine.gaitFrequency);
	ASSERT_EQ(40, configuration.walking.engine.doubleSupportSleep);
	ASSERT_EQ(0, configuration.walking.engine.singleSupportSleep);
	ASSERT_EQ(0.005, configuration.walking.engine.flyGain.roll);
	ASSERT_EQ(0.8, configuration.walking.engine.flyGain.pitch);
	ASSERT_EQ(0, configuration.walking.engine.flyGain.yaw);
	ASSERT_EQ(0, configuration.walking.engine.flySwingGain.x);
	ASSERT_EQ(0.05, configuration.walking.engine.flySwingGain.y);
	ASSERT_EQ(0.8, configuration.walking.engine.flySwingGain.z);
	ASSERT_EQ(0.12, configuration.walking.engine.supportGain.roll);
	ASSERT_EQ(0, configuration.walking.engine.supportGain.pitch);
	ASSERT_EQ(0, configuration.walking.engine.supportGain.yaw);
	ASSERT_EQ(0, configuration.walking.engine.supportSwingGain.x);
	ASSERT_EQ(0, configuration.walking.engine.supportSwingGain.y);
	ASSERT_EQ(-0.03, configuration.walking.engine.supportSwingGain.z);
	ASSERT_EQ(-1.0, configuration.walking.engine.bodySwingGain.x);
	ASSERT_EQ(0.1, configuration.walking.engine.bodySwingGain.y);
	ASSERT_EQ(0, configuration.walking.engine.bodySwingGain.z);

	ASSERT_EQ(-3.5, configuration.walking.stabilizer.armGain.pitch);
	ASSERT_EQ(2.2, configuration.walking.stabilizer.armGain.roll);
	ASSERT_EQ(0, configuration.walking.stabilizer.armElbowGain);
	ASSERT_EQ(0, configuration.walking.stabilizer.hipGain.roll);
	ASSERT_EQ(-0.05, configuration.walking.stabilizer.hipGain.pitch);
	ASSERT_EQ(0, configuration.walking.stabilizer.kneeGain);
	ASSERT_EQ(-0.05, configuration.walking.stabilizer.footGain.pitch);
	ASSERT_EQ(0, configuration.walking.stabilizer.footGain.roll);
	ASSERT_EQ(2, configuration.walking.stabilizer.comShiftGain.x);
	ASSERT_EQ(40, configuration.walking.stabilizer.comShiftGain.y);

	ASSERT_EQ(-0.000001, configuration.walking.gyroStabilizer.armGain.pitch);
	ASSERT_EQ(0, configuration.walking.gyroStabilizer.armGain.roll);
	ASSERT_EQ(0, configuration.walking.gyroStabilizer.armElbowGain);
	ASSERT_EQ(0, configuration.walking.gyroStabilizer.hipGain.roll);
	ASSERT_EQ(0, configuration.walking.gyroStabilizer.hipGain.pitch);
	ASSERT_EQ(0, configuration.walking.gyroStabilizer.kneeGain);
	ASSERT_EQ(-0.000002, configuration.walking.gyroStabilizer.footGain.pitch);
	ASSERT_EQ(0, configuration.walking.gyroStabilizer.footGain.roll);
	ASSERT_EQ(0, configuration.walking.gyroStabilizer.comShiftGain.x);
	ASSERT_EQ(0, configuration.walking.gyroStabilizer.comShiftGain.y);

	ASSERT_EQ(0, configuration.walking.hoppingGaitGain.x);
	ASSERT_EQ(0, configuration.walking.hoppingGaitGain.y);

	ASSERT_EQ(-5, configuration.walking.com.positionOffset.x);
	ASSERT_EQ(35, configuration.walking.com.positionOffset.y);
	ASSERT_EQ(110, configuration.walking.com.positionOffset.z);

	ASSERT_EQ(0, configuration.walking.com.angleOffset.roll);
	ASSERT_EQ(0.032, configuration.walking.com.angleOffset.pitch);
	ASSERT_EQ(0, configuration.walking.com.angleOffset.yaw);

	ASSERT_EQ(0.1, configuration.walking.leftLeg.hipAngleOffset.pitch);
	ASSERT_EQ(0, configuration.walking.leftLeg.hipAngleOffset.yaw);
	ASSERT_EQ(0, configuration.walking.leftLeg.hipAngleOffset.roll);
	ASSERT_EQ(0, configuration.walking.leftLeg.kneeOffset);
	ASSERT_EQ(0, configuration.walking.leftLeg.footOffset.pitch);
	ASSERT_EQ(0, configuration.walking.leftLeg.footOffset.roll);

	ASSERT_EQ(0.1, configuration.walking.rightLeg.hipAngleOffset.pitch);
	ASSERT_EQ(0, configuration.walking.rightLeg.hipAngleOffset.yaw);
	ASSERT_EQ(0, configuration.walking.rightLeg.hipAngleOffset.roll);
	ASSERT_EQ(0, configuration.walking.rightLeg.kneeOffset);
	ASSERT_EQ(0, configuration.walking.rightLeg.footOffset.pitch);
	ASSERT_EQ(0, configuration.walking.rightLeg.footOffset.roll);

	ASSERT_EQ(0, configuration.walking.leftLeg.positionOffset.x);
	ASSERT_EQ(10, configuration.walking.leftLeg.positionOffset.y);
	ASSERT_EQ(-3, configuration.walking.leftLeg.positionOffset.z);
	ASSERT_EQ(0, configuration.walking.leftLeg.angleOffset.roll);
	ASSERT_EQ(0, configuration.walking.leftLeg.angleOffset.pitch);
	ASSERT_EQ(0, configuration.walking.leftLeg.angleOffset.yaw);

	ASSERT_EQ(-4, configuration.walking.rightLeg.positionOffset.x);
	ASSERT_EQ(10, configuration.walking.rightLeg.positionOffset.y);
	ASSERT_EQ(0, configuration.walking.rightLeg.positionOffset.z);
	ASSERT_EQ(0, configuration.walking.rightLeg.angleOffset.roll);
	ASSERT_EQ(0, configuration.walking.rightLeg.angleOffset.pitch);
	ASSERT_EQ(0, configuration.walking.rightLeg.angleOffset.yaw);

	ASSERT_EQ(-0.2, configuration.walking.rightArm.angleOffset.roll);
	ASSERT_EQ(-1.9, configuration.walking.rightArm.angleOffset.pitch);
	ASSERT_EQ(1.1, configuration.walking.rightArm.elbowOffset);

	ASSERT_EQ(-0.2, configuration.walking.leftArm.angleOffset.roll);
	ASSERT_EQ(-1.9, configuration.walking.leftArm.angleOffset.pitch);
	ASSERT_EQ(1.1, configuration.walking.leftArm.elbowOffset);

	ASSERT_EQ(0.055, configuration.walking.imuOffset.x);
	ASSERT_EQ(0.07, configuration.walking.imuOffset.y);

	ASSERT_EQ(0.8, configuration.walking.gyroLowpassGain.x);
	ASSERT_EQ(0.7, configuration.walking.gyroLowpassGain.y);

	ASSERT_EQ(40, configuration.walking.kalmanRmRate.roll);
	ASSERT_EQ(100, configuration.walking.kalmanRmRate.pitch);
	ASSERT_EQ(50, configuration.walking.kalmanRmRate.yaw);

	ASSERT_EQ(0, configuration.walking.smoothingRatio.x);
	ASSERT_EQ(0, configuration.walking.smoothingRatio.y);
	ASSERT_EQ(0, configuration.walking.smoothingRatio.z);

	ASSERT_TRUE(configuration.simu ? true : false);
	ASSERT_EQ("127.0.0.1", configuration.simu->address);
	ASSERT_EQ(4572, configuration.simu->port);

	ASSERT_TRUE(configuration.controller ? true : false);
	ASSERT_EQ("0.0.0.0", configuration.controller->address);
	ASSERT_EQ(4573, configuration.controller->port);
}


////////////////////////////////////////////////////////////////////////////////////////////////////
// logical_ops.cpp
//
//  Copyright 2012 Eric Niebler. 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)

#include <type_traits>
#include <boost/proto/v5/proto.hpp>
#include "./unit_test.hpp"

namespace mpl = boost::mpl;
namespace proto = boost::proto;
using proto::_;

using int_ = proto::literal<int>;
using cttrue_ = std::true_type();
using ctfalse_ = std::false_type();
using true_ = proto::_true;
using false_ = proto::_false;

namespace
{
    constexpr auto const & _0 = proto::utility::static_const<int_>::value;
}

void test_and()
{
    // strictly run-time boolean logic tests
    {
        auto r0 = proto::def<proto::and_(true_, true_)>()(_0);
        static_assert(std::is_same<decltype(r0), bool>::value, "");
        BOOST_CHECK(r0);

        auto r1 = proto::def<proto::and_(true_, false_)>()(_0);
        static_assert(std::is_same<decltype(r1), bool>::value, "");
        BOOST_CHECK(!r1);

        auto r2 = proto::def<proto::and_(false_, true_)>()(_0);
        static_assert(std::is_same<decltype(r2), bool>::value, "");
        BOOST_CHECK(!r2);

        auto r3 = proto::def<proto::and_(false_, false_)>()(_0);
        static_assert(std::is_same<decltype(r3), bool>::value, "");
        BOOST_CHECK(!r3);
    }

    // strictly compile-time boolean logic tests
    {
        std::true_type r0 = proto::def<proto::and_(cttrue_, cttrue_)>()(_0);

        std::false_type r1 = proto::def<proto::and_(cttrue_, ctfalse_)>()(_0);

        std::false_type r2 = proto::def<proto::and_(ctfalse_, cttrue_)>()(_0);

        std::false_type r3 = proto::def<proto::and_(ctfalse_, ctfalse_)>()(_0);

        BOOST_PROTO_IGNORE_UNUSED(r0, r1, r2, r3);
    }

    // mixed run-time/compile-time boolean logic tests
    {
        auto r0 = proto::def<proto::and_(cttrue_, true_)>()(_0);
        static_assert(std::is_same<decltype(r0), bool>::value, "");
        BOOST_CHECK(r0);

        auto r1 = proto::def<proto::and_(cttrue_, false_)>()(_0);
        static_assert(std::is_same<decltype(r1), bool>::value, "");
        BOOST_CHECK(!r1);

        auto r2 = proto::def<proto::and_(ctfalse_, true_)>()(_0);
        static_assert(std::is_same<decltype(r2), bool>::value, "");
        BOOST_CHECK(!r2);

        auto r3 = proto::def<proto::and_(ctfalse_, false_)>()(_0);
        static_assert(std::is_same<decltype(r3), bool>::value, "");
        BOOST_CHECK(!r3);

        auto r4 = proto::def<proto::and_(true_, cttrue_)>()(_0);
        static_assert(std::is_same<decltype(r4), bool>::value, "");
        BOOST_CHECK(r4);

        auto r5 = proto::def<proto::and_(true_, ctfalse_)>()(_0);
        static_assert(std::is_same<decltype(r5), bool>::value, "");
        BOOST_CHECK(!r5);

        auto r6 = proto::def<proto::and_(false_, cttrue_)>()(_0);
        static_assert(std::is_same<decltype(r6), bool>::value, "");
        BOOST_CHECK(!r6);

        auto r7 = proto::def<proto::and_(false_, ctfalse_)>()(_0);
        static_assert(std::is_same<decltype(r7), bool>::value, "");
        BOOST_CHECK(!r7);
    }

    // A nice, long, purely compile-time computation
    std::false_type r0 = proto::def<proto::and_(cttrue_ , cttrue_ , cttrue_ , ctfalse_, cttrue_ )>()(_0);
    BOOST_PROTO_IGNORE_UNUSED(r0);

    // A nice, long, mixed runtime/compile-time computation
    auto r1 = proto::def<proto::and_(cttrue_ , cttrue_ , cttrue_ , false_, cttrue_ )>()(_0);
    static_assert(std::is_same<decltype(r1), bool>::value, "");
    BOOST_CHECK(!r1);
}

void test_or()
{
    // strictly run-time boolean logic tests
    {
        auto r0 = proto::def<proto::or_(true_, true_)>()(_0);
        static_assert(std::is_same<decltype(r0), bool>::value, "");
        BOOST_CHECK(r0);

        auto r1 = proto::def<proto::or_(true_, false_)>()(_0);
        static_assert(std::is_same<decltype(r1), bool>::value, "");
        BOOST_CHECK(r1);

        auto r2 = proto::def<proto::or_(false_, true_)>()(_0);
        static_assert(std::is_same<decltype(r2), bool>::value, "");
        BOOST_CHECK(r2);

        auto r3 = proto::def<proto::or_(false_, false_)>()(_0);
        static_assert(std::is_same<decltype(r3), bool>::value, "");
        BOOST_CHECK(!r3);
    }

    // strictly compile-time boolean logic tests
    {
        std::true_type r0 = proto::def<proto::or_(cttrue_, cttrue_)>()(_0);

        std::true_type r1 = proto::def<proto::or_(cttrue_, ctfalse_)>()(_0);

        std::true_type r2 = proto::def<proto::or_(ctfalse_, cttrue_)>()(_0);

        std::false_type r3 = proto::def<proto::or_(ctfalse_, ctfalse_)>()(_0);

        BOOST_PROTO_IGNORE_UNUSED(r0, r1, r2, r3);
    }

    // mixed run-time/compile-time boolean logic tests
    {
        auto r0 = proto::def<proto::or_(cttrue_, true_)>()(_0);
        static_assert(std::is_same<decltype(r0), bool>::value, "");
        BOOST_CHECK(r0);

        auto r1 = proto::def<proto::or_(cttrue_, false_)>()(_0);
        static_assert(std::is_same<decltype(r1), bool>::value, "");
        BOOST_CHECK(r1);

        auto r2 = proto::def<proto::or_(ctfalse_, true_)>()(_0);
        static_assert(std::is_same<decltype(r2), bool>::value, "");
        BOOST_CHECK(r2);

        auto r3 = proto::def<proto::or_(ctfalse_, false_)>()(_0);
        static_assert(std::is_same<decltype(r3), bool>::value, "");
        BOOST_CHECK(!r3);

        auto r4 = proto::def<proto::or_(true_, cttrue_)>()(_0);
        static_assert(std::is_same<decltype(r4), bool>::value, "");
        BOOST_CHECK(r4);

        auto r5 = proto::def<proto::or_(true_, ctfalse_)>()(_0);
        static_assert(std::is_same<decltype(r5), bool>::value, "");
        BOOST_CHECK(r5);

        auto r6 = proto::def<proto::or_(false_, cttrue_)>()(_0);
        static_assert(std::is_same<decltype(r6), bool>::value, "");
        BOOST_CHECK(r6);

        auto r7 = proto::def<proto::or_(false_, ctfalse_)>()(_0);
        static_assert(std::is_same<decltype(r7), bool>::value, "");
        BOOST_CHECK(!r7);
    }

    // A nice, long, purely compile-time computation
    std::true_type r0 = proto::def<proto::or_(ctfalse_, ctfalse_, ctfalse_, cttrue_, ctfalse_)>()(_0);
    BOOST_PROTO_IGNORE_UNUSED(r0);

    // A nice, long, mixed runtime/compile-time computation
    auto r1 = proto::def<proto::or_(ctfalse_, ctfalse_, ctfalse_, true_, ctfalse_)>()(_0);
    static_assert(std::is_same<decltype(r1), bool>::value, "");
    BOOST_CHECK(r1);
}

// Negating a bool should yield a bool. Negating a std::false_type should yield
// a std::true_type, and vice versa.
void test_not()
{
    auto x = proto::def<proto::not_(true_)>()(_0);
    static_assert(std::is_same<decltype(x), bool>::value, "");
    BOOST_CHECK(!x);

    std::false_type y = proto::def<proto::not_(cttrue_)>()(_0);

    std::true_type z = proto::def<proto::not_(ctfalse_)>()(_0);

    BOOST_PROTO_IGNORE_UNUSED(x, y, z);
}

using namespace boost::unit_test;
////////////////////////////////////////////////////////////////////////////////////////////////////
// init_unit_test_suite
//
test_suite* init_unit_test_suite( int argc, char* argv[] )
{
    test_suite *test = BOOST_TEST_SUITE("basic tests for the logical operators");

    test->add(BOOST_TEST_CASE(&test_and));
    test->add(BOOST_TEST_CASE(&test_or));
    test->add(BOOST_TEST_CASE(&test_not));

    return test;
}


// $Id$
// vim:tabstop=2

/***********************************************************************
Moses - factored phrase-based language decoder
Copyright (C) 2014- University of Edinburgh

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
***********************************************************************/

/**
  * Compute paraphrases from the phrase table
**/
#include <cmath>
#include <iostream>
#include <map>

#include <boost/program_options.hpp>

#include "util/double-conversion/double-conversion.h"
#include "util/exception.hh"
#include "util/file_piece.hh"
#include "util/string_piece.hh"
#include "util/tokenize_piece.hh"

//using namespace Moses;
using namespace std;

namespace po = boost::program_options;

typedef multimap<float,string> Probs;

static float threshold = 1e-04;
static size_t maxE = 10000; //histogram pruning

static void add(const string& e, const vector<float> scores,
  Probs& p_e_given_f, Probs& p_f_given_e) {
  if (scores[0] > threshold) {
    p_f_given_e.insert(pair<float,string>(scores[0],e));
  }
  while(p_f_given_e.size() > maxE) p_f_given_e.erase(p_f_given_e.begin());
  if (scores[2] > threshold) {
    p_e_given_f.insert(pair<float,string>(scores[2],e));
  }
  while(p_e_given_f.size() > maxE) p_e_given_f.erase(p_e_given_f.begin());
}

static void finalise(Probs& p_e_given_f, Probs& p_f_given_e) {
  //cerr << "Sizes: p(e|f): " << p_e_given_f.size() << " p(f|e): " << p_f_given_e.size() << endl;
  for (Probs::const_iterator e1_iter = p_f_given_e.begin() ;
          e1_iter !=  p_f_given_e.end(); ++e1_iter) {
    for (Probs::const_iterator e2_iter = p_e_given_f.begin() ;
          e2_iter != p_e_given_f.end(); ++e2_iter) {

      if (e1_iter->second == e2_iter->second) continue;
      cout << e1_iter->second << " ||| " << e2_iter->second << " ||| " <<
        e1_iter->first * e2_iter->first << " |||  " << endl;
    }
  }
  p_e_given_f.clear();
  p_f_given_e.clear();
}

int main(int argc, char** argv) {

  string input_file;

  po::options_description desc("Allowed options");
  desc.add_options()
    ("help,h", "Print help message and exit")
    ("threshold,t", po::value<float>(&threshold), "Threshold for p(e|f) and p(f|e)")
    ("max-target,m", po::value<size_t>(&maxE), "Maximum number of target phrases")
    ("input-file", po::value<string>(&input_file)->required(), "Input phrase table")
    ;

  po::positional_options_description pos;
  pos.add("input-file",1);

  po::variables_map vm;
  po::store(po::command_line_parser(argc,argv).options(desc).positional(pos).run(), vm);

  if (vm.count("help")) {
    cerr << "Usage: " << string(argv[0]) + " [options] input-file" << endl;
    cerr << desc << endl;
    return 0;
  }

  po::notify(vm);

  cerr << "Reading from " << input_file << endl;
  util::FilePiece in(input_file.c_str(), &std::cerr);
  vector<float> scoreVector;
  StringPiece line;
  double_conversion::StringToDoubleConverter converter(double_conversion::StringToDoubleConverter::NO_FLAGS, NAN, NAN, "inf", "nan");

  string previousSourcePhrase;
  Probs p_f_given_e_table;
  Probs p_e_given_f_table;

  size_t count = 0;
  while(true) {
    try {
      line = in.ReadLine();
    } catch (const util::EndOfFileException &e) {
      break;
    }
    ++count;

    util::TokenIter<util::MultiCharacter> pipes(line, " ||| ");
    StringPiece sourcePhrase(*pipes);
    StringPiece targetPhrase(*++pipes);
    StringPiece scoreString(*++pipes);
    scoreVector.clear();
    for (util::TokenIter<util::AnyCharacter, true> s(scoreString, " \t"); s; ++s) {
      int processed;
      float score = converter.StringToFloat(s->data(), s->length(), &processed);
      UTIL_THROW_IF2(isnan(score), "Bad score " << *s << " on line " << count);
      scoreVector.push_back(score);
    }

    if (sourcePhrase.size() && sourcePhrase != previousSourcePhrase) {
      finalise(p_e_given_f_table, p_f_given_e_table);
    }
    add(targetPhrase.as_string(),scoreVector, p_e_given_f_table, p_f_given_e_table);
    previousSourcePhrase = sourcePhrase.as_string();
  }
  finalise(p_e_given_f_table, p_f_given_e_table);

  return 0;
}