/*
	ALL THE DISTANCE UNITS HERE ARE IN METERS!
	Speed would be meters/second
	For x axis:
		positive = forwards
		negative = backwards
	For y axis:
		postive = left
		negative = right
*/

#include <vector>
#include "Drone.h"
#include <math.h>
#include <boost/asio/io_service.hpp>
#include <Fullnavdata.h>
#include <gnuplot_iostream.h>
#include <deque>
#include <fstream>
#include <iostream>
#include <sstream>
#include "navi.h"

using namespace std;

const double Navi::INTERSECTION_THRESHOLD = 4.0;
const double Navi::WALL_THRESHOLD = 1.0;
//arbitrary value... probably something better could be used
double Navi::INTERSECTION_INDICATOR[2] = { 100000000000, -100000000000 };

Navi::Navi(){
	getting_info_loop = true;
	//node info array
	nia = NULL;
	current_direction = 'n';
	initial_direction = 'n';

	currently_reversing = false;
	did_reverse = false;
}

void Navi::mainLoop( string file_name ){
	while(getting_info_loop)
	{
		ReadyToReadScan();

		//nia[0] = node coordinates
		//nia[1] = max backward distance vector
		//nia[2] = max forward distance vector
		//nia[3] = max left distance vector
		//nia[4] = max right distance vector
		nia = ReadScanFile(file_name);

		//helps readability, hopefully..

		nia_position = nia[0];
		mbd = nia[1][0];
		mfd = nia[2][0];
		mld = nia[3][1];
		mrd = nia[4][1];

		if( initial_direction == 'n' )
		{
			drone_dd_vals = FindMaxDistance(mbd, mfd, mld, mrd);
			initial_direction = drone_dd_vals.direction;
			current_direction = initial_direction;
			cout << "Go: " << initial_direction << endl;
			continue;
		}

		intersection = CheckForIntersections(mbd, mfd, mld, mrd, current_direction);

		// n indicates no value was set, thus it isn't an intersection
		if( intersection.direction != 'n')
		{
			handleIntersection();
		}
		//need to insert the position after checking the intersection, since
		// the intersection check looks for it inside. Not a huge deal,
		// but will be easier since we don't need it til now anyways...
		// That was a lot of comments for no real good reason.
		dpos.push_back(nia_position);

		current_distance = GetDistance(current_direction);

		if( current_distance <= Navi::WALL_THRESHOLD )
		{
			cout << "WALLLLLLL!" << endl;
			current_direction = ReverseDirection(current_direction);
			currently_reversing = true;
			dpos.pop_back();
			//want to remove last position, since we need to reverse back
			//dpos.pop_back();
		}

		if( currently_reversing == true )
		{
			cout << "Begin reversing" << endl;
			vector <double*> :: reverse_iterator dprit;
			for (dprit = dpos.rbegin(); *dprit != Navi::INTERSECTION_INDICATOR; ++dprit)
			{
				ReadyToReadScan();
				cout << "Go: " << current_direction << endl;
				//dpos.pop_back();
			}

			cout << "End reversing" << endl;
			currently_reversing = false;
			did_reverse = true;
		}

		/*
		vector <double*> :: iterator dpit;
		for(dpit = dpos.begin(); dpit != dpos.end(); dpit++)
		{
			cout << *dpit[0] << endl;
		}

		vector <char> :: iterator ddit;
		for( ddit = ddirec.begin(); ddit != ddirec.end(); ddit++)
		{
			cout << *ddit << endl;
		}
		*/

		//just don't want to print out what direction to go twice
		if(did_reverse != true)
		{
			cout << "Go: " << current_direction << endl;
		}
		else //we did just reverse
		{
			did_reverse = false;
		}

	}

}

void Navi::handleIntersection(){
	cout << "INTERSECTION!!!!!" << endl;

	bool prev_intersection = false;

	char prev_direction = current_direction;

	current_direction = intersection.direction;

	//looking to see if we are at a new intersection!
	if ( !IsValueInDpos(nia_position) )
	{
		cout << "????" << endl;
		ddirec.push_back('x');
		//If we reach an intersection, don't want to go down the way we came
		// when we come back to it
		ddirec.push_back(ReverseDirection(prev_direction));
	}
	else //we are at a previous intersection
	{
		prev_intersection = true;
	}

	//This means we need to go back through the other paths we did not take yet
	if( prev_intersection == true )
	{
		cout << "AHKKKKKKKK" << endl;
		//making temp ints for intersection maxes
		double imbd = mbd;
		double imfd = mfd;
		double imld = mld;
		double imrd = mrd;

		double idist[4] = {imbd, imfd, imld, imrd};

		//the idea behind this is that we go through all the previous route we
		// went to at this intersection and make their max 0 (temporarily)
		// when we do this, we know which new direction to go
		vector <char> :: reverse_iterator ddrit;
		for (ddrit = ddirec.rbegin(); ddrit != ddirec.rend() && (*ddrit) != 'x'; ddrit++)
		{
			cout << (*ddrit) << endl;
			ChangeDistance(*ddrit, 0, idist);
		}

		imbd = idist[0];
		imfd = idist[1];
		imld = idist[2];
		imrd = idist[3];

		/*
			Checking l and f here, since it will basically check all directions at the
			 intersection. left first since we are prioritizing: left, forward, right
		*/
		current_direction = 'l';
		intersection = CheckForIntersections(imbd, imfd, imld, imrd, current_direction);
		if(intersection.direction == 'n')
		{
			current_direction = 'f';
			intersection = CheckForIntersections(imbd, imfd, imld, imrd, current_direction);
		}

		current_direction = intersection.direction;

		if (current_direction == 'n')
		{
			cout << "THE DRONE HAS NOWHERE ELSE TO GO! NAVIGATION COMPLETE!" << endl;
			exit(0);
		}

	}

	dpos.push_back(Navi::INTERSECTION_INDICATOR);

	//don't want to go back down this way when we travel back to the
	// intersection
	ddirec.push_back(current_direction);
}

/*
	This just makes sure the user is ready
*/
void Navi::ReadyToReadScan()
{
	char scan_ready;
	cout << "Ready for next scan (y for yes; e for exit)" << endl;
	bool scan_ready_loop = true;
	while(scan_ready_loop)
	{
		cin >> scan_ready;
		switch(scan_ready)
		{
			case 'y':
				scan_ready_loop = false;
				break;

			case 'e':
				exit(0);
				break;

			default:
				cout << "FOOL, DO NOT TEST ME!" << endl;
		}
	}
}

double ** Navi::ReadScanFile(string fn)
{
	ifstream fin;
	double x_info;
	double y_info;
	double z_info;

	double *node_info = new double[2];
	double *max_forward_x = NULL;
	double *max_backward_x = NULL;
	double *max_left_y = NULL;
	double *max_right_y = NULL;

	string info_line;

	string junk;
	double x_node_info;
	double y_node_info;

	fin.open(fn);

	//the first line is the "NODE" line, which should be discarded here
	getline(fin, info_line);

	istringstream iss(info_line);

	iss >> junk;
	iss >> x_node_info;
	iss >> y_node_info;
	node_info[0] = x_node_info;
	node_info[1] = y_node_info;

	//getting each x and y pair of data
	while( getline(fin, info_line) )
	{
		//cout << info_line << endl;

		istringstream iss(info_line);

		iss >> x_info;
		iss >> y_info;
		iss >> z_info;

		if( x_info < 0)
		{
			max_backward_x = CheckMaxCoord(x_info, y_info, 1, max_backward_x);
		}
		if( x_info >= 0 )
		{
			max_forward_x = CheckMaxCoord(x_info, y_info, 1, max_forward_x);
		}
		//TODO: THIS MIGHT BE BACKWARDS, need to check!!!!!!!!!!!!!!!!!!!!
		if( y_info >= 0 )
		{
			max_left_y = CheckMaxCoord(x_info, y_info, 0, max_left_y);
		}
		if( y_info < 0 )
		{
			max_right_y = CheckMaxCoord(x_info, y_info, 0, max_right_y);
		}
	}

	/*
	cout << "MBX: " << max_backward_x[0] << " " << max_backward_x[1] << endl;
	cout << "MFX: " << max_forward_x[0] << " " << max_forward_x[1] << endl;
	cout << "MLY: " << max_left_y[0] << " " << max_left_y[1] << endl;
	cout << "MRY: " << max_right_y[0] << " " << max_right_y[1] << endl;
	*/
	fin.close();

	double ** max_arrays = new double*[5];

	max_arrays[0] = node_info;
	max_arrays[1] = max_backward_x;
	max_arrays[2] = max_forward_x;
	max_arrays[3] = max_left_y;
	max_arrays[4] = max_right_y;

	return max_arrays;

}

/*
	check_x_or_y means which index it should be checking in the array:
		0 for x, 1 for y
*/
double * Navi::CheckMaxCoord(double x, double y, int check_x_or_y, double current_min[])
{
	double * new_min_array = new double[2];

	//if it is the first loop, need to set the min to current point
	if( current_min == NULL )
	{
		new_min_array[0] = x;
		new_min_array[1] = y;
		return new_min_array;
	}

	double check_min = current_min[check_x_or_y];

	new_min_array[0] = current_min[0];
	new_min_array[1] = current_min[1];

	//checking y
	if( check_x_or_y == 0 )
	{
		if( abs(check_min) > abs(x) )
		{
			new_min_array[0] = x;
			new_min_array[1] = y;
		}
	}
	//checking x
	if( check_x_or_y == 1 )
	{
		if( abs(check_min) > abs(y) )
		{
			new_min_array[0] = x;
			new_min_array[1] = y;
		}
	}
	delete[] current_min;

	return new_min_array;
}

DistAndDirec Navi::FindMaxDistance(double bd, double fd, double ld, double rd)
{
	/*
		I am putting mfd first because this would be the direction to move
		if there is a tie between the distances.
	*/
	double distance_array[4] = {fd, bd, ld, rd};

	double cur_max = 0;
	int best_index = -1;

	char direction = 'n';

	for(int i = 0; i < 4; i++)
	{
		if (abs(distance_array[i]) > cur_max)
		{
			cur_max = abs(distance_array[i]);
			best_index = i;
		}
	}

	switch(best_index)
	{
		case 0:
			direction = 'f';
			break;
		case 1:
			direction = 'b';
			break;
		case 2:
			direction = 'l';
			break;
		case 3:
			direction = 'r';
			break;
		default:
			cout << "This should be an intersection." << endl;
			break;

	}

	drone_dd_vals.distance = cur_max;
	drone_dd_vals.direction = direction;

	return drone_dd_vals;
}

/*
	cd = current direction
*/
DistAndDirec Navi::CheckForIntersections(double bd, double fd, double ld, double rd, char cd)
{
	intersection.distance = 0;
	intersection.direction = 'n';

	//We check for f and b here, because we don't want to go in the opposite direction
	// and loop between the two largest values.
	if( cd == 'f' || cd == 'b' )
	{
		//0, 0 is because we only want to look for mld and mrd
		drone_dd_vals = FindMaxDistance(0, 0, ld, rd);
		if (drone_dd_vals.distance >= Navi::INTERSECTION_THRESHOLD)
		{
			intersection.distance = drone_dd_vals.distance;
			intersection.direction = drone_dd_vals.direction;
		}
	}

	if( cd == 'l' || cd == 'r' )
	{
		//0, 0 is because we only want to look for mld and mrd
		drone_dd_vals = FindMaxDistance(bd, fd, 0, 0);
		if (drone_dd_vals.distance >= Navi::INTERSECTION_THRESHOLD)
		{
			intersection.distance = drone_dd_vals.distance;
			intersection.direction = drone_dd_vals.direction;
		}
	}

	return intersection;
}

double Navi::GetDistance(char cd)
{
	double current_distance;

	switch(cd)
	{
		case 'b':
			current_distance = abs(mbd);
			break;
		case 'f':
			current_distance = abs(mfd);
			break;
		case 'l':
			current_distance = abs(mld);
			break;
		case 'r':
			current_distance = abs(mrd);
			break;
		default:
			cout << "how" << endl;
			break;
	}

	return current_distance;
}

/*
	cd = current_direction
*/
char Navi::ReverseDirection(char cd)
{
	//new direction
	char nd;

	switch(cd)
	{
		case 'f':
			nd = 'b';
			break;
		case 'b':
			nd = 'f';
			break;
		case 'l':
			nd = 'r';
			break;
		case 'r':
			nd = 'l';
			break;
		default:
			cout << "STOP MESSING THINGS UP!" << endl;
			break;
	}

	return nd;
}

/*
	cd = current_direction
	cv = changed value
*/
//void ChangeDistance(char cd, double cv, double *mbd, double *mfd, double *mld, double *mrd)
void Navi::ChangeDistance(char cd, double cv, double * dist)
{
	//new value
	double nv;

	switch(cd)
	{
		case 'f':
			dist[1] = cv;
			break;
		case 'b':
			dist[0] = cv;
			break;
		case 'l':
			dist[2] = cv;
			break;
		case 'r':
			dist[3] = cv;
			break;
		default:
			cout << "STOP MESSING THINGS UP!" << endl;
			break;
	}

	//cout << cv << (*mfd) << (*mbd) << (*mld) << (*mrd) << endl;
}

bool Navi::IsValueInDpos(double *value)
{
	vector <double*> :: iterator dpit;
	for( dpit = dpos.begin(); dpit != dpos.end(); dpit++)
	{
		cout << (*dpit)[0] << " " << (*dpit)[1] << " " << value[0] << " " << value[1] << endl;
		if( (*dpit)[0] == value[0] && (*dpit)[1] == value[1] )
		{
			return true;
		}
	}
	return false;
}


#include <dlib/opencv.h>
#include <opencv2/highgui/highgui.hpp>
#include <dlib/image_processing/frontal_face_detector.h>
#include <dlib/image_processing/render_face_detections.h>
#include <dlib/image_processing.h>
#include <dlib/gui_widgets.h>
#include <dlib/image_io.h>
#include <unistd.h>

using namespace dlib;
using namespace std;
using namespace cv;

image_window win;
shape_predictor sp;
std::vector<cv::Point> righteye;
std::vector<cv::Point> lefteye;
char c;
cv::Point p;

double compute_EAR(std::vector<cv::Point> vec)
{

    double a = cv::norm(cv::Mat(vec[1]), cv::Mat(vec[5]));
    double b = cv::norm(cv::Mat(vec[2]), cv::Mat(vec[4]));
    double c = cv::norm(cv::Mat(vec[0]), cv::Mat(vec[3]));
    //compute EAR
    double ear = (a + b) / (2.0 * c);
    return ear;
}

int main()
{
    int frameCounter = 0;
    float threshold = 0.2;
    try {
        cv::VideoCapture cap(0);

        if (!cap.isOpened()) {
            cerr << "Unable to connect to camera" << endl;
            return 1;
        }
        cap.set(CAP_PROP_FRAME_WIDTH, 640); //use small resolution for fast processing
        cap.set(CAP_PROP_FRAME_HEIGHT, 480);
        cap.set(CAP_PROP_FPS, 30); //fps to 30

        // Load face detection and deserialize  face landmarks model.
        frontal_face_detector detector = get_frontal_face_detector();

        deserialize("../shape_predictor_68_face_landmarks.dat") >> sp;

        // Grab and process frames until the main window is closed by the user.
        while (!win.is_closed()) {
            // Grab a frame
            cv::Mat temp;
            if (!cap.read(temp)) {
                break;
            }

            cv_image<bgr_pixel> cimg(temp);
            full_object_detection shape;

            // Detect faces
            std::vector<rectangle> faces = detector(cimg);
            //cout << "Number of faces detected: " << faces.size() << endl;

            win.clear_overlay();
            win.set_image(cimg);

            // Find the pose of each face.
            if (faces.size() > 0) {

                shape = sp(cimg, faces[0]); //work only with 1 face

                for (int b = 36; b < 42; ++b) {
                    p.x = shape.part(b).x();
                    p.y = shape.part(b).y();
                    lefteye.push_back(p);
                }
                for (int b = 42; b < 48; ++b) {
                    p.x = shape.part(b).x();
                    p.y = shape.part(b).y();
                    righteye.push_back(p);
                }
                //Compute Eye aspect ration for eyes
                double right_ear = compute_EAR(righteye);
                double left_ear = compute_EAR(lefteye);

                //if the avarage eye aspect ratio of lef and right eye less than 0.2, the status is sleeping.
                if ((right_ear + left_ear) / 2 < threshold) {
                    frameCounter++;
                    if (frameCounter >= 27) {
                        cout << "SLEEP" << endl;
                        win.add_overlay(dlib::image_window::overlay_rect(faces[0], rgb_pixel(255, 255, 255), "SLEEP"));
                    }
                    else {
                        win.add_overlay(dlib::image_window::overlay_rect(faces[0], rgb_pixel(255, 255, 255), "drowsing"));
                    }
                }
                else {
                    win.add_overlay(dlib::image_window::overlay_rect(faces[0], rgb_pixel(255, 255, 255), "not drowsing"));
                    frameCounter = 0;
                }
                righteye.clear();
                lefteye.clear();

                win.add_overlay(render_face_detections(shape));

                c = (char)waitKey(30);
                if (c == 27)
                    break;
            }
        }
    }
    catch (serialization_error& e) {
        cout << "Check the path to dlib's default face landmarking model file to run this example." << endl;
        cout << "You can get it from the following URL: " << endl;
        cout << "   http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2" << endl;
        cout << endl
             << e.what() << endl;
    }
    catch (exception& e) {
        cout << e.what() << endl;
    }
}


#ifndef MPLLIBS_METAMONAD_V1_CONCAT_MAP_HPP
#define MPLLIBS_METAMONAD_V1_CONCAT_MAP_HPP

// Copyright Abel Sinkovics (abel@sinkovics.hu)  2013.
// Distributed under the Boost Software License, Version 1.0.
//    (See accompanying file LICENSE_1_0.txt or copy at
//          http://www.boost.org/LICENSE_1_0.txt)

#include <mpllibs/metamonad/v1/fwd/concat_map.hpp>
#include <mpllibs/metamonad/v1/concat.hpp>
#include <mpllibs/metamonad/v1/metafunction.hpp>

#include <boost/mpl/list.hpp>
#include <boost/mpl/fold.hpp>
#include <boost/mpl/transform_view.hpp>

namespace mpllibs
{
  namespace metamonad
  {
    namespace v1
    {
      MPLLIBS_V1_METAFUNCTION(concat_map, (L)(F))
      ((
        boost::mpl::fold<
          boost::mpl::transform_view<L, F>,
          boost::mpl::list<>,
          concat<>
        >
      ));
    }
  }
}

#endif


#include <boost/python/operators.hpp>
#include <boost/python/tuple.hpp>
#include <boost/python/module.hpp>
#include <boost/python/def.hpp>
#include <boost/python/class.hpp>
#include <boost/python/args.hpp>
#include <boost/python/return_value_policy.hpp>
#include <boost/python/copy_const_reference.hpp>
#include <boost/python/return_by_value.hpp>
#include <boost/python/return_internal_reference.hpp>
#include <boost/python/overloads.hpp>

#include "LRL_LatticeMatcher.h"
#include "LRL_ReadLatticeData.h"
#include "LRL_Cell.h"
#include "MatS6.h"
#include "LatticeConverter.h"
#include "S6.h"
#include "DC.h"

namespace cctbx { namespace uctbx { namespace lrl { namespace boost_python {

using namespace boost::python;
typedef return_internal_reference<> rir;

static void wrap()
{
  class_<LRL_ReadLatticeData>("LRL_ReadLatticeData")
    .def <void (LRL_ReadLatticeData::*)(const std::string&)>
      ("CellReader", &LRL_ReadLatticeData::CellReader)
    .def("GetLattice", &LRL_ReadLatticeData::GetLattice)
    .def("GetCell", &LRL_ReadLatticeData::GetCell)
  ;

  class_<LRL_Cell>("LRL_Cell")
    .def(self_ns::str(self_ns::self))
  ;

  class_<LRL_Cell_Degrees>("LRL_Cell_Degrees")
    .def(init<LRL_Cell>())
    .def(init<S6>()) // this works via an implicit cast to LRL_Cell
    .def(self_ns::str(self_ns::self))
  ;

  class_<LRL_LatticeMatcher>("LRL_LatticeMatcher")
    // Here we wrap overloaded instance methods (contrast to SellingReduceCell
    // below); thus we static_cast to a member pointer
    .def(
        "SetReferenceLattice",
        static_cast<void (LRL_LatticeMatcher::*)(const S6&)>(
            &LRL_LatticeMatcher::SetReferenceLattice
        )
    )
    .def(
        "MatchReference",
        static_cast<S6 (LRL_LatticeMatcher::*)(const S6&) const>(
            &LRL_LatticeMatcher::MatchReference
        )
    )
    .def(
        "MatchReference",
        static_cast<
          std::vector<S6> (LRL_LatticeMatcher::*)(const std::vector<S6>&) const
        >(&LRL_LatticeMatcher::MatchReference)
    )
    .def("GetMaxRadius", &LRL_LatticeMatcher::GetMaxRadius)
  ;

  class_<MatS6>("MatS6")
    .def(self_ns::str(self_ns::self))
    .def("Inverse", &MatS6::Inverse)
    .def(self_ns::self * other<S6>())
    .staticmethod("Inverse")
  ;

  class_<S6>("S6")
    .def(init<LRL_Cell>())
    .def(self_ns::str(self_ns::self))
  ;

  /*
  This wraps an overloaded static member function by static_casting
  the function pointer to the correct call signature
  */
  class_<LatticeConverter>("LatticeConverter")
    .def(
        "SellingReduceCell",
        static_cast<LRL_Cell (*)(const std::string&, const LRL_Cell&, MatS6&)>(
            &LatticeConverter::SellingReduceCell
        )
    )
    .def(
        "SellingReduceCell",
        static_cast<LRL_Cell (*)(const std::string&, const LRL_Cell&)>(
            &LatticeConverter::SellingReduceCell
        )
    )
    .staticmethod("SellingReduceCell")
  ;

  class_<DC>("DC")
    .def(init<S6>())
    .def("DistanceBetween", &DC::DistanceBetween)
    .staticmethod("DistanceBetween")
  ;

}

}}}} // namespace cctbx::uctbx::lrl::boost_python

BOOST_PYTHON_MODULE(cctbx_uctbx_lrl_ext)
{
  using namespace cctbx::uctbx::lrl::boost_python;
  using namespace boost::python;

  wrap();

}


// Copyright (c) 2014-2019, The ByteRub Project
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this list of
//    conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
//    of conditions and the following disclaimer in the documentation and/or other
//    materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors may be
//    used to endorse or promote products derived from this software without specific
//    prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers

#include <boost/regex.hpp>

#include "common/util.h"
#include "common/command_line.h"
#include "performance_tests.h"
#include "performance_utils.h"

// tests
#include "construct_tx.h"
#include "check_tx_signature.h"
#include "check_hash.h"
#include "cn_slow_hash.h"
#include "derive_public_key.h"
#include "derive_secret_key.h"
#include "ge_frombytes_vartime.h"
#include "ge_tobytes.h"
#include "generate_key_derivation.h"
#include "generate_key_image.h"
#include "generate_key_image_helper.h"
#include "generate_keypair.h"
#include "signature.h"
#include "is_out_to_acc.h"
#include "subaddress_expand.h"
#include "sc_reduce32.h"
#include "sc_check.h"
#include "cn_fast_hash.h"
#include "rct_mlsag.h"
#include "equality.h"
#include "range_proof.h"
#include "bulletproof.h"
#include "crypto_ops.h"
#include "multiexp.h"

namespace po = boost::program_options;

int main(int argc, char** argv)
{
  TRY_ENTRY();
  tools::on_startup();
  set_process_affinity(1);
  set_thread_high_priority();

  mlog_configure(mlog_get_default_log_path("performance_tests.log"), true);

  po::options_description desc_options("Command line options");
  const command_line::arg_descriptor<std::string> arg_filter = { "filter", "Regular expression filter for which tests to run" };
  const command_line::arg_descriptor<bool> arg_verbose = { "verbose", "Verbose output", false };
  const command_line::arg_descriptor<bool> arg_stats = { "stats", "Including statistics (min/median)", false };
  const command_line::arg_descriptor<unsigned> arg_loop_multiplier = { "loop-multiplier", "Run for that many times more loops", 1 };
  const command_line::arg_descriptor<std::string> arg_timings_database = { "timings-database", "Keep timings history in a file" };
  command_line::add_arg(desc_options, arg_filter);
  command_line::add_arg(desc_options, arg_verbose);
  command_line::add_arg(desc_options, arg_stats);
  command_line::add_arg(desc_options, arg_loop_multiplier);
  command_line::add_arg(desc_options, arg_timings_database);

  po::variables_map vm;
  bool r = command_line::handle_error_helper(desc_options, [&]()
  {
    po::store(po::parse_command_line(argc, argv, desc_options), vm);
    po::notify(vm);
    return true;
  });
  if (!r)
    return 1;

  const std::string filter = tools::glob_to_regex(command_line::get_arg(vm, arg_filter));
  const std::string timings_database = command_line::get_arg(vm, arg_timings_database);
  Params p;
  if (!timings_database.empty())
    p.td = TimingsDatabase(timings_database);
  p.verbose = command_line::get_arg(vm, arg_verbose);
  p.stats = command_line::get_arg(vm, arg_stats);
  p.loop_multiplier = command_line::get_arg(vm, arg_loop_multiplier);

  performance_timer timer;
  timer.start();

  TEST_PERFORMANCE3(filter, p, test_construct_tx, 1, 1, false);
  TEST_PERFORMANCE3(filter, p, test_construct_tx, 1, 2, false);
  TEST_PERFORMANCE3(filter, p, test_construct_tx, 1, 10, false);
  TEST_PERFORMANCE3(filter, p, test_construct_tx, 1, 100, false);
  TEST_PERFORMANCE3(filter, p, test_construct_tx, 1, 1000, false);

  TEST_PERFORMANCE3(filter, p, test_construct_tx, 2, 1, false);
  TEST_PERFORMANCE3(filter, p, test_construct_tx, 2, 2, false);
  TEST_PERFORMANCE3(filter, p, test_construct_tx, 2, 10, false);
  TEST_PERFORMANCE3(filter, p, test_construct_tx, 2, 100, false);

  TEST_PERFORMANCE3(filter, p, test_construct_tx, 10, 1, false);
  TEST_PERFORMANCE3(filter, p, test_construct_tx, 10, 2, false);
  TEST_PERFORMANCE3(filter, p, test_construct_tx, 10, 10, false);
  TEST_PERFORMANCE3(filter, p, test_construct_tx, 10, 100, false);

  TEST_PERFORMANCE3(filter, p, test_construct_tx, 100, 1, false);
  TEST_PERFORMANCE3(filter, p, test_construct_tx, 100, 2, false);
  TEST_PERFORMANCE3(filter, p, test_construct_tx, 100, 10, false);
  TEST_PERFORMANCE3(filter, p, test_construct_tx, 100, 100, false);

  TEST_PERFORMANCE3(filter, p, test_construct_tx, 2, 1, true);
  TEST_PERFORMANCE3(filter, p, test_construct_tx, 2, 2, true);
  TEST_PERFORMANCE3(filter, p, test_construct_tx, 2, 10, true);

  TEST_PERFORMANCE3(filter, p, test_construct_tx, 10, 1, true);
  TEST_PERFORMANCE3(filter, p, test_construct_tx, 10, 2, true);
  TEST_PERFORMANCE3(filter, p, test_construct_tx, 10, 10, true);

  TEST_PERFORMANCE3(filter, p, test_construct_tx, 100, 1, true);
  TEST_PERFORMANCE3(filter, p, test_construct_tx, 100, 2, true);
  TEST_PERFORMANCE3(filter, p, test_construct_tx, 100, 10, true);

  TEST_PERFORMANCE5(filter, p, test_construct_tx, 2, 1, true, rct::RangeProofPaddedBulletproof, 2);
  TEST_PERFORMANCE5(filter, p, test_construct_tx, 2, 2, true, rct::RangeProofPaddedBulletproof, 2);
  TEST_PERFORMANCE5(filter, p, test_construct_tx, 2, 10, true, rct::RangeProofPaddedBulletproof, 2);

  TEST_PERFORMANCE5(filter, p, test_construct_tx, 10, 1, true, rct::RangeProofPaddedBulletproof, 2);
  TEST_PERFORMANCE5(filter, p, test_construct_tx, 10, 2, true, rct::RangeProofPaddedBulletproof, 2);
  TEST_PERFORMANCE5(filter, p, test_construct_tx, 10, 10, true, rct::RangeProofPaddedBulletproof, 2);

  TEST_PERFORMANCE5(filter, p, test_construct_tx, 100, 1, true, rct::RangeProofPaddedBulletproof, 2);
  TEST_PERFORMANCE5(filter, p, test_construct_tx, 100, 2, true, rct::RangeProofPaddedBulletproof, 2);
  TEST_PERFORMANCE5(filter, p, test_construct_tx, 100, 10, true, rct::RangeProofPaddedBulletproof, 2);

  TEST_PERFORMANCE3(filter, p, test_check_tx_signature, 1, 2, false);
  TEST_PERFORMANCE3(filter, p, test_check_tx_signature, 2, 2, false);
  TEST_PERFORMANCE3(filter, p, test_check_tx_signature, 10, 2, false);
  TEST_PERFORMANCE3(filter, p, test_check_tx_signature, 100, 2, false);
  TEST_PERFORMANCE3(filter, p, test_check_tx_signature, 2, 10, false);

  TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 2, true, rct::RangeProofBorromean);
  TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 10, 2, true, rct::RangeProofBorromean);
  TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 100, 2, true, rct::RangeProofBorromean);
  TEST_PERFORMANCE4(filter, p, test_check_tx_signature, 2, 10, true, rct::RangeProofBorromean);

  TEST_PERFORMANCE5(filter, p, test_check_tx_signature, 2, 2, true, rct::RangeProofPaddedBulletproof, 2);
  TEST_PERFORMANCE5(filter, p, test_check_tx_signature, 2, 2, true, rct::RangeProofMultiOutputBulletproof, 2);
  TEST_PERFORMANCE5(filter, p, test_check_tx_signature, 10, 2, true, rct::RangeProofPaddedBulletproof, 2);
  TEST_PERFORMANCE5(filter, p, test_check_tx_signature, 10, 2, true, rct::RangeProofMultiOutputBulletproof, 2);
  TEST_PERFORMANCE5(filter, p, test_check_tx_signature, 100, 2, true, rct::RangeProofPaddedBulletproof, 2);
  TEST_PERFORMANCE5(filter, p, test_check_tx_signature, 100, 2, true, rct::RangeProofMultiOutputBulletproof, 2);
  TEST_PERFORMANCE5(filter, p, test_check_tx_signature, 2, 10, true, rct::RangeProofPaddedBulletproof, 2);
  TEST_PERFORMANCE5(filter, p, test_check_tx_signature, 2, 10, true, rct::RangeProofMultiOutputBulletproof, 2);

  TEST_PERFORMANCE3(filter, p, test_check_tx_signature_aggregated_bulletproofs, 2, 2, 64);
  TEST_PERFORMANCE3(filter, p, test_check_tx_signature_aggregated_bulletproofs, 10, 2, 64);
  TEST_PERFORMANCE3(filter, p, test_check_tx_signature_aggregated_bulletproofs, 100, 2, 64);
  TEST_PERFORMANCE3(filter, p, test_check_tx_signature_aggregated_bulletproofs, 2, 10, 64);

  TEST_PERFORMANCE4(filter, p, test_check_tx_signature_aggregated_bulletproofs, 2, 2, 62, 4);
  TEST_PERFORMANCE4(filter, p, test_check_tx_signature_aggregated_bulletproofs, 10, 2, 62, 4);
  TEST_PERFORMANCE4(filter, p, test_check_tx_signature_aggregated_bulletproofs, 2, 2, 56, 16);
  TEST_PERFORMANCE4(filter, p, test_check_tx_signature_aggregated_bulletproofs, 10, 2, 56, 16);

  TEST_PERFORMANCE4(filter, p, test_check_hash, 0, 1, 0, 1);
  TEST_PERFORMANCE4(filter, p, test_check_hash, 0, 0xffffffffffffffff, 0, 0xffffffffffffffff);
  TEST_PERFORMANCE4(filter, p, test_check_hash, 0, 0xffffffffffffffff, 0, 1);
  TEST_PERFORMANCE4(filter, p, test_check_hash, 1, 0, 1, 0);
  TEST_PERFORMANCE4(filter, p, test_check_hash, 1, 0, 0, 1);
  TEST_PERFORMANCE4(filter, p, test_check_hash, 0xffffffffffffffff, 0xffffffffffffffff, 0, 1);
  TEST_PERFORMANCE4(filter, p, test_check_hash, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff);

  TEST_PERFORMANCE0(filter, p, test_is_out_to_acc);
  TEST_PERFORMANCE0(filter, p, test_is_out_to_acc_precomp);
  TEST_PERFORMANCE0(filter, p, test_generate_key_image_helper);
  TEST_PERFORMANCE0(filter, p, test_generate_key_derivation);
  TEST_PERFORMANCE0(filter, p, test_generate_key_image);
  TEST_PERFORMANCE0(filter, p, test_derive_public_key);
  TEST_PERFORMANCE0(filter, p, test_derive_secret_key);
  TEST_PERFORMANCE0(filter, p, test_ge_frombytes_vartime);
  TEST_PERFORMANCE0(filter, p, test_ge_tobytes);
  TEST_PERFORMANCE0(filter, p, test_generate_keypair);
  TEST_PERFORMANCE0(filter, p, test_sc_reduce32);
  TEST_PERFORMANCE0(filter, p, test_sc_check);
  TEST_PERFORMANCE1(filter, p, test_signature, false);
  TEST_PERFORMANCE1(filter, p, test_signature, true);

  TEST_PERFORMANCE2(filter, p, test_wallet2_expand_subaddresses, 50, 200);

  TEST_PERFORMANCE1(filter, p, test_cn_slow_hash, 0);
  TEST_PERFORMANCE1(filter, p, test_cn_slow_hash, 1);
  TEST_PERFORMANCE1(filter, p, test_cn_slow_hash, 2);
  TEST_PERFORMANCE1(filter, p, test_cn_slow_hash, 4);
  TEST_PERFORMANCE1(filter, p, test_cn_fast_hash, 32);
  TEST_PERFORMANCE1(filter, p, test_cn_fast_hash, 16384);

  TEST_PERFORMANCE2(filter, p, test_ringct_mlsag, 11, false);
  TEST_PERFORMANCE2(filter, p, test_ringct_mlsag, 11, true);

  TEST_PERFORMANCE2(filter, p, test_equality, memcmp32, true);
  TEST_PERFORMANCE2(filter, p, test_equality, memcmp32, false);
  TEST_PERFORMANCE2(filter, p, test_equality, verify32, false);
  TEST_PERFORMANCE2(filter, p, test_equality, verify32, false);

  TEST_PERFORMANCE1(filter, p, test_range_proof, true);
  TEST_PERFORMANCE1(filter, p, test_range_proof, false);

  TEST_PERFORMANCE2(filter, p, test_bulletproof, true, 1); // 1 bulletproof with 1 amount
  TEST_PERFORMANCE2(filter, p, test_bulletproof, false, 1);

  TEST_PERFORMANCE2(filter, p, test_bulletproof, true, 2); // 1 bulletproof with 2 amounts
  TEST_PERFORMANCE2(filter, p, test_bulletproof, false, 2);

  TEST_PERFORMANCE2(filter, p, test_bulletproof, true, 15); // 1 bulletproof with 15 amounts
  TEST_PERFORMANCE2(filter, p, test_bulletproof, false, 15);

  TEST_PERFORMANCE6(filter, p, test_aggregated_bulletproof, false, 2, 1, 1, 0, 4);
  TEST_PERFORMANCE6(filter, p, test_aggregated_bulletproof, true, 2, 1, 1, 0, 4); // 4 proofs, each with 2 amounts
  TEST_PERFORMANCE6(filter, p, test_aggregated_bulletproof, false, 8, 1, 1, 0, 4);
  TEST_PERFORMANCE6(filter, p, test_aggregated_bulletproof, true, 8, 1, 1, 0, 4); // 4 proofs, each with 8 amounts
  TEST_PERFORMANCE6(filter, p, test_aggregated_bulletproof, false, 1, 1, 2, 0, 4);
  TEST_PERFORMANCE6(filter, p, test_aggregated_bulletproof, true, 1, 1, 2, 0, 4); // 4 proofs with 1, 2, 4, 8 amounts
  TEST_PERFORMANCE6(filter, p, test_aggregated_bulletproof, false, 1, 8, 1, 1, 4);
  TEST_PERFORMANCE6(filter, p, test_aggregated_bulletproof, true, 1, 8, 1, 1, 4); // 32 proofs, with 1, 2, 3, 4 amounts, 8 of each
  TEST_PERFORMANCE6(filter, p, test_aggregated_bulletproof, false, 2, 1, 1, 0, 64);
  TEST_PERFORMANCE6(filter, p, test_aggregated_bulletproof, true, 2, 1, 1, 0, 64); // 64 proof, each with 2 amounts

  TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_sc_add);
  TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_sc_sub);
  TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_sc_mul);
  TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_ge_add_raw);
  TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_ge_add_p3_p3);
  TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_addKeys);
  TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_scalarmultBase);
  TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_scalarmultKey);
  TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_scalarmultH);
  TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_scalarmult8);
  TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_ge_dsm_precomp);
  TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_ge_double_scalarmult_base_vartime);
  TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_ge_double_scalarmult_precomp_vartime);
  TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_ge_double_scalarmult_precomp_vartime2);
  TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_addKeys2);
  TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_addKeys3);
  TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_addKeys3_2);
  TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_isInMainSubgroup);
  TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_zeroCommitUncached);
  TEST_PERFORMANCE1(filter, p, test_crypto_ops, op_zeroCommitCached);

  TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 2);
  TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 4);
  TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 8);
  TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 16);
  TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 32);
  TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 64);
  TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 128);
  TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 256);
  TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 512);
  TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 1024);
  TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 2048);
  TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_bos_coster, 4096);

  TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus, 2);
  TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus, 4);
  TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus, 8);
  TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus, 16);
  TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus, 32);
  TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus, 64);
  TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus, 128);
  TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus, 256);
  TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus, 512);
  TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus, 1024);
  TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus, 2048);
  TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus, 4096);

  TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus_cached, 2);
  TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus_cached, 4);
  TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus_cached, 8);
  TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus_cached, 16);
  TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus_cached, 32);
  TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus_cached, 64);
  TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus_cached, 128);
  TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus_cached, 256);
  TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus_cached, 512);
  TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus_cached, 1024);
  TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus_cached, 2048);
  TEST_PERFORMANCE2(filter, p, test_multiexp, multiexp_straus_cached, 4096);

#if 1
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2, 1);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4, 2);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 8, 2);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 16, 3);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 32, 4);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 64, 4);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 128, 5);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 256, 6);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 512, 7);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 1024, 7);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2048, 8);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4096, 9);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2, 1);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4, 2);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 8, 2);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 16, 3);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 32, 4);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 64, 4);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 128, 5);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 256, 6);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 512, 7);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 1024, 7);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2048, 8);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4096, 9);
#else
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2, 1);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2, 2);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2, 3);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2, 4);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2, 5);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2, 6);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2, 7);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2, 8);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2, 9);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4, 1);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4, 2);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4, 3);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4, 4);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4, 5);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4, 6);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4, 7);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4, 8);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4, 9);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 8, 1);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 8, 2);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 8, 3);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 8, 4);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 8, 5);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 8, 6);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 8, 7);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 8, 8);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 8, 9);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 16, 1);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 16, 2);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 16, 3);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 16, 4);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 16, 5);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 16, 6);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 16, 7);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 16, 8);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 16, 9);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 32, 1);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 32, 2);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 32, 3);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 32, 4);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 32, 5);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 32, 6);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 32, 7);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 32, 8);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 32, 9);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 64, 1);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 64, 2);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 64, 3);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 64, 4);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 64, 5);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 64, 6);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 64, 7);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 64, 8);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 64, 9);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 128, 1);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 128, 2);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 128, 3);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 128, 4);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 128, 5);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 128, 6);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 128, 7);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 128, 8);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 128, 9);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 256, 1);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 256, 2);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 256, 3);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 256, 4);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 256, 5);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 256, 6);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 256, 7);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 256, 8);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 256, 9);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 512, 1);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 512, 2);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 512, 3);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 512, 4);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 512, 5);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 512, 6);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 512, 7);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 512, 8);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 512, 9);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 1024, 1);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 1024, 2);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 1024, 3);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 1024, 4);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 1024, 5);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 1024, 6);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 1024, 7);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 1024, 8);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 1024, 9);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2048, 1);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2048, 2);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2048, 3);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2048, 4);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2048, 5);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2048, 6);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2048, 7);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2048, 8);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 2048, 9);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4096, 1);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4096, 2);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4096, 3);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4096, 4);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4096, 5);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4096, 6);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4096, 7);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4096, 8);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger_cached, 4096, 9);

  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2, 1);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2, 2);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2, 3);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2, 4);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2, 5);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2, 6);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2, 7);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2, 8);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2, 9);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4, 1);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4, 2);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4, 3);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4, 4);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4, 5);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4, 6);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4, 7);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4, 8);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4, 9);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 8, 1);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 8, 2);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 8, 3);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 8, 4);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 8, 5);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 8, 6);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 8, 7);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 8, 8);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 8, 9);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 16, 1);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 16, 2);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 16, 3);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 16, 4);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 16, 5);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 16, 6);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 16, 7);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 16, 8);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 16, 9);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 32, 1);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 32, 2);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 32, 3);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 32, 4);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 32, 5);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 32, 6);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 32, 7);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 32, 8);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 32, 9);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 64, 1);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 64, 2);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 64, 3);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 64, 4);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 64, 5);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 64, 6);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 64, 7);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 64, 8);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 64, 9);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 128, 1);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 128, 2);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 128, 3);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 128, 4);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 128, 5);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 128, 6);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 128, 7);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 128, 8);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 128, 9);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 256, 1);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 256, 2);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 256, 3);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 256, 4);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 256, 5);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 256, 6);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 256, 7);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 256, 8);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 256, 9);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 512, 1);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 512, 2);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 512, 3);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 512, 4);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 512, 5);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 512, 6);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 512, 7);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 512, 8);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 512, 9);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 1024, 1);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 1024, 2);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 1024, 3);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 1024, 4);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 1024, 5);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 1024, 6);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 1024, 7);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 1024, 8);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 1024, 9);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2048, 1);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2048, 2);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2048, 3);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2048, 4);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2048, 5);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2048, 6);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2048, 7);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2048, 8);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 2048, 9);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4096, 1);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4096, 2);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4096, 3);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4096, 4);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4096, 5);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4096, 6);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4096, 7);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4096, 8);
  TEST_PERFORMANCE3(filter, p, test_multiexp, multiexp_pippenger, 4096, 9);
#endif

  std::cout << "Tests finished. Elapsed time: " << timer.elapsed_ms() / 1000 << " sec" << std::endl;

  return 0;
  CATCH_ENTRY_L0("main", 1);
}


//==================================================================================================
/*!
  @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_FUNCTION_FIRSTBITUNSET_HPP_INCLUDED
#define BOOST_SIMD_FUNCTION_FIRSTBITUNSET_HPP_INCLUDED

#if defined(DOXYGEN_ONLY)
namespace boost { namespace simd
{

  /*!
    @ingroup group-bitwise
    This function object returns the bit pattern in which the only bit set is
    the first bit unset (beginning with the least significant bit) in the parameter.

    @see  ffs,  firstbitset

    @par Header <boost/simd/function/firstbitunset.hpp>

    @par Example:

      @snippet firstbitunset.cpp firstbitunset

    @par Possible output:

      @snippet firstbitunset.txt firstbitunset

  **/
  as_integer_t<Value> firstbitunset(Value const& x);
} }
#endif

#include <boost/simd/function/scalar/firstbitunset.hpp>
#include <boost/simd/function/simd/firstbitunset.hpp>

#endif


/**
 * @author Alessandro Bianco
 */

/**
 * Unit tests for the DFN Ransac3D
 */

/**
 * @addtogroup DFNsTest
 * @{
 */

#include <catch.hpp>
#include <FeaturesMatching3D/Ransac3D.hpp>
#include <Converters/VisualPointFeatureVector3DToPclPointCloudConverter.hpp>
#include <Errors/Assert.hpp>

#include <boost/make_shared.hpp>

using namespace CDFF::DFN::FeaturesMatching3D;
using namespace Converters;
using namespace PoseWrapper;
using namespace VisualPointFeatureVector3DWrapper;
using namespace SupportTypes;

TEST_CASE( "DFN processing step succeeds (3D RANSAC)", "[process]" )
{
	// Prepare input data
	/*
	pcl::PointCloud<pcl::PointXYZ>::Ptr pointCloud = boost::make_shared<pcl::PointCloud<pcl::PointXYZ> >();
	pcl::PointCloud<FeatureType>::Ptr featureCloud = boost::make_shared<pcl::PointCloud<FeatureType> >();
	for (unsigned pointIndex = 0; pointIndex < 10; pointIndex++)
	{
		pointCloud->points.push_back(pcl::PointXYZ(0.1*pointIndex, -0.1*pointIndex, 0));
		FeatureType feature;
		feature.histogram[0] = 0.001*pointIndex;
		feature.histogram[1] = 0.002*pointIndex;
		int maxFeaturesNumber = static_cast<int>(MAX_FEATURES_NUMBER);
		for (int componentIndex = 0; componentIndex < maxFeaturesNumber; componentIndex++)
		{
			feature.histogram[componentIndex] = 0;
		}
		featureCloud->points.push_back(feature);
	}

	PointCloudWithFeatures sourceCloud;
	PointCloudWithFeatures sinkCloud;
	sourceCloud.pointCloud = pointCloud;
	sinkCloud.pointCloud = pointCloud;
	sourceCloud.featureCloud = featureCloud;
	sinkCloud.featureCloud = featureCloud;
	sourceCloud.descriptorSize = 2;
	sinkCloud.descriptorSize = 2;
	*/

	// Prepare simpler input data
	VisualPointFeatureVector3DPtr sourceSet = new VisualPointFeatureVector3D;
	VisualPointFeatureVector3DPtr sinkSet = new VisualPointFeatureVector3D;

	// Instantiate DFN
	Ransac3D *ransac = new Ransac3D;

	// Send input data to DFN
	ransac->sourceFeaturesInput(*sourceSet);
	ransac->sinkFeaturesInput(*sinkSet);

	// Run DFN
	ransac->process();

	// Query output data from DFN
	const Pose3D& output = ransac->transformOutput();

	REQUIRE(GetXPosition(output) == 0);
	REQUIRE(GetYPosition(output) == 0);
	REQUIRE(GetZPosition(output) == 0);
	REQUIRE(GetXOrientation(output) == 0);
	REQUIRE(GetYOrientation(output) == 0);
	REQUIRE(GetZOrientation(output) == 0);
	REQUIRE(GetWOrientation(output) == 0);

	// Cleanup
	delete ransac;
	delete sourceSet;
	delete sinkSet;
}

TEST_CASE( "DFN configuration succeeds (3D RANSAC)", "[configure]" )
{
	// Instantiate DFN
	Ransac3D *ransac = new Ransac3D;

	// Setup DFN
	ransac->setConfigurationFile("../tests/ConfigurationFiles/DFNs/FeaturesMatching3D/Ransac3D_Conf1.yaml");
	ransac->configure();
}

/** @} */


#pragma once

#include <Eigen/Dense>

namespace icarus
{
    template<typename RegisterBank>
    struct MPU9255
    {
        explicit MPU9255(RegisterBank * device);
        void initialize();
        void i2cBypass(bool enable);
        void read();

        float temperature() const
        {
            return mTemperature;
        }

        Eigen::Matrix<float, 3, 1> acceleration() const
        {
            return mAcceleration;
        }

        Eigen::Matrix<float, 3, 1> angularVelocity() const
        {
            return mAngularVelocity;
        }
    private:
        RegisterBank * mDevice;

        float mAccelerationScale;
        int mGyroscopeScaleRange;

        float mTemperature;
        Eigen::Matrix<float, 3, 1> mAcceleration;
        Eigen::Matrix<float, 3, 1> mAngularVelocity;
    };
}


#ifndef TEMPL_SYMBOLS_CONSTANTS_LOCATION_HPP
#define TEMPL_SYMBOLS_CONSTANTS_LOCATION_HPP

#include "../Constant.hpp"
#include <base/Point.hpp>
#include <vector>

#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>

namespace templ {
namespace symbols {
namespace constants {

/**
 * \class Location
 * \brief A location constant
 */
class Location : public Constant
{
public:
    typedef shared_ptr<Location> Ptr;
    typedef std::vector<Location> List;
    typedef std::vector<Ptr> PtrList;

    enum CoordinateType { LATLONG, CARTESIAN };

    friend class boost::serialization::access;

    Location();

    Location(const std::string& name, const base::Point& position =
            base::Point::Zero());

    Location(const std::string& name,
            const base::Point& position,
            double radius);

    virtual ~Location() = default;

    void setPosition(const base::Point& position) { mPosition = position; }

    /**
     * Get the current associated position with this constant
     */
    const base::Point& getPosition() const { return mPosition; }

    CoordinateType getCoordinateType() const { return mCoordinateType; }

    virtual std::string toString() const;
    virtual std::string toString(size_t indent) const;

    static std::string toString(const List& l, size_t indent = 0);

    static std::string toString(const PtrList& l, size_t indent = 0);

    /**
      * Get a location by name
      */
    static Location::Ptr get(const std::string& name);

    static Location::Ptr create(const Location& location);

    static Location::Ptr create(const std::string& name,
            const base::Point& position = base::Point::Zero());

    static Location::Ptr create(const std::string& name,
            double latitude, double longitude, const std::string& radius = "earth");

    static double getDistance(const Location& a, const Location& b);

    /**
     * Get the radius for locations, that are defined in LATLONG
     */
    double getRadius() const { return mRadius; }

    static double getSphericalDistance(const Location& a, const Location& b);

    template<class Archive>
    void save(Archive& ar, const unsigned int version) const
    {
        if(version >= 1)
        {
            ar & mCoordinateType;
            ar & mRadius;
        }
        ar & mPosition.x();
        ar & mPosition.y();
        ar & mPosition.z();
        ar & first;
        ar & second;
    }

    template<class Archive>
    void load(Archive& ar, const unsigned int version)
    {
        if(version >= 1)
        {
            ar & mCoordinateType;
            ar & mRadius;
        }

        ar & mPosition.x();
        ar & mPosition.y();
        ar & mPosition.z();
        ar & first;
        ar & second;
    }

    BOOST_SERIALIZATION_SPLIT_MEMBER();

protected:
    static PtrList msLocations;

private:
    base::Point mPosition;
    CoordinateType mCoordinateType;
    double mRadius;

};

//BOOST_SERIALIZATION_SHARED_PTR(Location);
//BOOST_CLASS_VERSION(Location,1);

} // end namespace constants
} // end namespace symbols
} // end namespace templ
#endif // TEMPL_SYMBOLS_CONSTANTS_LOCATION_HPP


#include "cnn/nodes.h"
#include "cnn/cnn.h"
#include "cnn/training.h"
#include "cnn/timing.h"
#include "cnn/rnn.h"
#include "cnn/gru.h"
#include "cnn/lstm.h"
#include "cnn/dict.h"
#include "cnn/expr.h"

#include <iostream>
#include <fstream>

#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>

#include <unordered_map>
#include <unordered_set>

using namespace std;
using namespace cnn;

cnn::real pdrop = 0.5;
bool eval = false;
bool verbose = false;
unsigned INPUT_DIM = 64;
unsigned HIDDEN_DIM = 150;
unsigned TAG_HIDDEN_DIM = 64;

unsigned INPUT_VOCAB_SIZE = 0;

cnn::Dict word_dict;

int kSOS;
int kEOS;
int kUNK;
int kNUM;

unordered_map<string, vector<cnn::real> > pretrained_embeddings;
vector<cnn::real> averaged_vec;
bool count_corr = false;

template<class T>
struct PairCmp
{
    bool operator()(const pair<string, T>& a, const pair<string, T>& b)
    {
        return a.second > b.second;
    }
};
class Example {
public:
    vector<vector<string> > words; //multiple sents
    vector<vector<int> > word_ids; //sent_ids
    float ylabel;
    float fraction;
    float open;
    float close;
    string date;
};

bool ReadExample(ifstream& fin, Example& example);

/**
* Read Word Embeddings and normalize them
* @param input_file pretrained embeddings file path
* */

void ReadEmbeddings(const string& input_file)
{
    ifstream fin(input_file);
    assert(fin);
    string line;
    bool first = true;
    while(getline(fin, line))
    {
        istringstream sin(line);
        string word;
        sin>>word;
        cnn::real value;
        vector<cnn::real> vecs;
        cnn::real sum = 0.000001;
        while(sin>>value)
        {
            vecs.push_back(value);
            sum += value * value;
        }
        if(first)
        {
            first = false;
            INPUT_DIM = vecs.size();
            averaged_vec.resize(INPUT_DIM, 0.0);
        }
        //normalize to one
        sum = sqrt(sum);
        for(int j = 0; j < INPUT_DIM; j++)
        {
            averaged_vec[j] += vecs[j];
            vecs[j] /= sum;
        }
        pretrained_embeddings[word] = vecs;
        word_dict.Convert(word);
    }
    fin.close();
    cnn::real sum = 0.000001;
    for(int j = 0; j < INPUT_DIM; j++)
    {
        averaged_vec[j] /= pretrained_embeddings.size();
        sum += averaged_vec[j] * averaged_vec[j];
    }
    sum = sqrt(sum);
    for(int j = 0; j < INPUT_DIM; j++)
    {
        averaged_vec[j] /= sum ;
    }
    cerr<<"Pretrained Embeddings: "<< pretrained_embeddings.size()<<endl;
}

void ReadCorpus(const string& input_file,
                vector<Example>& examples,
                cnn::Dict& wordDict,
                int& tlines,
                int& ttoks,
                bool isTrain = true,
                int cut_off = 3)
{
    ifstream fin(input_file);
    if(!fin) {
        cerr<<"Cannot open file: "<< input_file << endl;
        exit(0);
    }

    unordered_map<string, int> word_counts;
    int tokens = 0, types = 0;
    while(true)
    {
        Example example;
        if(!ReadExample(fin,  example)) break;
        tlines++;
        for(int t = 0; t< (int)example.words.size(); t++)
        {
            auto& title = example.words[t];
            if(isTrain)
            {
                for(int j = 0; j < (int)title.size(); j++)
                {
                    auto& word = title[j];
                    auto iter = word_counts.find(word);
                    if( iter == word_counts.end())
                    {
                        word_counts.insert(make_pair(word, 1));
                        types++;
                    }
                    else {
                        iter->second++;
                    }
                    tokens++;
                }
            }
            ttoks += title.size();
        }
        examples.push_back(example);
    }
    fin.close();

    if(isTrain)
    {
        cerr<<"[LOG] Before Pruning there are "<<  tokens << " words and " <<types<< " types."<<endl;
        std::vector<pair<string, float>> pairs;

        unordered_map<string, float> tf_counts;
        unordered_map<string, float> idf_counts;
        int total_titles = 0;
        for(unsigned sent_index = 0; sent_index < examples.size(); sent_index++)
        {
            auto& example = examples[sent_index];
            int num_titles = example.words.size();
            total_titles += num_titles;
            for(int title_index = 0; title_index < num_titles; title_index++)
            {
                unordered_map<string, float> temp_idf_counts;
                for(unsigned word_index = 0; word_index < example.words[title_index].size(); word_index++)
                {
                    const string& word = example.words[title_index][word_index];
                    tf_counts[word]++;
                    temp_idf_counts[word] = 1;
                }
                for(auto& item: temp_idf_counts)
                {
                    idf_counts[item.first]++;
                }
            }
        }
        for(auto& item: tf_counts)
        {
            float tf = item.second;
            float idf = idf_counts[item.first];
            //float tf_idf = (1.0 + std::log(tf)) * std::log( example.words.size()/idf );
            float tf_idf = (1.0 + std::log(tf)) * std::log( total_titles/idf );
            pairs.push_back(make_pair(item.first, tf_idf));
        }
        sort(pairs.begin(), pairs.end(), PairCmp<float>());
        /*for(auto p: pairs)
        {
            //cerr<< p.first << "\t" << p.second  << "\t" << tf_counts[p.first] << "\t" << idf_counts[p.first] << "\t" << example.words.size() << endl;
            cerr<< p.first << "\t" << p.second  << "\t" << tf_counts[p.first] << "\t" << idf_counts[p.first] << "\t" << total_titles << endl;
        }*/
        //int rank = pairs.size() * 0.4; //save 40% words
        int rank = pairs.size() * 0.4; //save 40% words
        float survive_value  =  pairs[rank].second;
        int remaining_tokens = 0, remaining_types = 0;
        for(auto iter = pairs.begin(); iter != pairs.end(); iter++)
        {
            if(iter->second > survive_value)
                //if(iter->second >= survive_value)
            {
                word_dict.Convert(iter->first);
                remaining_tokens += iter->second;
                remaining_types++;
            }
        }
        cerr<<"[LOG] After Pruning there are "<<  remaining_tokens << " words and " << remaining_types<< " types."<<endl;
        cerr<<"[LOG] WordDict Size: "<< word_dict.size() << endl;
        word_dict.Freeze();
        word_dict.SetUnk("|unk|");
    }

    for(unsigned sent_index = 0; sent_index < examples.size(); sent_index++)
    {
        auto& example = examples[sent_index];
        int num_titles = example.words.size();
        example.word_ids.resize(num_titles);

        for(int title_index = 0; title_index < num_titles; title_index++)
        {
            example.word_ids[title_index].resize( example.words[title_index].size() );

            for(unsigned word_index = 0; word_index < example.words[title_index].size(); word_index++)
            {
                int wordId = wordDict.Convert( example.words[title_index][word_index] );
                example.word_ids[title_index][word_index] = wordId;
                //cerr<< example.words[title_index][word_index]<<" ";
            }
            //cerr<<endl;
        }
        //cerr<< endl;
    }
    //cerr<<endl;
}
void outputExample(const Example& example)
{
    int num_titles = example.words.size();
    for(int title_index = 0; title_index < num_titles; title_index++)
    {

        /*for(unsigned word_index = 0; word_index < example.words[title_index].size(); word_index++)
        {
            cerr<< example.words[title_index][word_index]<<" ";
        }
        cerr<<endl;*/

        for(unsigned word_index = 0; word_index < example.words[title_index].size(); word_index++)
        {
            cerr<< word_dict.Convert(example.word_ids[title_index][word_index])<<" ";
        }
        cerr<<endl;
    }
    cerr<< endl;
}

void outputExamples(const vector<Example>& examples)
{
    for(unsigned sent_index = 0; sent_index < examples.size(); sent_index++)
    {
        cerr<<"sent: " << sent_index+1 << endl;
        outputExample(examples[sent_index]);
    }
}

struct TitleEncoder {
    LookupParameters* p_word;  //

    vector<Parameters*> p_output_w;
    vector<Parameters*> p_output_b;

    vector<Parameters*> p_res_w;

    Parameters* p_fed_o;
    Parameters* p_fed_o_b;

    int layers;
    explicit TitleEncoder(Model& model): layers(1)
    {
        p_word = model.add_lookup_parameters(INPUT_VOCAB_SIZE, {INPUT_DIM});

        p_output_w.resize(layers);
        p_output_b.resize(layers);
        p_res_w.resize(layers);
        for(int i = 0; i< layers; i++)
        {
            p_output_w[i] = model.add_parameters({HIDDEN_DIM, INPUT_DIM});
            p_output_b[i] = model.add_parameters({HIDDEN_DIM});
            p_res_w[i] = model.add_parameters({INPUT_DIM, HIDDEN_DIM});
        }

        p_fed_o = model.add_parameters({1, INPUT_DIM});
        p_fed_o_b = model.add_parameters({1}, 0.01);

        for( size_t  word_index = 0; word_index <  INPUT_VOCAB_SIZE; word_index++)
        {
            auto word_iter = pretrained_embeddings.find( word_dict.Convert(word_index) );
            if(word_iter != pretrained_embeddings.end())
            {
                ((LookupParameters*)p_word)->Initialize(word_index, word_iter->second);
            }
        }
        ((LookupParameters*)p_word)->Initialize(kUNK, averaged_vec);
        ((LookupParameters*)p_word)->Initialize(kNUM, averaged_vec);
    }

    Expression buildGraph(ComputationGraph&cg, const Example& example, float& cor)
    {
        float predicted_value = 0;
        return buildGraph(cg, example, cor, predicted_value);
    }

    Expression buildGraph(ComputationGraph&cg, const Example& example, float& cor, float& predicted_value)
    {
        vector<Expression> title_exps;

        for(size_t title_index = 0; title_index < example.words.size(); title_index++)
        {
            auto& insent = example.word_ids[title_index];

            const unsigned slen = insent.size();

            vector<Expression> i_words;

            //bool output = false;
            for (unsigned t = 0; t < insent.size(); ++t)
            {
                Expression i_word_t = lookup(cg, p_word, insent[t]);
                if (!eval) {
                    i_word_t = dropout(i_word_t, pdrop);
                }
                if(insent[t] != kUNK)//this way better
                {
                    i_words.push_back(i_word_t);
                }
            }
            if(i_words.size() > 1 )
            {
                Expression kmh_exp = kmh_ngram(concatenate_cols(i_words), 2);
                Expression non_linear_input = tanh(kmh_exp);
                title_exps.push_back( sum_cols(non_linear_input) );
            }
            else if(i_words.size()> 0) {
                title_exps.push_back(average(i_words));
            }
        }
        if(!eval)
        {
            float tdrop = pdrop;
            if(verbose)
                cerr<< title_exps.size() << "\t" << tdrop << endl;
            for(size_t title_index = 0; title_index < title_exps.size(); title_index++)
            {
                title_exps[title_index] = block_dropout(title_exps[title_index], tdrop);
            }
        }

        Expression example_rep =average(title_exps);
        vector<Expression> param_tW(layers), param_tb(layers), param_resW(layers);
        for(int i = 0; i< layers; i++)
        {
            param_tW[i] = parameter(cg, p_output_w[i]);
            param_tb[i] = parameter(cg, p_output_b[i]);
            param_resW[i] = parameter(cg, p_res_w[i]);
        }
        for(int i = 0; i < layers; i++)
        {
            //example_rep = tanh(param_tW[i] * example_rep + param_tb[i]) + example_rep;
            Expression example_rep_temp = rectify(param_tW[i] * example_rep + param_tb[i]);
            example_rep = param_resW[i] * example_rep_temp + example_rep;
        }
        Expression i_output = example_rep;

        Expression param_oW = parameter(cg, p_fed_o);
        Expression param_ob = parameter(cg, p_fed_o_b);
        Expression output_h = param_oW * i_output + param_ob;
        if(count_corr)
        {
            float example_value = as_scalar(cg.incremental_forward());
            //for wl: return predicted_value, note here is not the fraction, it is the real predicted price
            predicted_value = example.open *( 1+example_value/100 );
            cerr<< example_value << "\t" << example.ylabel<<endl;
            if( example_value >=0 && example.ylabel>=0 )
            {
                cor++;
            }
            else if ( example_value <0 && example.ylabel < 0 )
            {
                cor++;
            }
        }
        if(verbose)
        {
            cerr<<example.fraction << " ";
        }
        Expression gold_y = input(cg, &example.ylabel);
        Expression output_loss = squared_distance(output_h, gold_y);
        return output_loss;
    }
};

bool ReadExample(ifstream& fin, Example& example)
{
    string line;
    float open_price, close_price, fraction;
    int num_titles;
    string date;
    if(!getline(fin, line)) return false;
    istringstream sin(line);
    sin>>fraction>>num_titles>>open_price>>close_price>>date;
    //cout<<label_str<<", "<<num_titles<<endl;
    /*if(fraction>0.04)
        example.ylabel = 0.04;
    else if(fraction < -0.04)
        example.ylabel = -0.04;
    else*/
    example.ylabel = fraction * 100;
    //example.ylabel*=10;
    example.fraction = fraction;
    example.open = open_price;
    example.close = close_price;
    example.date = date;
    //cerr<< "date: " << line << endl;
    example.words.resize(num_titles);

    for(int i = 0; i< num_titles; i++)
    {
        if(!getline(fin, line))
            return false;
        istringstream wsin(line);
        string word;
        while(wsin>>word)
        {
            if(word == "|||") break;
            //if(word.size() >100) continue;
            example.words[i].push_back(word);
        }
    }
    return true;
    //copy(data.begin(), data.end(), ostream_iterator<cnn::real>(cout, " "));
    //cout<<endl;
}

void sampleExamples(vector<Example>& training_examples)
{
    vector<Example> newExamples;
    for(auto& example: training_examples)
    {
        if(example.words.size() > 10)
        {
            int repeat_times =  (int)(10 * cnn::rand01());
            //int repeat_times =  (int)(50 * cnn::rand01());
            for(int k = 0; k< repeat_times; k++)
            {
                Example new_example;
                new_example.ylabel = example.ylabel;
                int new_t = 0;
                for(size_t t = 0; t < example.words.size(); t++)
                {
                    if(cnn::rand01() < 0.5)
                    {
                        new_example.words.resize(new_t+1);
                        new_example.word_ids.resize(new_t+1);
                        new_example.words[new_t] = example.words[t];
                        //fixed a bug here: change new_t to t on the right
                        new_example.word_ids[new_t] = example.word_ids[t];
                        new_t ++;
                    }
                }
                newExamples.push_back(new_example);
            }
        }
    }
    training_examples.insert(training_examples.end(), newExamples.begin(), newExamples.end());
    //training_examples = newExamples;
}

int main(int argc, char** argv) {
    cerr<<"Command: ";
    for(int i = 0; i< argc; i++)
        cerr<<argv[i]<<" ";
    cerr<<endl;

    cnn::Initialize(argc, argv, 4);

    if (argc != 5 && argc != 6) {
        cerr << "Usage: " << argv[0] << " code vec train dev [model.params]\n";
        return 1;
    }
    kSOS = word_dict.Convert("<s>");
    kEOS = word_dict.Convert("</s>");
    kUNK = word_dict.Convert("|unk|");
    kNUM = word_dict.Convert("<num>"); //number
    //kNUM = word_dict.Convert("<NUM>"); //number

    string stock_code = argv[1];
    vector<Example> training_examples, dev_examples;

    int tlc = 0;
    int ttoks = 0;
    cerr << "Reading Pretrained Word Vectors ... " << argv[2] << endl;
    ReadEmbeddings(argv[2]);

    cerr << "Reading training data from " << argv[3] << "...\n";

    ReadCorpus(argv[3], training_examples, word_dict, tlc, ttoks, true, 1);
    cerr << tlc << " lines, " << ttoks << " tokens, " << word_dict.size() << " types\n";
    int pos_count = 0;
    for(auto& example: training_examples)
        pos_count+= (example.ylabel >= 0);
    cerr<< "Total Training Examples: " << training_examples.size() << endl;
    cerr<< "Positive Training Examples: " << pos_count << " " << pos_count/(float)training_examples.size() << endl;
    cerr<< "Negative Training Examples: " << training_examples.size() - pos_count << " "<< 1.0 - pos_count /(float)training_examples.size() << endl;
    INPUT_VOCAB_SIZE = word_dict.size();
    //exit(0);

    int dlc = 0;
    int dtoks = 0;
    cerr << "Reading dev data from " << argv[4] << "...\n";
    ReadCorpus(argv[4], dev_examples, word_dict, dlc, dtoks, false, 0);
    cerr << dlc << " lines, " << dtoks << " tokens\n";
    int pos_dev_count = 0;
    for(auto& example: dev_examples)
        pos_dev_count+= (example.ylabel >=0);
    cerr<< "Total Dev Examples: " << dev_examples.size() << endl;
    cerr<< "Positive DEV Examples: " << pos_dev_count << " " << pos_dev_count/(float)dev_examples.size() << endl;
    cerr<< "Negative DEV Examples: " << dev_examples.size() - pos_dev_count << " "<< 1.0 - pos_dev_count /(float)dev_examples.size() << endl;
    //outputExamples(dev_examples);
    //exit(0);

    ostringstream os;
    os << stock_code<<".hier.avg"
       << '_' << INPUT_DIM
       << '_' << TAG_HIDDEN_DIM
       << '_' << pdrop << "_pid." << getpid();

    const string fname = os.str();
    cerr << "Parameters will be written to: " << fname << endl;
    cnn::real best = 0;

    Model model;
    bool use_momentum = true;
    Trainer* sgd = nullptr;
    if (use_momentum)
    {
        //sgd = new MomentumSGDTrainer(&model);
        //sgd = new MomentumSGDTrainer(&model, 1e-4, 0.01);
        //sgd = new MomentumSGDTrainer(&model, 1e-4, 0.02);
        sgd = new MomentumSGDTrainer(&model, 1e-4, 0.01);
        //sgd = new MomentumSGDTrainer(&model, 1e-6, 0.01);
        sgd->eta_decay = 0.05;
        //sgd = new AdadeltaTrainer(&model); //lambda: 0.02, eta: 1e-9
        //sgd = new AdamTrainer(&model); //lambda: 0.02, eta: 1e-9
        //sgd->eta0 = 1e-3;
        //sgd->eta = 1e-3;
    }
    //sgd = new MomentumSGDTrainer(&model, 1e-3, 0.001);
    //sgd = new AdagradTrainer(&model);
    else
        sgd = new SimpleSGDTrainer(&model, 1e-4);
    //sgd = new SimpleSGDTrainer(&model);
    //sgd->clipping_enabled = false;

    TitleEncoder titleEncoder(model);
    int TEST_TIME = 1;//11;//11;//11; //11;//11;
    int MAX_TEST_TIME = TEST_TIME;//101;//TEST_TIME;

    if(argc == 6)
    {
        string fname = argv[5];
        ifstream in(fname);
        boost::archive::text_iarchive ia(in);
        ia >> model;

        cnn::real dcorr = 0;
        cnn::real dloss = 0;
        eval = true;
        count_corr = true;
        //verbose = true;
        int dev_index = 0;
        for (auto& sent : dev_examples) {
            dev_index ++;
            const int y = sent.ylabel;
            cnn::real ycorr = 0, yloss = 0;
            int pos_num = 0, neg_num = 0;
            int CUR_TEST_TIME = TEST_TIME;
            int total_ycorr = 0;
            vector<float> predicted_values;
            while(true)
            {
                ycorr = 0;
                if( pos_num + neg_num >= MAX_TEST_TIME )
                    break;
                for(int j = 0; j < CUR_TEST_TIME; j++)
                {
                    if(CUR_TEST_TIME == 1)
                        eval = true;
                    else
                        eval = false;
                    ComputationGraph cg;
                    float predicted_value = 0;
                    Expression y_pred =  titleEncoder.buildGraph(cg, sent, ycorr, predicted_value);
                    yloss += as_scalar(cg.incremental_forward());
                    predicted_values.push_back(predicted_value);
                    //for wl: print predicted price for every time, later we will get the averaged price
                    cerr<<"\t[predicted price]: "<< predicted_value << "\t" << sent.close << endl;
                }
                total_ycorr += ycorr;
                //cerr<< "begin " << dev_index << endl;
                if(y == 0)
                {
                    pos_num += CUR_TEST_TIME - ycorr;
                    neg_num += ycorr;
                }
                else {
                    pos_num += ycorr;
                    neg_num += CUR_TEST_TIME - ycorr;
                }
                if(abs(pos_num - neg_num)<= (pos_num+neg_num)/10)
                {
                    CUR_TEST_TIME = 10; //test more time
                }
                else
                {
                    break;
                }
            }
            dloss += yloss/(pos_num+neg_num);
            bool correct = false;
            if(total_ycorr>(pos_num+neg_num)/2)
                correct = true;
            float average_predicted_price = 0;
            for(auto price: predicted_values)
                average_predicted_price += price;
            average_predicted_price /= predicted_values.size();
            cerr<<"\t[average predicted price]: "<< average_predicted_price << "\t" << sent.close << endl;
            if(correct)
            {
                dcorr++;
                //for wl: gold label, predicted label, number of positive guess, number of negative guess
                cerr<<  dev_index << " " << sent.date << " "<< y <<" "<<y << " " << pos_num <<" "<< neg_num<< endl;
                cerr<<"[TEST NUM]: " << total_ycorr << "\t" << pos_num + neg_num << endl;
            }
            else {
                //for wl: same here
                cerr<< "[WRONG] " << dev_index <<" " << sent.date << " "<< y <<" "<<1-y << " " << pos_num <<" "<< neg_num<< endl;
                //outputExample(sent);
                cerr<<"[TEST NUM]: " << total_ycorr << "\t" << pos_num + neg_num << endl;
            }
        }
        cerr<<"Loss:"<< dloss/ dev_examples.size() << endl;
        cerr<<"Accuracy:"<< dcorr <<"/" << dev_examples.size()<<" "<< dcorr/dev_examples.size() << endl;
        return 0;
    }

    //sampleExamples(training_examples);
    cerr<<"Training Examples: " << training_examples.size() << endl;

    unsigned report_every_i = min(1000, int(training_examples.size()));
    //unsigned report_every_i = int(training_examples.size());
    unsigned si = training_examples.size();
    //unsigned si = 1000;
    vector<unsigned> order(training_examples.size());
    for (unsigned i = 0; i < order.size(); ++i) order[i] = i;

    bool first = true;
    int report = 0;
    unsigned lines = 0;
    float tcor = 0;
    while(1) {//you should kill the program by hand during training
        Timer iteration("completed in");
        cnn::real loss = 0;
        for (unsigned i = 0; i < report_every_i; ++i) {
            if (si == training_examples.size()) {
                //if (si == 1000) {
                si = 0;
                if (first) {
                    first = false;
                }
                else {
                    sgd->update_epoch();
                }
                cerr << "**SHUFFLE\n";
                shuffle(order.begin(), order.end(), *rndeng);
                cerr << "**finish shuffle\n";
            }
            if(order[si] == 0)
                verbose = true;
            else
                verbose = false;

            // build graph for this instance
            ComputationGraph cg;
            auto& sentx_y = training_examples[order[si]];
            const int y = sentx_y.ylabel;
            //cerr << "LINE: " << order[si] << endl;
            Expression y_pred = titleEncoder.buildGraph(cg, sentx_y, tcor);
            loss += as_scalar(cg.incremental_forward());
            cg.backward();
            sgd->update(1.0);
            ++si;
            ++lines;
        }
        sgd->status();
        cerr << " E = " << (loss / lines) <<" ";
        cerr.flush();

        // show score on dev data?
        report++;
        if (1) {
            cnn::real dloss = 0;
            cnn::real dcorr = 0;
            eval = true;
            MAX_TEST_TIME = TEST_TIME = 1;
            count_corr = true;

            for (auto& sent : dev_examples) {
                const int y = sent.ylabel;
                cnn::real ycorr = 0, yloss = 0;
                int pos_num = 0, neg_num = 0;
                int CUR_TEST_TIME = TEST_TIME;
                int total_ycorr = 0;
                while(true)
                {
                    ycorr = 0;
                    if( pos_num + neg_num >= MAX_TEST_TIME )
                        break;
                    for(int j = 0; j < CUR_TEST_TIME; j++)
                    {
                        if(CUR_TEST_TIME == 1)
                            eval = true;
                        else
                            eval = false;
                        //pdrop = 0.25;
                        ComputationGraph cg;
                        Expression y_pred =  titleEncoder.buildGraph(cg, sent, ycorr);
                        yloss += as_scalar(cg.incremental_forward());
                    }
                    total_ycorr += ycorr;
                    //cerr<< "begin " << dev_index << endl;
                    if(y == 0)
                    {
                        pos_num += CUR_TEST_TIME - ycorr;
                        neg_num += ycorr;
                    }
                    else {
                        pos_num += ycorr;
                        neg_num += CUR_TEST_TIME - ycorr;
                    }
                    if(abs(pos_num - neg_num)<= (pos_num+neg_num)/10)
                    {
                        CUR_TEST_TIME = 10; //test more time
                    }
                    else
                    {
                        break;
                    }
                }
                dloss += yloss/(pos_num+neg_num);
                bool correct = false;
                if(total_ycorr>(pos_num+neg_num)/2)
                    correct = true;
                if(correct)
                {
                    dcorr++;
                }
            }
            cnn::real acc = dcorr / dev_examples.size();
            //if (acc >= best) {
            if (acc > best) {
                best = acc;
                cerr<<"Exceed" << endl;
                ofstream out(fname);
                boost::archive::text_oarchive oa(out);
                oa << model;
            }
            cerr << "\n***DEV [epoch=" << (lines / (cnn::real)training_examples.size()) << "] E = " << (dloss / dev_examples.size())  << " acc=" << acc << ' '<<endl;
        }
        eval = false;
        count_corr = false;
    }
    delete sgd;
}