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

#include <stdexcept>
#include <boost/foreach.hpp>
#include <boost/regex.hpp>

#include "slg/film/film.h"
#include "slg/film/imagepipeline/plugins/cameraresponse.h"
#include "slg/film/imagepipeline/plugins/cameraresponsefunctions.h"

using namespace std;
using namespace luxrays;
using namespace slg;

//------------------------------------------------------------------------------
// CameraResponse filter plugin
//------------------------------------------------------------------------------

BOOST_CLASS_EXPORT_IMPLEMENT(slg::CameraResponsePlugin)

ImagePipelinePlugin *CameraResponsePlugin::Copy() const {
	CameraResponsePlugin *crp = new CameraResponsePlugin();
	crp->color = color;
	crp->RedI = RedI;
	crp->RedB = RedB;
	crp->GreenI = GreenI;
	crp->GreenB = GreenB;
	crp->BlueI = BlueI;
	crp->BlueB = BlueB;

	return crp;
}

// Estimates gamma in y = x^gamma using Gauss-Newton iterations
static float EstimateGamma(const vector<float> &x, const vector<float> &y, float *rmse) {
	const size_t n = x.size();

	double gamma = 1.0; // Initial guess
	int iter = 0;

	double last_ssr = 1e30;

	while (iter < 100) {
		double ssr = 0;
		double JTr = 0.0;
		double JTJ = 0.0;

		size_t i;

		for (i = 0; i < n; i++) {
			// log() doesn't behave nice near zero
			if (x[i] < 1e-12)
				continue;

			// pow() has issues with small but negative values of gamma
			// this is more stable
			const double lx = log(x[i]);
			const double xg = exp(gamma * lx);

			// residual
			double r = y[i] - xg;
			ssr += r*r;

			// jacobian
			double J = lx * xg;

			JTr += J * r;
			JTJ += J * J;
		}

		if (rmse)
			*rmse = static_cast<float>(sqrt(ssr/i));

		if (fabs(ssr - last_ssr) < 1e-6)
			break;

		const double delta = JTr / JTJ;

		if (fabs(delta) < 1e-9)
			break;

		gamma = gamma + delta;

		last_ssr = ssr;

		iter++;
	}

	return static_cast<float>(gamma);
}

static void AdjustGamma(const vector<float> &from, vector<float> &to, float gamma = 2.2f) {
	for (size_t i = 0; i < from.size(); i++)
		to[i] = powf(to[i], gamma);
}

CameraResponsePlugin::CameraResponsePlugin(const string &name) {
	if (!LoadPreset(name))
		LoadFile(name);

	// Compensate for built-in gamma. Use Y for gamma estimation.
	vector<float> YI, YB;

	// use all unique input values
	YI.insert(YI.end(), RedI.begin(), RedI.end());
	YI.insert(YI.end(), GreenI.begin(), GreenI.end());
	YI.insert(YI.end(), BlueI.begin(), BlueI.end());

	sort(YI.begin(), YI.end());
	const size_t n = unique(YI.begin(), YI.end()) - YI.begin();

	YI.resize(n);
	YB.resize(n);

	for (size_t i = 0; i < n; i++) {
		RGBColor c(YI[i]);

		// Map handles color / monochrome and interpolation
		Map(c);

		YB[i] = c.Y();
	}

	float rmse;
	const float sourceGamma = EstimateGamma(YI, YB, &rmse);

	// Compensate for built-in gamma
	AdjustGamma(RedI, RedB, 1.f / sourceGamma);
	AdjustGamma(GreenI, GreenB, 1.f / sourceGamma);
	AdjustGamma(BlueI, BlueB, 1.f / sourceGamma);
}

void CameraResponsePlugin::Apply(const Film &film, Spectrum *pixels, vector<bool> &pixelsMask) const {
	const u_int pixelCount = film.GetWidth() * film.GetHeight();

	for (u_int i = 0; i < pixelCount; ++i) {
		if (pixelsMask[i])
			Map(pixels[i]);
	}
}

void CameraResponsePlugin::Map(RGBColor &rgb) const {
	if (color) {
		rgb.c[0] = ApplyCrf(rgb.c[0], RedI, RedB);
		rgb.c[1] = ApplyCrf(rgb.c[1], GreenI, GreenB);
		rgb.c[2] = ApplyCrf(rgb.c[2], BlueI, BlueB);
	} else {
		const float y = rgb.Y();
		rgb.c[0] = rgb.c[1] = rgb.c[2] = ApplyCrf(y, RedI, RedB);
	}
}

float CameraResponsePlugin::ApplyCrf(float point, const vector<float> &from, const vector<float> &to) const {
	if (point <= from.front())
		return to.front();
	if (point >= from.back())
		return to.back();

	int index = upper_bound(from.begin(), from.end(), point) -
		from.begin();
	float x1 = from[index - 1];
	float x2 = from[index];
	float y1 = to[index - 1];
	float y2 = to[index];
	return Lerp((point - x1) / (x2 - x1), y1, y2);
}

void CameraResponsePlugin::LoadFile(const string &filmName) {
	ifstream file(filmName.c_str());
	if (!file)
		throw runtime_error("Unable to open Camera Response Function file: " + filmName);

	string crfdata;
	{
		// Read file into memory
		stringstream ss("");
		ss << file.rdbuf();
		crfdata = ss.str();
	}

	boost::regex func_expr("(?-s)(?<funcname>\\S+)\\s+graph.+\\s+I\\s*=\\s*(?<I>.+$)\\s+B\\s*=\\s*(?<B>.+$)");
	boost::regex channel_expr("^(?<name>.*)(?<channel>Red|Green|Blue)$");
	boost::regex float_expr("-?\\d*\\.?\\d+(?:[eE][-+]?\\d+)?");

	boost::sregex_iterator rit(crfdata.begin(), crfdata.end(), func_expr);
	boost::sregex_iterator rend;

	bool red, green, blue;
	red = green = blue = false;

	string crfname("");

	for (; rit != rend; ++rit) {
		const boost::smatch &m = *rit;
		boost::smatch cm;

		// Smatch references input string, keep copy
		const string funcname = m["funcname"].str();
		string name = funcname;
		string channel = "Red";

		if (boost::regex_match(funcname, cm, channel_expr)) {
			name = cm["name"].str();
			channel = cm["channel"].str();
		}
		const string Istr = m["I"].str();
		const string Bstr = m["B"].str();

		if (crfname == "")
			crfname = name;
		else if (crfname != name)
			throw runtime_error("Expected Camera Response Function name '" + crfname + "' but found '" + name + "'");

		vector<float> *I = NULL;
		vector<float> *B = NULL;
		bool *channel_flag = NULL;

		if (channel == "Red") {
			I = &RedI;
			B = &RedB;
			channel_flag = &red;
		} else if (channel == "Green") {
			I = &GreenI;
			B = &GreenB;
			channel_flag = &green;
		} else if (channel == "Blue") {
			I = &BlueI;
			B = &BlueB;
			channel_flag = &blue;
		} else
			throw runtime_error("Error parsing Camera Response file");

		if (*channel_flag) {
			SLG_LOG("WARNING: " << channel << " channel already specified in '" << filmName << "', ignoring");
			continue;
		}

		// Parse functions
		for (boost::sregex_iterator rit(Istr.begin(), Istr.end(), float_expr); rit != rend; ++rit) {
			I->push_back(boost::lexical_cast<float>(rit->str(0)));
		}

		for (boost::sregex_iterator rit(Bstr.begin(), Bstr.end(), float_expr); rit != rend; ++rit) {
			B->push_back(boost::lexical_cast<float>(rit->str(0)));
		}

		*channel_flag = I->size() == B->size();

		if (!(*channel_flag) || I->empty()) {
			SLG_LOG("WARNING: Inconsistent " << channel << " data for '" << filmName << "', ignoring");
			I->clear();
			B->clear();
			*channel_flag = false;
		}

		color = red && green && blue;
		if (color)
			break;
	}

	// Need at least red for either monochrome or color
	if (!red)
		throw runtime_error("No valid Camera Response Functions in: " + filmName);
}

bool CameraResponsePlugin::LoadPreset(const string &filmName) {
/*
	Valid preset names (case sensitive):

	Advantix_100CD
	Advantix_200CD
	Advantix_400CD
	Agfachrome_ctpecisa_200CD
	Agfachrome_ctprecisa_100CD
	Agfachrome_rsx2_050CD
	Agfachrome_rsx2_100CD
	Agfachrome_rsx2_200CD
	Agfacolor_futura_100CD
	Agfacolor_futura_200CD
	Agfacolor_futura_400CD
	Agfacolor_futuraII_100CD
	Agfacolor_futuraII_200CD
	Agfacolor_futuraII_400CD
	Agfacolor_hdc_100_plusCD
	Agfacolor_hdc_200_plusCD
	Agfacolor_hdc_400_plusCD
	Agfacolor_optimaII_100CD
	Agfacolor_optimaII_200CD
	Agfacolor_ultra_050_CD
	Agfacolor_vista_100CD
	Agfacolor_vista_200CD
	Agfacolor_vista_400CD
	Agfacolor_vista_800CD
	Agfapan_apx_025CD
	Agfapan_apx_100CD
	Agfapan_apx_400CD
	Ektachrome_100_plusCD
	Ektachrome_100CD
	Ektachrome_320TCD
	Ektachrome_400XCD
	Ektachrome_64CD
	Ektachrome_64TCD
	Ektachrome_E100SCD
	F125CD
	F250CD
	F400CD
	FCICD
	Gold_100CD
	Gold_200CD
	Kodachrome_200CD
	Kodachrome_25CD
	Kodachrome_64CD
	Max_Zoom_800CD
	Portra_100TCD
	Portra_160NCCD
	Portra_160VCCD
	Portra_400NCCD
	Portra_400VCCD
	Portra_800CD
*/

	// Generated by make_embedded_crfs.py
	if (filmName == "Advantix_100CD") {
		const size_t nRed = sizeof(Advantix_100CDRed_I) / sizeof(Advantix_100CDRed_I[0]);
		RedI.assign(Advantix_100CDRed_I, Advantix_100CDRed_I + nRed);
		RedB.assign(Advantix_100CDRed_B, Advantix_100CDRed_B + nRed);
		const size_t nGreen = sizeof(Advantix_100CDGreen_I) / sizeof(Advantix_100CDGreen_I[0]);
		GreenI.assign(Advantix_100CDGreen_I, Advantix_100CDGreen_I + nGreen);
		GreenB.assign(Advantix_100CDGreen_B, Advantix_100CDGreen_B + nGreen);
		const size_t nBlue = sizeof(Advantix_100CDBlue_I) / sizeof(Advantix_100CDBlue_I[0]);
		BlueI.assign(Advantix_100CDBlue_I, Advantix_100CDBlue_I + nBlue);
		BlueB.assign(Advantix_100CDBlue_B, Advantix_100CDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Advantix_200CD") {
		const size_t nRed = sizeof(Advantix_200CDRed_I) / sizeof(Advantix_200CDRed_I[0]);
		RedI.assign(Advantix_200CDRed_I, Advantix_200CDRed_I + nRed);
		RedB.assign(Advantix_200CDRed_B, Advantix_200CDRed_B + nRed);
		const size_t nGreen = sizeof(Advantix_200CDGreen_I) / sizeof(Advantix_200CDGreen_I[0]);
		GreenI.assign(Advantix_200CDGreen_I, Advantix_200CDGreen_I + nGreen);
		GreenB.assign(Advantix_200CDGreen_B, Advantix_200CDGreen_B + nGreen);
		const size_t nBlue = sizeof(Advantix_200CDBlue_I) / sizeof(Advantix_200CDBlue_I[0]);
		BlueI.assign(Advantix_200CDBlue_I, Advantix_200CDBlue_I + nBlue);
		BlueB.assign(Advantix_200CDBlue_B, Advantix_200CDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Advantix_400CD") {
		const size_t nRed = sizeof(Advantix_400CDRed_I) / sizeof(Advantix_400CDRed_I[0]);
		RedI.assign(Advantix_400CDRed_I, Advantix_400CDRed_I + nRed);
		RedB.assign(Advantix_400CDRed_B, Advantix_400CDRed_B + nRed);
		const size_t nGreen = sizeof(Advantix_400CDGreen_I) / sizeof(Advantix_400CDGreen_I[0]);
		GreenI.assign(Advantix_400CDGreen_I, Advantix_400CDGreen_I + nGreen);
		GreenB.assign(Advantix_400CDGreen_B, Advantix_400CDGreen_B + nGreen);
		const size_t nBlue = sizeof(Advantix_400CDBlue_I) / sizeof(Advantix_400CDBlue_I[0]);
		BlueI.assign(Advantix_400CDBlue_I, Advantix_400CDBlue_I + nBlue);
		BlueB.assign(Advantix_400CDBlue_B, Advantix_400CDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Agfachrome_ctpecisa_200CD") {
		const size_t nRed = sizeof(Agfachrome_ctpecisa_200CDRed_I) / sizeof(Agfachrome_ctpecisa_200CDRed_I[0]);
		RedI.assign(Agfachrome_ctpecisa_200CDRed_I, Agfachrome_ctpecisa_200CDRed_I + nRed);
		RedB.assign(Agfachrome_ctpecisa_200CDRed_B, Agfachrome_ctpecisa_200CDRed_B + nRed);
		const size_t nGreen = sizeof(Agfachrome_ctpecisa_200CDGreen_I) / sizeof(Agfachrome_ctpecisa_200CDGreen_I[0]);
		GreenI.assign(Agfachrome_ctpecisa_200CDGreen_I, Agfachrome_ctpecisa_200CDGreen_I + nGreen);
		GreenB.assign(Agfachrome_ctpecisa_200CDGreen_B, Agfachrome_ctpecisa_200CDGreen_B + nGreen);
		const size_t nBlue = sizeof(Agfachrome_ctpecisa_200CDBlue_I) / sizeof(Agfachrome_ctpecisa_200CDBlue_I[0]);
		BlueI.assign(Agfachrome_ctpecisa_200CDBlue_I, Agfachrome_ctpecisa_200CDBlue_I + nBlue);
		BlueB.assign(Agfachrome_ctpecisa_200CDBlue_B, Agfachrome_ctpecisa_200CDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Agfachrome_ctprecisa_100CD") {
		const size_t nRed = sizeof(Agfachrome_ctprecisa_100CDRed_I) / sizeof(Agfachrome_ctprecisa_100CDRed_I[0]);
		RedI.assign(Agfachrome_ctprecisa_100CDRed_I, Agfachrome_ctprecisa_100CDRed_I + nRed);
		RedB.assign(Agfachrome_ctprecisa_100CDRed_B, Agfachrome_ctprecisa_100CDRed_B + nRed);
		const size_t nGreen = sizeof(Agfachrome_ctprecisa_100CDGreen_I) / sizeof(Agfachrome_ctprecisa_100CDGreen_I[0]);
		GreenI.assign(Agfachrome_ctprecisa_100CDGreen_I, Agfachrome_ctprecisa_100CDGreen_I + nGreen);
		GreenB.assign(Agfachrome_ctprecisa_100CDGreen_B, Agfachrome_ctprecisa_100CDGreen_B + nGreen);
		const size_t nBlue = sizeof(Agfachrome_ctprecisa_100CDBlue_I) / sizeof(Agfachrome_ctprecisa_100CDBlue_I[0]);
		BlueI.assign(Agfachrome_ctprecisa_100CDBlue_I, Agfachrome_ctprecisa_100CDBlue_I + nBlue);
		BlueB.assign(Agfachrome_ctprecisa_100CDBlue_B, Agfachrome_ctprecisa_100CDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Agfachrome_rsx2_050CD") {
		const size_t nRed = sizeof(Agfachrome_rsx2_050CDRed_I) / sizeof(Agfachrome_rsx2_050CDRed_I[0]);
		RedI.assign(Agfachrome_rsx2_050CDRed_I, Agfachrome_rsx2_050CDRed_I + nRed);
		RedB.assign(Agfachrome_rsx2_050CDRed_B, Agfachrome_rsx2_050CDRed_B + nRed);
		const size_t nGreen = sizeof(Agfachrome_rsx2_050CDGreen_I) / sizeof(Agfachrome_rsx2_050CDGreen_I[0]);
		GreenI.assign(Agfachrome_rsx2_050CDGreen_I, Agfachrome_rsx2_050CDGreen_I + nGreen);
		GreenB.assign(Agfachrome_rsx2_050CDGreen_B, Agfachrome_rsx2_050CDGreen_B + nGreen);
		const size_t nBlue = sizeof(Agfachrome_rsx2_050CDBlue_I) / sizeof(Agfachrome_rsx2_050CDBlue_I[0]);
		BlueI.assign(Agfachrome_rsx2_050CDBlue_I, Agfachrome_rsx2_050CDBlue_I + nBlue);
		BlueB.assign(Agfachrome_rsx2_050CDBlue_B, Agfachrome_rsx2_050CDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Agfachrome_rsx2_100CD") {
		const size_t nRed = sizeof(Agfachrome_rsx2_100CDRed_I) / sizeof(Agfachrome_rsx2_100CDRed_I[0]);
		RedI.assign(Agfachrome_rsx2_100CDRed_I, Agfachrome_rsx2_100CDRed_I + nRed);
		RedB.assign(Agfachrome_rsx2_100CDRed_B, Agfachrome_rsx2_100CDRed_B + nRed);
		const size_t nGreen = sizeof(Agfachrome_rsx2_100CDGreen_I) / sizeof(Agfachrome_rsx2_100CDGreen_I[0]);
		GreenI.assign(Agfachrome_rsx2_100CDGreen_I, Agfachrome_rsx2_100CDGreen_I + nGreen);
		GreenB.assign(Agfachrome_rsx2_100CDGreen_B, Agfachrome_rsx2_100CDGreen_B + nGreen);
		const size_t nBlue = sizeof(Agfachrome_rsx2_100CDBlue_I) / sizeof(Agfachrome_rsx2_100CDBlue_I[0]);
		BlueI.assign(Agfachrome_rsx2_100CDBlue_I, Agfachrome_rsx2_100CDBlue_I + nBlue);
		BlueB.assign(Agfachrome_rsx2_100CDBlue_B, Agfachrome_rsx2_100CDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Agfachrome_rsx2_200CD") {
		const size_t nRed = sizeof(Agfachrome_rsx2_200CDRed_I) / sizeof(Agfachrome_rsx2_200CDRed_I[0]);
		RedI.assign(Agfachrome_rsx2_200CDRed_I, Agfachrome_rsx2_200CDRed_I + nRed);
		RedB.assign(Agfachrome_rsx2_200CDRed_B, Agfachrome_rsx2_200CDRed_B + nRed);
		const size_t nGreen = sizeof(Agfachrome_rsx2_200CDGreen_I) / sizeof(Agfachrome_rsx2_200CDGreen_I[0]);
		GreenI.assign(Agfachrome_rsx2_200CDGreen_I, Agfachrome_rsx2_200CDGreen_I + nGreen);
		GreenB.assign(Agfachrome_rsx2_200CDGreen_B, Agfachrome_rsx2_200CDGreen_B + nGreen);
		const size_t nBlue = sizeof(Agfachrome_rsx2_200CDBlue_I) / sizeof(Agfachrome_rsx2_200CDBlue_I[0]);
		BlueI.assign(Agfachrome_rsx2_200CDBlue_I, Agfachrome_rsx2_200CDBlue_I + nBlue);
		BlueB.assign(Agfachrome_rsx2_200CDBlue_B, Agfachrome_rsx2_200CDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Agfacolor_futura_100CD") {
		const size_t nRed = sizeof(Agfacolor_futura_100CDRed_I) / sizeof(Agfacolor_futura_100CDRed_I[0]);
		RedI.assign(Agfacolor_futura_100CDRed_I, Agfacolor_futura_100CDRed_I + nRed);
		RedB.assign(Agfacolor_futura_100CDRed_B, Agfacolor_futura_100CDRed_B + nRed);
		const size_t nGreen = sizeof(Agfacolor_futura_100CDGreen_I) / sizeof(Agfacolor_futura_100CDGreen_I[0]);
		GreenI.assign(Agfacolor_futura_100CDGreen_I, Agfacolor_futura_100CDGreen_I + nGreen);
		GreenB.assign(Agfacolor_futura_100CDGreen_B, Agfacolor_futura_100CDGreen_B + nGreen);
		const size_t nBlue = sizeof(Agfacolor_futura_100CDBlue_I) / sizeof(Agfacolor_futura_100CDBlue_I[0]);
		BlueI.assign(Agfacolor_futura_100CDBlue_I, Agfacolor_futura_100CDBlue_I + nBlue);
		BlueB.assign(Agfacolor_futura_100CDBlue_B, Agfacolor_futura_100CDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Agfacolor_futura_200CD") {
		const size_t nRed = sizeof(Agfacolor_futura_200CDRed_I) / sizeof(Agfacolor_futura_200CDRed_I[0]);
		RedI.assign(Agfacolor_futura_200CDRed_I, Agfacolor_futura_200CDRed_I + nRed);
		RedB.assign(Agfacolor_futura_200CDRed_B, Agfacolor_futura_200CDRed_B + nRed);
		const size_t nGreen = sizeof(Agfacolor_futura_200CDGreen_I) / sizeof(Agfacolor_futura_200CDGreen_I[0]);
		GreenI.assign(Agfacolor_futura_200CDGreen_I, Agfacolor_futura_200CDGreen_I + nGreen);
		GreenB.assign(Agfacolor_futura_200CDGreen_B, Agfacolor_futura_200CDGreen_B + nGreen);
		const size_t nBlue = sizeof(Agfacolor_futura_200CDBlue_I) / sizeof(Agfacolor_futura_200CDBlue_I[0]);
		BlueI.assign(Agfacolor_futura_200CDBlue_I, Agfacolor_futura_200CDBlue_I + nBlue);
		BlueB.assign(Agfacolor_futura_200CDBlue_B, Agfacolor_futura_200CDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Agfacolor_futura_400CD") {
		const size_t nRed = sizeof(Agfacolor_futura_400CDRed_I) / sizeof(Agfacolor_futura_400CDRed_I[0]);
		RedI.assign(Agfacolor_futura_400CDRed_I, Agfacolor_futura_400CDRed_I + nRed);
		RedB.assign(Agfacolor_futura_400CDRed_B, Agfacolor_futura_400CDRed_B + nRed);
		const size_t nGreen = sizeof(Agfacolor_futura_400CDGreen_I) / sizeof(Agfacolor_futura_400CDGreen_I[0]);
		GreenI.assign(Agfacolor_futura_400CDGreen_I, Agfacolor_futura_400CDGreen_I + nGreen);
		GreenB.assign(Agfacolor_futura_400CDGreen_B, Agfacolor_futura_400CDGreen_B + nGreen);
		const size_t nBlue = sizeof(Agfacolor_futura_400CDBlue_I) / sizeof(Agfacolor_futura_400CDBlue_I[0]);
		BlueI.assign(Agfacolor_futura_400CDBlue_I, Agfacolor_futura_400CDBlue_I + nBlue);
		BlueB.assign(Agfacolor_futura_400CDBlue_B, Agfacolor_futura_400CDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Agfacolor_futuraII_100CD") {
		const size_t nRed = sizeof(Agfacolor_futuraII_100CDRed_I) / sizeof(Agfacolor_futuraII_100CDRed_I[0]);
		RedI.assign(Agfacolor_futuraII_100CDRed_I, Agfacolor_futuraII_100CDRed_I + nRed);
		RedB.assign(Agfacolor_futuraII_100CDRed_B, Agfacolor_futuraII_100CDRed_B + nRed);
		const size_t nGreen = sizeof(Agfacolor_futuraII_100CDGreen_I) / sizeof(Agfacolor_futuraII_100CDGreen_I[0]);
		GreenI.assign(Agfacolor_futuraII_100CDGreen_I, Agfacolor_futuraII_100CDGreen_I + nGreen);
		GreenB.assign(Agfacolor_futuraII_100CDGreen_B, Agfacolor_futuraII_100CDGreen_B + nGreen);
		const size_t nBlue = sizeof(Agfacolor_futuraII_100CDBlue_I) / sizeof(Agfacolor_futuraII_100CDBlue_I[0]);
		BlueI.assign(Agfacolor_futuraII_100CDBlue_I, Agfacolor_futuraII_100CDBlue_I + nBlue);
		BlueB.assign(Agfacolor_futuraII_100CDBlue_B, Agfacolor_futuraII_100CDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Agfacolor_futuraII_200CD") {
		const size_t nRed = sizeof(Agfacolor_futuraII_200CDRed_I) / sizeof(Agfacolor_futuraII_200CDRed_I[0]);
		RedI.assign(Agfacolor_futuraII_200CDRed_I, Agfacolor_futuraII_200CDRed_I + nRed);
		RedB.assign(Agfacolor_futuraII_200CDRed_B, Agfacolor_futuraII_200CDRed_B + nRed);
		const size_t nGreen = sizeof(Agfacolor_futuraII_200CDGreen_I) / sizeof(Agfacolor_futuraII_200CDGreen_I[0]);
		GreenI.assign(Agfacolor_futuraII_200CDGreen_I, Agfacolor_futuraII_200CDGreen_I + nGreen);
		GreenB.assign(Agfacolor_futuraII_200CDGreen_B, Agfacolor_futuraII_200CDGreen_B + nGreen);
		const size_t nBlue = sizeof(Agfacolor_futuraII_200CDBlue_I) / sizeof(Agfacolor_futuraII_200CDBlue_I[0]);
		BlueI.assign(Agfacolor_futuraII_200CDBlue_I, Agfacolor_futuraII_200CDBlue_I + nBlue);
		BlueB.assign(Agfacolor_futuraII_200CDBlue_B, Agfacolor_futuraII_200CDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Agfacolor_futuraII_400CD") {
		const size_t nRed = sizeof(Agfacolor_futuraII_400CDRed_I) / sizeof(Agfacolor_futuraII_400CDRed_I[0]);
		RedI.assign(Agfacolor_futuraII_400CDRed_I, Agfacolor_futuraII_400CDRed_I + nRed);
		RedB.assign(Agfacolor_futuraII_400CDRed_B, Agfacolor_futuraII_400CDRed_B + nRed);
		const size_t nGreen = sizeof(Agfacolor_futuraII_400CDGreen_I) / sizeof(Agfacolor_futuraII_400CDGreen_I[0]);
		GreenI.assign(Agfacolor_futuraII_400CDGreen_I, Agfacolor_futuraII_400CDGreen_I + nGreen);
		GreenB.assign(Agfacolor_futuraII_400CDGreen_B, Agfacolor_futuraII_400CDGreen_B + nGreen);
		const size_t nBlue = sizeof(Agfacolor_futuraII_400CDBlue_I) / sizeof(Agfacolor_futuraII_400CDBlue_I[0]);
		BlueI.assign(Agfacolor_futuraII_400CDBlue_I, Agfacolor_futuraII_400CDBlue_I + nBlue);
		BlueB.assign(Agfacolor_futuraII_400CDBlue_B, Agfacolor_futuraII_400CDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Agfacolor_hdc_100_plusCD") {
		const size_t nRed = sizeof(Agfacolor_hdc_100_plusCDRed_I) / sizeof(Agfacolor_hdc_100_plusCDRed_I[0]);
		RedI.assign(Agfacolor_hdc_100_plusCDRed_I, Agfacolor_hdc_100_plusCDRed_I + nRed);
		RedB.assign(Agfacolor_hdc_100_plusCDRed_B, Agfacolor_hdc_100_plusCDRed_B + nRed);
		const size_t nGreen = sizeof(Agfacolor_hdc_100_plusCDGreen_I) / sizeof(Agfacolor_hdc_100_plusCDGreen_I[0]);
		GreenI.assign(Agfacolor_hdc_100_plusCDGreen_I, Agfacolor_hdc_100_plusCDGreen_I + nGreen);
		GreenB.assign(Agfacolor_hdc_100_plusCDGreen_B, Agfacolor_hdc_100_plusCDGreen_B + nGreen);
		const size_t nBlue = sizeof(Agfacolor_hdc_100_plusCDBlue_I) / sizeof(Agfacolor_hdc_100_plusCDBlue_I[0]);
		BlueI.assign(Agfacolor_hdc_100_plusCDBlue_I, Agfacolor_hdc_100_plusCDBlue_I + nBlue);
		BlueB.assign(Agfacolor_hdc_100_plusCDBlue_B, Agfacolor_hdc_100_plusCDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Agfacolor_hdc_200_plusCD") {
		const size_t nRed = sizeof(Agfacolor_hdc_200_plusCDRed_I) / sizeof(Agfacolor_hdc_200_plusCDRed_I[0]);
		RedI.assign(Agfacolor_hdc_200_plusCDRed_I, Agfacolor_hdc_200_plusCDRed_I + nRed);
		RedB.assign(Agfacolor_hdc_200_plusCDRed_B, Agfacolor_hdc_200_plusCDRed_B + nRed);
		const size_t nGreen = sizeof(Agfacolor_hdc_200_plusCDGreen_I) / sizeof(Agfacolor_hdc_200_plusCDGreen_I[0]);
		GreenI.assign(Agfacolor_hdc_200_plusCDGreen_I, Agfacolor_hdc_200_plusCDGreen_I + nGreen);
		GreenB.assign(Agfacolor_hdc_200_plusCDGreen_B, Agfacolor_hdc_200_plusCDGreen_B + nGreen);
		const size_t nBlue = sizeof(Agfacolor_hdc_200_plusCDBlue_I) / sizeof(Agfacolor_hdc_200_plusCDBlue_I[0]);
		BlueI.assign(Agfacolor_hdc_200_plusCDBlue_I, Agfacolor_hdc_200_plusCDBlue_I + nBlue);
		BlueB.assign(Agfacolor_hdc_200_plusCDBlue_B, Agfacolor_hdc_200_plusCDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Agfacolor_hdc_400_plusCD") {
		const size_t nRed = sizeof(Agfacolor_hdc_400_plusCDRed_I) / sizeof(Agfacolor_hdc_400_plusCDRed_I[0]);
		RedI.assign(Agfacolor_hdc_400_plusCDRed_I, Agfacolor_hdc_400_plusCDRed_I + nRed);
		RedB.assign(Agfacolor_hdc_400_plusCDRed_B, Agfacolor_hdc_400_plusCDRed_B + nRed);
		const size_t nGreen = sizeof(Agfacolor_hdc_400_plusCDGreen_I) / sizeof(Agfacolor_hdc_400_plusCDGreen_I[0]);
		GreenI.assign(Agfacolor_hdc_400_plusCDGreen_I, Agfacolor_hdc_400_plusCDGreen_I + nGreen);
		GreenB.assign(Agfacolor_hdc_400_plusCDGreen_B, Agfacolor_hdc_400_plusCDGreen_B + nGreen);
		const size_t nBlue = sizeof(Agfacolor_hdc_400_plusCDBlue_I) / sizeof(Agfacolor_hdc_400_plusCDBlue_I[0]);
		BlueI.assign(Agfacolor_hdc_400_plusCDBlue_I, Agfacolor_hdc_400_plusCDBlue_I + nBlue);
		BlueB.assign(Agfacolor_hdc_400_plusCDBlue_B, Agfacolor_hdc_400_plusCDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Agfacolor_optimaII_100CD") {
		const size_t nRed = sizeof(Agfacolor_optimaII_100CDRed_I) / sizeof(Agfacolor_optimaII_100CDRed_I[0]);
		RedI.assign(Agfacolor_optimaII_100CDRed_I, Agfacolor_optimaII_100CDRed_I + nRed);
		RedB.assign(Agfacolor_optimaII_100CDRed_B, Agfacolor_optimaII_100CDRed_B + nRed);
		const size_t nGreen = sizeof(Agfacolor_optimaII_100CDGreen_I) / sizeof(Agfacolor_optimaII_100CDGreen_I[0]);
		GreenI.assign(Agfacolor_optimaII_100CDGreen_I, Agfacolor_optimaII_100CDGreen_I + nGreen);
		GreenB.assign(Agfacolor_optimaII_100CDGreen_B, Agfacolor_optimaII_100CDGreen_B + nGreen);
		const size_t nBlue = sizeof(Agfacolor_optimaII_100CDBlue_I) / sizeof(Agfacolor_optimaII_100CDBlue_I[0]);
		BlueI.assign(Agfacolor_optimaII_100CDBlue_I, Agfacolor_optimaII_100CDBlue_I + nBlue);
		BlueB.assign(Agfacolor_optimaII_100CDBlue_B, Agfacolor_optimaII_100CDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Agfacolor_optimaII_200CD") {
		const size_t nRed = sizeof(Agfacolor_optimaII_200CDRed_I) / sizeof(Agfacolor_optimaII_200CDRed_I[0]);
		RedI.assign(Agfacolor_optimaII_200CDRed_I, Agfacolor_optimaII_200CDRed_I + nRed);
		RedB.assign(Agfacolor_optimaII_200CDRed_B, Agfacolor_optimaII_200CDRed_B + nRed);
		const size_t nGreen = sizeof(Agfacolor_optimaII_200CDGreen_I) / sizeof(Agfacolor_optimaII_200CDGreen_I[0]);
		GreenI.assign(Agfacolor_optimaII_200CDGreen_I, Agfacolor_optimaII_200CDGreen_I + nGreen);
		GreenB.assign(Agfacolor_optimaII_200CDGreen_B, Agfacolor_optimaII_200CDGreen_B + nGreen);
		const size_t nBlue = sizeof(Agfacolor_optimaII_200CDBlue_I) / sizeof(Agfacolor_optimaII_200CDBlue_I[0]);
		BlueI.assign(Agfacolor_optimaII_200CDBlue_I, Agfacolor_optimaII_200CDBlue_I + nBlue);
		BlueB.assign(Agfacolor_optimaII_200CDBlue_B, Agfacolor_optimaII_200CDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Agfacolor_ultra_050_CD") {
		const size_t nRed = sizeof(Agfacolor_ultra_050_CDRed_I) / sizeof(Agfacolor_ultra_050_CDRed_I[0]);
		RedI.assign(Agfacolor_ultra_050_CDRed_I, Agfacolor_ultra_050_CDRed_I + nRed);
		RedB.assign(Agfacolor_ultra_050_CDRed_B, Agfacolor_ultra_050_CDRed_B + nRed);
		const size_t nGreen = sizeof(Agfacolor_ultra_050_CDGreen_I) / sizeof(Agfacolor_ultra_050_CDGreen_I[0]);
		GreenI.assign(Agfacolor_ultra_050_CDGreen_I, Agfacolor_ultra_050_CDGreen_I + nGreen);
		GreenB.assign(Agfacolor_ultra_050_CDGreen_B, Agfacolor_ultra_050_CDGreen_B + nGreen);
		const size_t nBlue = sizeof(Agfacolor_ultra_050_CDBlue_I) / sizeof(Agfacolor_ultra_050_CDBlue_I[0]);
		BlueI.assign(Agfacolor_ultra_050_CDBlue_I, Agfacolor_ultra_050_CDBlue_I + nBlue);
		BlueB.assign(Agfacolor_ultra_050_CDBlue_B, Agfacolor_ultra_050_CDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Agfacolor_vista_100CD") {
		const size_t nRed = sizeof(Agfacolor_vista_100CDRed_I) / sizeof(Agfacolor_vista_100CDRed_I[0]);
		RedI.assign(Agfacolor_vista_100CDRed_I, Agfacolor_vista_100CDRed_I + nRed);
		RedB.assign(Agfacolor_vista_100CDRed_B, Agfacolor_vista_100CDRed_B + nRed);
		const size_t nGreen = sizeof(Agfacolor_vista_100CDGreen_I) / sizeof(Agfacolor_vista_100CDGreen_I[0]);
		GreenI.assign(Agfacolor_vista_100CDGreen_I, Agfacolor_vista_100CDGreen_I + nGreen);
		GreenB.assign(Agfacolor_vista_100CDGreen_B, Agfacolor_vista_100CDGreen_B + nGreen);
		const size_t nBlue = sizeof(Agfacolor_vista_100CDBlue_I) / sizeof(Agfacolor_vista_100CDBlue_I[0]);
		BlueI.assign(Agfacolor_vista_100CDBlue_I, Agfacolor_vista_100CDBlue_I + nBlue);
		BlueB.assign(Agfacolor_vista_100CDBlue_B, Agfacolor_vista_100CDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Agfacolor_vista_200CD") {
		const size_t nRed = sizeof(Agfacolor_vista_200CDRed_I) / sizeof(Agfacolor_vista_200CDRed_I[0]);
		RedI.assign(Agfacolor_vista_200CDRed_I, Agfacolor_vista_200CDRed_I + nRed);
		RedB.assign(Agfacolor_vista_200CDRed_B, Agfacolor_vista_200CDRed_B + nRed);
		const size_t nGreen = sizeof(Agfacolor_vista_200CDGreen_I) / sizeof(Agfacolor_vista_200CDGreen_I[0]);
		GreenI.assign(Agfacolor_vista_200CDGreen_I, Agfacolor_vista_200CDGreen_I + nGreen);
		GreenB.assign(Agfacolor_vista_200CDGreen_B, Agfacolor_vista_200CDGreen_B + nGreen);
		const size_t nBlue = sizeof(Agfacolor_vista_200CDBlue_I) / sizeof(Agfacolor_vista_200CDBlue_I[0]);
		BlueI.assign(Agfacolor_vista_200CDBlue_I, Agfacolor_vista_200CDBlue_I + nBlue);
		BlueB.assign(Agfacolor_vista_200CDBlue_B, Agfacolor_vista_200CDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Agfacolor_vista_400CD") {
		const size_t nRed = sizeof(Agfacolor_vista_400CDRed_I) / sizeof(Agfacolor_vista_400CDRed_I[0]);
		RedI.assign(Agfacolor_vista_400CDRed_I, Agfacolor_vista_400CDRed_I + nRed);
		RedB.assign(Agfacolor_vista_400CDRed_B, Agfacolor_vista_400CDRed_B + nRed);
		const size_t nGreen = sizeof(Agfacolor_vista_400CDGreen_I) / sizeof(Agfacolor_vista_400CDGreen_I[0]);
		GreenI.assign(Agfacolor_vista_400CDGreen_I, Agfacolor_vista_400CDGreen_I + nGreen);
		GreenB.assign(Agfacolor_vista_400CDGreen_B, Agfacolor_vista_400CDGreen_B + nGreen);
		const size_t nBlue = sizeof(Agfacolor_vista_400CDBlue_I) / sizeof(Agfacolor_vista_400CDBlue_I[0]);
		BlueI.assign(Agfacolor_vista_400CDBlue_I, Agfacolor_vista_400CDBlue_I + nBlue);
		BlueB.assign(Agfacolor_vista_400CDBlue_B, Agfacolor_vista_400CDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Agfacolor_vista_800CD") {
		const size_t nRed = sizeof(Agfacolor_vista_800CDRed_I) / sizeof(Agfacolor_vista_800CDRed_I[0]);
		RedI.assign(Agfacolor_vista_800CDRed_I, Agfacolor_vista_800CDRed_I + nRed);
		RedB.assign(Agfacolor_vista_800CDRed_B, Agfacolor_vista_800CDRed_B + nRed);
		const size_t nGreen = sizeof(Agfacolor_vista_800CDGreen_I) / sizeof(Agfacolor_vista_800CDGreen_I[0]);
		GreenI.assign(Agfacolor_vista_800CDGreen_I, Agfacolor_vista_800CDGreen_I + nGreen);
		GreenB.assign(Agfacolor_vista_800CDGreen_B, Agfacolor_vista_800CDGreen_B + nGreen);
		const size_t nBlue = sizeof(Agfacolor_vista_800CDBlue_I) / sizeof(Agfacolor_vista_800CDBlue_I[0]);
		BlueI.assign(Agfacolor_vista_800CDBlue_I, Agfacolor_vista_800CDBlue_I + nBlue);
		BlueB.assign(Agfacolor_vista_800CDBlue_B, Agfacolor_vista_800CDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Agfapan_apx_025CD") {
		const size_t nRed = sizeof(Agfapan_apx_025CDRed_I) / sizeof(Agfapan_apx_025CDRed_I[0]);
		RedI.assign(Agfapan_apx_025CDRed_I, Agfapan_apx_025CDRed_I + nRed);
		RedB.assign(Agfapan_apx_025CDRed_B, Agfapan_apx_025CDRed_B + nRed);
		color = false;
	} else if (filmName == "Agfapan_apx_100CD") {
		const size_t nRed = sizeof(Agfapan_apx_100CDRed_I) / sizeof(Agfapan_apx_100CDRed_I[0]);
		RedI.assign(Agfapan_apx_100CDRed_I, Agfapan_apx_100CDRed_I + nRed);
		RedB.assign(Agfapan_apx_100CDRed_B, Agfapan_apx_100CDRed_B + nRed);
		color = false;
	} else if (filmName == "Agfapan_apx_400CD") {
		const size_t nRed = sizeof(Agfapan_apx_400CDRed_I) / sizeof(Agfapan_apx_400CDRed_I[0]);
		RedI.assign(Agfapan_apx_400CDRed_I, Agfapan_apx_400CDRed_I + nRed);
		RedB.assign(Agfapan_apx_400CDRed_B, Agfapan_apx_400CDRed_B + nRed);
		color = false;
	} else if (filmName == "Ektachrome_100_plusCD") {
		const size_t nRed = sizeof(Ektachrome_100_plusCDRed_I) / sizeof(Ektachrome_100_plusCDRed_I[0]);
		RedI.assign(Ektachrome_100_plusCDRed_I, Ektachrome_100_plusCDRed_I + nRed);
		RedB.assign(Ektachrome_100_plusCDRed_B, Ektachrome_100_plusCDRed_B + nRed);
		const size_t nGreen = sizeof(Ektachrome_100_plusCDGreen_I) / sizeof(Ektachrome_100_plusCDGreen_I[0]);
		GreenI.assign(Ektachrome_100_plusCDGreen_I, Ektachrome_100_plusCDGreen_I + nGreen);
		GreenB.assign(Ektachrome_100_plusCDGreen_B, Ektachrome_100_plusCDGreen_B + nGreen);
		const size_t nBlue = sizeof(Ektachrome_100_plusCDBlue_I) / sizeof(Ektachrome_100_plusCDBlue_I[0]);
		BlueI.assign(Ektachrome_100_plusCDBlue_I, Ektachrome_100_plusCDBlue_I + nBlue);
		BlueB.assign(Ektachrome_100_plusCDBlue_B, Ektachrome_100_plusCDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Ektachrome_100CD") {
		const size_t nRed = sizeof(Ektachrome_100CDRed_I) / sizeof(Ektachrome_100CDRed_I[0]);
		RedI.assign(Ektachrome_100CDRed_I, Ektachrome_100CDRed_I + nRed);
		RedB.assign(Ektachrome_100CDRed_B, Ektachrome_100CDRed_B + nRed);
		const size_t nGreen = sizeof(Ektachrome_100CDGreen_I) / sizeof(Ektachrome_100CDGreen_I[0]);
		GreenI.assign(Ektachrome_100CDGreen_I, Ektachrome_100CDGreen_I + nGreen);
		GreenB.assign(Ektachrome_100CDGreen_B, Ektachrome_100CDGreen_B + nGreen);
		const size_t nBlue = sizeof(Ektachrome_100CDBlue_I) / sizeof(Ektachrome_100CDBlue_I[0]);
		BlueI.assign(Ektachrome_100CDBlue_I, Ektachrome_100CDBlue_I + nBlue);
		BlueB.assign(Ektachrome_100CDBlue_B, Ektachrome_100CDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Ektachrome_320TCD") {
		const size_t nRed = sizeof(Ektachrome_320TCDRed_I) / sizeof(Ektachrome_320TCDRed_I[0]);
		RedI.assign(Ektachrome_320TCDRed_I, Ektachrome_320TCDRed_I + nRed);
		RedB.assign(Ektachrome_320TCDRed_B, Ektachrome_320TCDRed_B + nRed);
		const size_t nGreen = sizeof(Ektachrome_320TCDGreen_I) / sizeof(Ektachrome_320TCDGreen_I[0]);
		GreenI.assign(Ektachrome_320TCDGreen_I, Ektachrome_320TCDGreen_I + nGreen);
		GreenB.assign(Ektachrome_320TCDGreen_B, Ektachrome_320TCDGreen_B + nGreen);
		const size_t nBlue = sizeof(Ektachrome_320TCDBlue_I) / sizeof(Ektachrome_320TCDBlue_I[0]);
		BlueI.assign(Ektachrome_320TCDBlue_I, Ektachrome_320TCDBlue_I + nBlue);
		BlueB.assign(Ektachrome_320TCDBlue_B, Ektachrome_320TCDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Ektachrome_400XCD") {
		const size_t nRed = sizeof(Ektachrome_400XCDRed_I) / sizeof(Ektachrome_400XCDRed_I[0]);
		RedI.assign(Ektachrome_400XCDRed_I, Ektachrome_400XCDRed_I + nRed);
		RedB.assign(Ektachrome_400XCDRed_B, Ektachrome_400XCDRed_B + nRed);
		const size_t nGreen = sizeof(Ektachrome_400XCDGreen_I) / sizeof(Ektachrome_400XCDGreen_I[0]);
		GreenI.assign(Ektachrome_400XCDGreen_I, Ektachrome_400XCDGreen_I + nGreen);
		GreenB.assign(Ektachrome_400XCDGreen_B, Ektachrome_400XCDGreen_B + nGreen);
		const size_t nBlue = sizeof(Ektachrome_400XCDBlue_I) / sizeof(Ektachrome_400XCDBlue_I[0]);
		BlueI.assign(Ektachrome_400XCDBlue_I, Ektachrome_400XCDBlue_I + nBlue);
		BlueB.assign(Ektachrome_400XCDBlue_B, Ektachrome_400XCDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Ektachrome_64CD") {
		const size_t nRed = sizeof(Ektachrome_64CDRed_I) / sizeof(Ektachrome_64CDRed_I[0]);
		RedI.assign(Ektachrome_64CDRed_I, Ektachrome_64CDRed_I + nRed);
		RedB.assign(Ektachrome_64CDRed_B, Ektachrome_64CDRed_B + nRed);
		const size_t nGreen = sizeof(Ektachrome_64CDGreen_I) / sizeof(Ektachrome_64CDGreen_I[0]);
		GreenI.assign(Ektachrome_64CDGreen_I, Ektachrome_64CDGreen_I + nGreen);
		GreenB.assign(Ektachrome_64CDGreen_B, Ektachrome_64CDGreen_B + nGreen);
		const size_t nBlue = sizeof(Ektachrome_64CDBlue_I) / sizeof(Ektachrome_64CDBlue_I[0]);
		BlueI.assign(Ektachrome_64CDBlue_I, Ektachrome_64CDBlue_I + nBlue);
		BlueB.assign(Ektachrome_64CDBlue_B, Ektachrome_64CDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Ektachrome_64TCD") {
		const size_t nRed = sizeof(Ektachrome_64TCDRed_I) / sizeof(Ektachrome_64TCDRed_I[0]);
		RedI.assign(Ektachrome_64TCDRed_I, Ektachrome_64TCDRed_I + nRed);
		RedB.assign(Ektachrome_64TCDRed_B, Ektachrome_64TCDRed_B + nRed);
		const size_t nGreen = sizeof(Ektachrome_64TCDGreen_I) / sizeof(Ektachrome_64TCDGreen_I[0]);
		GreenI.assign(Ektachrome_64TCDGreen_I, Ektachrome_64TCDGreen_I + nGreen);
		GreenB.assign(Ektachrome_64TCDGreen_B, Ektachrome_64TCDGreen_B + nGreen);
		const size_t nBlue = sizeof(Ektachrome_64TCDBlue_I) / sizeof(Ektachrome_64TCDBlue_I[0]);
		BlueI.assign(Ektachrome_64TCDBlue_I, Ektachrome_64TCDBlue_I + nBlue);
		BlueB.assign(Ektachrome_64TCDBlue_B, Ektachrome_64TCDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Ektachrome_E100SCD") {
		const size_t nRed = sizeof(Ektachrome_E100SCDRed_I) / sizeof(Ektachrome_E100SCDRed_I[0]);
		RedI.assign(Ektachrome_E100SCDRed_I, Ektachrome_E100SCDRed_I + nRed);
		RedB.assign(Ektachrome_E100SCDRed_B, Ektachrome_E100SCDRed_B + nRed);
		const size_t nGreen = sizeof(Ektachrome_E100SCDGreen_I) / sizeof(Ektachrome_E100SCDGreen_I[0]);
		GreenI.assign(Ektachrome_E100SCDGreen_I, Ektachrome_E100SCDGreen_I + nGreen);
		GreenB.assign(Ektachrome_E100SCDGreen_B, Ektachrome_E100SCDGreen_B + nGreen);
		const size_t nBlue = sizeof(Ektachrome_E100SCDBlue_I) / sizeof(Ektachrome_E100SCDBlue_I[0]);
		BlueI.assign(Ektachrome_E100SCDBlue_I, Ektachrome_E100SCDBlue_I + nBlue);
		BlueB.assign(Ektachrome_E100SCDBlue_B, Ektachrome_E100SCDBlue_B + nBlue);
		color = true;
	} else if (filmName == "F125CD") {
		const size_t nRed = sizeof(F125CDRed_I) / sizeof(F125CDRed_I[0]);
		RedI.assign(F125CDRed_I, F125CDRed_I + nRed);
		RedB.assign(F125CDRed_B, F125CDRed_B + nRed);
		const size_t nGreen = sizeof(F125CDGreen_I) / sizeof(F125CDGreen_I[0]);
		GreenI.assign(F125CDGreen_I, F125CDGreen_I + nGreen);
		GreenB.assign(F125CDGreen_B, F125CDGreen_B + nGreen);
		const size_t nBlue = sizeof(F125CDBlue_I) / sizeof(F125CDBlue_I[0]);
		BlueI.assign(F125CDBlue_I, F125CDBlue_I + nBlue);
		BlueB.assign(F125CDBlue_B, F125CDBlue_B + nBlue);
		color = true;
	} else if (filmName == "F250CD") {
		const size_t nRed = sizeof(F250CDRed_I) / sizeof(F250CDRed_I[0]);
		RedI.assign(F250CDRed_I, F250CDRed_I + nRed);
		RedB.assign(F250CDRed_B, F250CDRed_B + nRed);
		const size_t nGreen = sizeof(F250CDGreen_I) / sizeof(F250CDGreen_I[0]);
		GreenI.assign(F250CDGreen_I, F250CDGreen_I + nGreen);
		GreenB.assign(F250CDGreen_B, F250CDGreen_B + nGreen);
		const size_t nBlue = sizeof(F250CDBlue_I) / sizeof(F250CDBlue_I[0]);
		BlueI.assign(F250CDBlue_I, F250CDBlue_I + nBlue);
		BlueB.assign(F250CDBlue_B, F250CDBlue_B + nBlue);
		color = true;
	} else if (filmName == "F400CD") {
		const size_t nRed = sizeof(F400CDRed_I) / sizeof(F400CDRed_I[0]);
		RedI.assign(F400CDRed_I, F400CDRed_I + nRed);
		RedB.assign(F400CDRed_B, F400CDRed_B + nRed);
		const size_t nGreen = sizeof(F400CDGreen_I) / sizeof(F400CDGreen_I[0]);
		GreenI.assign(F400CDGreen_I, F400CDGreen_I + nGreen);
		GreenB.assign(F400CDGreen_B, F400CDGreen_B + nGreen);
		const size_t nBlue = sizeof(F400CDBlue_I) / sizeof(F400CDBlue_I[0]);
		BlueI.assign(F400CDBlue_I, F400CDBlue_I + nBlue);
		BlueB.assign(F400CDBlue_B, F400CDBlue_B + nBlue);
		color = true;
	} else if (filmName == "FCICD") {
		const size_t nRed = sizeof(FCICDRed_I) / sizeof(FCICDRed_I[0]);
		RedI.assign(FCICDRed_I, FCICDRed_I + nRed);
		RedB.assign(FCICDRed_B, FCICDRed_B + nRed);
		const size_t nGreen = sizeof(FCICDGreen_I) / sizeof(FCICDGreen_I[0]);
		GreenI.assign(FCICDGreen_I, FCICDGreen_I + nGreen);
		GreenB.assign(FCICDGreen_B, FCICDGreen_B + nGreen);
		const size_t nBlue = sizeof(FCICDBlue_I) / sizeof(FCICDBlue_I[0]);
		BlueI.assign(FCICDBlue_I, FCICDBlue_I + nBlue);
		BlueB.assign(FCICDBlue_B, FCICDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Gold_100CD") {
		const size_t nRed = sizeof(Gold_100CDRed_I) / sizeof(Gold_100CDRed_I[0]);
		RedI.assign(Gold_100CDRed_I, Gold_100CDRed_I + nRed);
		RedB.assign(Gold_100CDRed_B, Gold_100CDRed_B + nRed);
		const size_t nGreen = sizeof(Gold_100CDGreen_I) / sizeof(Gold_100CDGreen_I[0]);
		GreenI.assign(Gold_100CDGreen_I, Gold_100CDGreen_I + nGreen);
		GreenB.assign(Gold_100CDGreen_B, Gold_100CDGreen_B + nGreen);
		const size_t nBlue = sizeof(Gold_100CDBlue_I) / sizeof(Gold_100CDBlue_I[0]);
		BlueI.assign(Gold_100CDBlue_I, Gold_100CDBlue_I + nBlue);
		BlueB.assign(Gold_100CDBlue_B, Gold_100CDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Gold_200CD") {
		const size_t nRed = sizeof(Gold_200CDRed_I) / sizeof(Gold_200CDRed_I[0]);
		RedI.assign(Gold_200CDRed_I, Gold_200CDRed_I + nRed);
		RedB.assign(Gold_200CDRed_B, Gold_200CDRed_B + nRed);
		const size_t nGreen = sizeof(Gold_200CDGreen_I) / sizeof(Gold_200CDGreen_I[0]);
		GreenI.assign(Gold_200CDGreen_I, Gold_200CDGreen_I + nGreen);
		GreenB.assign(Gold_200CDGreen_B, Gold_200CDGreen_B + nGreen);
		const size_t nBlue = sizeof(Gold_200CDBlue_I) / sizeof(Gold_200CDBlue_I[0]);
		BlueI.assign(Gold_200CDBlue_I, Gold_200CDBlue_I + nBlue);
		BlueB.assign(Gold_200CDBlue_B, Gold_200CDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Kodachrome_200CD") {
		const size_t nRed = sizeof(Kodachrome_200CDRed_I) / sizeof(Kodachrome_200CDRed_I[0]);
		RedI.assign(Kodachrome_200CDRed_I, Kodachrome_200CDRed_I + nRed);
		RedB.assign(Kodachrome_200CDRed_B, Kodachrome_200CDRed_B + nRed);
		const size_t nGreen = sizeof(Kodachrome_200CDGreen_I) / sizeof(Kodachrome_200CDGreen_I[0]);
		GreenI.assign(Kodachrome_200CDGreen_I, Kodachrome_200CDGreen_I + nGreen);
		GreenB.assign(Kodachrome_200CDGreen_B, Kodachrome_200CDGreen_B + nGreen);
		const size_t nBlue = sizeof(Kodachrome_200CDBlue_I) / sizeof(Kodachrome_200CDBlue_I[0]);
		BlueI.assign(Kodachrome_200CDBlue_I, Kodachrome_200CDBlue_I + nBlue);
		BlueB.assign(Kodachrome_200CDBlue_B, Kodachrome_200CDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Kodachrome_25CD") {
		const size_t nRed = sizeof(Kodachrome_25CDRed_I) / sizeof(Kodachrome_25CDRed_I[0]);
		RedI.assign(Kodachrome_25CDRed_I, Kodachrome_25CDRed_I + nRed);
		RedB.assign(Kodachrome_25CDRed_B, Kodachrome_25CDRed_B + nRed);
		const size_t nGreen = sizeof(Kodachrome_25CDGreen_I) / sizeof(Kodachrome_25CDGreen_I[0]);
		GreenI.assign(Kodachrome_25CDGreen_I, Kodachrome_25CDGreen_I + nGreen);
		GreenB.assign(Kodachrome_25CDGreen_B, Kodachrome_25CDGreen_B + nGreen);
		const size_t nBlue = sizeof(Kodachrome_25CDBlue_I) / sizeof(Kodachrome_25CDBlue_I[0]);
		BlueI.assign(Kodachrome_25CDBlue_I, Kodachrome_25CDBlue_I + nBlue);
		BlueB.assign(Kodachrome_25CDBlue_B, Kodachrome_25CDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Kodachrome_64CD") {
		const size_t nRed = sizeof(Kodachrome_64CDRed_I) / sizeof(Kodachrome_64CDRed_I[0]);
		RedI.assign(Kodachrome_64CDRed_I, Kodachrome_64CDRed_I + nRed);
		RedB.assign(Kodachrome_64CDRed_B, Kodachrome_64CDRed_B + nRed);
		const size_t nGreen = sizeof(Kodachrome_64CDGreen_I) / sizeof(Kodachrome_64CDGreen_I[0]);
		GreenI.assign(Kodachrome_64CDGreen_I, Kodachrome_64CDGreen_I + nGreen);
		GreenB.assign(Kodachrome_64CDGreen_B, Kodachrome_64CDGreen_B + nGreen);
		const size_t nBlue = sizeof(Kodachrome_64CDBlue_I) / sizeof(Kodachrome_64CDBlue_I[0]);
		BlueI.assign(Kodachrome_64CDBlue_I, Kodachrome_64CDBlue_I + nBlue);
		BlueB.assign(Kodachrome_64CDBlue_B, Kodachrome_64CDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Max_Zoom_800CD") {
		const size_t nRed = sizeof(Max_Zoom_800CDRed_I) / sizeof(Max_Zoom_800CDRed_I[0]);
		RedI.assign(Max_Zoom_800CDRed_I, Max_Zoom_800CDRed_I + nRed);
		RedB.assign(Max_Zoom_800CDRed_B, Max_Zoom_800CDRed_B + nRed);
		const size_t nGreen = sizeof(Max_Zoom_800CDGreen_I) / sizeof(Max_Zoom_800CDGreen_I[0]);
		GreenI.assign(Max_Zoom_800CDGreen_I, Max_Zoom_800CDGreen_I + nGreen);
		GreenB.assign(Max_Zoom_800CDGreen_B, Max_Zoom_800CDGreen_B + nGreen);
		const size_t nBlue = sizeof(Max_Zoom_800CDBlue_I) / sizeof(Max_Zoom_800CDBlue_I[0]);
		BlueI.assign(Max_Zoom_800CDBlue_I, Max_Zoom_800CDBlue_I + nBlue);
		BlueB.assign(Max_Zoom_800CDBlue_B, Max_Zoom_800CDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Portra_100TCD") {
		const size_t nRed = sizeof(Portra_100TCDRed_I) / sizeof(Portra_100TCDRed_I[0]);
		RedI.assign(Portra_100TCDRed_I, Portra_100TCDRed_I + nRed);
		RedB.assign(Portra_100TCDRed_B, Portra_100TCDRed_B + nRed);
		const size_t nGreen = sizeof(Portra_100TCDGreen_I) / sizeof(Portra_100TCDGreen_I[0]);
		GreenI.assign(Portra_100TCDGreen_I, Portra_100TCDGreen_I + nGreen);
		GreenB.assign(Portra_100TCDGreen_B, Portra_100TCDGreen_B + nGreen);
		const size_t nBlue = sizeof(Portra_100TCDBlue_I) / sizeof(Portra_100TCDBlue_I[0]);
		BlueI.assign(Portra_100TCDBlue_I, Portra_100TCDBlue_I + nBlue);
		BlueB.assign(Portra_100TCDBlue_B, Portra_100TCDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Portra_160NCCD") {
		const size_t nRed = sizeof(Portra_160NCCDRed_I) / sizeof(Portra_160NCCDRed_I[0]);
		RedI.assign(Portra_160NCCDRed_I, Portra_160NCCDRed_I + nRed);
		RedB.assign(Portra_160NCCDRed_B, Portra_160NCCDRed_B + nRed);
		const size_t nGreen = sizeof(Portra_160NCCDGreen_I) / sizeof(Portra_160NCCDGreen_I[0]);
		GreenI.assign(Portra_160NCCDGreen_I, Portra_160NCCDGreen_I + nGreen);
		GreenB.assign(Portra_160NCCDGreen_B, Portra_160NCCDGreen_B + nGreen);
		const size_t nBlue = sizeof(Portra_160NCCDBlue_I) / sizeof(Portra_160NCCDBlue_I[0]);
		BlueI.assign(Portra_160NCCDBlue_I, Portra_160NCCDBlue_I + nBlue);
		BlueB.assign(Portra_160NCCDBlue_B, Portra_160NCCDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Portra_160VCCD") {
		const size_t nRed = sizeof(Portra_160VCCDRed_I) / sizeof(Portra_160VCCDRed_I[0]);
		RedI.assign(Portra_160VCCDRed_I, Portra_160VCCDRed_I + nRed);
		RedB.assign(Portra_160VCCDRed_B, Portra_160VCCDRed_B + nRed);
		const size_t nGreen = sizeof(Portra_160VCCDGreen_I) / sizeof(Portra_160VCCDGreen_I[0]);
		GreenI.assign(Portra_160VCCDGreen_I, Portra_160VCCDGreen_I + nGreen);
		GreenB.assign(Portra_160VCCDGreen_B, Portra_160VCCDGreen_B + nGreen);
		const size_t nBlue = sizeof(Portra_160VCCDBlue_I) / sizeof(Portra_160VCCDBlue_I[0]);
		BlueI.assign(Portra_160VCCDBlue_I, Portra_160VCCDBlue_I + nBlue);
		BlueB.assign(Portra_160VCCDBlue_B, Portra_160VCCDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Portra_400NCCD") {
		const size_t nRed = sizeof(Portra_400NCCDRed_I) / sizeof(Portra_400NCCDRed_I[0]);
		RedI.assign(Portra_400NCCDRed_I, Portra_400NCCDRed_I + nRed);
		RedB.assign(Portra_400NCCDRed_B, Portra_400NCCDRed_B + nRed);
		const size_t nGreen = sizeof(Portra_400NCCDGreen_I) / sizeof(Portra_400NCCDGreen_I[0]);
		GreenI.assign(Portra_400NCCDGreen_I, Portra_400NCCDGreen_I + nGreen);
		GreenB.assign(Portra_400NCCDGreen_B, Portra_400NCCDGreen_B + nGreen);
		const size_t nBlue = sizeof(Portra_400NCCDBlue_I) / sizeof(Portra_400NCCDBlue_I[0]);
		BlueI.assign(Portra_400NCCDBlue_I, Portra_400NCCDBlue_I + nBlue);
		BlueB.assign(Portra_400NCCDBlue_B, Portra_400NCCDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Portra_400VCCD") {
		const size_t nRed = sizeof(Portra_400VCCDRed_I) / sizeof(Portra_400VCCDRed_I[0]);
		RedI.assign(Portra_400VCCDRed_I, Portra_400VCCDRed_I + nRed);
		RedB.assign(Portra_400VCCDRed_B, Portra_400VCCDRed_B + nRed);
		const size_t nGreen = sizeof(Portra_400VCCDGreen_I) / sizeof(Portra_400VCCDGreen_I[0]);
		GreenI.assign(Portra_400VCCDGreen_I, Portra_400VCCDGreen_I + nGreen);
		GreenB.assign(Portra_400VCCDGreen_B, Portra_400VCCDGreen_B + nGreen);
		const size_t nBlue = sizeof(Portra_400VCCDBlue_I) / sizeof(Portra_400VCCDBlue_I[0]);
		BlueI.assign(Portra_400VCCDBlue_I, Portra_400VCCDBlue_I + nBlue);
		BlueB.assign(Portra_400VCCDBlue_B, Portra_400VCCDBlue_B + nBlue);
		color = true;
	} else if (filmName == "Portra_800CD") {
		const size_t nRed = sizeof(Portra_800CDRed_I) / sizeof(Portra_800CDRed_I[0]);
		RedI.assign(Portra_800CDRed_I, Portra_800CDRed_I + nRed);
		RedB.assign(Portra_800CDRed_B, Portra_800CDRed_B + nRed);
		const size_t nGreen = sizeof(Portra_800CDGreen_I) / sizeof(Portra_800CDGreen_I[0]);
		GreenI.assign(Portra_800CDGreen_I, Portra_800CDGreen_I + nGreen);
		GreenB.assign(Portra_800CDGreen_B, Portra_800CDGreen_B + nGreen);
		const size_t nBlue = sizeof(Portra_800CDBlue_I) / sizeof(Portra_800CDBlue_I[0]);
		BlueI.assign(Portra_800CDBlue_I, Portra_800CDBlue_I + nBlue);
		BlueB.assign(Portra_800CDBlue_B, Portra_800CDBlue_B + nBlue);
		color = true;
	} else
		return false;

	return true;
}


/* ROS-CHOMP.
 *
 * Copyright (C) 2015 Jafar Qutteineh. All rights reserved.
 * License (3-Cluase BSD): https://github.com/j3sq/ROS-CHOMP/blob/master/LICENSE
 *
 * This code uses and is based on code from:
 *   Project: trychomp https://github.com/poftwaresatent/trychomp
 *   Copyright (C) 2014 Roland Philippsen. All rights reserved.
 *   License (3-Clause BSD) : https://github.com/poftwaresatent/trychomp
 * **
 * \file chomp.hpp
 *
 * CHOMP for point vehicles (x,y) moving holonomously in the plane. It will
 * plan a trajectory (xi) connecting start point (qs) to end point (qe) while
 * avoiding obstacles (obs)
 */

#ifndef CHOMP_HPP
#define CHOMP_HPP
#include <Eigen/Dense>
namespace chomp {
void generatePath(Eigen::VectorXd const &qs, Eigen::VectorXd const &qe, Eigen::VectorXd &xi, Eigen::MatrixXd const &obs);
}
#endif // CHOMP_HPP


// Copyright John Maddock 2006.

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

#ifndef BOOST_MATH_DISTRIBUTIONS_FISHER_F_HPP
#define BOOST_MATH_DISTRIBUTIONS_FISHER_F_HPP

#include <boost/math/distributions/fwd.hpp>
#include <boost/math/special_functions/beta.hpp> // for incomplete beta.
#include <boost/math/distributions/complement.hpp> // complements
#include <boost/math/distributions/detail/common_error_handling.hpp> // error checks
#include <boost/math/special_functions/fpclassify.hpp>

#include <utility>

namespace boost{ namespace math{

template <class RealType = double, class Policy = policies::policy<> >
class fisher_f_distribution
{
public:
   typedef RealType value_type;
   typedef Policy policy_type;

   fisher_f_distribution(const RealType& i, const RealType& j) : m_df1(i), m_df2(j)
   {
      static const char* function = "fisher_f_distribution<%1%>::fisher_f_distribution";
      RealType result;
      detail::check_df(
         function, m_df1, &result, Policy());
      detail::check_df(
         function, m_df2, &result, Policy());
   } // fisher_f_distribution

   RealType degrees_of_freedom1()const
   {
      return m_df1;
   }
   RealType degrees_of_freedom2()const
   {
      return m_df2;
   }

private:
   //
   // Data members:
   //
   RealType m_df1;  // degrees of freedom are a real number.
   RealType m_df2;  // degrees of freedom are a real number.
};

typedef fisher_f_distribution<double> fisher_f;

template <class RealType, class Policy>
inline const std::pair<RealType, RealType> range(const fisher_f_distribution<RealType, Policy>& /*dist*/)
{ // Range of permissible values for random variable x.
   using boost::math::tools::max_value;
   return std::pair<RealType, RealType>(static_cast<RealType>(0), max_value<RealType>());
}

template <class RealType, class Policy>
inline const std::pair<RealType, RealType> support(const fisher_f_distribution<RealType, Policy>& /*dist*/)
{ // Range of supported values for random variable x.
   // This is range where cdf rises from 0 to 1, and outside it, the pdf is zero.
   using boost::math::tools::max_value;
   return std::pair<RealType, RealType>(static_cast<RealType>(0),  max_value<RealType>());
}

template <class RealType, class Policy>
RealType pdf(const fisher_f_distribution<RealType, Policy>& dist, const RealType& x)
{
   BOOST_MATH_STD_USING  // for ADL of std functions
   RealType df1 = dist.degrees_of_freedom1();
   RealType df2 = dist.degrees_of_freedom2();
   // Error check:
   RealType error_result;
   static const char* function = "boost::math::pdf(fisher_f_distribution<%1%> const&, %1%)";
   if(false == detail::check_df(
         function, df1, &error_result, Policy())
         && detail::check_df(
         function, df2, &error_result, Policy()))
      return error_result;

   if((x < 0) || !(boost::math::isfinite)(x))
   {
      return policies::raise_domain_error<RealType>(
         function, "Random variable parameter was %1%, but must be > 0 !", x, Policy());
   }

   if(x == 0)
   {
      // special cases:
      if(df1 < 2)
         return policies::raise_overflow_error<RealType>(
            function, 0, Policy());
      else if(df1 == 2)
         return 1;
      else
         return 0;
   }

   //
   // You reach this formula by direct differentiation of the
   // cdf expressed in terms of the incomplete beta.
   //
   // There are two versions so we don't pass a value of z
   // that is very close to 1 to ibeta_derivative: for some values
   // of df1 and df2, all the change takes place in this area.
   //
   RealType v1x = df1 * x;
   RealType result;
   if(v1x > df2)
   {
      result = (df2 * df1) / ((df2 + v1x) * (df2 + v1x));
      result *= ibeta_derivative(df2 / 2, df1 / 2, df2 / (df2 + v1x), Policy());
   }
   else
   {
      result = df2 + df1 * x;
      result = (result * df1 - x * df1 * df1) / (result * result);
      result *= ibeta_derivative(df1 / 2, df2 / 2, v1x / (df2 + v1x), Policy());
   }
   return result;
} // pdf

template <class RealType, class Policy>
inline RealType cdf(const fisher_f_distribution<RealType, Policy>& dist, const RealType& x)
{
   static const char* function = "boost::math::cdf(fisher_f_distribution<%1%> const&, %1%)";
   RealType df1 = dist.degrees_of_freedom1();
   RealType df2 = dist.degrees_of_freedom2();
   // Error check:
   RealType error_result;
   if(false == detail::check_df(
         function, df1, &error_result, Policy())
         && detail::check_df(
         function, df2, &error_result, Policy()))
      return error_result;

   if((x < 0) || !(boost::math::isfinite)(x))
   {
      return policies::raise_domain_error<RealType>(
         function, "Random Variable parameter was %1%, but must be > 0 !", x, Policy());
   }

   RealType v1x = df1 * x;
   //
   // There are two equivalent formulas used here, the aim is
   // to prevent the final argument to the incomplete beta
   // from being too close to 1: for some values of df1 and df2
   // the rate of change can be arbitrarily large in this area,
   // whilst the value we're passing will have lost information
   // content as a result of being 0.999999something.  Better
   // to switch things around so we're passing 1-z instead.
   //
   return v1x > df2
      ? boost::math::ibetac(df2 / 2, df1 / 2, df2 / (df2 + v1x), Policy())
      : boost::math::ibeta(df1 / 2, df2 / 2, v1x / (df2 + v1x), Policy());
} // cdf

template <class RealType, class Policy>
inline RealType quantile(const fisher_f_distribution<RealType, Policy>& dist, const RealType& p)
{
   static const char* function = "boost::math::quantile(fisher_f_distribution<%1%> const&, %1%)";
   RealType df1 = dist.degrees_of_freedom1();
   RealType df2 = dist.degrees_of_freedom2();
   // Error check:
   RealType error_result;
   if(false == detail::check_df(
            function, df1, &error_result, Policy())
         && detail::check_df(
            function, df2, &error_result, Policy())
         && detail::check_probability(
            function, p, &error_result, Policy()))
      return error_result;

   // With optimizations turned on, gcc wrongly warns about y being used
   // uninitializated unless we initialize it to something:
   RealType x, y(0);

   x = boost::math::ibeta_inv(df1 / 2, df2 / 2, p, &y, Policy());

   return df2 * x / (df1 * y);
} // quantile

template <class RealType, class Policy>
inline RealType cdf(const complemented2_type<fisher_f_distribution<RealType, Policy>, RealType>& c)
{
   static const char* function = "boost::math::cdf(fisher_f_distribution<%1%> const&, %1%)";
   RealType df1 = c.dist.degrees_of_freedom1();
   RealType df2 = c.dist.degrees_of_freedom2();
   RealType x = c.param;
   // Error check:
   RealType error_result;
   if(false == detail::check_df(
         function, df1, &error_result, Policy())
         && detail::check_df(
         function, df2, &error_result, Policy()))
      return error_result;

   if((x < 0) || !(boost::math::isfinite)(x))
   {
      return policies::raise_domain_error<RealType>(
         function, "Random Variable parameter was %1%, but must be > 0 !", x, Policy());
   }

   RealType v1x = df1 * x;
   //
   // There are two equivalent formulas used here, the aim is
   // to prevent the final argument to the incomplete beta
   // from being too close to 1: for some values of df1 and df2
   // the rate of change can be arbitrarily large in this area,
   // whilst the value we're passing will have lost information
   // content as a result of being 0.999999something.  Better
   // to switch things around so we're passing 1-z instead.
   //
   return v1x > df2
      ? boost::math::ibeta(df2 / 2, df1 / 2, df2 / (df2 + v1x), Policy())
      : boost::math::ibetac(df1 / 2, df2 / 2, v1x / (df2 + v1x), Policy());
}

template <class RealType, class Policy>
inline RealType quantile(const complemented2_type<fisher_f_distribution<RealType, Policy>, RealType>& c)
{
   static const char* function = "boost::math::quantile(fisher_f_distribution<%1%> const&, %1%)";
   RealType df1 = c.dist.degrees_of_freedom1();
   RealType df2 = c.dist.degrees_of_freedom2();
   RealType p = c.param;
   // Error check:
   RealType error_result;
   if(false == detail::check_df(
            function, df1, &error_result, Policy())
         && detail::check_df(
            function, df2, &error_result, Policy())
         && detail::check_probability(
            function, p, &error_result, Policy()))
      return error_result;

   RealType x, y;

   x = boost::math::ibetac_inv(df1 / 2, df2 / 2, p, &y, Policy());

   return df2 * x / (df1 * y);
}

template <class RealType, class Policy>
inline RealType mean(const fisher_f_distribution<RealType, Policy>& dist)
{ // Mean of F distribution = v.
   static const char* function = "boost::math::mean(fisher_f_distribution<%1%> const&)";
   RealType df1 = dist.degrees_of_freedom1();
   RealType df2 = dist.degrees_of_freedom2();
   // Error check:
   RealType error_result;
   if(false == detail::check_df(
            function, df1, &error_result, Policy())
         && detail::check_df(
            function, df2, &error_result, Policy()))
      return error_result;
   if(df2 <= 2)
   {
      return policies::raise_domain_error<RealType>(
         function, "Second degree of freedom was %1% but must be > 2 in order for the distribution to have a mean.", df2, Policy());
   }
   return df2 / (df2 - 2);
} // mean

template <class RealType, class Policy>
inline RealType variance(const fisher_f_distribution<RealType, Policy>& dist)
{ // Variance of F distribution.
   static const char* function = "boost::math::variance(fisher_f_distribution<%1%> const&)";
   RealType df1 = dist.degrees_of_freedom1();
   RealType df2 = dist.degrees_of_freedom2();
   // Error check:
   RealType error_result;
   if(false == detail::check_df(
            function, df1, &error_result, Policy())
         && detail::check_df(
            function, df2, &error_result, Policy()))
      return error_result;
   if(df2 <= 4)
   {
      return policies::raise_domain_error<RealType>(
         function, "Second degree of freedom was %1% but must be > 4 in order for the distribution to have a valid variance.", df2, Policy());
   }
   return 2 * df2 * df2 * (df1 + df2 - 2) / (df1 * (df2 - 2) * (df2 - 2) * (df2 - 4));
} // variance

template <class RealType, class Policy>
inline RealType mode(const fisher_f_distribution<RealType, Policy>& dist)
{
   static const char* function = "boost::math::mode(fisher_f_distribution<%1%> const&)";
   RealType df1 = dist.degrees_of_freedom1();
   RealType df2 = dist.degrees_of_freedom2();
   // Error check:
   RealType error_result;
   if(false == detail::check_df(
            function, df1, &error_result, Policy())
         && detail::check_df(
            function, df2, &error_result, Policy()))
      return error_result;
   if(df2 <= 2)
   {
      return policies::raise_domain_error<RealType>(
         function, "Second degree of freedom was %1% but must be > 2 in order for the distribution to have a mode.", df2, Policy());
   }
   return df2 * (df1 - 2) / (df1 * (df2 + 2));
}

//template <class RealType, class Policy>
//inline RealType median(const fisher_f_distribution<RealType, Policy>& dist)
//{ // Median of Fisher F distribution is not defined.
//  return tools::domain_error<RealType>(BOOST_CURRENT_FUNCTION, "Median is not implemented, result is %1%!", std::numeric_limits<RealType>::quiet_NaN());
//  } // median

// Now implemented via quantile(half) in derived accessors.

template <class RealType, class Policy>
inline RealType skewness(const fisher_f_distribution<RealType, Policy>& dist)
{
   static const char* function = "boost::math::skewness(fisher_f_distribution<%1%> const&)";
   BOOST_MATH_STD_USING // ADL of std names
   // See http://mathworld.wolfram.com/F-Distribution.html
   RealType df1 = dist.degrees_of_freedom1();
   RealType df2 = dist.degrees_of_freedom2();
   // Error check:
   RealType error_result;
   if(false == detail::check_df(
            function, df1, &error_result, Policy())
         && detail::check_df(
            function, df2, &error_result, Policy()))
      return error_result;
   if(df2 <= 6)
   {
      return policies::raise_domain_error<RealType>(
         function, "Second degree of freedom was %1% but must be > 6 in order for the distribution to have a skewness.", df2, Policy());
   }
   return 2 * (df2 + 2 * df1 - 2) * sqrt((2 * df2 - 8) / (df1 * (df2 + df1 - 2))) / (df2 - 6);
}

template <class RealType, class Policy>
RealType kurtosis_excess(const fisher_f_distribution<RealType, Policy>& dist);

template <class RealType, class Policy>
inline RealType kurtosis(const fisher_f_distribution<RealType, Policy>& dist)
{
   return 3 + kurtosis_excess(dist);
}

template <class RealType, class Policy>
inline RealType kurtosis_excess(const fisher_f_distribution<RealType, Policy>& dist)
{
   static const char* function = "boost::math::kurtosis_excess(fisher_f_distribution<%1%> const&)";
   // See http://mathworld.wolfram.com/F-Distribution.html
   RealType df1 = dist.degrees_of_freedom1();
   RealType df2 = dist.degrees_of_freedom2();
   // Error check:
   RealType error_result;
   if(false == detail::check_df(
            function, df1, &error_result, Policy())
         && detail::check_df(
            function, df2, &error_result, Policy()))
      return error_result;
   if(df2 <= 8)
   {
      return policies::raise_domain_error<RealType>(
         function, "Second degree of freedom was %1% but must be > 8 in order for the distribution to have a kutosis.", df2, Policy());
   }
   RealType df2_2 = df2 * df2;
   RealType df1_2 = df1 * df1;
   RealType n = -16 + 20 * df2 - 8 * df2_2 + df2_2 * df2 + 44 * df1 - 32 * df2 * df1 + 5 * df2_2 * df1 - 22 * df1_2 + 5 * df2 * df1_2;
   n *= 12;
   RealType d = df1 * (df2 - 6) * (df2 - 8) * (df1 + df2 - 2);
   return n / d;
}

} // namespace math
} // namespace boost

// This include must be at the end, *after* the accessors
// for this distribution have been defined, in order to
// keep compilers that support two-phase lookup happy.
#include <boost/math/distributions/detail/derived_accessors.hpp>

#endif // BOOST_MATH_DISTRIBUTIONS_FISHER_F_HPP


#include <boost/shared_ptr.hpp>
#include <gtest/gtest.h>
#include "nuturtlebot/WheelCommands.h"
#include "nuturtlebot/SensorData.h"
#include <geometry_msgs/Twist.h>
#include <ros/ros.h>
#include <ros/console.h>
#include <sensor_msgs/JointState.h>
#include <thread>
#include <chrono>
#include <cmath>

/// \file
/// Test cases for ros tesing all nodes in turtle interface file

constexpr float PI = 3.141592653589793238;

class TurtleInterface {

   public:

     ros::Publisher cmdVelPublisher, sensorDataPublisher;
     ros::Subscriber wheelCmdSubscriber, jspSubscriber;
     double maxMotorVelocity, robotMaxTransVel, robotMaxRotVel;
     double wheelBase, wheelRadius, encoderTicksPerRevolution;
     std::string leftWheelJoint, rightWheelJoint;
     bool twistMessageReceived, jspMessageReceived;
     int maxWheelCommand;
     nuturtlebot::WheelCommands receivedCommand;
     sensor_msgs::JointState receivedJoint;
     ros::NodeHandle n;

        TurtleInterface()
        {

         //ros::Rate loop_rate(10);
         ros::param::get("max_rot_vel_motor", maxMotorVelocity);
         ros::param::get("max_trans_vel", robotMaxTransVel);
         ros::param::get("max_rot_vel_motor", robotMaxRotVel);
         ros::param::get("wheel_base", wheelBase);
         ros::param::get("wheel_radius", wheelRadius);
         ros::param::get("~left_wheel_joint", leftWheelJoint);
         ros::param::get("~right_wheel_joint", rightWheelJoint);
         ros::param::get("encoder_ticks_per_rev", encoderTicksPerRevolution);
         ros::param::get("max_wheel_command", maxWheelCommand);

         cmdVelPublisher = n.advertise<geometry_msgs::Twist>("/cmd_vel", 1000, true);
         sensorDataPublisher = n.advertise<nuturtlebot::SensorData>("/sensor_data", 1000, true);
         wheelCmdSubscriber = n.subscribe("/wheel_cmd", 1000, &TurtleInterface::wheelCmdCallback, this);
         jspSubscriber = n.subscribe("/joint_states", 1000, &TurtleInterface::jspCallback, this);
         twistMessageReceived = false;
         jspMessageReceived = true;
         //loop_rate.sleep();
        }

   void publishTwist(const geometry_msgs::Twist zeroRotTwist)
   {
     twistMessageReceived = false;

   cmdVelPublisher.publish(zeroRotTwist);

   }

   void publishSensorData(const nuturtlebot::SensorData turtleBotData)
   {
   sensorDataPublisher.publish(turtleBotData);
   jspMessageReceived = false;
   }

   void wheelCmdCallback(const nuturtlebot::WheelCommands movementCommand)
   {
     receivedCommand = movementCommand;
     twistMessageReceived = true;

   }

  void jspCallback(const sensor_msgs::JointState jointState)
  {
    receivedJoint = jointState;
    jspMessageReceived = true;

  }

};

  TEST(JointState, EncoderToJointStates)
 {

    TurtleInterface turtleInterface;
    double encoderTicksPerRevolution = turtleInterface.encoderTicksPerRevolution;

   nuturtlebot::SensorData turtleBotData1;
   turtleBotData1.left_encoder = 0;
   turtleBotData1.right_encoder = 100;
   auto firstTime = ros::Time::now();
   turtleBotData1.stamp = firstTime;
   turtleInterface.publishSensorData(turtleBotData1);
   while(not turtleInterface.jspMessageReceived)
    {
      ros::spinOnce();
    }

   auto firstJointStates = turtleInterface.receivedJoint;
   turtleInterface.jspMessageReceived = false;

   nuturtlebot::SensorData turtleBotData2;
   turtleBotData2.left_encoder = 100;
   turtleBotData2.right_encoder = 0;
   auto secondTime = ros::Time::now();
   turtleBotData2.stamp = secondTime;

   turtleInterface.publishSensorData(turtleBotData2);
   while(not turtleInterface.jspMessageReceived)
    {
      ros::spinOnce();
    }

   auto secondJointStates = turtleInterface.receivedJoint;
   turtleInterface.jspMessageReceived = false;

   ros::Duration totalDuration = secondTime - firstTime;
   double totalTime = totalDuration.toSec();

   ASSERT_NEAR(firstJointStates.position[0],
               double(turtleBotData1.left_encoder) / encoderTicksPerRevolution * PI * 2,
               0.01)<<"Error in calculating joint states from sensor message";
   ASSERT_NEAR(firstJointStates.position[1],
               double(turtleBotData1.right_encoder) / encoderTicksPerRevolution * PI * 2,
               0.01)<<"Error in calculating joint states from sensor reading";

   ASSERT_NEAR(secondJointStates.position[0],
               double(turtleBotData2.left_encoder) / encoderTicksPerRevolution * PI * 2,
               0.01)<<"Error in calculating joint states from sensor message";
   ASSERT_NEAR(secondJointStates.position[1],
               double(turtleBotData2.right_encoder) / encoderTicksPerRevolution * PI * 2,
               0.01)<<"Error in calculating joint states from sensor reading";

   ASSERT_NEAR(secondJointStates.velocity[0],
               double(100) / encoderTicksPerRevolution * PI * 2 / totalTime,
               0.01)<<"Error in calculating joint states from sensor message";
   ASSERT_NEAR(secondJointStates.velocity[1],
               double(-100) / encoderTicksPerRevolution * PI * 2 / totalTime,
               0.01)<<"Error in calculating joint states from sensor reading";

 }

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

   ros::init(argc, argv, "turtle_interface_test");
   ::testing::InitGoogleTest(&argc, argv);
   ROS_INFO("stated node");
   return RUN_ALL_TESTS();

  // testing::InitGoogleTest(&argc, argv);
  // ros::init(argc, argv, "TurtleInterfaceTest");

  // ros::AsyncSpinner spinner(1);
  // spinner.start();
  // int ret = RUN_ALL_TESTS();
  // spinner.stop();
  // ros::shutdown();
  // return ret;
}


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

/* simd_vector_benchmark.cc
   Jeremy Barnes, 21 February 2007
   Copyright (c) 2007 Jeremy Barnes.  All rights reserved.

   Benchmark of SIMD vector operations.
*/

#define BOOST_TEST_MAIN
#define BOOST_TEST_DYN_LINK

#include "mldb/arch/arch.h"
#include "mldb/arch/simd_vector.h"
#include "mldb/arch/demangle.h"
#include "mldb/arch/tick_counter.h"

#include <boost/test/unit_test.hpp>
#include <boost/test/tools/floating_point_comparison.hpp>
#include <vector>
#include <set>
#include <iostream>
#include <cmath>

using namespace MLDB;
using namespace std;

using boost::unit_test::test_suite;

#if MLDB_INTEL_ISA
namespace MLDB {
namespace SIMD {
namespace Generic {
double vec_dotprod_sse2(const double * x, const double * y, size_t n);
} // namespace Generic

namespace Avx {
double vec_dotprod(const double * x, const double * y, size_t n);
} // namespace Avx
} // namespace SIMD
} // namespace MLDB
#endif // MLDB_INTEL_ISA

double vec_dotprod_generic(const double * x, const double * y, size_t n)
{
    double result = 0.0;
    for (unsigned i = 0;  i < n;  ++i)
        result += x[i] * y[i];
    return result;
}

BOOST_AUTO_TEST_CASE( benchmark )
{
    for (int nvals: { 1, 16, 64, 256, 1024, 4096, 16386, 65536, 1000000 }) {
        cerr << "nvals = " << nvals << endl;
        double min1 = INFINITY, min2 = INFINITY, min3 = INFINITY;

        // Initialize the test case
        vector<double> x(nvals), y(nvals);

        for (unsigned i = 0; i < nvals;  ++i) {
            x[i] = rand() / 16384.0;
            y[i] = rand() / 16384.0;
        }

        for (unsigned i = 0;  i < 100;  ++i) {

#if MLDB_INTEL_ISA
            uint64_t t0 = ticks();
            SIMD::Generic::vec_dotprod_sse2(&x[0], &y[0], nvals);
            uint64_t t1 = ticks();
            SIMD::Avx::vec_dotprod(&x[0], &y[0], nvals);
#else  // MLDB_INTEL_ISA
            uint64_t t0 = ticks(), t1 = t0;
#endif // MLDB_INTEL_ISA
            uint64_t t2 = ticks();
            vec_dotprod_generic(&x[0], &y[0], nvals);
            uint64_t t3 = ticks();

            double res_sse2 = t1 - t0;
            double res_avx  = t2 - t1;
            double res_generic = t3 - t2;

            min1 = std::min(min1, res_sse2);
            min2 = std::min(min2, res_avx);
            min3 = std::min(min3, res_generic);
        }

        cerr << "Minimum: sse2 " << min1 << " avx " << min2 << " gen " << min3 << endl;
        cerr << "cycles/op: sse2 " << min1 / nvals << " avx " << min2 / nvals
             << " generic " << min3 / nvals
             << endl;
    }
}


/**
@file

Copyright John Reid 2006, 2007, 2013
*/

#include "bio_test_defs.h"
#include "bio/defs.h"
#include "bio/lcs.h"
#include "bio/max_chain.h"

#include <boost/random/mersenne_twister.hpp>
#include <boost/random/variate_generator.hpp>
#include <boost/random/uniform_int.hpp>
#include <boost/random/uniform_01.hpp>
#include <boost/random/uniform_real.hpp>
#include <boost/random/mersenne_twister.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/test/parameterized_test.hpp>
#include <boost/test/floating_point_comparison.hpp>
#include <boost/assign/list_of.hpp>
#include <boost/range/algorithm/equal.hpp>

using namespace boost;
using namespace boost::assign;
using boost::unit_test::test_suite;
using namespace std;
USING_BIO_NS;

//#define VERBOSE_CHECKING

struct random_gen
{
	typedef boost::mt19937 rng;                 // produces randomness out of thin air
	typedef boost::uniform_int< int > int_dist;
	typedef boost::uniform_int< unsigned > unsigned_dist;
	typedef boost::uniform_real< double > weight_dist;
	typedef boost::variate_generator< rng &, int_dist > int_gen;
	typedef boost::variate_generator< rng &, unsigned_dist > unsigned_gen;
	typedef boost::variate_generator< rng &, weight_dist > weight_gen;

	unsigned_dist _num_points_dist;
	unsigned_gen _num_points_gen;

	unsigned_dist _char_dist;
	unsigned_gen _char_gen;

	int_dist _coord_dist;
	int_gen _coord_gen;

	weight_dist _weight_dist;
	weight_gen _weight_gen;

	random_gen(
		rng & _rng,
		unsigned max_num_points,
		int max_coord,
		unsigned alphabet_size = 26 )
		: _num_points_dist( 0, max_num_points )
		, _num_points_gen( _rng, _num_points_dist )
		, _char_dist( 0, alphabet_size - 1 )
		, _char_gen( _rng, _char_dist )
		, _coord_dist( 0, max_coord )
		, _coord_gen( _rng, _coord_dist )
		, _weight_dist( 0.0, 1.0 )
		, _weight_gen( _rng, _weight_dist )
	{
	}

	char character( )
	{
		return 'a' + _char_gen();
	}

	int coord( )
	{
		return _coord_gen();
	}

	std::pair< int, int > coord_range( )
	{
		std::pair< int, int > result( coord(), coord() );
		if( result.first > result.second )
		{
			std::swap( result.first, result.second );
		}
		return result;
	}

	int num_points( )
	{
		return _num_points_gen();
	}

	double weight( )
	{
		return _weight_gen();
	}

	template< unsigned d >
	boost::array< int, d > point()
	{
		typedef boost::array< int, d > point;
		point p;
		std::generate( p.begin(), p.end(), boost::bind< int >( &random_gen::coord, this ) );
		return p;
	}

	template< unsigned d >
	std::pair< boost::array< int, d >, boost::array< int, d > > range()
	{
		typedef std::pair< boost::array< int, d >, boost::array< int, d > > point_pair;
		point_pair points( point< d >(), point< d >() );
		//make sure first <= second in every dimension
		for( unsigned i = 0; d != i; ++i )
		{
			if( points.first[ i ] > points.second[ i ] )
			{
				std::swap( points.first[ i ], points.second[ i ] );
			}
		}
		return points;
	}

};

namespace range_tree {

template< unsigned d >
struct test
{
	struct traits : point_traits< int, d >
	{
		typedef double weight;
	};

	typedef std::pair< typename traits::coord_array, typename traits::weight > test_point;
	typedef std::vector< test_point > test_point_vec;

	typedef RangeTree< d, traits > range_tree;
	typedef typename range_tree_ns::node< d, traits, d > node;
	typedef typename node::ptr tree;

	random_gen::rng _rng; //random number generator

	test() : _rng( 42u ) { } //seed the rng

	void generate_and_check( unsigned num_test_cases, unsigned max_points, unsigned max_coord )
	{
		cout
			<< "******* lcs::range_tree::test::check(): "
			<< num_test_cases << " test cases of up to "
			<< max_points << " points of dimension " << d << "\n";

		for( unsigned i = 0; num_test_cases != i; ++i )
		{
			check( create_case( max_points, max_coord ) );
		}
	}

	test_point_vec create_case( unsigned max_points, unsigned max_coord )
	{
		random_gen gen( _rng, max_points, max_coord );

		const unsigned num_points = gen.num_points();				// generate a random number of points

		test_point_vec test_case;
		for( unsigned i = 0; num_points != i; ++i )
		{
			test_point p( gen.point< d >(), gen.weight() );
			test_case.push_back( p );
		}

		return test_case;
	}

	void print_point( typename traits::point p )
	{
		for( unsigned i = 0; d != i; ++i )
		{
			std::cout << ( *p )[ i ];
			if( d - 1 != i )
			{
				std::cout << ",";
			}
		}
	}

	void
	check( const test_point_vec & test_points )
	{
		using namespace range_tree;
		using namespace boost::lambda;
		using boost::lambda::_1;

		//get the set of all points...
		typedef std::set< typename traits::point, typename traits::point_less > point_set;
		point_set points;
		for( unsigned i = 0; test_points.size() != i; ++i )
		{
			typename traits::point p = boost::addressof( test_points[ i ].first );
			points.find( p );
			points.insert( p );
#ifdef VERBOSE_CHECKING
			print_point( p );
			std::cout << std::endl;
#endif //VERBOSE_CHECKING
		}
#ifdef VERBOSE_CHECKING
		std::cout << std::endl;
#endif //VERBOSE_CHECKING

		//build tree
		tree t;
		typename range_tree_ns::create_tree< d, traits, d >()( t, points );
		typename range_tree_ns::check_tree< d, traits, d >()( t );
#ifdef VERBOSE_CHECKING
		typename range_tree_ns::visit_tree< d, traits, d >()( t, typename range_tree::print_point< d, traits, d >() );
#endif //VERBOSE_CHECKING

		//check tree has the correct points
		point_set tree_points;
		typename range_tree_ns::visit_tree< d, traits, d >()(
			t,
			range_tree_ns::make_get_points< d, traits, d >(
				std::inserter( tree_points, tree_points.begin() ) ) );
		BOOST_ASSERT( points == tree_points );

		//insert points and weights
		BOOST_FOREACH( const typename test_point_vec::value_type & v, test_points )
		{
			typename range_tree_ns::insert_point< d, traits, d >()( t, boost::addressof( v.first ), v.second );
			//std::cout << v.first[ 0 ] << "," << v.first[ 1 ] << " - " << max_chain::max_weight< 2 >()( t, addressof( v.first ) ) << "\n";
		}
		//std::cout << "\n";

#ifdef VERBOSE_CHECKING
		//query points
		BOOST_FOREACH( traits::point p, points )
		{
			std::copy( p->begin(), p->end(), std::ostream_iterator< traits::coord >( std::cout, "," ) );
			std::cout << " - " << range_tree::max_weight< d >()( t, p ) << "\n";
		}
#endif //VERBOSE_CHECKING

		const typename range_tree::heaviest_point null_point =
			typename range_tree_ns::max_weight< d, traits, d >()( t, traits::always_dominated_point() );
		BOOST_CHECK_EQUAL( null_point.first, traits::always_dominated_point() );
		BOOST_CHECK_EQUAL( null_point.second, 0.0 );

		BOOST_FOREACH( typename traits::point p1, points )
		{
			const typename range_tree::heaviest_point h1 =
				typename range_tree_ns::max_weight< d, traits, d >()( t, p1 );

			BOOST_FOREACH( typename traits::point p2, points )
			{
				if( typename traits::point_dominate()( p1, p2 ) )
				{
					const typename range_tree::heaviest_point h2 =
						range_tree_ns::max_weight< d, traits, d >()( t, p2 );

					BOOST_CHECK( h1.second >= h2.second );
				}
			}
		}
	}
};

void
generate_and_check( )
{
	const unsigned num_tests = 10;
	const std::vector< unsigned > max_points = list_of( 4 )( 15 )( 300 );

	BOOST_FOREACH( unsigned mp, max_points )
	{
		const unsigned max_coord = mp;

		test<  1 >().generate_and_check( num_tests, mp, max_coord );
		test<  2 >().generate_and_check( num_tests, mp, max_coord );
#ifndef _MSC_VER
		test<  3 >().generate_and_check( num_tests, mp, max_coord );
		test<  4 >().generate_and_check( num_tests, mp, max_coord );

#if 0
		test<  5 >().generate_and_check( num_tests, mp, max_coord );
		test<  6 >().generate_and_check( num_tests, mp, max_coord );
		test<  7 >().generate_and_check( num_tests, mp, max_coord );
		test<  7 >().generate_and_check( num_tests, mp, max_coord );
		test<  8 >().generate_and_check( num_tests, mp, max_coord );
		test<  9 >().generate_and_check( num_tests, mp, max_coord );
#endif
#endif //_MSC_VER
	}
}

}

namespace max_chain_test {

struct V
{
	typedef char character;

	V( char c = ' ', int start = -1, int end = 0, double score = 1.0 )
		: _start( start )
		, _end( end )
		, _c( c )
		, _score( score )
	{
		if( _start >= end )
		{
			throw std::invalid_argument( "start must be < end" );
		}
	}

	int _start;
	int _end;
	char _c;
	double _score;
};

struct VTraits
{
    typedef V hit;
    typedef const V * data;
	typedef char character;
	typedef double weight;
	typedef int coord;

	static data get_data( const V & v ) { return boost::addressof( v ); }
	static character get_char( const V & v ) { return v._c; }
	static weight get_weight( const V & v ) { return v._score; }
	static coord get_start( const V & v ) { return v._start; }
	static coord get_end( const V & v ) { return v._end; }
};

struct CharExtractor
{
	char operator()( const V & v ) const
	{
		return v._c;
	}
};

struct StartExtractor
{
	int operator()( const V & v ) const
	{
		return v._start;
	}
};

struct EndExtractor
{
	int operator()( const V & v ) const
	{
		return v._end;
	}
};

struct ScoreExtractor
{
	double operator()( const V & v ) const
	{
		return v._score;
	}
};

template< unsigned d >
struct VBox
{
	struct traits : point_traits< int, d >
	{
		typedef VBox< d > box;
		typedef const box * box_ptr;
		typedef double weight;
		typedef const V * data;
		typedef std::vector< box > box_vec;					/**< Vector of boxes. */
		typedef std::list< box_ptr > box_ptr_list;			/**< List of box pointers. */
		typedef typename point_traits< int, d >::coord_array coord_array;
		typedef typename point_traits< int, d >::point point;

		static unsigned get_dimensions() { return d; }

		static box make_box( data _d, weight w, coord_array s, coord_array e ) { return box( _d, w, s, e ); }
		static point get_start( const box & b ) { return boost::addressof( b._start ); }
		static point get_end( const box & b ) { return boost::addressof( b._end ); }
	};

	typedef typename traits::box_ptr box_ptr;
	typedef typename traits::weight weight;
	typedef typename traits::data data;
	typedef typename traits::coord_array point;

	VBox(
		typename traits::data _d,
		typename traits::weight w,
		typename traits::coord_array s,
		typename traits::coord_array e )
		: _data( _d )
		, _weight( w )
		, _start( s )
		, _end( e )
	{
	}

    typename traits::data _data;
	typename traits::weight _weight;
	typename traits::coord_array _start;
	typename traits::coord_array _end;

};

typedef LCS< V, char, CharExtractor, StartExtractor, EndExtractor, ScoreExtractor > lcs;

typedef std::vector< V > v_vec;
typedef std::vector< v_vec > v_array;
typedef boost::tuple< std::string, v_array, lcs::string, double > lcs_test_case;
typedef std::vector< lcs_test_case > lcs_test_case_vec;

} // namespace max_chain_test

bool
operator==( const max_chain_test::lcs::string & lhs, const max_chain_test::lcs::string & rhs )
{
    return boost::equal( lhs, rhs );
}

std::ostream &
operator<<( std::ostream & os, const max_chain_test::lcs::string & s )
{
    os << "\"";
    BOOST_FOREACH( max_chain_test::lcs::character c, s )
    {
        os << c;
    }
    os << "\"";
    return os;
}

namespace max_chain_test {

struct generate_lcs_test_case
{
	random_gen::rng _rng;
	generate_lcs_test_case() : _rng( 42u ) { }

	template< typename Int >
	Int gen_int( Int max )
	{
		typedef uniform_int< Int > dist;
		return boost::variate_generator< random_gen::rng &, dist >( _rng, dist( 0, max ) )();
	};

	template< typename Real >
	Real gen_real( )
	{
		typedef uniform_real< Real > dist;
		return boost::variate_generator< random_gen::rng &, dist >( _rng, dist( 0.0, 1.0 ) )();
	};

	v_array operator()( unsigned num_sequences, unsigned max_sequence_length, unsigned max_coord, unsigned alphabet_size )
	{
		v_array result( num_sequences );
		for( unsigned i = 0; num_sequences != i; ++i )
		{
			const unsigned length = gen_int< unsigned >( max_sequence_length );
			result[ i ].resize( length );
			for( unsigned j = 0; length != j; ++j )
			{
				const char c = 'a' + gen_int< unsigned >( alphabet_size - 1 );
				int start = gen_int< int >( max_coord );
				int end = gen_int< int >( max_coord );
				if( start > end )
				{
					std::swap( start, end );
				}
				else if( start == end )
				{
					++end;
				}
				const double score = gen_real< double >();
				result[ i ][ j ] = V( c, start, end, score );
			}
		}
		return result;
	}
};

//using boost::assign::tuple_list_of;
const lcs_test_case_vec test_cases = tuple_list_of
    (
        "empty"
        ,
        v_array()
        ,
        lcs::string()
        ,
        0.0
    )
    (
        "singular - 2D"
        ,
        list_of< v_vec >
            ( list_of ( V( 'a',  0, 10, 1. ) ) )
            ( list_of ( V( 'a', 10, 20, 2. ) ) )
        ,
        list_of( 'a' )
        ,
        1.5
    )
    (
        "singular - 3D"
        ,
        list_of< v_vec >
            ( list_of ( V( 'a',  0, 10, 1. ) ) )
            ( list_of ( V( 'a', 10, 20, 2. ) ) )
            ( list_of ( V( 'a', 20, 30, 3. ) ) )
        ,
        list_of( 'a' )
        ,
        2.
    )
    (
        "singular - 4D"
        ,
        list_of< v_vec >
            ( list_of ( V( 'a',  0, 10, 1. ) ) )
            ( list_of ( V( 'a', 10, 20, 2. ) ) )
            ( list_of ( V( 'a', 20, 30, 3. ) ) )
            ( list_of ( V( 'a', 30, 40, 4. ) ) )
        ,
        list_of( 'a' )
        ,
        2.5
    )
	(
		"problem from random tests - max_chain swaps first two matches "
		,
		list_of< v_vec >
			( list_of
                ( V( 'd', 17, 20, 0.996334 ) )( V( 'f', 6, 14,  0.448611 ) )( V( 'h', 9, 14, 0.886196 ) )
                ( V( 'e', 7, 16,  0.391526 ) )( V( 'h', 15, 20, 0.695618 ) )( V( 'f', 5, 7, 0.619589 ) )
                ( V( 'b', 14, 15, 0.794197 ) )( V( 'd', 5, 19,  0.588202 ) )( V( 'a', 13, 20, 0.642326 ) )
                ( V( 'b', 6, 13,  0.579984 ) )( V( 'b', 0, 4,    0.56066 ) )( V( 'i', 8, 17, 0.676468 ) )
                ( V( 'h', 0, 18,  0.269821 ) )( V( 'g', 13, 17, 0.498256 ) )( V( 'd', 1, 2, 0.0585509 ) )
                ( V( 'e', 8, 20,  0.784897 ) ) )
			( list_of
                ( V( 'j', 13, 18, 0.825131 ) )( V( 'j', 15, 20, 0.373463 ) )( V( 'b', 0, 15, 0.735506 ) )
                ( V( 'i', 0, 11,  0.371422 ) )( V( 'b', 7, 8,   0.801367 ) )( V('g', 0, 9, 0.733616 ) )
                ( V( 'b', 13, 17,  0.17348 ) )( V( 'c', 3, 14,  0.496166 ) )( V( 'j', 3, 11, 0.685911 ) )
                ( V( 'j', 12, 13,  0.74722 ) )( V( 'g', 1, 13,  0.950313 ) )( V( 'd', 3, 20, 0.56555 ) )
                ( V( 'b', 3, 10,  0.165294 ) )( V( 'd', 3, 9,    0.58581 ) )( V( 'd', 10, 16, 0.697671 ) )
                ( V( 'b', 11, 13, 0.782582 ) )( V( 'g', 3, 18,  0.852728 ) )( V( 'j', 9, 20, 0.376914 ) )
                ( V( 'e', 2, 12,  0.480906 ) )( V( 'd', 15, 16, 0.937353 ) )( V( 'j', 1, 8, 0.631171 ) )
                ( V( 'f', 9, 20,  0.903913 ) )( V( 'g', 8, 15,  0.780213 ) )( V( 'e', 13, 15, 0.0572803 ) )
                ( V( 'f', 0, 18,  0.131349 ) )( V( 'h', 2, 12,  0.922623 ) )( V( 'f', 3, 4, 0.0134026 ) )
                ( V( 'b', 5, 6,  0.0445949 ) )( V( 'h', 1, 3,    0.27572 ) )( V( 'f', 10, 11, 0.787127 ) ) )
		,
		list_of( 'b' )( 'f' )( 'b' )( 'd' )
		,
		3.1396045
	)
	(
		"problem with start == end in first V"
		,
		list_of< v_vec >
			( list_of( V( 'b', 5, 6, 0.386 ) )( V( 'f', 3, 11, 0.191 ) )( V( 'h', 4, 11, 0.619 ) )( V( 'b', 12, 14, 0.484 ) )( V( 'c', 14, 18, 0.602 ) ) )
		,
		list_of( 'h' )( 'b' )( 'c' )
		,
		1.705
	)
	(
		"one good hit score"
		,
		list_of< v_vec >
			( list_of( V( 'a', 0, 1, 0.1 ) )( V( 'b', 1, 2, 0.1 ) )( V( 'c', 2, 3, 0.1 ) )( V( 'd', 3, 4, 0.1 ) ) )
			( list_of( V( 'd', 0, 1, 0.9 ) )( V( 'a', 0, 1, 0.1 ) )( V( 'b', 1, 2, 0.1 ) )( V( 'c', 2, 3, 0.1 ) )( V( 'd', 3, 4, 0.1 ) ) )
		,
		list_of( 'd' )
		,
		0.5
	)
	(
		"long chain score"
		,
		list_of< v_vec >
			( list_of( V( 'a', 0, 1, 0.1 ) )( V( 'b', 1, 2, 0.1 ) )( V( 'c', 2, 3, 0.1 ) )( V( 'd', 3, 4, 0.1 ) ) )
			( list_of( V( 'd', 0, 1, 0.3 ) )( V( 'a', 0, 1, 0.1 ) )( V( 'b', 1, 2, 0.1 ) )( V( 'c', 2, 3, 0.1 ) )( V( 'd', 3, 4, 0.1 ) ) )
		,
		list_of( 'a' )( 'b' )( 'c' )( 'd' )
		,
		0.4
	)
	(
		"next to"
		,
		list_of< v_vec >
			( list_of( V( 'a', 0, 1 ) )( V( 'c', 1, 2 ) )( V( 'g', 2, 3 ) )( V( 't', 3, 4 ) ) )
			( list_of( V( 'a', 0, 1 ) )( V( 'c', 1, 2 ) )( V( 'g', 2, 3 ) )( V( 't', 3, 4 ) ) )
			( list_of( V( 'a', 0, 5 ) )( V( 'c', 1, 2 ) )( V( 'g', 2, 3 ) ) )
		,
		list_of( 'c' )( 'g' )
		,
		2.0
	)
	(
		"overlapping"
		,
		list_of< v_vec >
			( list_of( V( 'a', 0, 3 ) )( V( 'c', 1, 3 ) )( V( 'g', 2, 3 ) )( V( 't', 3, 4 ) ) )
			( list_of( V( 'a', 0, 1 ) )( V( 'c', 1, 2 ) )( V( 'g', 2, 3 ) )( V( 't', 3, 4 ) ) )
			( list_of( V( 'a', 0, 9 ) )( V( 'c', 1, 9 ) )( V( 'g', 2, 9 ) ) )
		,
		list_of( 'a' )
		,
		1.0
	)
	(
		"empty sequence"
		,
		list_of< v_vec >
			( list_of( V( 'a', 0, 1 ) )( V( 'c', 1, 2 ) )( V( 'g', 2, 3 ) )( V( 't', 3, 4 ) ) )
			( v_vec() )
		,
		lcs::string()
		,
		0.0
	)
	(
		"empty sequence 2"
		,
		list_of< v_vec >
			( v_vec() )
			( list_of( V( 'a', 0, 1 ) )( V( 'c', 1, 2 ) )( V( 'g', 2, 3 ) )( V( 't', 3, 4 ) ) )
		,
		lcs::string()
		,
		0.0
	)
	(
		"identical"
		,
		list_of< v_vec >
			( list_of( V( 'a', 0, 1 ) )( V( 'c', 1, 2 ) )( V( 'g', 2, 3 ) )( V( 't', 3, 4 ) ) )
			( list_of( V( 'a', 0, 1 ) )( V( 'c', 1, 2 ) )( V( 'g', 2, 3 ) )( V( 't', 3, 4 ) ) )
		,
		list_of( 'a' )( 'c' )( 'g' )( 't' )
		,
		4.0
	)
	(
		"score"
		,
		list_of< v_vec >
			( list_of( V( 'a', 0, 1, 0.1 ) )( V( 'c', 1, 2, 1.0 ) ) )
			( list_of( V( 'c', 0, 1, 1.0 ) )( V( 'c', 1, 2, 0.1 ) ) )
		,
		list_of( 'c' )
		,
		1.0
	)
	;

struct int_cmp
{
	bool operator()( int lhs, int rhs ) { return lhs < rhs; }
};

typedef std::pair< lcs::string, double > lcs_result;

template< unsigned d >
lcs_result
get_lcs_using_max_chain_d( const v_array & sequences, bool print = false )
{
    typedef typename VBox< d >::traits box_traits;
    typename box_traits::box_vec boxes;
    auto values = box_generator< VTraits >::template boxes_from_sequences< box_traits >(
        sequences,
        std::back_inserter( boxes ),
        50000
    );

	typedef typename VBox< d >::traits traits;
	typedef MaxChain< d, VBox< d >, traits > max_chain;
	max_chain _max_chain( boxes );

	lcs::string _longest;
	double weight = 0.0;
	BOOST_FOREACH( typename max_chain::box_ptr b, _max_chain.maximal_chain )
	{
		_longest.push_back( b->_data->_c );
#if 0
		std::cout
			<< b->_data->_start << ","
			<< b->_data->_end << ","
			<< b->_data->_c << ","
			<< b->_data->_score << endl;
#endif
		weight += b->_weight;
	}
	BOOST_CHECK_CLOSE( _max_chain.maximal_weight, weight, 0.001 );

	return std::make_pair( _longest, _max_chain.maximal_weight );
}

lcs_result
get_lcs_using_max_chain( const v_array & sequences, bool print = false )
{
	switch( boost::size( sequences ) )
	{
	case 0: return get_lcs_using_max_chain_d< 0 >( sequences, print ); break;
	case 1: return get_lcs_using_max_chain_d< 1 >( sequences, print ); break;
	case 2: return get_lcs_using_max_chain_d< 2 >( sequences, print ); break;
	case 3: return get_lcs_using_max_chain_d< 3 >( sequences, print ); break;
	case 4: return get_lcs_using_max_chain_d< 4 >( sequences, print ); break;
	case 5: return get_lcs_using_max_chain_d< 5 >( sequences, print ); break;
	case 6: return get_lcs_using_max_chain_d< 6 >( sequences, print ); break;
	case 7: return get_lcs_using_max_chain_d< 7 >( sequences, print ); break;
	//case 8: return get_lcs_using_max_chain_d< 8 >( sequences, print ); break;
	default: throw std::logic_error( "Too many test input sequences" );
	}
}

void
check_max_chain( const lcs_test_case & test_case )
{
    using namespace boost;

	cout << "******* check_max_chain(): \"" << test_case.get< 0 >() << "\"\n";

	if( 1 != test_case.get< 1 >().size() ) //does not work with one sequence at the moment
	{
		lcs_result result = get_lcs_using_max_chain( test_case.get< 1 >() );
//        std::cout
//            << size( test_case.get< 2 >() ) << " : " << size( result.first ) << ", "
//            << test_case.get< 2 >() << " : " << result.first
//            << ", weights " << test_case.get< 3 >() << " : " << result.second << "\n";

//        if( ! boost::equal( test_case.get< 2 >(), result.first ) ) {
//            std::cout << "Not equal under boost range equality\n";
//        }
//        if( test_case.get< 2 >() != result.first) {
//            std::cout << "Not equal under != operator\n";
//        }
		BOOST_CHECK_EQUAL( test_case.get< 2 >(), result.first );
		BOOST_CHECK_CLOSE( test_case.get< 3 >(), result.second, 0.001 );
	}
}

lcs_result
get_lcs_using_lcs_algorithm( const v_array & sequences )
{
	lcs _lcs( sequences );
	_lcs.calculate_best();

	return lcs_result( _lcs.get_best().get_string(), _lcs.get_best().get_score() );
}

void
check_random_lcs_test_cases( )
{
	typedef boost::array< unsigned, 5 > test_params;
	typedef std::vector< test_params > test_params_vec;

	// # tests, # seqs, max seq length, alphabet_size, max coord
	const test_params_vec _params = list_of
		( list_of( 300 )( 0 )( 100 )( 20 )( 100 ) )
		( list_of(   1 )( 1 )( 100 )( 20 )( 100 ) )
		( list_of( 300 )( 2 )( 100 )( 20 )( 100 ) )
		( list_of( 300 )( 3 )( 100 )( 20 )( 100 ) )
		( list_of( 300 )( 4 )(  30 )( 20 )( 100 ) )
		( list_of( 300 )( 5 )(  30 )( 20 )( 100 ) )
		( list_of( 300 )( 5 )(  30 )(  4 )( 100 ) )
		( list_of( 300 )( 5 )(  30 )(  4 )(  10 ) )
		( list_of( 300 )( 6 )(  30 )(  4 )(  10 ) )
		( list_of( 300 )( 7 )(  30 )(  4 )(  10 ) )
		;

	generate_lcs_test_case gen;

	BOOST_FOREACH( const test_params & p, _params )
	{
		cout
			<< "******* check_random_lcs_test_cases(): checking " << p[ 0 ] << " random cases\n"
			<< "# seqs: " << p[ 1 ] << "\n"
			<< "max length: " << p[ 2 ] << "\n"
			<< "alphabet size: " << p[ 3 ] << "\n"
			<< "max coord: " << p[ 4 ] << "\n"
			;

		double lcs_cumulative_time = 0.0;
		double mc_cumulative_time = 0.0;
		for( unsigned i = 0; p[ 0 ] != i; ++i )
		{
			//generate a test case
			v_array sequences( gen( p[ 1 ], p[ 2 ], p[ 3 ], p[ 4 ] ) );
#ifdef VERBOSE_CHECKING
			cout << "# seqs: " << sequences.size();
#endif //VERBOSE_CHECKING

			boost::timer _timer;
			const lcs_result _lcs_result = get_lcs_using_lcs_algorithm( sequences );
			lcs_cumulative_time += _timer.elapsed();
#ifdef VERBOSE_CHECKING
			cout  << ", LCS: " << _timer.elapsed();
#endif //VERBOSE_CHECKING

			if( 1 == sequences.size() )
			{
				//max_chain does not work for single sequences...
#ifdef VERBOSE_CHECKING
				cout << "\n";
#endif //VERBOSE_CHECKING
				continue;
			}

			_timer.restart();
			const lcs_result max_chain_result = get_lcs_using_max_chain( sequences );
			mc_cumulative_time += _timer.elapsed();
#ifdef VERBOSE_CHECKING
			cout  << ", MC: " << _timer.elapsed() << "\n";
#endif //VERBOSE_CHECKING

			//do we have a mismatch?
			if( max_chain_result.first != _lcs_result.first )
			{
				get_lcs_using_max_chain( sequences, true ); //run again with print = true
				BOOST_FOREACH( const v_vec & seq, sequences )
				{
					//( list_of( V( 'a', 0, 1 ) )( V( 'c', 1, 2 ) )( V( 'g', 2, 3 ) )( V( 't', 3, 4 ) ) )
					cout << "( list_of";
					BOOST_FOREACH( const V & v, seq )
					{
						cout << "( V( '" << v._c << "', " << v._start << ", " << v._end << ", " << v._score << " ) )";
					}
					cout << " )\n";
				}
				//throw std::logic_error( "Results don't agree" );
			}

			//check
			BOOST_CHECK_EQUAL( max_chain_result.first, _lcs_result.first );
			BOOST_CHECK_CLOSE( max_chain_result.second, _lcs_result.second, 0.001 );
		}
		std::cout << "LCS:" << lcs_cumulative_time << " secs\n";
		std::cout << "MC :" << mc_cumulative_time << " secs\n";
	}
}

void
check_lcs( const lcs_test_case & test_case )
{
	cout << "******* check_lcs(): \"" << test_case.get< 0 >() << "\"\n";

	lcs _lcs( test_case.get< 1 >() );
	_lcs.calculate_best();

#ifdef VERBOSE_CHECKING
	std::cout << "Universe:\n";
	BOOST_FOREACH( lcs::character c, _lcs._universe )
	{
		std::cout << c << "\n";
	}

	std::cout << "Sequences:\n";
	BOOST_FOREACH( lcs::seq s, _lcs._sequences )
	{
		BOOST_FOREACH( lcs::value c, s )
		{
			std::cout << _lcs.get_char( c  )<< " ";
		}
		std::cout << "\n";
	}

	std::cout << "End positions:\n";
	BOOST_FOREACH( lcs::seq s, _lcs._sequences )
	{
		BOOST_FOREACH( lcs::value v, s )
		{
			std::cout << _lcs.get_end( v  )<< " ";
		}
		std::cout << "\n";
	}

	std::cout << "End position\n";
	BOOST_FOREACH( unsigned i, _lcs._end_position )
	{
		std::cout << i << " ";
	}
	std::cout << "\n";

	std::cout << "End value\n";
	BOOST_FOREACH( unsigned i, _lcs._end_value )
	{
		std::cout << i << " ";
	}
	std::cout << "\n";

	std::cout << "Indices\n";
	lcs::multi_iterator end_it = _lcs.get_end_iterator();
	for( lcs::multi_iterator end_it = _lcs.get_end_iterator(); ! end_it.at_end(); end_it.next() )
	{
		BOOST_FOREACH( unsigned i, end_it._ind )
		{
			std::cout << i << " ";
		}
		std::cout << "\n";

		for( lcs::multi_iterator value_it = _lcs.get_value_range_for_end( end_it._ind ); ! value_it.at_end(); value_it.next() )
		{
			for( unsigned d = 0; _lcs.get_num_seqs() != d; ++d )
			{
				std::cout << " " << _lcs.get_end( _lcs.get_value( d, value_it._ind[ d ] ) );
			}
			std::cout << "\n";
		}
	}
#endif

	BOOST_CHECK_EQUAL( test_case.get< 2 >(), _lcs.get_best().get_string() );
	BOOST_CHECK_CLOSE( test_case.get< 3 >(), _lcs.get_best().get_score(), 0.001 );

	lcs::value_holder::vec values = _lcs.get_best().get_values();

#ifdef VERBOSE_CHECKING
	BOOST_FOREACH( const lcs::value_holder & v, values )
	{
		std::cout
			<< v._start
			<< "," << v._end
			<< "," << v._score
			<< "," << v._c
			<< "\n";
	}
#endif //VERBOSE_CHECKING
}

}

BOOST_TEST_DONT_PRINT_LOG_VALUE( max_chain_test::lcs::string )

#ifdef TEST_AGAINST_CGAL
#include <CGAL/Cartesian.h>
#include <CGAL/Range_segment_tree_traits.h>
#include <CGAL/Range_tree_k.h>

void
check_cgal_range_tree()
{
	cout << "******* check_cgal_range_tree()\n";

	typedef CGAL::Cartesian< int > K;
	typedef CGAL::Range_segment_tree_set_traits_2< K > Traits;
	typedef CGAL::Range_tree_2< Traits > Range_tree_2_type;

	typedef Traits::Key Key;
	typedef std::vector<Key> KeyVec;
	typedef Traits::Interval Interval;
	std::vector<Key>::iterator first, last, current;

	KeyVec InputList = list_of
		( Key( 1, 1) )
		( Key( 1, 2) )
		( Key( 1, 3) )
		( Key( 2, 1) )
		( Key( 2, 2) )
		( Key( 2, 3) )
		( Key( 3, 1) )
		( Key( 3, 2) )
		( Key( 3, 3) )
		;

	//create a range tree
	Range_tree_2_type Range_tree_2( InputList.begin(), InputList.end() );

	const Key l( 1, 1 );
	const Key u( 3, 3 );
	const Interval win = Interval( l, u );
	std::cout
		<< "Window Query: lower left point: "
		<< l
		<< "\nupper right point: "
		<< u
		<< "\n";

	KeyVec OutputList;
	Range_tree_2.window_query( win, std::back_inserter(OutputList) );
	current = OutputList.begin();
	while( current != OutputList.end())
	{
		std::cout << (*current).x()<< "-" << (*current).y() << std::endl;
		current++;
	}
}

#endif //TEST_AGAINST_CGAL

void
register_lcs_tests( boost::unit_test::test_suite * test )
{
	using namespace max_chain_test;
	using namespace range_tree;

    test->add( BOOST_TEST_CASE( &check_random_lcs_test_cases ) );
	test->add( BOOST_PARAM_TEST_CASE( &check_lcs, test_cases.begin(), test_cases.end() ) );
	test->add( BOOST_PARAM_TEST_CASE( &check_max_chain, test_cases.begin(), test_cases.end() ) );
    test->add( BOOST_TEST_CASE( &range_tree::generate_and_check ) );
#ifdef TEST_AGAINST_CGAL
	test->add( BOOST_TEST_CASE( &check_cgal_range_tree ) );
#endif //TEST_AGAINST_CGAL
}

//
// Are we going to compile this test into its own executable?
//
#ifdef BIO_STANDALONE_TEST
BIO_DEFINE_STANDALONE_TEST( "lcs", register_lcs_tests )
#endif //BIO_STANDALONE_TEST


#include <boost/test/unit_test.hpp>
#include "algorithms/math/polynomial_multiplication.hpp"

BOOST_AUTO_TEST_SUITE(PolynomialMultiplication)

BOOST_AUTO_TEST_CASE(zero_polynomials)
{
    PolMult::Solution solution;
    std::vector<int> pol1;
    std::vector<int> pol2;
    std::vector<int> expected;

    BOOST_CHECK(expected == solution.PolynomialMultNaive(0, pol1, pol2));
}

BOOST_AUTO_TEST_CASE(invalid_polynomials)
{
    PolMult::Solution solution;
    std::vector<int> pol1 = {0, 1, 2};
    std::vector<int> pol2 = {12};
    std::vector<int> expected;

    BOOST_CHECK(expected == solution.PolynomialMultNaive(
                    pol1.size(), pol1, pol2));
}

BOOST_AUTO_TEST_CASE(simple_polynomials)
{
    PolMult::Solution solution;
    std::vector<int> pol1 = {1, 2};
    std::vector<int> pol2 = {3, 4};
    std::vector<int> expected = {3, 10, 8};

    BOOST_CHECK(expected == solution.PolynomialMultNaive(
                    pol1.size(), pol1, pol2));
}

BOOST_AUTO_TEST_CASE(polynomials)
{
    PolMult::Solution solution;
    std::vector<int> pol1 = {3, 2, 5};
    std::vector<int> pol2 = {5, 1, 2};
    std::vector<int> expected = {15, 13, 33, 9, 10};

    BOOST_CHECK(expected == solution.PolynomialMultNaive(
                    pol1.size(), pol1, pol2));
}

BOOST_AUTO_TEST_SUITE_END()