/*
 * UI Widget to display back projected 3D points on 2D images
 */

#include <QtWidgets>

#include <iostream>

#include "VisibleImagesPanel.h"
#include "ImageTileWidget.h"
#include "core/Camera.h"
#include "core/datastructs/Data.h"
#include <Eigen/Eigen>

using namespace std;
using namespace cv;

VisibleImagesPanel::VisibleImagesPanel(QWidget *parent)
		   : QWidget(parent) {
	createWidgets();
	connectWidgets();
	setImagePaths(QString(),QList<QString>());
}

VisibleImagesPanel::~VisibleImagesPanel() {
	// TODO Auto-generated destructor stub
}

void VisibleImagesPanel::createWidgets(){
	imageTileView 			= new ImageTileWidget;
	QScrollArea *scrollArea = new QScrollArea;
	scrollArea->setWidget(imageTileView);
	QVBoxLayout *vLayout = new QVBoxLayout;
	vLayout->addWidget(scrollArea);
	this->setLayout(vLayout);
}
void VisibleImagesPanel::connectWidgets(){
	connect(imageTileView,SIGNAL(deletePressed()), this, SLOT(deleteMeasures()));
}

void VisibleImagesPanel::setImagePaths(const QString &root, const QList<QString> &list){
	imgRoot = root;
	imgs	= list;
}

void VisibleImagesPanel::showMeasurements(vector<Measurement::Ptr> &ms){

	QMap<int, QList<TileMark> > img2tileMarks;

	for(vector<Measurement::Ptr>::iterator it = ms.begin(); it!=ms.end(); ++it){
		TileMark tm;
		tm.imgIdx	= (*it)->frame->imgIdx;
		tm.pt2DIdx 	= (*it)->featureIdx;

		//Point2f &pt	= (*it)->frame->kpts[tm.pt2DIdx].pt;
		Camera::ProjectionStatus s;
		Eigen::Vector2d ppt= Camera::GetInstance().project((*it)->frame, (*it)->landmark->pt, s);
		assert(s == Camera::ProjectionStatus::Success);

		//tm.pt		= QPointF(pt.x, pt.y);
		tm.pt		= QPointF(ppt[0], ppt[1]);
		tm.selected	= false;
		if(img2tileMarks.find(tm.imgIdx) == img2tileMarks.end()){
			img2tileMarks[tm.imgIdx] = QList<TileMark>();
		}
		img2tileMarks[tm.imgIdx].push_back(tm);
	}

	QList<QString> 			visibleImgs;
	QList<QList<TileMark> >	marks;
	for(QMap<int, QList<TileMark> >::const_iterator it = img2tileMarks.begin(); it!=img2tileMarks.end(); ++it){
		visibleImgs.push_back(imgs[it.key()]);
		marks.push_back(it.value());
	}
	imageTileView->setImages(imgRoot, visibleImgs);
	imageTileView->setMarks(marks);
}

//show projections of all map points to all frames, including non-keyframes
void VisibleImagesPanel::showProjections(){
	Data &data = Data::GetInstance();
	Camera &camera = Camera::GetInstance();
	const vector<LandMark::Ptr> &lms = data.getLandMarks();
	const vector<Frame::Ptr> &frames = data.getFrames();
	const vector<Frame::Ptr> &nonKeyFrames = data.nonKeyFrames;

	int kfCnt = frames.size();
	int nonkfCnt = nonKeyFrames.size();
	int totalCnt = kfCnt+nonkfCnt;
	QList<QList<TileMark> >	marks;
	QList<QImage> images;
	for(int i =0; i<totalCnt; i++){
		const Frame::Ptr &frame = (i<kfCnt) ? frames[i] : nonKeyFrames [i-kfCnt];
		QList<TileMark> tms;
		for(vector<LandMark::Ptr>::const_iterator jt = lms.begin(); jt != lms.end(); ++jt){
			Camera::ProjectionStatus s;
			Eigen::Vector2d ppt= Camera::GetInstance().project(frame, (*jt)->pt, s);
			if(s == Camera::ProjectionStatus::Success){
				TileMark tm;
				tm.imgIdx	= frame->imgIdx;
				tm.pt2DIdx 	= 0;
				tm.pt		= QPointF(ppt[0], ppt[1]);
				tm.selected	= false;
				tms.push_back(tm);
			}
		}
		if(!tms.empty()){
			marks.push_back(tms);
			QImage frameImg;
			if(frame->img.empty()){
				//try load from path
				std::string imgPath = frame->imgRoot+"/"+frame->imgName;
				frameImg = QImage(QString::fromStdString(imgPath));
				if(frameImg.isNull()){
					//failed to load, create black image
					cout<<"empty: "<<frame->imgIdx<<endl;
					frameImg = QImage(camera.w, camera.h, QImage::Format_RGB888);
					frameImg.fill(0);
				}
			}else{

				if(frame->img.channels()==3){
					cout<<"non empty rgb image: "<<frame->imgIdx<<endl;
					frameImg = QImage((uchar*) frame->img.data, frame->img.cols, frame->img.rows, frame->img.step, QImage::Format_RGB888);
				}else if(frame->img.channels()==1){
					cout<<"non empty gray scale image: "<<frame->imgIdx<<endl;
					cv::Mat tmpImg;
					cv::cvtColor(frame->img,tmpImg,cv::COLOR_GRAY2RGB);
					frameImg = QImage((uchar*) tmpImg.data, tmpImg.cols, tmpImg.rows, tmpImg.step, QImage::Format_RGB888).copy();

				}

			}
			images.push_back(frameImg);
		}
	}

	imageTileView->setImages(images);
	imageTileView->setMarks(marks);

}

//slot functions

void VisibleImagesPanel::deleteMeasures(){
	Data &data = Data::GetInstance();
	QList<TileMark> selectedMarks;
	imageTileView->getSelectedMarks(selectedMarks);
	QList<QPair<int,int> > img2pt2DIdxs;
	for(int i=0; i<selectedMarks.size(); i++){
		Frame::Ptr frame = data.getFrame(selectedMarks[i].imgIdx);
		assert(frame);
		Measurement::Ptr m = data.getMeasurement(frame, selectedMarks[i].pt2DIdx);
		assert(m);
		data.deleteMeasurement(m);
	}
	emit measuresDeleted();
}


#include <Eigen/Core>

#include <numpy_eigen/boost_python_headers.hpp>
Eigen::Matrix<boost::int64_t, 2, 1> test_long_2_1(const Eigen::Matrix<boost::int64_t, 2, 1> & M)
{
	return M;
}
void export_long_2_1()
{
	boost::python::def("test_long_2_1",test_long_2_1);
}


#include <boost/math/special_functions.hpp>

#include "UtilOpencv.hpp"

namespace RGM
{

// -------- OpencvUtil -------

cv::Mat OpencvUtil::pictureHOG(cv::Mat_<Scalar> & filter, int bs)
{
    // construct a "glyph" for each orientaion
    cv::Mat_<Scalar> bim1(bs, bs, Scalar(0));

    int hbs = floor(bs/2.0f + 0.5f);
    cv::Range rg(hbs-1, hbs+1);
    bim1.colRange(rg) = 1;

    int ht     = filter.size[0];
    int wd     = filter.size[1];
    int numOri = filter.size[2];

    float degreeUnit = (float)180.0F / numOri;

    std::vector<cv::Mat_<Scalar> > bim(numOri);
    bim[0] = bim1;

    for (int i=1; i<numOri; ++i) {
        bim[i] = OpencvUtil::rotate(cv::Mat(bim1), -i*degreeUnit);
    }

    // make pictures of positive weights bs adding up weighted glyphs
    cv::Mat_<Scalar> posf = cv::max(filter, 0);
    cv::Mat_<Scalar> img(bs*ht, bs*wd, Scalar(0));
    Scalar maxV = -1;
    for ( int i=0; i<ht; ++i ) {
        for ( int j=0; j<wd; ++j ) {
            for ( int k=0; k<numOri; ++k) {
                img(cv::Rect(j*bs, i*bs, bs, bs)) += (bim[k] * posf(i, j, k));
                maxV = std::max<Scalar>(maxV, posf(i, j, k));
            }
        }
    }

    img *= (255.0F / maxV);

    /*cv::Mat imgShow;
    cv::normalize(img, imgShow, 255, 0.0, CV_MINMAX, CV_8UC1);

    cv::imshow("Debug", imgShow);
    cv::waitKey(0);*/

    return cv::Mat(img);
}

cv::Mat OpencvUtil::rotate(cv::Mat m, float degree)
{
    cv::Point2f src_center(m.cols/2.0F, m.rows/2.0F);
    cv::Mat rot_mat = cv::getRotationMatrix2D(src_center, degree, 1.0);
    cv::Mat dst;
    cv::warpAffine(m, dst, rot_mat, m.size());
    return dst;
}

cv::Mat OpencvUtil::subarray(cv::Mat img, cv::Rect roi, float padFactor, int padType) {

    int ht = ROUND( roi.height * (1+padFactor) );
    int wd = ROUND( roi.width * (1+padFactor) );

    int padx  = (wd - roi.width) / 2;
    int padx1 = wd - roi.width - padx;
    int pady  = (ht - roi.height) / 2;
    int pady1 = ht - roi.height - pady;

    int x1 = roi.x - padx;
    int x2 = roi.br().x + padx1;
    int y1 = roi.y - pady;
    int y2 = roi.br().y + pady1;

    cv::Rect newroi(x1, y1, x2-x1, y2-y1);

    return subarray(img, newroi, padType);
}

cv::Mat OpencvUtil::subarray(cv::Mat img, cv::Rect roi, int padType)
{
    int x1 = roi.x;
    int x2 = roi.br().x;
    int y1 = roi.y;
    int y2 = roi.br().y;

    if (x1>=0 && y1>=0 && x2<=img.cols && y2<=img.rows) {
        return img(cv::Rect(x1, y1, x2-x1, y2-y1)).clone();
    } else {
        cv::Mat subImg(y2-y1, x2-x1, img.type(), cv::Scalar::all(0));

        if (padType==1) {
            for ( int y=y1; y<y2; ++y ) {
                int yy = std::min<int>(img.rows-1, std::max<int>(0, y));
                for ( int x=x1; x<x2; ++x ) {
                    int xx = std::min<int>(img.cols-1, std::max<int>(0, x));

                    if (img.channels()==3) {
                        subImg.at<cv::Vec3b>(y-y1, x-x1) = img.at<cv::Vec3b>(yy, xx);
                    } else {
                        subImg.at<unsigned char>(y-y1, x-x1) = img.at<unsigned char>(yy, xx);
                    }
                }
            }
        } else {
            int xx1 = std::max<int>(0, x1);
            int xx2 = std::min<int>(img.cols, x2);
            int yy1 = std::max<int>(0, y1);
            int yy2 = std::min<int>(img.rows, y2);
            int wd = xx2 - xx1;
            int ht = yy2 - yy1;

            int padx = xx1 - x1;
            int pady = yy1 - y1;

            img(cv::Rect(xx2, yy1, wd, ht)).copyTo( subImg(cv::Rect(padx, pady, wd, ht)) );
        }

        return subImg;
    }
}

// -------- OpencvUtil_ -------

template<typename T>
cv::Mat_<T> OpencvUtil_<T>::resize(cv::Mat_<T> & m, float factor, int method)
{
    int ht = floor(m.size[0] * factor + 0.5f);
    int wd = floor(m.size[1] * factor + 0.5f);

    int dims[] = {ht, wd, m.size[2]};

    cv::Mat_<T> result(3, dims);

    for ( int d=0; d<dims[2]; ++d ) {
        cv::Mat m1(m.size[0], m.size[1], CV_MAKE_TYPE(cv::DataDepth<T>::value, 1));
        for ( int r=0; r<m1.rows; ++r) {
            for ( int c=0; c<m1.cols; ++c ) {
                m1.at<T>(r, c) = m(r, c, d);
            }
        }

        cv::Mat rm;
        cv::resize(m1, rm, cv::Size(wd, ht), 0, 0, method);

        for ( int r=0; r<ht; ++r) {
            for ( int c=0; c<wd; ++c) {
                result(r, c, d) = rm.at<T>(r, c);
            }
        }
    }

    return result;
}

// instantiation
template class OpencvUtil_<int>;
template class OpencvUtil_<float>;
template class OpencvUtil_<double>;

} // namespace RGM


#include <iostream>
#include <fstream>
#include <boost/dynamic_bitset.hpp>
#include <numeric>
#include <climits>
#include <sstream>
#include "golomb.hpp"

std::string golomb(std::vector<uint32_t> stream)
{
	/**
   	Converts a vector of integers to a string of its Golomb codes

    @param 	vector to be processed
    @return string of golomb codes
	*/

	std::string output;
	for (auto value: stream)
		output += golomb(value);
	return output;
}

std::string golomb(std::vector<uint32_t> stream, std::vector<uint32_t> LUT)
{
	/**
   	Converts a vector of integers to a string of its Golomb codes, taking into account a LUT that
   	maps the shortest codewords to most probable symbols. See the createLUT function.

    @param 	vector to be processed
    		vector representing the LUT
    @return string of golomb codes
	*/
	std::string output;
	std::for_each(stream.begin(), stream.end(),
		[LUT, &output](uint32_t value){output += golomb(std::distance(LUT.begin(), std::find(LUT.begin(), LUT.end(), value)));});
	return output;
}

std::string golomb(uint32_t value)
{
	/**
   	Converts an unisgned integer to a string of its Golomb code

    @param 	unsigned integer value to be coded
    @return string of golomb code
	*/
 	std::string buffer;
 	++value;
	to_string(boost::dynamic_bitset<> (2 * (CHAR_BIT*sizeof(value) - __builtin_clz(value)) - 1, value), buffer);
	return buffer;
}

uint32_t golomb(std::string value)
{
	/**
   	Inverse Golomb coding

    @param 	string of the golomb code
    @return converted integer value
	*/

	return (uint32_t)(std::stol(value, nullptr, 2) - 1);
}

std::vector<uint32_t> golomb(std::string filename, size_t size)
{
	/**
   	Opens the file "filename", reads the content and converts each Golomb code to its
	corresponding integer.

    @param 	name of the file to be processed
    		size of the data, ie number of golomb codes (must be called as reference to be used as output)
    @return vector of decoded values
	*/
	std::vector<uint32_t> encoded;
	encoded.reserve(size);
	std::ifstream file (filename);
	uint32_t count = 0;
	std::string str = "1";
	if(file)
	{
		while(!file.eof())
		{
			++count;
			if(file.get()=='1')
			{
				for (char c; str.size() != count && file.get(c); )
    				str += c;
    			encoded.push_back(golomb(str));
    			str = "1";
    			count = 0;
			}
		}
	}
	else
		std::cout << "Cannot read " << filename << std::endl;
	return encoded;
}

std::vector<uint32_t> golomb(std::string filename, size_t size, std::vector<uint32_t> LUT)
{
	/**
   	Opens the file "filename", reads the content and converts each Golomb code to its
	corresponding integer, taking into account the mapping of the LUT.

    @param 	name of the file to be processed
    		size of the data, ie number of golomb codes (must be called as reference to be used as output)
    		vector representing the LUT
    @return vector of decoded values
	*/
	std::vector<uint32_t> encoded;
	encoded.reserve(size);
	std::ifstream file (filename);
	uint32_t count = 0;
	std::string str = "1";
	if(file)
	{
		while(!file.eof())
		{
			++count;
			if(file.get()=='1')
			{
				for (char c; str.size() != count && file.get(c); )
    				str += c;
    			encoded.push_back(LUT[golomb(str)]);
    			str = "1";
    			count = 0;
			}
		}
	}
	else
		std::cout << "Cannot read " << filename << std::endl;
	return encoded;
}

std::vector<uint32_t> golomb(std::string golombed, std::vector<uint32_t> LUT)
{
	std::vector<uint32_t> encoded;
	encoded.reserve(golombed.length());
	std::stringstream file(golombed);
	uint32_t count = 0;
	std::string str = "1";
	while(!file.eof())
	{
		++count;
		if(file.get()=='1')
		{
			for (char c; str.size() != count && file.get(c); )
				str += c;
			encoded.push_back(LUT[golomb(str)]);
			str = "1";
			count = 0;
		}
	}
	return encoded;
}

std::vector<float> normalize(std::vector<float> P)
{
	float sum = std::accumulate(P.begin(), P.end(), 0.);
	std::transform(P.begin(), P.end(), P.begin(), [sum](float val){return val/sum;});
	return P;
}

std::vector<uint32_t> nbOccurences(std::vector<uint32_t> encoded)
{
	/**
   	Counts the number of occurences of each individual values in a vector

    @param 	vector to be processed
    @return vector where the index i represents a value, and the corresponding element occ[i] is its number
    			of occurences in the initial vector
	*/
	std::vector<uint32_t> occ(*std::max_element(encoded.begin(), encoded.end()) + 1, 0);
	for (uint32_t i : encoded)
		++occ[i];
	return occ;
}

std::vector<uint32_t> createLUT(std::vector<uint32_t> occurences, std::vector<uint32_t> input)
{
	/**
    Creates a LUT for entropy coding, its purpose being to map short
    codewords to probable symbols

    @param  vector containing the number of occurences of each run length
    @return vector of which the index indicates to what symbol its value is
    		mapped. Example:
    		vector {2,5,1,3,4} means that the mapping is 2-0,5-1,1-2,3-3,4-4
	*/

	// initialize original index locations
	std::sort(begin(input), end(input));
	size_t size = std::unique(begin(input), end(input)) - begin(input);
	std::vector<uint32_t> idx(occurences.size());
	iota(idx.begin(), idx.end(), 0);
	// sort indexes based on comparing values in occurences
	std::sort(idx.begin(), idx.end(),
	   [&occurences](uint32_t const& i1, uint32_t const& i2) {return occurences[i1] > occurences[i2];});
	std::vector<uint32_t> LUT(size);

	// Fill LUT
	for (size_t i = 0; i <  size; ++i)
	{
			LUT[i] = idx[i];
	}
	return LUT;
}


#include <boost/optional.hpp>
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <cmath>

using boost::optional;

optional<int> get_even_random_number()
{
  int i = std::rand();
  return (i % 2 == 0) ? i : optional<int>{};
}

int main()
{
  std::srand(static_cast<unsigned int>(std::time(0)));
  optional<int> i = get_even_random_number();
  if (i)
    std::cout << std::sqrt(static_cast<float>(*i)) << '\n';
}


/*
 *   Copyright (c) 2020 Chen Xiaoxiao
 *   All rights reserved.

 *   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 "mesh.h"
#include "csvparser.h"
#include "string_split.h"
#include <boost/geometry.hpp>

namespace SmileGL
{

    inline void Face::convertScalarToVector(String scalar_0, String scalar_1,
                                            String scalar_2, String vector)
    {
        vectors[vector] =
            Vector(scalars[scalar_0], scalars[scalar_1], scalars[scalar_2]);
    }
    // Zone::Zone(int n) {}
    void Mesh::createZone(int num)
    {
        if (zones.find(num) == zones.end())
        {
            zones[num] = Zone();
            zones[num].id = num;
        }
    }

    void Mesh::transform(Scalar x, Scalar y, Scalar z)
    {
        Vector move(x, y, z);
        for (auto &n : nodes)
            n.coord += move;
    }

    bool Surface::readOBJ(String filename)
    {
        std::cerr << "Read .obj file from " << filename << "...";

        String filePath = filename;
        std::ifstream file;
        file.open(filePath, std::ios::in);
        if (!file)
        {
            std::cerr << "File no Found." << std::endl;
            return EXIT_FAILURE;
        }
        String strLine;
        String zone_name = (String) "";
        // 逐行读取
        label lineCount = 0;
        label faceCount = 0;
        while (getline(file, strLine))
        {
            lineCount++;
            //std::istringstream out(strLine);
            //String split_str;
            Node node_temp;
            Face face_temp;

            if (strLine.size() < 1)
                continue;
            //std::cout << strLine << std::endl;
            auto line_list = splitStr(strLine, (String) " ");

            if (line_list[0] == (String) "g")
            {
                if (line_list.size() > 1)
                {
                    zone_name = line_list[1];
                    // zone_name.resize(zone_name.size() - 1);
                    // std::cout << zone_name << std::endl;
                }
                else
                {
                    std::cerr << "Cannot get object name in .obj file."
                              << std::endl;
                    return EXIT_FAILURE;
                }
            }

            if (line_list[0] == (String) "v")
                if (line_list.size() > 3)
                {
                    Scalar x, y, z;
                    std::stringstream x_str(line_list[1]), y_str(line_list[2]),
                        z_str(line_list[3]);
                    x_str >> x;
                    y_str >> y;
                    z_str >> z;

                    node_temp.coord << x, y, z;
                    node_temp.id = nodes.size();
                    nodes.push_back(node_temp);
                    node_temp = Node();
                }
                else
                {
                    std::cerr << "illegal vertice info in .obj file." << std::endl;
                    return EXIT_FAILURE;
                }

            if (line_list[0] == (String) "f")
            {
                size_t index;
                for (size_t i = 1; i < line_list.size(); i++)
                {
                    if ((int)line_list[i][0] < 48 || (int)line_list[i][0] > 57)
                        continue;
                    auto temp = splitStr(line_list[i], (String) "/");
                    std::stringstream sin(temp[0]);
                    sin >> index;
                    if (index > nodes.size() + 1)
                    {
                        std::cerr << "exceed node number: " << index << " at line "
                                  << lineCount << std::endl;
                        return EXIT_FAILURE;
                    }
                    face_temp.nodes.push_back(index - 1);
                    if (index == 0)
                        std::cout << "Zero index: " << lineCount << std::endl;
                }

                face_temp.zone_name = zone_name;
                face_temp.id = faceCount;
                faces.push_back(face_temp);
                faceCount++;
                face_temp = Face();
            }
        }
        return EXIT_SUCCESS;
    }

    void Surface::transform(Scalar x, Scalar y, Scalar z)
    {
        Vector move(x, y, z);
        for (auto &n : nodes)
            n.coord += move;
    }

    void Surface::calcFaceCenter()
    {
        for (auto &f : faces)
        {
            f.centroid = Vector(0., 0., 0.);
            for (auto n_i : f.nodes)
            {
                f.centroid += nodes[n_i].coord;
            }
            f.centroid /= (Scalar)f.nodes.size();
        }
    }

    void Surface::calcAABB(bool ifSetFace)
    {
        if (ifSetFace)
            for (auto &f : faces)
            {
                label length = f.nodes.size();
                Eigen::Matrix<Scalar, -1, -1, Eigen::RowMajor> poly(length, 3);

                for (size_t i = 0; i < length; i++)
                    poly.row(i) << nodes[f.nodes[i]].coord;

                f.AA << poly.col(0).minCoeff(), poly.col(1).minCoeff(),
                    poly.col(2).minCoeff();

                f.BB << poly.col(0).maxCoeff(), poly.col(1).maxCoeff(),
                    poly.col(2).maxCoeff();
                // f.centroid /= f.nodes.size();
            }

        // 此处的内存性能可能需要优化
        Eigen::Matrix<Scalar, -1, -1, Eigen::RowMajor> nodeCollect(nodes.size(), 3);
        for (size_t i = 0; i < nodes.size(); i++)
        {
            nodeCollect.row(i) << nodes[i].coord;
        }

        AA << nodeCollect.col(0).minCoeff(), nodeCollect.col(1).minCoeff(),
            nodeCollect.col(2).minCoeff();

        BB << nodeCollect.col(0).maxCoeff(), nodeCollect.col(1).maxCoeff(),
            nodeCollect.col(2).maxCoeff();
    }

    void Surface::splitSubZone()
    {
        for (auto &f : faces)
        {
            if (subZone.find(f.zone_name) == subZone.end())
            {
                subZone[f.zone_name] = Surface();
                subZone[f.zone_name].title = f.zone_name;
            }
            subZone[f.zone_name].faces.push_back(f);
        }
    }

    void Surface::mapDataFromPointCloud(PointCloud *cloud) {}

    void Surface::calculateValueFromFace(String valueName, String weight)
    {
        // maxValue[valueName] = -DBL_MAX;
        // minValue[valueName] = DBL_MAX;
        averageValue[valueName] = 0.;
        Scalar count = 0;
        for (auto f : faces)
        {
            if (f.scalars.find(valueName) != f.scalars.end())
            {
                averageValue[valueName] +=
                    f.scalars[valueName] * f.vectors[weight].norm();
                count += f.vectors[weight].norm();
            }
        }

        if (count)
            averageValue[valueName] /= count;
    }

    void Surface::convertScalarToVector(String scalar_0, String scalar_1,
                                        String scalar_2, String vector)
    {
        for (auto &face : faces)
            face.convertScalarToVector(scalar_0, scalar_1, scalar_2, vector);
    }

    Block::Block(Vector referencePoint, label i, label j, label k, Scalar size_in)
    {
        min = referencePoint;
        I = i;
        J = j;
        K = k;
        size = size_in;
        max = min + Vector(I * size, J * size, K * size);

        faceLink = List<List<Face *>>(I * J * K);
    }

    void Block::linkRelatedFaces(Surface *sf)
    {
        for (auto &f : sf->faces)
        {
            label i_min = (label)((f.AA(0) - min(0)) / size);
            label j_min = (label)((f.AA(1) - min(1)) / size);
            label k_min = (label)((f.AA(2) - min(2)) / size);

            label i_max = (label)((f.BB(0) - min(0)) / size);
            label j_max = (label)((f.BB(1) - min(1)) / size);
            label k_max = (label)((f.BB(2) - min(2)) / size);

            for (label i = i_min; i <= i_max; i++)
                for (label j = j_min; j <= j_max; j++)
                    for (label k = k_min; k <= k_max; k++)
                        faceLink[k * I * J + j * I + i].push_back(&f);
        }
    }

    void Block::mapSurfaceFromPointCloud(PointCloud *cloud, Surface *sf,
                                         Scalar tolerance)
    {
        typedef boost::geometry::model::point<Scalar, 3,
                                              boost::geometry::cs::cartesian>
            point_type;
        typedef boost::geometry::model::polygon<point_type> polygon_type;

        label lost = 0;
        label finish = 0;
        label percent = 0;
        label length = cloud->storage.size();

#pragma omp parallel for
        for (auto &dataPoint : cloud->storage)
        //for (long p_i = 0; p_i < cloud->storage.size(); p_i++)
        {
            //Point &dataPoint = &(cloud->storage[p_i]);
            Scalar min_d = boost::numeric::bounds<double>::highest();
            label min_f;

            point_type dataPointCoord(dataPoint.coord(0), dataPoint.coord(1),
                                      dataPoint.coord(2));

            label i_mid = (label)((dataPoint.coord(0) - min(0)) / size);
            label j_mid = (label)((dataPoint.coord(1) - min(1)) / size);
            label k_mid = (label)((dataPoint.coord(2) - min(2)) / size);

            label i_min = ((i_mid - 1) < 0) ? 0 : i_mid - 1;
            label j_min = ((j_mid - 1) < 0) ? 0 : j_mid - 1;
            label k_min = ((k_mid - 1) < 0) ? 0 : k_mid - 1;

            label i_max = ((i_mid + 1) > I - 1) ? I - 1 : i_mid + 1;
            label j_max = ((j_mid + 1) > J - 1) ? J - 1 : j_mid + 1;
            label k_max = ((k_mid + 1) > K - 1) ? K - 1 : k_mid + 1;

            for (label i = i_min; i <= i_max; i++)
                for (label j = j_min; j <= j_max; j++)
                    for (label k = k_min; k <= k_max; k++)
                        for (auto &f : faceLink[k * I * J + j * I + i])
                        {
                            polygon_type poly;
                            for (auto n : f->nodes)
                            {

                                boost::geometry::append(
                                    poly.outer(),
                                    point_type(sf->nodes[n].coord(0),
                                               sf->nodes[n].coord(1),
                                               sf->nodes[n].coord(2)));
                            }
                            Scalar d =
                                boost::geometry::distance(dataPointCoord, poly);
                            if (d < min_d)
                            {
                                min_d = d;
                                min_f = f->id;
                            }
                        }
#pragma omp critical
            {
                if (min_d > tolerance)
                {
                    lost++;
                }
                else
                {
                    dataPoint.linkFace = min_f;
                    dataPoint.linked = true;
                    // std::cout << min_f << std::endl;
                    finish++;
                }

                if (((label)((Scalar)finish / length * 100)) > percent)
                {
                    percent = (label)((Scalar)finish / length * 100);
                    std::cerr << "Finsh point: " << percent << "%"
                              << "\r";
                }
            }
        }
        std::cerr << std::endl;
        std::cerr << "Warning: " << lost << "(" << (Scalar)lost / length * 100
                  << "%"
                  << ") points lost by tolerance " << tolerance << "." << std::endl;
    }

    bool PointCloud::readData(String filename, int *coordIndex,
                              Map<String, label> field_index, bool hasHeader)
    {
        std::cout << "Read .csv file from " << filename << "." << std::endl;
        std::fstream _file;
        _file.open(filename.c_str(), std::ios::in);
        if (!_file)
        {
            std::cout << "Error read point data.\n";
            return EXIT_FAILURE;
        }

        CsvParser *csvparser = CsvParser_new(filename.c_str(), ",", hasHeader);
        CsvRow *row;

        label idCount = 0;
        while ((row = CsvParser_getRow(csvparser)))
        {
            const char **rowFields = CsvParser_getFields(row);

            Point p_temp;
            p_temp.name = rowFields[0];
            p_temp.coord << std::stod(rowFields[coordIndex[0]]),
                std::stod(rowFields[coordIndex[1]]),
                std::stod(rowFields[coordIndex[2]]);
            p_temp.id = idCount;
            for (auto field : field_index)
            {
                //std::cout << field.first << ", " << rowFields[field.second] << std::endl;
                p_temp.scalars[field.first] = std::stod(rowFields[field.second]);
            }

            storage.push_back(p_temp);
            idCount++;
            CsvParser_destroy_row(row);
        }
        CsvParser_destroy(csvparser);

        return EXIT_SUCCESS;
    }

    void PointCloud::convertDataToSurface(Surface *sf)
    {
        label i = 0;
        // std::cout << storage.size() << std::endl;
        for (auto &p : storage)
        {
            if (!p.linked)
                continue;
            // std::cout << i++ << std::endl;
            //std::cerr << p.linkFace << std::endl;
            for (auto _value : p.scalars)
            {
                sf->faces[p.linkFace].scalars[_value.first] = _value.second;
            }

            for (auto _value : p.vectors)
            {
                sf->faces[p.linkFace].vectors[_value.first] = _value.second;
            }
        }
    }

    void PointCloud::calcAABB()
    {
        // 此处的内存性能可能需要优化
        Eigen::Matrix<Scalar, -1, -1, Eigen::RowMajor> nodeCollect(storage.size(),
                                                                   3);
        for (size_t i = 0; i < storage.size(); i++)
        {
            nodeCollect.row(i) << storage[i].coord;
        }

        AA << nodeCollect.col(0).minCoeff(), nodeCollect.col(1).minCoeff(),
            nodeCollect.col(2).minCoeff();

        BB << nodeCollect.col(0).maxCoeff(), nodeCollect.col(1).maxCoeff(),
            nodeCollect.col(2).maxCoeff();
    }

    void PointCloud::convertScalarToVector(String scalar_0, String scalar_1,
                                           String scalar_2, String vector)
    {
        for (auto &p : storage)
            p.vectors[vector] = Vector(p.scalars[scalar_0], p.scalars[scalar_1],
                                       p.scalars[scalar_2]);
    }

    void PointCloud::rotate(Scalar theta, Vector axis)
    {
        Eigen::AngleAxis<Scalar> t_V(theta, axis);
        Eigen::Matrix3d t_R = t_V.matrix();
        // std::cout << t_R << std::endl;

        for (auto &n : storage)
        {
            n.coord *= t_R;
            for (auto vectorKey : n.vectors)
            {
                n.vectors[vectorKey.first] *= t_R;
            }
        }
    }

    Scalar PointCloud::integral(String x, String y)
    {
        Scalar result = 0.;
        for (auto p : storage)
            result += p.scalars[x] * p.scalars[y];
        return result;
    }

    Vector PointCloud::integralVector(String scalar, String vector)
    {
        Vector result(0., 0., 0.);
        for (auto p : storage)
            result += p.scalars[scalar] * p.vectors[vector];
        return result;
    }

    Vector PointCloud::integralVectorWithCoord(String scalar, String vector,
                                               Vector refPoint)
    {
        Vector result(0., 0., 0.);
        for (auto p : storage)
            result +=
                (p.coord - refPoint).cross(p.scalars[scalar] * p.vectors[vector]);
        return result;
    }

    /*
Scalar PointCloud::integral(String x, String y)
{
    Scalar result = 0.;
    for (auto p : storage)
        result += p.scalar[x] * p.scalar[y];
    return result;
}
*/
} // namespace SmileGL


//==============================================================================
//         Copyright 2009 - 2013 LRI    UMR 8623 CNRS/Univ Paris Sud XI
//         Copyright 2012 - 2014 MetaScale SAS
//
//          Distributed under the Boost Software License, Version 1.0.
//                 See accompanying file LICENSE.txt or copy at
//                     http://www.boost.org/LICENSE_1_0.txt
//==============================================================================

#include <boost/fusion/include/at.hpp>
#include <iostream>

#include <nt2/sdk/bench/benchmark.hpp>
#include <nt2/sdk/bench/metric/fps.hpp>
#include <nt2/sdk/bench/metric/cycles_per_element.hpp>
#include <nt2/sdk/bench/protocol/max_duration.hpp>
#include <nt2/sdk/bench/setup/geometric.hpp>
#include <nt2/sdk/bench/setup/combination.hpp>
#include <nt2/sdk/bench/setup/constant.hpp>
#include <nt2/sdk/bench/stats/median.hpp>

using namespace nt2::bench;

template<typename T> struct sigmadelta_scalar
{
  template<typename Setup>
  sigmadelta_scalar(Setup const& s)
                    :  height(boost::fusion::at_c<1>(s))
                    ,  width(boost::fusion::at_c<2>(s))
                    ,  size_(height*width)
                    ,  nb_frames(boost::fusion::at_c<0>(s))
  {
    variance_img.resize(size_);
    background_img.resize(size_);
    binary_label.resize(size_);
    frames.resize(nb_frames);

    for(std::size_t k=0; k<nb_frames; k++)
      frames[k].resize(size_);
    for(std::size_t k=0; k<nb_frames; k++)
    {
      for(std::size_t j=0; j<width; j++)
      {
        for(std::size_t i=0; i<height;i++)
        {
          if(i>(height/4) && i<(height/2) && j>((width/4)+k%10) && j<((width/2)+k%10))
            frames[k][i*width+j] = 255;
          else
            frames[k][i*width+j] = 0;
        }
      }
    }
    std::fill(variance_img.begin(), variance_img.end(), 1);
    background_img = frames[0];
    std::fill(binary_label.begin(), binary_label.end(), 0);
  }

  void operator()()
  {
    unsigned char d,mul;
    for(std::size_t k=1; k<nb_frames; k++)
    {
      for(std::size_t i=0; i < size_; i++)
      {
        if(background_img[i] < frames[k][i])
        {
          background_img[i] += 1;
        }
        else
        {
          if(background_img[i] > frames[k][i])
          {
            background_img[i] -= 1;
          }
        }

        d = abs(background_img[i]-frames[k][i]);
        mul = sigma * d;
        if(d != 0)
        {
          if(variance_img[i] < mul)
          {
            variance_img[i] += 1;
          }
          else
          {
            if(variance_img[i] > mul)
            {
              variance_img[i] -= 1;
            }
          }
        }
        if(d < variance_img[i])
        {
          binary_label[i] = 0;
        }
        else
        {
          binary_label[i] = 1;
        }
      }
    }
  }

  friend std::ostream& operator<<(std::ostream& os, sigmadelta_scalar<T> const& p)
  {
    return os << "(" << p.height << " x " << p.width << " @" << p.nb_frames << ")";
  }

  std::size_t size() const { return size_ * nb_frames; }
  std::size_t frame_count()  const { return nb_frames; }

  private:
  std::size_t height;
  std::size_t width;
  std::size_t size_;
  std::vector<std::vector<T> > frames;
  std::vector<T>  variance_img, background_img, diff_img, binary_label;
  static const T sigma=3;
  std::size_t nb_frames;
};

NT2_REGISTER_BENCHMARK( sigmadelta_scalar )
{
  std::size_t frame = args("frame",  5);
  std::size_t hmin  = args("hmin",  32);
  std::size_t hmax  = args("hmax", 128);
  std::size_t hstep = args("hstep",  2);
  std::size_t wmin  = args("wmin",  32);
  std::size_t wmax  = args("wmax", 128);
  std::size_t wstep = args("wstep",  2);

  run_during_with< sigmadelta_scalar<nt2::uint8_t> >( 1.
                                                     , and_( constant( frame )
                                                           , geometric(hmin,hmax,hstep)
                                                           , geometric(wmin,wmax,wstep)
                                                           )
                                                      , cycles_per_element<stats::median_>()
                                                      , fps<stats::median_>()
                                                    );
}


// Boost.Geometry
// Unit Test

// Copyright (c) 2015 Oracle and/or its affiliates.

// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle

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

#include <geometry_test_common.hpp>

#include <limits>
#include <boost/geometry/util/condition.hpp>
#include <boost/geometry/util/math.hpp>

namespace bgm = bg::math;

template <typename T>
void test_all()
{
    BOOST_CHECK(bgm::equals(0, 0));
    BOOST_CHECK(bgm::equals(1, 1));
    BOOST_CHECK(bgm::equals(123456, 123456));

    T eps = std::numeric_limits<T>::epsilon();
    if ( eps > 0 )
    {
        BOOST_CHECK(bgm::equals(0, 0+eps));
        BOOST_CHECK(bgm::equals(0+eps, 0));
        BOOST_CHECK(bgm::equals(1, 1+eps));
        BOOST_CHECK(bgm::equals(1+eps, 1));
        BOOST_CHECK(bgm::equals(12345+eps, 12345));
    }

    if (BOOST_GEOMETRY_CONDITION(std::numeric_limits<T>::has_infinity))
    {
        T inf = std::numeric_limits<T>::infinity();
        BOOST_CHECK(!bgm::equals(0, inf));
        BOOST_CHECK(!bgm::equals(0, -inf));
        BOOST_CHECK(!bgm::equals(1, inf));
        BOOST_CHECK(!bgm::equals(1, -inf));
        BOOST_CHECK(!bgm::equals(12345, inf));
        BOOST_CHECK(!bgm::equals(12345, -inf));
        BOOST_CHECK(!bgm::equals(inf, 0));
        BOOST_CHECK(!bgm::equals(-inf, 0));
        BOOST_CHECK(!bgm::equals(inf, 1));
        BOOST_CHECK(!bgm::equals(-inf, 1));
        BOOST_CHECK(!bgm::equals(inf, 12345));
        BOOST_CHECK(!bgm::equals(-inf, 12345));
        BOOST_CHECK(bgm::equals(inf, inf));
        BOOST_CHECK(bgm::equals(-inf, -inf));
        BOOST_CHECK(!bgm::equals(inf, -inf));
        BOOST_CHECK(!bgm::equals(-inf, inf));
    }

    if (BOOST_GEOMETRY_CONDITION(std::numeric_limits<T>::has_quiet_NaN))
    {
        T nan = std::numeric_limits<T>::quiet_NaN();
        BOOST_CHECK(!bgm::equals(0, nan));
        BOOST_CHECK(!bgm::equals(nan, 0));
        BOOST_CHECK(!bgm::equals(nan, nan));
        BOOST_CHECK(!bgm::equals(1, nan));
        BOOST_CHECK(!bgm::equals(nan, 1));
        BOOST_CHECK(!bgm::equals(12345, nan));
        BOOST_CHECK(!bgm::equals(nan, 12345));
    }
}

int test_main(int, char* [])
{
    test_all<int>();
    test_all<float>();
    test_all<double>();

    return 0;
}


#define BOOST_TEST_MODULE pcraster fieldapi readonly_spatial
#include <boost/test/unit_test.hpp>
#include "pcrtypes.h"
#include "fieldapi_readonlyspatial.h"
#include "fieldapi_testfield.h"

template<typename UseAsT,typename StoredAsT>
void testType()
{
  using namespace fieldapi;

 StoredAsT  d1[ ] = { 0, 1, 2 , 3, 40, 5 };
 pcr::setMV(d1[4]);
 TestField<StoredAsT,2,3> d2(d1);
 ReadOnlySpatial<UseAsT,StoredAsT> t(TEST_FIELD_INIT(d2));
 UseAsT v;

 BOOST_CHECK(t.spatial());
 BOOST_CHECK(t.nrRows()==2);
 BOOST_CHECK(t.nrCols()==3);

 // out of range
 BOOST_CHECK(!t.get(v, 2,3));
 BOOST_CHECK(!t.get(v,-1,0));

 // get value
 BOOST_CHECK(t.get(v,0,0)); BOOST_CHECK(v==0);
 BOOST_CHECK(t.get(v,0,1)); BOOST_CHECK(v==1);
 BOOST_CHECK(t.get(v,0,2)); BOOST_CHECK(v==2);
 BOOST_CHECK(t.get(v,1,0)); BOOST_CHECK(v==3);
 BOOST_CHECK(t.get(v,1,2)); BOOST_CHECK(v==5);

 // get MV
 BOOST_CHECK(!t.get(v,1,1));

 // value
 BOOST_CHECK(t.value(0,0)==0);
 BOOST_CHECK(t.value(0,1)==1);
 BOOST_CHECK(t.value(0,2)==2);
 BOOST_CHECK(t.value(1,0)==3);
 BOOST_CHECK(t.value(1,2)==5);
}

BOOST_AUTO_TEST_CASE(all)
{
  using namespace fieldapi;

 testType<UINT1,UINT1>();

 testType<INT4, UINT1>();
 testType<INT4, INT4>();

 testType<REAL8,UINT1>();
 testType<REAL8,INT4>();
 testType<REAL8,REAL4>();
}


// Copyright (c) 2019 Graphcore Ltd. All rights reserved.
#define BOOST_TEST_MODULE ConstExprTransposeTest

#include <boost/test/unit_test.hpp>
#include <filereader.hpp>
#include <popart/builder.hpp>
#include <popart/dataflow.hpp>
#include <popart/devicemanager.hpp>
#include <popart/inputshapeinfo.hpp>
#include <popart/ir.hpp>
#include <popart/names.hpp>
#include <popart/ndarraywrapper.hpp>
#include <popart/op/l1.hpp>
#include <popart/session.hpp>
#include <popart/sgd.hpp>
#include <popart/tensordata.hpp>
#include <popart/testdevice.hpp>

using namespace popart;

// more constexpr transpose tests in fp16_test.py
BOOST_AUTO_TEST_CASE(ConstExprTest_Transpose1) {

  Shape inShape = {2, 5};
  TensorInfo inInfo{"INT32", inShape};

  Shape constShape = {5, 2};
  std::vector<int> rawConstInputData(5 * 2);
  std::iota(rawConstInputData.begin(), rawConstInputData.end(), 1);

  popart::NDArrayWrapper<int> constData(rawConstInputData.data(), {5, 2});

  ConstVoidData constShapeData = {rawConstInputData.data(),
                                  {"INT32", constShape}};

  // Build an onnx model
  auto builder = Builder::create();
  auto aiOnnx  = builder->aiOnnxOpset9();

  auto constId = aiOnnx.constant(constShapeData, "out0ShapeData");
  auto inId    = builder->addInputTensor(inInfo);

  auto outShapeId = aiOnnx.transpose({constId}, {});
  auto out        = aiOnnx.add({outShapeId, inId});
  builder->addOutputTensor(out);

  auto proto      = builder->getModelProto();
  auto modelProto = io::getModelFromString(proto);

  // Create the IR, adding outId as an anchor
  auto art      = AnchorReturnType("All");
  auto dataFlow = DataFlow(1, {{out, art}});

  auto device = popart::createTestDevice(TEST_TARGET);

  auto session = popart::InferenceSession::createFromOnnxModel(
      proto,
      dataFlow,
      device,
      popart::InputShapeInfo(),
      {},
      popart::Patterns::create({"PostNRepl"}).enableRuntimeAsserts(false));

  // prepare the anchors
  int rawOutputData[10];
  popart::NDArrayWrapper<int> outData(rawOutputData, {2, 5});

  std::map<popart::TensorId, popart::IArray &> anchors = {
      {out, outData},
  };

  session->prepareDevice();

  int rawInputData[10] = {
      0,
  };
  popart::NDArrayWrapper<int> inData(rawInputData, {2, 5});
  std::map<popart::TensorId, popart::IArray &> inputs = {{inId, inData}};

  popart::StepIO stepio(inputs, anchors);

  session->run(stepio);

  popart::logging::ir::err("const : {}", constData);
  popart::logging::ir::err("input : {}", inData);
  popart::logging::ir::err("output : {}", outData);

  int expectedOutput[10] = {1, 3, 5, 7, 9, 2, 4, 6, 8, 10};
  BOOST_CHECK(std::equal(&expectedOutput[0],
                         &expectedOutput[10],
                         &rawOutputData[0]) == true);
}

BOOST_AUTO_TEST_CASE(ConstExprTest_Transpose2) {

  Shape inShape = {4, 2, 3};
  TensorInfo inInfo{"INT32", inShape};

  Shape constShape = {2, 3, 4};
  std::vector<int> rawConstInputData(4 * 2 * 3);
  std::iota(rawConstInputData.begin(), rawConstInputData.end(), 1);

  popart::NDArrayWrapper<int> constData(rawConstInputData.data(), {2, 3, 4});

  ConstVoidData constShapeData = {rawConstInputData.data(),
                                  {"INT32", constShape}};

  // Build an onnx model
  auto builder = Builder::create();
  auto aiOnnx  = builder->aiOnnxOpset9();

  auto constId = aiOnnx.constant(constShapeData, "constShapeData");
  auto inId    = builder->addInputTensor(inInfo);

  auto outShapeId = aiOnnx.transpose({constId}, {2, 0, 1});
  auto out        = aiOnnx.add({outShapeId, inId});
  builder->addOutputTensor(out);

  auto proto      = builder->getModelProto();
  auto modelProto = io::getModelFromString(proto);

  // Create the IR, adding outId as an anchor
  auto art      = AnchorReturnType("All");
  auto dataFlow = DataFlow(1, {{out, art}});

  auto device = popart::createTestDevice(TEST_TARGET);

  auto session = popart::InferenceSession::createFromOnnxModel(
      proto,
      dataFlow,
      device,
      popart::InputShapeInfo(),
      {},
      popart::Patterns::create({"PostNRepl"}).enableRuntimeAsserts(false));

  // prepare the anchors
  int rawOutputData[24];
  popart::NDArrayWrapper<int> outData(rawOutputData, {4, 2, 3});

  std::map<popart::TensorId, popart::IArray &> anchors = {
      {out, outData},
  };

  session->prepareDevice();

  // prepare the inputs
  int rawInputData[24] = {
      0,
  };
  popart::NDArrayWrapper<int> inData(rawInputData, {4, 2, 3});
  std::map<popart::TensorId, popart::IArray &> inputs = {{inId, inData}};

  popart::StepIO stepio(inputs, anchors);

  session->run(stepio);

  popart::logging::ir::err("const : {}", constData);
  popart::logging::ir::err("input : {}", inData);
  popart::logging::ir::err("output : {}", outData);

  int expectedOutput[24] = {1, 5, 9,  13, 17, 21, 2, 6, 10, 14, 18, 22,
                            3, 7, 11, 15, 19, 23, 4, 8, 12, 16, 20, 24};

  BOOST_CHECK(std::equal(&expectedOutput[0],
                         &expectedOutput[24],
                         &rawOutputData[0]) == true);
}


#include <gtest/gtest.h>
#include <boost/random.hpp>
#include <map>
#include <queue>
#include <string>

#include "../Octree.hpp"

namespace
{

class Point3f
{
 public:
  Point3f(float x, float y, float z) : x(x), y(y), z(z)
  {
  }

  float x, y, z;
};

// simple bruteforce search.
template <typename PointT>
class NaiveNeighborSearch
{
 public:
  void initialize(const std::vector<PointT>& points)
  {
    data_ = &points;
  }

  template <typename Distance>
  uint32_t findNeighbor(const PointT& query, float minDistance = -1.0f)
  {
    const std::vector<PointT>& pts = *data_;
    if (pts.size() == 0) return -1;

    float maxDistance = std::numeric_limits<float>::infinity();
    float sqrMinDistance = (minDistance < 0) ? minDistance : Distance::sqr(minDistance);
    uint32_t resultIndex = -1;
    for (uint32_t i = 0; i < pts.size(); ++i)
    {
      float dist = Distance::compute(query, pts[i]);
      if ((dist > sqrMinDistance) && (dist < maxDistance))
      {
        maxDistance = dist;
        resultIndex = i;
      }
    }

    return resultIndex;
  }

  template <typename Distance>
  void radiusNeighbors(const PointT& query, float radius, std::vector<uint32_t>& resultIndices)
  {
    const std::vector<PointT>& pts = *data_;
    resultIndices.clear();
    float sqrRadius = Distance::sqr(radius);

    for (uint32_t i = 0; i < pts.size(); ++i)
    {
      if (Distance::compute(query, pts[i]) < sqrRadius)
      {
        resultIndices.push_back(i);
      }
    }
  }

 protected:
  const std::vector<PointT>* data_;
};

// The fixture for testing class Foo.
class OctreeTest : public ::testing::Test
{
 public:
  typedef unibn::Octree<Point3f>::Octant Octant;

 protected:
  // helper methods to access the protected parts of octree for consistency
  // checks.
  template <typename PointT>
  const typename unibn::Octree<PointT>::Octant* getRoot(const unibn::Octree<PointT>& oct)
  {
    return oct.root_;
  }

  template <typename PointT>
  const std::vector<uint32_t>& getSuccessors(const unibn::Octree<PointT>& oct)
  {
    return oct.successors_;
  }

  template <typename Distance>
  bool overlaps(const Point3f& query, float radius, float sqRadius, const Octant* o)
  {
    return unibn::Octree<Point3f>::template overlaps<Distance>(query, radius, sqRadius, o);
  }
};

template <typename PointT>
void randomPoints(std::vector<PointT>& pts, uint32_t N, uint32_t seed = 0)
{
  boost::mt11213b mtwister(seed);
  boost::uniform_01<> gen;
  pts.clear();
  pts.reserve(N);
  // generate N random points in [-5.0,5.0] x [-5.0,5.0] x [-5.0,5.0]...
  for (uint32_t i = 0; i < N; ++i)
  {
    float x = 10.0f * gen(mtwister) - 5.0f;
    float y = 10.0f * gen(mtwister) - 5.0f;
    float z = 10.0f * gen(mtwister) - 5.0f;

    pts.push_back(Point3f(x, y, z));
  }
}

TEST_F(OctreeTest, Initialize)
{

  uint32_t N = 1000;
  unibn::OctreeParams params;
  params.bucketSize = 16;

  unibn::Octree<Point3f> oct;

  const Octant* root = getRoot(oct);
  const std::vector<uint32_t>& successors = getSuccessors(oct);

  ASSERT_EQ(0, root);

  std::vector<Point3f> points;
  randomPoints(points, N, 1337);

  oct.initialize(points, params);

  root = getRoot(oct);

  // check first some pre-requisits.
  ASSERT_EQ(true, (root != 0));
  ASSERT_EQ(N, successors.size());

  std::vector<uint32_t> elementCount(N, 0);
  uint32_t idx = root->start;
  for (uint32_t i = 0; i < N; ++i)
  {
    ASSERT_LT(idx, N);
    ASSERT_LE(successors[idx], N);
    elementCount[idx] += 1;
    ASSERT_EQ(1, elementCount[idx]);
    idx = successors[idx];
  }

  // check that each index was found.
  for (uint32_t i = 0; i < N; ++i)
  {
    ASSERT_EQ(1, elementCount[i]);
  }

  // test if each Octant contains only points inside the octant and child
  // octants have only real subsets of parents!
  std::queue<const Octant*> queue;
  queue.push(root);
  std::vector<int32_t> assignment(N, -1);

  while (!queue.empty())
  {
    const Octant* octant = queue.front();
    queue.pop();

    // check points.
    ASSERT_LT(octant->start, N);

    // test if each point assigned to a octant really is inside the octant.

    uint32_t idx = octant->start;
    uint32_t lastIdx = octant->start;
    for (uint32_t i = 0; i < octant->size; ++i)
    {
      float x = points[idx].x - octant->x;
      float y = points[idx].y - octant->y;
      float z = points[idx].z - octant->z;

      ASSERT_LE(std::abs(x), octant->extent);
      ASSERT_LE(std::abs(y), octant->extent);
      ASSERT_LE(std::abs(z), octant->extent);
      assignment[idx] = -1;  // reset of child assignments.
      lastIdx = idx;
      idx = successors[idx];
    }
    ASSERT_EQ(octant->end, lastIdx);

    bool shouldBeLeaf = true;
    Octant* firstchild = 0;
    Octant* lastchild = 0;
    uint32_t pointSum = 0;

    for (uint32_t c = 0; c < 8; ++c)
    {
      Octant* child = octant->child[c];
      if (child == 0) continue;
      shouldBeLeaf = false;

      // child nodes should have start end intervals, which are true subsets of
      // the parent.
      if (firstchild == 0) firstchild = child;
      // the child nodes should have intervals, where succ(e_{c-1}) == s_{c},
      // and \sum_c size(c) = parent size!
      if (lastchild != 0) ASSERT_EQ(child->start, successors[lastchild->end]);

      pointSum += child->size;
      lastchild = child;
      uint32_t idx = child->start;
      for (uint32_t i = 0; i < child->size; ++i)
      {
        // check if points are uniquely assigned to single child octant.
        ASSERT_EQ(-1, assignment[idx]);
        assignment[idx] = c;
        idx = successors[idx];
      }

      queue.push(child);
    }

    // consistent start/end of octant and its first and last children.
    if (firstchild != 0) ASSERT_EQ(octant->start, firstchild->start);
    if (lastchild != 0) ASSERT_EQ(octant->end, lastchild->end);

    // check leafs flag.
    ASSERT_EQ(shouldBeLeaf, octant->isLeaf);
    ASSERT_EQ((octant->size <= params.bucketSize), octant->isLeaf);

    // test if every point is assigned to a child octant.
    if (!octant->isLeaf)
    {
      ASSERT_EQ(octant->size, pointSum);
      uint32_t idx = octant->start;
      for (uint32_t i = 0; i < octant->size; ++i)
      {
        ASSERT_GT(assignment[idx], -1);
        idx = successors[idx];
      }
    }
  }
}

TEST_F(OctreeTest, Initialize_minExtent)
{

  uint32_t N = 1000;
  unibn::OctreeParams params;
  params.bucketSize = 16;
  params.minExtent = 1.0f;

  unibn::Octree<Point3f> oct;

  const Octant* root = getRoot(oct);
  const std::vector<uint32_t>& successors = getSuccessors(oct);

  ASSERT_EQ(0, root);

  std::vector<Point3f> points;
  randomPoints(points, N, 1337);

  oct.initialize(points, params);

  root = getRoot(oct);

  // check first some pre-requisits.
  ASSERT_EQ(true, (root != 0));
  ASSERT_EQ(N, successors.size());

  std::vector<uint32_t> elementCount(N, 0);
  uint32_t idx = root->start;
  for (uint32_t i = 0; i < N; ++i)
  {
    ASSERT_LT(idx, N);
    ASSERT_LE(successors[idx], N);
    elementCount[idx] += 1;
    ASSERT_EQ(1, elementCount[idx]);
    idx = successors[idx];
  }

  // check that each index was found.
  for (uint32_t i = 0; i < N; ++i)
  {
    ASSERT_EQ(1, elementCount[i]);
  }

  // test if each Octant contains only points inside the octant and child
  // octants have only real subsets of parents!
  std::queue<const Octant*> queue;
  queue.push(root);
  std::vector<int32_t> assignment(N, -1);

  while (!queue.empty())
  {
    const Octant* octant = queue.front();
    queue.pop();

    // check points.
    ASSERT_LT(octant->start, N);

    // test if each point assigned to a octant really is inside the octant.

    uint32_t idx = octant->start;
    uint32_t lastIdx = octant->start;
    for (uint32_t i = 0; i < octant->size; ++i)
    {
      float x = points[idx].x - octant->x;
      float y = points[idx].y - octant->y;
      float z = points[idx].z - octant->z;

      ASSERT_LE(std::abs(x), octant->extent);
      ASSERT_LE(std::abs(y), octant->extent);
      ASSERT_LE(std::abs(z), octant->extent);
      assignment[idx] = -1;  // reset of child assignments.
      lastIdx = idx;
      idx = successors[idx];
    }
    ASSERT_EQ(octant->end, lastIdx);

    bool shouldBeLeaf = true;
    Octant* firstchild = 0;
    Octant* lastchild = 0;
    uint32_t pointSum = 0;

    for (uint32_t c = 0; c < 8; ++c)
    {
      Octant* child = octant->child[c];
      if (child == 0) continue;
      shouldBeLeaf = false;

      // child nodes should have start end intervals, which are true subsets of
      // the parent.
      if (firstchild == 0) firstchild = child;
      // the child nodes should have intervals, where succ(e_{c-1}) == s_{c},
      // and \sum_c size(c) = parent size!
      if (lastchild != 0) ASSERT_EQ(child->start, successors[lastchild->end]);

      pointSum += child->size;
      lastchild = child;
      uint32_t idx = child->start;
      for (uint32_t i = 0; i < child->size; ++i)
      {
        // check if points are uniquely assigned to single child octant.
        ASSERT_EQ(-1, assignment[idx]);
        assignment[idx] = c;
        idx = successors[idx];
      }

      queue.push(child);
    }

    // consistent start/end of octant and its first and last children.
    if (firstchild != 0) ASSERT_EQ(octant->start, firstchild->start);
    if (lastchild != 0) ASSERT_EQ(octant->end, lastchild->end);

    // check leafs flag.
    ASSERT_EQ(shouldBeLeaf, octant->isLeaf);
    ASSERT_EQ((octant->size <= params.bucketSize || octant->extent < 2.0f * params.minExtent), octant->isLeaf);
    ASSERT_GE(octant->extent, params.minExtent);

    // test if every point is assigned to a child octant.
    if (!octant->isLeaf)
    {
      ASSERT_EQ(octant->size, pointSum);
      uint32_t idx = octant->start;
      for (uint32_t i = 0; i < octant->size; ++i)
      {
        ASSERT_GT(assignment[idx], -1);
        idx = successors[idx];
      }
    }
  }
}

TEST_F(OctreeTest, FindNeighbor)
{
  // compare with bruteforce search.
  uint32_t N = 1000;

  boost::mt11213b mtwister(1234);
  boost::uniform_int<> uni_dist(0, N - 1);

  std::vector<Point3f> points;
  randomPoints(points, N, 1234);

  NaiveNeighborSearch<Point3f> bruteforce;
  bruteforce.initialize(points);
  unibn::Octree<Point3f> octree;
  octree.initialize(points);

  for (uint32_t i = 0; i < 10; ++i)
  {
    uint32_t index = uni_dist(mtwister);
    const Point3f& query = points[index];

    // allow self-match
    ASSERT_EQ(index, bruteforce.findNeighbor<unibn::L2Distance<Point3f> >(query));
    ASSERT_EQ(bruteforce.findNeighbor<unibn::L2Distance<Point3f> >(query),
              octree.findNeighbor<unibn::L2Distance<Point3f> >(query));

    // disallow self-match
    uint32_t bfneighbor = bruteforce.findNeighbor<unibn::L2Distance<Point3f> >(query, 0.0f);
    uint32_t octneighbor = octree.findNeighbor<unibn::L2Distance<Point3f> >(query, 0.0f);

    ASSERT_EQ(bruteforce.findNeighbor<unibn::L2Distance<Point3f> >(query, 0.3f),
              octree.findNeighbor<unibn::L2Distance<Point3f> >(query, 0.3f));
  }
}

template <typename T>
bool similarVectors(std::vector<T>& vec1, std::vector<T>& vec2)
{
  if (vec1.size() != vec2.size())
  {
    std::cout << "expected size = " << vec1.size() << ", but got size = " << vec2.size() << std::endl;
    return false;
  }

  for (uint32_t i = 0; i < vec1.size(); ++i)
  {
    bool found = false;
    for (uint32_t j = 0; j < vec2.size(); ++j)
    {
      if (vec1[i] == vec2[j])
      {
        found = true;
        break;
      }
    }
    if (!found)
    {
      std::cout << i << "-th element (" << vec1[i] << ") not found." << std::endl;
      return false;
    }
  }

  return true;
}

TEST_F(OctreeTest, RadiusNeighbors)
{
  uint32_t N = 1000;

  boost::mt11213b mtwister(1234);
  boost::uniform_int<> uni_dist(0, N - 1);

  std::vector<Point3f> points;
  randomPoints(points, N, 1234);

  NaiveNeighborSearch<Point3f> bruteforce;
  bruteforce.initialize(points);
  unibn::Octree<Point3f> octree;
  octree.initialize(points);

  float radii[4] = {0.5, 1.0, 2.0, 5.0};

  for (uint32_t r = 0; r < 4; ++r)
  {
    for (uint32_t i = 0; i < 10; ++i)
    {
      std::vector<uint32_t> neighborsBruteforce;
      std::vector<uint32_t> neighborsOctree;

      const Point3f& query = points[uni_dist(mtwister)];

      bruteforce.radiusNeighbors<unibn::L2Distance<Point3f> >(query, radii[r], neighborsBruteforce);
      octree.radiusNeighbors<unibn::L2Distance<Point3f> >(query, radii[r], neighborsOctree);
      ASSERT_EQ(true, similarVectors(neighborsBruteforce, neighborsOctree));

      bruteforce.radiusNeighbors<unibn::L1Distance<Point3f> >(query, radii[r], neighborsBruteforce);
      octree.radiusNeighbors<unibn::L1Distance<Point3f> >(query, radii[r], neighborsOctree);

      ASSERT_EQ(true, similarVectors(neighborsBruteforce, neighborsOctree));

      bruteforce.radiusNeighbors<unibn::MaxDistance<Point3f> >(query, radii[r], neighborsBruteforce);
      octree.radiusNeighbors<unibn::MaxDistance<Point3f> >(query, radii[r], neighborsOctree);

      ASSERT_EQ(true, similarVectors(neighborsBruteforce, neighborsOctree));
    }
  }
}

TEST_F(OctreeTest, OverlapTest)
{
  Octant octant;

  octant.x = 1.0f;
  octant.y = 1.0f;
  octant.z = 1.0f;
  octant.extent = 0.5f;

  // completely inside
  Point3f query(1.25, 1.25, 0.5);
  float radius = 1.0f;

  ASSERT_TRUE(overlaps<unibn::L2Distance<Point3f> >(query, radius, radius * radius, &octant));

  // faces of octant.
  query = Point3f(1.75, 1.0, 1.0);
  radius = 0.5f;

  ASSERT_TRUE(overlaps<unibn::L2Distance<Point3f> >(query, radius, radius * radius, &octant));

  query = Point3f(1.0, 1.75, 1.0);
  ASSERT_TRUE(overlaps<unibn::L2Distance<Point3f> >(query, radius, radius * radius, &octant));

  query = Point3f(1.0, 1.0, 1.75);
  ASSERT_TRUE(overlaps<unibn::L2Distance<Point3f> >(query, radius, radius * radius, &octant));

  query = Point3f(1.0, 1.0, 2.75);
  ASSERT_FALSE(overlaps<unibn::L2Distance<Point3f> >(query, radius, radius * radius, &octant));

  // Edge cases:
  query = Point3f(1.65, 1.65, 1.25);
  ASSERT_TRUE(overlaps<unibn::L2Distance<Point3f> >(query, radius, radius * radius, &octant));

  query = Point3f(1.25, 1.65, 1.65);
  ASSERT_TRUE(overlaps<unibn::L2Distance<Point3f> >(query, radius, radius * radius, &octant));

  query = Point3f(1.65, 1.25, 1.75);
  ASSERT_TRUE(overlaps<unibn::L2Distance<Point3f> >(query, radius, radius * radius, &octant));

  query = Point3f(1.9, 1.25, 1.9);
  ASSERT_FALSE(overlaps<unibn::L2Distance<Point3f> >(query, radius, radius * radius, &octant));

  query = Point3f(1.25, 1.9, 1.9);
  ASSERT_FALSE(overlaps<unibn::L2Distance<Point3f> >(query, radius, radius * radius, &octant));

  query = Point3f(1.9, 1.9, 1.25);
  ASSERT_FALSE(overlaps<unibn::L2Distance<Point3f> >(query, radius, radius * radius, &octant));

  // corner cases:
  query = Point3f(1.65, 1.65, 1.65);
  ASSERT_TRUE(overlaps<unibn::L2Distance<Point3f> >(query, radius, radius * radius, &octant));

  query = Point3f(1.95, 1.95, 1.95);
  ASSERT_FALSE(overlaps<unibn::L2Distance<Point3f> >(query, radius, radius * radius, &octant));

  // edge special case, see Issue #3 -- Edge
  octant.x = 0.025;
  octant.y = -0.025;
  octant.z = -0.025;
  octant.extent = 0.025;

  query = Point3f(0.025, 0.025, 0.025);
  radius = 0.025;

  ASSERT_FALSE(overlaps<unibn::L2Distance<Point3f> >(query, radius, radius * radius, &octant));
}
}

int main(int argc, char** argv)
{
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}


///////////////////////////////////////////////////////////////////////////////
// importance_sampling::random::include.hpp                                  //
//                                                                           //
//  Copyright 2009 Erwann Rogard. Distributed under the Boost                //
//  Software License, Version 1.0. (See accompanying file                    //
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)         //
///////////////////////////////////////////////////////////////////////////////
#ifndef BOOST_STATISTICS_DETAIL_IMPORTANCE_SAMPLING_RANDOM_INCLUDE_HPP_ER_2009
#define BOOST_STATISTICS_DETAIL_IMPORTANCE_SAMPLING_RANDOM_INCLUDE_HPP_ER_2009

#include <boost/statistics/detail/importance_sampling/random/generate.hpp>
#include <boost/statistics/detail/importance_sampling/random/grid.hpp>
#include <boost/statistics/detail/importance_sampling/random/sampler.hpp>

#endif


/*
* TS Elements
* Copyright 2015-2018 M. Newhouse
* Released under the MIT license.
*/

#pragma once

#include "vector2.hpp"

#include <boost/iterator/iterator_facade.hpp>

#include <vector>
#include <type_traits>

namespace ts
{
  namespace utility
  {
    struct BezierCurve
    {
      Vector2<double> start_point, start_control, end_control, end_point;
    };

    class BezierPath;

    namespace detail
    {
      template <typename Point>
      struct BezierPathReference
      {
        using point_reference_ = Point&;
        point_reference_ start_point, start_control, end_control, end_point;

        operator BezierCurve() const
        {
          return{ start_point, start_control, end_control, end_point };
        }
      };

      template <typename From, typename To>
      using is_bezier_point_convertible = std::enable_if_t<
        !std::is_same<From, To>::value && std::is_convertible<From, To>::value
      >;

      template <typename Point>
      class BezierPathIterator
        : public boost::iterator_facade < BezierPathIterator<Point>, BezierCurve,
        boost::random_access_traversal_tag, BezierPathReference < Point >>
      {
      public:
        BezierPathIterator() = default;

        template <typename OtherPoint, typename = is_bezier_point_convertible<OtherPoint, Point>>
        BezierPathIterator(const BezierPathIterator<OtherPoint>& other);

        using base_type = boost::iterator_facade < BezierPathIterator<Point>, BezierCurve,
          boost::random_access_traversal_tag, BezierPathReference < Point >> ;

        using reference = typename base_type::reference;
        using difference_type = typename base_type::difference_type;
        using value_type = typename base_type::value_type;
        using iterator_category = typename base_type::iterator_category;

      private:
        friend boost::iterator_core_access;
        friend BezierPath;

        template <typename OtherPoint>
        friend class BezierPathIterator;

        explicit BezierPathIterator(Point* point);

        bool equal(const BezierPathIterator& other) const;

        void increment();
        void decrement();

        typename base_type::reference dereference() const;

        void advance(typename base_type::difference_type d);
        typename base_type::difference_type distance_to(const BezierPathIterator& other) const;

        Point* point_ = nullptr;
      };
    }

    class BezierPath
    {
    public:
      using point = Vector2<double>;
      using iterator = detail::BezierPathIterator<point>;
      using const_iterator = detail::BezierPathIterator<const point>;

      BezierPath() = default;

      template <typename InputIt>
      explicit BezierPath(InputIt it, InputIt end);

      std::size_t size() const;
      bool empty() const;

      iterator insert(const_iterator pos, const BezierCurve& curve);

      template <typename InputIt>
      iterator insert(const_iterator pos, InputIt begin, InputIt end);

      void push_back(const BezierCurve& curve);
      void reserve(std::size_t new_cap);

      iterator begin();
      const_iterator begin() const;

      iterator end();
      const_iterator end() const;

    private:
      std::vector<point> points_;
    };

    // time_point in range [0.0-1.0]
    template <typename CurveType>
    Vector2<double> bezier_point_at(const CurveType& curve, double time_point);

    template <typename CurveType>
    Vector2<double> bezier_normal_at(const CurveType& curve, double time_point);
  }
}


#include "FWCore/Utilities/interface/EDMException.h"
#include "PhysicsTools/Utilities/interface/Parameter.h"
#include "PhysicsTools/Utilities/interface/Gaussian.h"
#include "PhysicsTools/Utilities/interface/Numerical.h"
#include "PhysicsTools/Utilities/interface/Exponential.h"
#include "PhysicsTools/Utilities/interface/Polynomial.h"
#include "PhysicsTools/Utilities/interface/Constant.h"
#include "PhysicsTools/Utilities/interface/Operations.h"
#include "PhysicsTools/Utilities/interface/MultiHistoChiSquare.h"
#include "PhysicsTools/Utilities/interface/RootMinuit.h"
#include "PhysicsTools/Utilities/interface/RootMinuitCommands.h"
#include "PhysicsTools/Utilities/interface/FunctClone.h"
#include "PhysicsTools/Utilities/interface/rootPlot.h"
#include "PhysicsTools/Utilities/interface/Expression.h"
#include "PhysicsTools/Utilities/interface/HistoPdf.h"
#include "TROOT.h"
#include "TH1.h"
#include "TFile.h"
#include <boost/program_options.hpp>
using namespace boost;
namespace po = boost::program_options;

#include <iostream>
#include <algorithm>
#include <exception>
#include <iterator>
#include <string>
#include <vector>
using namespace std;

// A helper function to simplify the main part.
template<class T>
ostream& operator<<(ostream& os, const vector<T>& v) {
  copy(v.begin(), v.end(), ostream_iterator<T>(cout, " "));
  return os;
}

//A function that sets istogram contents to 0
//if they are too small
void fix(TH1* histo) {
  for(int i = 1; i <= histo->GetNbinsX(); ++i) {
    if(histo->GetBinContent(i) < 0.1) {
      histo->SetBinContent(i, 0.0);
      histo->SetBinError(i, 0.0);
    }
  }
}

struct sig_tag;
struct bkg_tag;

int main(int ac, char *av[]) {
  gROOT->SetStyle("Plain");
  try {
    typedef funct::FunctExpression Expr;
    typedef fit::MultiHistoChiSquare<Expr, Expr, Expr, Expr> ChiSquared;

    double fMin, fMax;
    string ext;
    po::options_description desc("Allowed options");
    desc.add_options()
      ("help,h", "produce help message")
      ("input-file,i", po::value< vector<string> >(), "input file")
      ("min,m", po::value<double>(&fMin)->default_value(60), "minimum value for fit range")
      ("max,M", po::value<double>(&fMax)->default_value(120), "maximum value for fit range")
      ("plot-format,p", po::value<string>(&ext)->default_value("ps"),
       "output plot format")
      ;

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

    po::variables_map vm;
    po::store(po::command_line_parser(ac, av).
	    options(desc).positional(p).run(), vm);
    po::notify(vm);

    if (vm.count("help")) {
      cout << "Usage: options_description [options]\n";
      cout << desc;
      return 0;
      }

    fit::RootMinuitCommands<ChiSquared> commands("csa08NewZFit.txt");

    const unsigned int rebinMuMuNoIso = 2,rebinMuMu = 1, rebinMuTk = 2, rebinMuSa = 8;
    // assume that the bin size is 1 GeV!!!
    funct::Constant rebinMuMuNoIsoConst(rebinMuMuNoIso),rebinMuMuConst(rebinMuMu), rebinMuTkConst(rebinMuTk), rebinMuSaConst(rebinMuSa);

    if (vm.count("input-file")) {
      cout << "Input files are: "
	   << vm["input-file"].as< vector<string> >() << "\n";
      vector<string> v_file = vm["input-file"].as< vector<string> >();
      for(vector<string>::const_iterator it = v_file.begin();
	  it != v_file.end(); ++it) {
	TFile * root_file = new TFile(it->c_str(),"read");

	TH1D * histoZMuMuNoIso = (TH1D*) root_file->Get("nonIsolatedZToMuMuPlots/zMass");
	histoZMuMuNoIso->Rebin(rebinMuMuNoIso);
	fix(histoZMuMuNoIso);

	TH1D * histoZMuMu = (TH1D*) root_file->Get("goodZToMuMuPlots/zMass");
	histoZMuMu->Rebin(rebinMuMu);
	fix(histoZMuMu);

	TH1D * histoZMuTk = (TH1D*) root_file->Get("goodZToMuMuOneTrackPlots/zMass");
	histoZMuTk->Rebin(rebinMuTk);
	fix(histoZMuTk);

	TH1D * histoZMuSa = (TH1D*) root_file->Get("goodZToMuMuOneStandAloneMuonPlots/zMass");
	histoZMuSa->Rebin(rebinMuSa);
	fix(histoZMuSa);

	cout << ">>> histogram loaded\n";
	string f_string = *it;
	replace(f_string.begin(), f_string.end(), '.', '_');
	replace(f_string.begin(), f_string.end(), '/', '_');
	string plot_string = f_string + "." + ext;
	cout << ">>> Input files loaded\n";

	const char * kYieldZMuMu = "YieldZMuMu";
	const char * kEfficiencyTk = "EfficiencyTk";
	const char * kEfficiencySa = "EfficiencySa";
	const char * kEfficiencyIso = "EfficiencyIso";
	const char * kYieldBkgZMuTk = "YieldBkgZMuTk";
	const char * kYieldBkgZMuSa = "YieldBkgZMuSa";
	const char * kYieldBkgZMuMuNotIso = "YieldBkgZMuMuNotIso";
	const char * kAlpha = "Alpha";
	const char * kBeta = "Beta";
	const char * kLambda = "Lambda";
	const char * kA0 = "A0";
	const char * kA1 = "A1";
	const char * kA2 = "A2";
	const char * kB0 = "B0";
	const char * kB1 = "B1";
	const char * kB2 = "B2";
	const char * kMeanZMuSa = "MeanZMuSa";
	const char * kSigmaZMuSa = "SigmaZMuSa";

	funct::Parameter yieldZMuMu(kYieldZMuMu, commands.par(kYieldZMuMu));
	funct::Parameter effTk(kEfficiencyTk, commands.par(kEfficiencyTk));
	funct::Parameter effSa(kEfficiencySa, commands.par(kEfficiencySa));
	funct::Parameter effIso(kEfficiencyIso, commands.par(kEfficiencyIso));
	funct::Parameter yieldBkgZMuTk(kYieldBkgZMuTk, commands.par(kYieldBkgZMuTk));
	funct::Parameter yieldBkgZMuSa(kYieldBkgZMuSa, commands.par(kYieldBkgZMuSa));
	funct::Parameter yieldBkgZMuMuNotIso(kYieldBkgZMuMuNotIso, commands.par(kYieldBkgZMuMuNotIso));
	funct::Parameter meanZMuSa(kMeanZMuSa, commands.par(kMeanZMuSa));
	funct::Parameter sigmaZMuSa(kSigmaZMuSa, commands.par(kSigmaZMuSa));
	funct::Parameter alpha(kAlpha, commands.par(kAlpha));
	funct::Parameter beta(kBeta, commands.par(kBeta));
	funct::Parameter lambda(kLambda, commands.par(kLambda));
	funct::Parameter a0(kA0, commands.par(kA0));
	funct::Parameter a1(kA1, commands.par(kA1));
	funct::Parameter a2(kA2, commands.par(kA2));
	funct::Parameter b0(kB0, commands.par(kB0));
	funct::Parameter b1(kB1, commands.par(kB1));
	funct::Parameter b2(kB2, commands.par(kB2));
	funct::Constant cFMin(fMin), cFMax(fMax);

	// count ZMuMu Yield
	double nZMuMu = 0;
	{
	  unsigned int nBins = histoZMuMu->GetNbinsX();
	  double xMin = histoZMuMu->GetXaxis()->GetXmin();
	  double xMax = histoZMuMu->GetXaxis()->GetXmax();
	  double deltaX =(xMax - xMin) / nBins;
	  for(unsigned int i = 0; i < nBins; ++i) {
	    double x = xMin + (i +.5) * deltaX;
	    if(x > fMin && x < fMax)
	      nZMuMu += histoZMuMu->GetBinContent(i+1);
	  }
	}

	cout << ">>> count of ZMuMu yield in the range [" << fMin << ", " << fMax << "]: " << nZMuMu << endl;

	funct::RootHistoPdf zPdfMuMuNonIso(*histoZMuMu, fMin, fMax);
	funct::RootHistoPdf zPdfMuTk = zPdfMuMuNonIso;
	zPdfMuMuNonIso.rebin(rebinMuMuNoIso);
	zPdfMuTk.rebin(rebinMuTk);

	funct::Numerical<2> _2;
	funct::Numerical<1> _1;

	Expr zMuMuEffTerm = (effTk ^ _2) *  (effSa ^ _2) * (effIso ^ _2);
	Expr zMuMuNoIsoEffTerm = (effTk ^ _2) * (effSa ^ _2) * (_1 - (effIso ^ _2));
	Expr zMuTkEffTerm = _2 * (effTk ^ _2) * effSa * (_1 - effSa) * (effIso ^ _2);
	Expr zMuSaEffTerm = _2 * (effSa ^ _2) * effTk * (_1 - effTk) * (effIso ^ _2);

	Expr zMuMu = rebinMuMuConst * zMuMuEffTerm * yieldZMuMu;

	Expr zMuTkBkg = yieldBkgZMuTk * funct::Exponential(lambda) * funct::Polynomial<2>(a0, a1, a2);
	Expr zMuTkBkgScaled = rebinMuTkConst * zMuTkBkg;
	Expr zMuTk = rebinMuTkConst * (zMuTkEffTerm * yieldZMuMu * zPdfMuTk + zMuTkBkg);
	Expr zMuMuNoIsoBkg = yieldBkgZMuMuNotIso * funct::Exponential(alpha) * funct::Polynomial<2>(b0, b1, b2);
	Expr zMuMuNoIsoBkgScaled = rebinMuMuNoIsoConst * zMuMuNoIsoBkg;
	Expr zMuMuNoIso = rebinMuMuNoIsoConst * (zMuMuNoIsoEffTerm * yieldZMuMu * zPdfMuMuNonIso + zMuMuNoIsoBkg);
	Expr zMuSa = rebinMuSaConst * (zMuSaEffTerm * yieldZMuMu * funct::Gaussian(meanZMuSa, sigmaZMuSa)
				       + (yieldBkgZMuSa * funct::Exponential(beta)));

	TH1D histoZCount("histoZCount", "", 1, fMin, fMax);
	histoZCount.Fill(100, nZMuMu);

	ChiSquared chi2(zMuMu, & histoZCount,
			zMuTk, histoZMuTk,
			zMuSa, histoZMuSa,
			zMuMuNoIso,histoZMuMuNoIso,
			fMin, fMax);
	cout << "N. deg. of freedom: " << chi2.degreesOfFreedom() << endl;
	fit::RootMinuit<ChiSquared> minuit(chi2, true);
	commands.add(minuit, yieldZMuMu);
	commands.add(minuit, effTk);
	commands.add(minuit, effSa);
	commands.add(minuit, effIso);
	commands.add(minuit, yieldBkgZMuTk);
	commands.add(minuit, yieldBkgZMuSa);
	commands.add(minuit, yieldBkgZMuMuNotIso);
	commands.add(minuit, meanZMuSa);
	commands.add(minuit, sigmaZMuSa);
	commands.add(minuit, lambda);
	commands.add(minuit, alpha);
	commands.add(minuit, beta);
	commands.add(minuit, a0);
	commands.add(minuit, a1);
	commands.add(minuit, a2);
	commands.add(minuit, b0);
	commands.add(minuit, b1);
	commands.add(minuit, b2);
	commands.run(minuit);
	const unsigned int nPar = 18;//WARNIG: this must be updated manually for now
	ROOT::Math::SMatrix<double, nPar, nPar, ROOT::Math::MatRepSym<double, nPar> > err;
	minuit.getErrorMatrix(err);
	std::cout << "error matrix:" << std::endl;
	for(unsigned int i = 0; i < nPar; ++i) {
	  for(unsigned int j = 0; j < nPar; ++j) {
	    std::cout << err(i, j) << "\t";
	  }
	  std::cout << std::endl;
	}
	minuit.printFitResults();

	double s;
	s = 0;
	for(int i = 1; i <= histoZMuMuNoIso->GetNbinsX(); ++i)
	  s += histoZMuMuNoIso->GetBinContent(i);
	histoZMuMuNoIso->SetEntries(s);
	for(int i = 1; i <= histoZMuMu->GetNbinsX(); ++i)
	  s += histoZMuMu->GetBinContent(i);
	histoZMuMu->SetEntries(s);
	s = 0;
	for(int i = 1; i <= histoZMuTk->GetNbinsX(); ++i)
	  s += histoZMuTk->GetBinContent(i);
	histoZMuTk->SetEntries(s);
	s = 0;
	for(int i = 1; i <= histoZMuSa->GetNbinsX(); ++i)
	  s += histoZMuSa->GetBinContent(i);
	histoZMuSa->SetEntries(s);
	string ZMuMuPlot = "ZMuMuFit_" + plot_string;
	root::plot<Expr>(ZMuMuPlot.c_str(), *histoZMuMu, zMuMu, fMin, fMax,
			  effTk, effSa, effIso, yieldZMuMu,
			  kRed, 2, kDashed, 100,
			  "Z -> #mu #mu mass", "#mu #mu invariant mass (GeV/c^{2})",
			  "Events");

	string ZMuMuNoIsoPlot = "ZMuMuNoIsoFit_" + plot_string;
	root::plot<Expr>(ZMuMuNoIsoPlot.c_str(), *histoZMuMuNoIso, zMuMuNoIso, fMin, fMax,
			 effTk, effSa, effIso, yieldZMuMu,
			 kRed, 2, kDashed, 100,
			 "Z -> #mu #mu Not Iso mass", "#mu #mu invariant mass (GeV/c^{2})",
			 "Events");

	string ZMuTkPlot = "ZMuTkFit_X_" + plot_string;
	root::plot<Expr>(ZMuTkPlot.c_str(), *histoZMuTk, zMuTk, fMin, fMax,
			 effTk, effSa, effIso, yieldZMuMu,
			 yieldBkgZMuTk, lambda, a0, a1, a2,
			 kRed, 2, kDashed, 100,
                         "Z -> #mu + (unmatched) track mass", "#mu #mu invariant mass (GeV/c^{2})",
                         "Events");
	ZMuTkPlot = "ZMuTkFit_" + plot_string;
	TF1 funZMuTk = root::tf1_t<sig_tag, Expr>("ZMuTkFunction", zMuTk, fMin, fMax,
						  effTk, effSa, effIso, yieldZMuMu,
						  yieldBkgZMuTk, lambda, a0, a1, a2);
	funZMuTk.SetLineColor(kRed);
	funZMuTk.SetLineWidth(2);
	funZMuTk.SetLineStyle(kDashed);
	funZMuTk.SetNpx(10000);
	TF1 funZMuTkBkg = root::tf1_t<bkg_tag, Expr>("ZMuTkBack", zMuTkBkgScaled, fMin, fMax,
						     yieldBkgZMuTk, lambda, a0, a1, a2);
	funZMuTkBkg.SetLineColor(kGreen);
	funZMuTkBkg.SetLineWidth(2);
	funZMuTkBkg.SetLineStyle(kDashed);
	funZMuTkBkg.SetNpx(10000);
	histoZMuTk->SetTitle("Z -> #mu + (unmatched) track mass");
	histoZMuTk->SetXTitle("#mu + (unmatched) track invariant mass (GeV/c^{2})");
	histoZMuTk->SetYTitle("Events");
	TCanvas *canvas = new TCanvas("canvas");
	histoZMuTk->Draw("e");
	funZMuTkBkg.Draw("same");
	funZMuTk.Draw("same");
	canvas->SaveAs(ZMuTkPlot.c_str());
	canvas->SetLogy();
	string logZMuTkPlot = "log_" + ZMuTkPlot;
	canvas->SaveAs(logZMuTkPlot.c_str());
	string ZMuSaPlot = "ZMuSaFit_" + plot_string;
	root::plot<Expr>(ZMuSaPlot.c_str(), *histoZMuSa, zMuSa, fMin, fMax,
			 effSa, effTk, effIso,
			 yieldZMuMu, meanZMuSa, sigmaZMuSa, yieldBkgZMuSa,
			 kRed, 2, kDashed, 10000,
			 "Z -> #mu + (unmatched) standalone mass",
			 "#mu + (unmatched) standalone invariant mass (GeV/c^{2})",
			 "Events");
      }
    }
  }
  catch(exception& e) {
    cerr << "error: " << e.what() << "\n";
    return 1;
  }
  catch(...) {
    cerr << "Exception of unknown type!\n";
  }

  return 0;
}


/*
 * Copyright 2009-2020 The VOTCA Development Team (http://www.votca.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 *
 *     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.
 *
 */

#define BOOST_TEST_MAIN
#define BOOST_TEST_MODULE CudaPipeline_test

// Standard includes
#include <string>

// Third party includes
#include <boost/test/unit_test.hpp>

// Local VOTCA includes
#include "votca/xtp/cudapipeline.h"
#include "votca/xtp/eigen.h"

using namespace votca::xtp;

BOOST_AUTO_TEST_SUITE(CudaPipeline_test)

BOOST_AUTO_TEST_CASE(right_matrix_multiplication) {
  // Call the class to handle GPU resources
  CudaPipeline cuda_pip;

  // Call matrix multiplication GPU
  Eigen::MatrixXd A = Eigen::MatrixXd::Zero(2, 2);
  Eigen::MatrixXd B = Eigen::MatrixXd::Zero(3, 2);
  Eigen::MatrixXd C = Eigen::MatrixXd::Zero(3, 2);
  Eigen::MatrixXd D = Eigen::MatrixXd::Zero(3, 2);
  Eigen::MatrixXd X = Eigen::MatrixXd::Zero(3, 2);
  Eigen::MatrixXd Y = Eigen::MatrixXd::Zero(3, 2);
  Eigen::MatrixXd Z = Eigen::MatrixXd::Zero(3, 2);

  // Define matrices
  A << 1., 2., 3., 4.;
  B << 5., 6., 7., 8., 9., 10.;
  C << 9., 10., 11., 12., 13., 14.;
  D << 13., 14., 15., 16., 17., 18.;
  X << 23., 34., 31., 46., 39., 58.;
  Y << 39., 58., 47., 70., 55., 82.;
  Z << 55., 82., 63., 94., 71., 106.;

  std::vector<Eigen::MatrixXd> tensor{B, C, D};
  std::vector<Eigen::MatrixXd> results(3, Eigen::MatrixXd::Zero(3, 2));
  CudaMatrix cuma_A{A, cuda_pip.get_stream()};
  CudaMatrix cuma_B{3, 2, cuda_pip.get_stream()};
  CudaMatrix cuma_C{3, 2, cuda_pip.get_stream()};

  for (votca::Index i = 0; i < 3; i++) {
    cuma_B.copy_to_gpu(tensor[i]);
    cuda_pip.gemm(cuma_B, cuma_A, cuma_C);
    results[i] = cuma_C;
  }
  // Expected results
  BOOST_TEST(X.isApprox(results[0]));
  BOOST_TEST(Y.isApprox(results[1]));
  BOOST_TEST(Z.isApprox(results[2]));
}

BOOST_AUTO_TEST_CASE(wrong_shape_cublas) {
  Eigen::MatrixXd A = Eigen::MatrixXd::Random(2, 2);
  Eigen::MatrixXd B = Eigen::MatrixXd::Random(5, 5);

  CudaPipeline cuda_pip;
  CudaMatrix cuma_A{A, cuda_pip.get_stream()};
  CudaMatrix cuma_B{B, cuda_pip.get_stream()};
  CudaMatrix cuma_C{2, 5, cuda_pip.get_stream()};

  BOOST_REQUIRE_THROW(cuda_pip.gemm(cuma_A, cuma_B, cuma_C),
                      std::runtime_error);
}

BOOST_AUTO_TEST_SUITE_END()


#pragma once

#include <fmt/format.h>
#include <is/msgs/camera.pb.h>
#include <is/msgs/common.pb.h>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/graph/graph_traits.hpp>
#include <boost/optional.hpp>
#include <tl/expected.hpp>
#include <unordered_map>
#include "edge.hpp"

namespace is {

using namespace tl;

class FrameConversion {
  using Graph = boost::adjacency_list<boost::listS, boost::vecS, boost::undirectedS, int64_t,
                                      boost::property<boost::edge_weight_t, float>>;
  using Vertex = boost::graph_traits<Graph>::vertex_descriptor;

  std::unordered_map<Edge, common::Tensor, EdgeHash> tensors;
  Graph graph;
  std::unordered_map<int64_t, Vertex> vertices;

  auto has_vertex(int64_t id) const -> bool;
  auto get_vertex(int64_t id) const -> Vertex;
  auto add_vertex(int64_t id) -> Vertex;
  void remove_vertex(int64_t);

  void add_edge(Edge const&, float weight = 1.0);
  void remove_edge(Edge const&);

 public:
  FrameConversion() = default;
  FrameConversion(FrameConversion const&) = default;
  FrameConversion(FrameConversion&&) = default;

  void update_transformation(Edge const&, common::Tensor const&);
  void remove_transformation(Edge const&);

  void update_transformation(vision::FrameTransformation const&);
  void remove_transformation(vision::FrameTransformation const&);

  auto transformations() const -> std::unordered_map<Edge, common::Tensor, EdgeHash> const&;

  // Try to find shortest path that connects the two given vertices
  auto find_path(Edge const&) const -> expected<Path, std::string>;
  // Try to find shortest path that connects all the vertices
  auto find_path(Path const&) const -> expected<Path, std::string>;
  // Compose all the transformations on the given path resulting in a single transformation
  auto compose_path(Path const&) const -> common::Tensor;
};

}  // namespace is


#include <boost/random/random_device.hpp>
#include <iostream>

int main()
{
  boost::random::random_device gen;
  std::cout << gen() << '\n';
}


#include "nanocv/nanocv.h"
#include "nanocv/sampler.h"
#include "nanocv/tabulator.h"
#include "nanocv/measure.hpp"
#include "nanocv/accumulator.h"
#include "nanocv/math/random.hpp"
#include "nanocv/thread/thread.h"
#include "nanocv/tasks/task_synthetic_shapes.h"
#include <boost/program_options.hpp>

namespace
{
        using namespace ncv;

        void make_random_samples(
                size_t cmd_samples, size_t cmd_rows, size_t cmd_cols, size_t cmd_outputs, color_mode cmd_color,
                tensors_t& inputs, vectors_t& targets)
        {
                inputs.resize(cmd_samples);
                for (auto& input : inputs)
                {
                        input.resize(cmd_color == color_mode::luma ? 1 : 3, cmd_rows, cmd_cols);
                        input.setRandom(random_t<scalar_t>(0.0, 1.0));
                }

                random_t<size_t> trgen(0, cmd_outputs);

                targets.resize(cmd_samples);
                for (auto& target : targets)
                {
                        target = ncv::class_target(trgen() % cmd_outputs, cmd_outputs);
                }
        }
}

int main(int argc, char *argv[])
{
        ncv::init();

        using namespace ncv;

        // parse the command line
        boost::program_options::options_description po_desc("", 160);
        po_desc.add_options()("help,h", "benchmark models");
        po_desc.add_options()("samples,s",
                boost::program_options::value<size_t>()->default_value(10000),
                "number of samples to use [1000, 100000]");
        po_desc.add_options()("forward",
                "evaluate the \'forward\' pass (output)");
        po_desc.add_options()("backward",
                "evaluate the \'backward' pass (gradient)");

        boost::program_options::variables_map po_vm;
        boost::program_options::store(
                boost::program_options::command_line_parser(argc, argv).options(po_desc).run(),
                po_vm);
        boost::program_options::notify(po_vm);

        // check arguments and options
        if (	po_vm.empty() ||
                po_vm.count("help"))
        {
                std::cout << po_desc;
                return EXIT_FAILURE;
        }

        const size_t cmd_samples = math::clamp(po_vm["samples"].as<size_t>(), 1000, 100 * 1000);
        const bool cmd_forward = po_vm.count("forward");
        const bool cmd_backward = po_vm.count("backward");

        if (!cmd_forward && !cmd_backward)
        {
                std::cout << po_desc;
                return EXIT_FAILURE;
        }

        const size_t cmd_rows = 28;
        const size_t cmd_cols = 28;
        const size_t cmd_outputs = 10;
        const color_mode cmd_color = color_mode::luma;

        const size_t cmd_min_nthreads = 1;
        const size_t cmd_max_nthreads = ncv::n_threads();

        // generate random samples
        tensors_t inputs;
        vectors_t targets;
        make_random_samples(cmd_samples, cmd_rows, cmd_cols, cmd_outputs, cmd_color, inputs, targets);

        // generate synthetic task
        synthetic_shapes_task_t task(cmd_rows, cmd_cols, cmd_outputs, cmd_color, cmd_samples);
        task.load("");

        // construct models
        const string_t lmodel0;
        const string_t lmodel1 = lmodel0 + "linear:dims=100;act-snorm;";
        const string_t lmodel2 = lmodel1 + "linear:dims=100;act-snorm;";
        const string_t lmodel3 = lmodel2 + "linear:dims=100;act-snorm;";
        const string_t lmodel4 = lmodel3 + "linear:dims=100;act-snorm;";
        const string_t lmodel5 = lmodel4 + "linear:dims=100;act-snorm;";

        string_t cmodel;
        cmodel = cmodel + "conv:dims=16,rows=9,cols=9;pool-max;act-snorm;";
        cmodel = cmodel + "conv:dims=32,rows=5,cols=5;pool-max;act-snorm;";
        cmodel = cmodel + "conv:dims=64,rows=3,cols=3;act-snorm;";

        const string_t outlayer = "linear:dims=" + text::to_string(cmd_outputs) + ";";

        strings_t cmd_networks =
        {
                lmodel0 + outlayer,
                lmodel1 + outlayer,
                lmodel2 + outlayer,
                lmodel3 + outlayer,
                lmodel4 + outlayer,
                lmodel5 + outlayer,

                cmodel + outlayer
        };

        strings_t cmd_names =
        {
                "lmodel0",
                "lmodel1",
                "lmodel2",
                "lmodel3",
                "lmodel4",
                "lmodel5",

                "cmodel"
        };

        const rloss_t loss = ncv::get_losses().get("logistic");
        assert(loss);

        // construct tables to compare models
        tabulator_t ftable_rand("model-forward (rand)\\threads");
        tabulator_t ftable_task("model-forward (task)\\threads");

        tabulator_t btable_rand("model-backward (rand)\\threads");
        tabulator_t btable_task("model-backward (task)\\threads");

        for (size_t nthreads = cmd_min_nthreads; nthreads <= cmd_max_nthreads; nthreads ++)
        {
                ftable_rand.header() << (text::to_string(nthreads) + "xCPU [ms]");
                ftable_task.header() << (text::to_string(nthreads) + "xCPU [ms]");

                btable_rand.header() << (text::to_string(nthreads) + "xCPU [ms]");
                btable_task.header() << (text::to_string(nthreads) + "xCPU [ms]");
        }

        // evaluate models
        for (size_t im = 0; im < cmd_networks.size(); im ++)
        {
                const string_t cmd_network = cmd_networks[im];
                const string_t cmd_name = cmd_names[im];

                tabulator_t::row_t& frow_rand = ftable_rand.append(cmd_name + "(rand)");
                tabulator_t::row_t& frow_task = ftable_task.append(cmd_name + "(task)");

                tabulator_t::row_t& brow_rand = btable_rand.append(cmd_name + "(rand)");
                tabulator_t::row_t& brow_task = btable_task.append(cmd_name + "(task)");

                log_info() << "<<< running network [" << cmd_network << "] ...";

                // create feed-forward network
                const rmodel_t model = ncv::get_models().get("forward-network", cmd_network);
                assert(model);
                model->resize(cmd_rows, cmd_cols, cmd_outputs, cmd_color, true);
                model->random_params();

                // select random samples
                sampler_t sampler(task);
                sampler.setup(sampler_t::stype::uniform, cmd_samples);
                sampler.setup(sampler_t::atype::annotated);

                const samples_t samples = sampler.get();

                // process the samples
                for (size_t nthreads = cmd_min_nthreads; nthreads <= cmd_max_nthreads; nthreads ++)
                {
                        if (cmd_forward)
                        {
                                accumulator_t ldata(*model, nthreads, "l2n-reg", criterion_t::type::value, 0.1);

                                const auto milis_rand = ncv::measure_robustly_usec([&] ()
                                {
                                        ldata.reset();
                                        ldata.update(inputs, targets, *loss);
                                }, 1) / 1000;

                                const auto milis_task = ncv::measure_robustly_usec([&] ()
                                {
                                        ldata.reset();
                                        ldata.update(task, samples, *loss);
                                }, 1) / 1000;

                                log_info() << "<<< processed [" << ldata.count()
                                           << "] forward samples in " << milis_rand << "/" << milis_task << " ms.";

                                frow_rand << milis_rand;
                                frow_task << milis_task;
                        }

                        if (cmd_backward)
                        {
                                accumulator_t gdata(*model, nthreads, "l2n-reg", criterion_t::type::vgrad, 0.1);

                                const auto milis_rand = ncv::measure_robustly_usec([&] ()
                                {
                                        gdata.update(inputs, targets, *loss);
                                }, 1) / 1000;

                                const auto milis_task = ncv::measure_robustly_usec([&] ()
                                {
                                        gdata.update(task, samples, *loss);
                                }, 1) / 1000;

                                log_info() << "<<< processed [" << gdata.count()
                                           << "] backward samples in " << milis_rand << "/" << milis_task << " ms.";

                                brow_rand << milis_rand;
                                brow_task << milis_task;
                        }
                }

                log_info();
        }

        // print results
        if (cmd_forward)
        {
                ftable_rand.print(std::cout);
                ftable_task.print(std::cout);
        }
        log_info();
        if (cmd_backward)
        {
                btable_rand.print(std::cout);
                btable_task.print(std::cout);
        }

        // OK
        log_info() << done;
        return EXIT_SUCCESS;
}


// Boost.Geometry

// Copyright (c) 2016-2017, Oracle and/or its affiliates.
// Contributed and/or modified by Adam Wulkiewicz, on behalf of Oracle

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

#ifndef BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_INTERSECTION_ELLIPTIC_HPP
#define BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_INTERSECTION_ELLIPTIC_HPP

#include <boost/geometry/core/srs.hpp>

#include <boost/geometry/formulas/geographic.hpp>

#include <boost/geometry/strategies/spherical/intersection.hpp>

namespace boost { namespace geometry
{

namespace strategy { namespace intersection
{

template <typename Spheroid>
struct great_elliptic_segments_calc_policy
    : spherical_segments_calc_policy
{
    explicit great_elliptic_segments_calc_policy(Spheroid const& spheroid = Spheroid())
        : m_spheroid(spheroid)
    {}

    template <typename Point, typename Point3d>
    Point from_cart3d(Point3d const& point_3d) const
    {
        return formula::cart3d_to_geo<Point>(point_3d, m_spheroid);
    }

    template <typename Point3d, typename Point>
    Point3d to_cart3d(Point const& point) const
    {
        return formula::geo_to_cart3d<Point3d>(point, m_spheroid);
    }

    // relate_xxx_calc_policy must live londer than plane because it contains
    // Spheroid object and plane keeps the reference to that object.
    template <typename Point3d>
    struct plane
    {
        typedef typename coordinate_type<Point3d>::type coord_t;

        // not normalized
        plane(Point3d const& p1, Point3d const& p2)
            : normal(cross_product(p1, p2))
        {}

        int side_value(Point3d const& pt) const
        {
            return formula::sph_side_value(normal, pt);
        }

        coord_t cos_angle_between(Point3d const& p1, Point3d const& p2) const
        {
            Point3d v1 = p1;
            detail::vec_normalize(v1);
            Point3d v2 = p2;
            detail::vec_normalize(v2);

            return dot_product(v1, v2);
        }

        coord_t cos_angle_between(Point3d const& p1, Point3d const& p2, bool & is_forward) const
        {
            coord_t const c0 = 0;

            Point3d v1 = p1;
            detail::vec_normalize(v1);
            Point3d v2 = p2;
            detail::vec_normalize(v2);

            is_forward = dot_product(normal, cross_product(v1, v2)) >= c0;
            return dot_product(v1, v2);
        }

        Point3d normal;
    };

    template <typename Point3d>
    plane<Point3d> get_plane(Point3d const& p1, Point3d const& p2) const
    {
        return plane<Point3d>(p1, p2);
    }

    template <typename Point3d>
    bool intersection_points(plane<Point3d> const& plane1,
                             plane<Point3d> const& plane2,
                             Point3d & ip1, Point3d & ip2) const
    {
        typedef typename coordinate_type<Point3d>::type coord_t;

        Point3d id = cross_product(plane1.normal, plane2.normal);
        // NOTE: the length should be greater than 0 at this point
        // NOTE: no need to normalize in this case

        ip1 = formula::projected_to_surface(id, m_spheroid);

        ip2 = ip1;
        multiply_value(ip2, coord_t(-1));

        return true;
    }

private:
    Spheroid m_spheroid;
};

template <typename Spheroid>
struct experimental_elliptic_segments_calc_policy
{
    explicit experimental_elliptic_segments_calc_policy(Spheroid const& spheroid = Spheroid())
        : m_spheroid(spheroid)
    {}

    template <typename Point, typename Point3d>
    Point from_cart3d(Point3d const& point_3d) const
    {
        return formula::cart3d_to_geo<Point>(point_3d, m_spheroid);
    }

    template <typename Point3d, typename Point>
    Point3d to_cart3d(Point const& point) const
    {
        return formula::geo_to_cart3d<Point3d>(point, m_spheroid);
    }

    // relate_xxx_calc_policy must live londer than plane because it contains
    // Spheroid object and plane keeps the reference to that object.
    template <typename Point3d>
    struct plane
    {
        typedef typename coordinate_type<Point3d>::type coord_t;

        // not normalized
        plane(Point3d const& p1, Point3d const& p2, Spheroid const& spheroid)
            : m_spheroid(spheroid)
        {
            formula::experimental_elliptic_plane(p1, p2, origin, normal, m_spheroid);
        }

        int side_value(Point3d const& pt) const
        {
            return formula::elliptic_side_value(origin, normal, pt);
        }

        coord_t cos_angle_between(Point3d const& p1, Point3d const& p2) const
        {
            Point3d const v1 = normalized_vec(p1);
            Point3d const v2 = normalized_vec(p2);
            return dot_product(v1, v2);
        }

        coord_t cos_angle_between(Point3d const& p1, Point3d const& p2, bool & is_forward) const
        {
            coord_t const c0 = 0;

            Point3d const v1 = normalized_vec(p1);
            Point3d const v2 = normalized_vec(p2);

            is_forward = dot_product(normal, cross_product(v1, v2)) >= c0;
            return dot_product(v1, v2);
        }

        Point3d origin;
        Point3d normal;

    private:
        Point3d normalized_vec(Point3d const& p) const
        {
            Point3d v = p;
            subtract_point(v, origin);
            detail::vec_normalize(v);
            return v;
        }

        Spheroid const& m_spheroid;
    };

    template <typename Point3d>
    plane<Point3d> get_plane(Point3d const& p1, Point3d const& p2) const
    {
        return plane<Point3d>(p1, p2, m_spheroid);
    }

    template <typename Point3d>
    bool intersection_points(plane<Point3d> const& plane1,
                             plane<Point3d> const& plane2,
                             Point3d & ip1, Point3d & ip2) const
    {
        return formula::planes_spheroid_intersection(plane1.origin, plane1.normal,
                                                     plane2.origin, plane2.normal,
                                                     ip1, ip2, m_spheroid);
    }

private:
    Spheroid m_spheroid;
};

template
<
    typename Spheroid = srs::spheroid<double>,
    typename CalculationType = void
>
struct great_elliptic_segments
    : ecef_segments
        <
            great_elliptic_segments_calc_policy<Spheroid>,
            CalculationType
        >
{};

template
<
    typename Spheroid = srs::spheroid<double>,
    typename CalculationType = void
>
struct experimental_elliptic_segments
    : ecef_segments
        <
            experimental_elliptic_segments_calc_policy<Spheroid>,
            CalculationType
        >
{};

}} // namespace strategy::intersection

}} // namespace boost::geometry

#endif // BOOST_GEOMETRY_STRATEGIES_GEOGRAPHIC_INTERSECTION_ELLIPTIC_HPP


//==================================================================================================
/*!
  @file

  @copyright 2016 NumScale SAS

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

#if defined(DOXYGEN_ONLY)
namespace boost { namespace simd
{
  /*!

    @ingroup group-constant

    Generates constant Log2_e.This constant is coupled with Log2_lo and is
    used in the float logarithms computations
    We have double(Log2_e<float>())+double(Log2_lo<float>()) == Log_2<double>()

    @par Header <boost/simd/constant/log2_e.hpp>

    @par Semantic:

    @code
    T r = log2_e<T>();
    @endcode

**/
  template<typename T> T Log2_e();

  namespace functional
  {
    /*!
      @ingroup group-callable-constant

      Generates constant Log2_e.This constant is coupled with Log2_lo and is
      used in the float logarithms computations
      We have double(Log2_e<float>())+double(Log2_lo<float>()) == Log_2<double>()

      Generate the  constant log2_e.

      @return The Log2_e constant for the proper type
    **/
    Value log2_e<Value>();
  }
} }
#endif

#include <boost/simd/constant/scalar/log2_e.hpp>
#include <boost/simd/constant/simd/log2_e.hpp>

#endif