#include <iostream>

#include <Eigen/Sparse>

#include "scomplex/graph_utils.hpp"
#include "scomplex/path_snapper.hpp"
#include "scomplex/simplicial_complex.hpp"
#include "scomplex/types.hpp"

#include "KDTree.hpp"

namespace gsimp {

struct path_snapper::impl {
    friend class clusterer;

    KDTree point_tree;
    graph_t vertex_graph;  // defined in graph_utils.hpp
    std::shared_ptr< simplicial_complex > s_comp;

    impl(std::shared_ptr< simplicial_complex > p_sc) {
        s_comp = p_sc;
        auto points = s_comp->get_points();
        point_tree = KDTree(points);
        vertex_graph = calculate_one_skelleton_graph(*s_comp);
    };

    impl(simplicial_complex& sc) {
        s_comp = std::make_shared< simplicial_complex >(sc);
        auto points = s_comp->get_points();
        point_tree = KDTree(points);
        vertex_graph = calculate_one_skelleton_graph(*s_comp);
    }

    impl(std::vector< point_t >& pts, std::vector< cell_t >& cells) {
        s_comp = std::make_shared< simplicial_complex >(pts, cells);
        point_tree = KDTree(pts);
        vertex_graph = calculate_one_skelleton_graph(*s_comp);
    }

    ~impl(){};

    std::vector< size_t > snap_path(std::vector< point_t > path) {
        std::vector< size_t > way_points;
        for (point_t pt : path) {
            size_t pti = point_tree.nearest_index(pt);
            way_points.push_back(pti);
        }

        // std::cout << "processed path: \n";
        // for (size_t i = 0 ; i < way_points.size() ; ++i ) {
        //     for (auto c : path[i] ) std::cout << c << " ";
        //     auto pt = s_comp->get_point( way_points[i] );
        //     std::cout << " -> ";
        //     for (auto c : pt ) std::cout << c << " ";
        //     std::cout << "\n";
        // }
        auto snapped_path = complete_path(vertex_graph, way_points);
        return snapped_path;
    }

    std::vector< std::pair< cell_t, int > > index_pairs(
        std::vector< point_t > path) {
        auto vertex_path = snap_path(path);
        std::vector< std::pair< cell_t, int > > pair_seq;
        for (auto it = vertex_path.begin(); std::next(it) != vertex_path.end();
             ++it) {
            //
            //
            size_t src(*it), trg(*(std::next(it)));                //
            if (src < trg) {                                       //
                pair_seq.push_back(                                //
                    std::make_pair< std::vector< size_t >, int >(  //
                        std::vector< size_t >{src, trg}, 1));      //
            } else {                                               //
                pair_seq.push_back(                                //
                    std::make_pair< std::vector< size_t >, int >(  //
                        std::vector< size_t >{trg, src}, -1));     //
            }                                                      //
        }
        return pair_seq;
    }
};

path_snapper::path_snapper(std::shared_ptr< simplicial_complex > sc) {
    p_impl = std::make_shared< impl >(sc);
};

path_snapper::path_snapper(simplicial_complex& sc) {
    p_impl = std::make_shared< impl >(sc);
}

path_snapper::path_snapper(std::vector< point_t >& pts,
                           std::vector< cell_t >& cells) {
    p_impl = std::make_shared< impl >(pts, cells);
}

path_snapper::~path_snapper() {}

path_snapper::path_snapper(path_snapper& other) { p_impl = other.p_impl; }

path_snapper& path_snapper::operator=(const path_snapper& other) {
    p_impl = other.p_impl;
    return *this;
}

std::vector< point_t > path_snapper::snap_path_to_points(
    std::vector< point_t > path) {
    auto index_path = p_impl->snap_path(path);
    std::vector< point_t > point_path;
    for (size_t p : index_path)
        point_path.push_back(p_impl->s_comp->get_points().at(p));
    return point_path;
}

std::vector< size_t > path_snapper::snap_path_to_indices(
    std::vector< point_t > path) {
    return p_impl->snap_path(path);
}

chain_v path_snapper::snap_path_to_v_chain(std::vector< point_t > path) {
    auto pair_seq = p_impl->index_pairs(path);
    chain_v rep = p_impl->s_comp->new_v_chain(1);
    for (auto pair : pair_seq) {
        auto cell = std::get< 0 >(pair);
        if (cell[0] != cell[1]) {
            size_t index = p_impl->s_comp->cell_to_index(std::get< 0 >(pair));
            chain_val(rep, index) += std::get< 1 >(pair);
        }
    }
    return rep;
}
chain_t path_snapper::snap_path_to_chain(std::vector< point_t > path) {
    auto pair_seq = p_impl->index_pairs(path);
    chain_t rep = p_impl->s_comp->new_chain(1);
    for (auto pair : pair_seq) {
        auto cell = std::get< 0 >(pair);
        if (cell[0] != cell[1]) {
            size_t index = p_impl->s_comp->cell_to_index(std::get< 0 >(pair));
            chain_val(rep, index) += std::get< 1 >(pair);
        }
    }
    return rep;
}

std::vector< point_t > path_snapper::index_sequence_to_point(
    std::vector< size_t > ind_path) {
    std::vector< point_t > pt_path;
    for (size_t ind : ind_path)
        pt_path.push_back(p_impl->s_comp->get_points().at(ind));
    return pt_path;
}

std::vector< size_t > path_snapper::point_sequence_to_index(
    std::vector< point_t > pt_path) {
    std::vector< size_t > ind_path;
    for (point_t pt : pt_path) {
        ind_path.push_back(p_impl->point_tree.nearest_index(pt));
    }
    return ind_path;
}

chain_v path_snapper::index_sequence_to_v_chain(
    std::vector< size_t > ind_path) {
    auto it = ind_path.begin();
    chain_v chain = p_impl->s_comp->new_v_chain(1);
    for (; std::next(it) != ind_path.end(); ++it) {
        auto ind = p_impl->s_comp->cell_to_index({*it, *std::next(it)});
        chain_val(chain, ind) += (*it < *std::next(it)) ? 1 : -1;
    }
    return chain;
}

chain_t path_snapper::index_sequence_to_chain(std::vector< size_t > ind_path) {
    auto it = ind_path.begin();
    chain_t chain = p_impl->s_comp->new_chain(1);
    for (; std::next(it) != ind_path.end(); ++it) {
        auto ind = p_impl->s_comp->cell_to_index({*it, *std::next(it)});
        chain_val(chain, ind) += (*it < *std::next(it)) ? 1 : -1;
    }
    return chain;
}

chain_t path_snapper::point_sequence_to_chain(std::vector< point_t > pt_path) {
    return index_sequence_to_chain(point_sequence_to_index(pt_path));
}

std::shared_ptr< simplicial_complex > path_snapper::get_underlying_complex() {
    return p_impl->s_comp;
}
};  // namespace gsimp


#include "vtkDepthImageUtils.h"

#include "vtkNew.h"
#include "vtkSmartPointer.h"
#include "vtkPolyData.h"
#include "vtkMath.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkObjectFactory.h"
#include "vtkTransform.h"
#include "vtkMatrix4x4.h"
#include "vtkCamera.h"
#include "vtkImageData.h"
#include "vtkUnsignedCharArray.h"
#include <Eigen/Dense>

//-----------------------------------------------------------------------------
vtkStandardNewMacro(vtkDepthImageUtils);

//-----------------------------------------------------------------------------
vtkDepthImageUtils::vtkDepthImageUtils()
{

}

//-----------------------------------------------------------------------------
vtkDepthImageUtils::~vtkDepthImageUtils()
{
}

namespace {

  Eigen::Matrix4f toEigenMatrix(vtkMatrix4x4* mat)
  {
    Eigen::Matrix4f eigenMat;

    for (int i = 0; i < 4; ++i)
    {
      for (int j = 0; j < 4; ++j)
      {
        eigenMat(i, j) = static_cast<float>(mat->Element[i][j]);
      }
    }
    return eigenMat;
  }
}

//-----------------------------------------------------------------------------
void vtkDepthImageUtils::DepthBufferToDepthImage(vtkImageData* depthBuffer, vtkImageData* colorBuffer, vtkCamera* camera, vtkImageData* depthImage, vtkPoints* pts, vtkUnsignedCharArray* ptColors)
{
  depthImage->DeepCopy(depthBuffer);
  ptColors->SetNumberOfComponents(3);

  int imageWidth = depthImage->GetDimensions()[0];
  int imageHeight = depthImage->GetDimensions()[1];
  double aspectRatio = static_cast<double>(imageWidth)/imageHeight;

  float* depthData = static_cast<float*>(depthImage->GetScalarPointer(0, 0, 0));
  unsigned char* colorData = static_cast<unsigned char*>(colorBuffer->GetScalarPointer(0, 0, 0));

  Eigen::Matrix4f cameraToViewport = toEigenMatrix(camera->GetProjectionTransformMatrix(aspectRatio, 0, 1));
  Eigen::Matrix4f viewportToCamera = cameraToViewport.inverse();
  Eigen::Matrix4f worldToCamera = toEigenMatrix(camera->GetViewTransformMatrix());
  Eigen::Matrix4f cameraToWorld = worldToCamera.inverse();

  int ptr = 0;
  int colorPtr = 0;
  for (int y = 0; y < imageHeight; ++y)
  {
    for (int x = 0; x < imageWidth; ++x, ++ptr, colorPtr+=3)
    {
      float z = depthData[ptr];
      if (z == 1.0)
      {
        depthData[ptr] = std::numeric_limits<float>::quiet_NaN();
        continue;
      }

      Eigen::Vector4f ptToViewport(2*float(x)/imageWidth - 1,
                                  2*float(y)/imageHeight - 1,
                                  z,
                                  1.0);

      Eigen::Vector4f ptToCamera = viewportToCamera * ptToViewport;

      float w3 = 1.0f / ptToCamera[3];

      ptToCamera[0] *= w3;
      ptToCamera[1] *= w3;
      ptToCamera[2] *= w3;
      ptToCamera[3] = 1.0;

      Eigen::Vector4f ptToWorld = cameraToWorld * ptToCamera;
      depthData[ptr] = -ptToCamera[2];

      //pts->InsertNextPoint(ptToWorld[0], ptToWorld[1], ptToWorld[2]);
      pts->InsertNextPoint(ptToCamera[0], ptToCamera[1], ptToCamera[2]);
      ptColors->InsertNextTypedTuple(&colorData[colorPtr]);
    }
  }
}

//-----------------------------------------------------------------------------
void vtkDepthImageUtils::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
}


#pragma once
#include <opencv2/opencv.hpp>

#include <Eigen/Core>
#include <Eigen/LU>
#include <Eigen/Geometry>
#include <iostream>
#include <ctype.h>
#include <dirent.h>
#include <iostream>
#include <vector>
#include <string>
#include <utility>
#include <queue>

#define normalization
//#define win64
#define linux

#define same_id

const int G_land_num = 74;
const int G_train_pic_id_num = 3300;
const int G_nShape = 47;
const int G_nVerts = 11510;
const int G_nFaces = 11540;
const int G_test_num = 77;
const int G_iden_num = 77;
const int G_inner_land_num = 59;
const int G_line_num = 50;
const int G_jaw_land_num = 20;

const int G_left_eye_idx = 29;
const int G_right_eye_idx = 33;

struct Target_type {
	Eigen::VectorXf exp;
	Eigen::RowVector3f tslt;
	Eigen::Matrix3f rot;
	Eigen::MatrixX2f dis;

};

struct DataPoint
{
	cv::Mat image;
	std::vector<cv::Point2d> landmarks;
	//std::vector<cv::Point2d> init_shape;
	Target_type shape;
	Eigen::VectorXf user;
	Eigen::RowVector2f center;
	Eigen::MatrixX2f land_2d;
#ifdef posit
	float f;
#endif // posit
#ifdef normalization
	Eigen::MatrixX3f s;
#endif

	Eigen::VectorXi land_cor;
	float eye_dis;
};

void load_bldshps(Eigen::MatrixXf &bldshps, std::string &name);

void load_lv(std::string name, DataPoint &temp);

void get_mesh2d_i(
	DataPoint &data, Eigen::MatrixX2f &mesh_2d_i, std::string path_over01_idx,
	Eigen::MatrixXf &bldshps, Eigen::VectorXi &over01_idx);

void get_mesh2d_i_spfbds(
	DataPoint &data, Eigen::MatrixX2f &mesh_2d_i, std::string path_over01_idx,
	Eigen::MatrixXf &spf_bldshps, Eigen::VectorXi &over01_idx);

double dis_cv_pt(cv::Point2d pointO, cv::Point2d pointA);

void cal_data_landmarks(DataPoint &data, Eigen::MatrixXf &bldshps);

void cal_data_landmarks_spfbds(DataPoint &data, Eigen::MatrixXf &spf_bldshps);

void cal_spf_bldshps(
	Eigen::MatrixXf &bldshps, DataPoint &data, Eigen::MatrixXf &result);


#include "TransformUtil.hpp"

#include <boost/python.hpp>

using namespace boost::python;

btScalar
calculate_diff_axis_angle_quaternion(const btQuaternion& orn0,const btQuaternion& orn1,btVector3& axis)
{
    btScalar angle;
    btTransformUtil::calculateDiffAxisAngleQuaternion(
        orn0, orn1, axis, angle
    );
    return angle;
}

btScalar
calculate_diff_axis_angle(const btTransform& t1,
                          const btTransform& t2,
                          btVector3& axis)
{
    btScalar angle;
    btTransformUtil::calculateDiffAxisAngle(t1, t2, axis, angle);
    return angle;
}

void defineTransformUtil()
{
    class_<btTransformUtil>("btTransformUtil")
        .def("integrate_transform", btTransformUtil::integrateTransform)
        .staticmethod("integrate_transform")
        .def("calculate_velocity_quaternion",
             btTransformUtil::calculateVelocityQuaternion)
        .staticmethod("calculate_velocity_quaternion")
        .def("calculate_diff_axis_angle_quaternion",
             calculate_diff_axis_angle_quaternion)
        .staticmethod("calculate_diff_axis_angle_quaternion")
        .def("calculate_velocity", btTransformUtil::calculateVelocity)
        .staticmethod("calculate_velocity")
        .def("calculate_diff_axis_angle",
             calculate_diff_axis_angle)
        .staticmethod("calculate_diff_axis_angle")
    ;
}


#include <boost/program_options.hpp>
#include <basix/e-lagrange.h>
#include <cmath>
#include <iostream>

#include <cuda_profiler_api.h>

// Helper functions
#include <cuda/allocator.hpp>
#include <cuda/array.hpp>
#include <cuda/la.hpp>
#include <cuda/spectral_mass.hpp>
#include <cuda/utils.hpp>

using namespace dolfinx;
namespace po = boost::program_options;

int main(int argc, char* argv[]) {
  po::options_description desc("Allowed options");
  desc.add_options()("help, h", "print usage message")(
    "size", po::value<std::size_t>()->default_value(32))(
    "degree", po::value<int>()->default_value(2));

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

  if (vm.count("help")){
    std::cout << desc << "\n";
    return 0;
  }

  const std::size_t Nx = vm["size"].as<std::size_t>();
  const int degree = vm["degree"].as<int>();

  common::subsystem::init_logging(argc, argv);
  common::subsystem::init_mpi(argc, argv);

  {

    // Create mesh and function space
    std::shared_ptr<mesh::Mesh> mesh = std::make_shared<mesh::Mesh>(
        mesh::create_box(MPI_COMM_WORLD, {{{0.0, 0.0, 0.0}, {1.0, 1.0, 1.0}}}, {Nx, Nx, Nx},
                         mesh::CellType::hexahedron, mesh::GhostMode::none));

    // Create a Basix continuous Lagrange element of a given degree
    basix::FiniteElement e = basix::element::create_lagrange(
        mesh::cell_type_to_basix_type(mesh::CellType::hexahedron), degree,
        basix::element::lagrange_variant::equispaced, false);

    // Create a scalar function space
    std::shared_ptr<fem::FunctionSpace> V
        = std::make_shared<fem::FunctionSpace>(fem::create_functionspace(mesh, e, 1));

    auto index_map = V->dofmap()->index_map;
    auto bs = V->dofmap()->index_map_bs();
    int ncells = mesh->topology().index_map(3)->size_local();
    int ndofs = e.dim();

    // Create input and output array
    CUDA::allocator<double> allocator{};
    la::Vector<double, decltype(allocator)> u(index_map, bs, allocator);
    la::Vector<double, decltype(allocator)> m(index_map, bs, allocator);
    std::fill(u.mutable_array().begin(), u.mutable_array().end(), 1.0);

    linalg::prefetch(0, u);
    linalg::prefetch(0, m);

    // Create mass operator
    SpectralMassOperator<double> op(V, degree);

    // Apply operator
    double t = MPI_Wtime();
    op.apply(u, m);
    t = MPI_Wtime() - t;

    std::cout << "Number of cells: " << op.num_cells() << "\n";
    std::cout << "Number of quads: " << op.num_quads() << "\n";
    std::cout << "Elapsed time: " << t << "\n";
    std::cout << "DOF/s: " << V->dofmap()->index_map->size_local() / t << "\n";
  }

  common::subsystem::finalize_mpi();
  return 0;
}


#include <stan/math/prim/mat.hpp>
#include <stan/math/prim/scal.hpp>
#include <test/unit/math/prim/prob/vector_rng_test_helper.hpp>
#include <test/unit/math/prim/prob/VectorIntRNGTestRig.hpp>
#include <boost/random/mersenne_twister.hpp>
#include <boost/math/distributions.hpp>
#include <gtest/gtest.h>
#include <limits>
#include <vector>

class BetaBinomialTestRig : public VectorIntRNGTestRig {
 public:
  BetaBinomialTestRig()
      : VectorIntRNGTestRig(10000, 10, {0, 1, 2, 3, 4, 5, 6}, {}, {0, 1, 3, 8},
                            {}, {-1, -5, -7}, {0.1, 1.7, 3.99}, {1, 2, 3},
                            {-2.1, -0.5, 0.0}, {-3, -1, 0}, {0.1, 1.1, 4.99},
                            {1, 2, 3}, {-3.0, -2.0, 0.0}, {-3, -1, 0}) {}

  template <typename T1, typename T2, typename T3, typename T_rng>
  auto generate_samples(const T1& N, const T2& alpha, const T3& beta,
                        T_rng& rng) const {
    return stan::math::beta_binomial_rng(N, alpha, beta, rng);
  }

  template <typename T1>
  double pmf(int y, T1 N, double alpha, double beta) const {
    if (y <= N) {
      return std::exp(stan::math::beta_binomial_lpmf(y, N, alpha, beta));
    } else {
      return 0.0;
    }
  }
};

TEST(ProbDistributionsBetaBinomial, errorCheck) {
  check_dist_throws_int_first_argument(BetaBinomialTestRig());
}

TEST(ProbDistributionsBetaBinomial, distributionCheck) {
  check_counts_int_real_real(BetaBinomialTestRig());
}

TEST(ProbDistributionBetaBinomial, error_check) {
  boost::random::mt19937 rng;
  EXPECT_NO_THROW(stan::math::beta_binomial_rng(4, 0.6, 2.0, rng));

  EXPECT_THROW(stan::math::beta_binomial_rng(-4, 0.6, 2, rng),
               std::domain_error);
  EXPECT_THROW(stan::math::beta_binomial_rng(4, -0.6, 2, rng),
               std::domain_error);
  EXPECT_THROW(stan::math::beta_binomial_rng(4, 0.6, -2, rng),
               std::domain_error);
  EXPECT_THROW(
      stan::math::beta_binomial_rng(4, stan::math::positive_infinity(), 2, rng),
      std::domain_error);
  EXPECT_THROW(stan::math::beta_binomial_rng(
                   4, 0.6, stan::math::positive_infinity(), rng),
               std::domain_error);
}


#include "NBC.h"
//#include <boost/algorithm/string.hpp>
struct dict {
    string word;
    double likelihood[NUM_CLASS];
};

struct dict_h {
    string word;
    double logodd;
};

int sortAccrodingto = 0;

int cmp(dict a, dict b)  {
    return a.likelihood[sortAccrodingto] > b.likelihood[sortAccrodingto];
}
int cmp_h(dict_h a, dict_h b)  {
    return a.logodd > b.logodd;
}

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

	NBC nbc;

	if (argc != 2){
		cout<< "Use . or .nostop. as the para" << endl;
		exit(1);
	}

	string trainFile = "data/8.trainingdata"+(string)argv[1]+"txt";
	string testFile = "data/8.testingdata"+(string)argv[1]+"txt";
	ofstream classout("word.txt");
	ifstream train(trainFile.c_str());
	ifstream test(testFile.c_str());

	for(int i = 0; i < 8; i++)
		nbc.numw_inclass[i] = 0;

	if(train.is_open() ){
		string line;
		int l =-1;
		vector<int> category;
		while (!train.eof()){
			getline(train, line, '\n');
			istringstream linestr(line);

			vector<string> words;
			string cate;
			linestr >> cate;

			string temp;
			while(linestr >> temp){
				int pos = temp.find(":");
				string word =  temp.substr(0, pos);
				int repeat_time = atoi(temp.substr(pos + 1, temp.length()).c_str());

				nbc.numw_inclass[atoi(cate.c_str())] += repeat_time;

				if(nbc.features.count(word) > 0){
					nbc.features[word].at(atoi(cate.c_str())) += repeat_time;
				}
				else{
					for(int k = 0; k < 8; k++)
						nbc.features[word].push_back(0);
					nbc.features[word].at(atoi(cate.c_str())) += repeat_time;
				}

				while(repeat_time--){
					words.push_back(word);
				}

			}

			nbc.ltrain.push_back(atoi(cate.c_str()));
			nbc.trainset.push_back(words);
		}
	}
	train.close();

	if(test.is_open()){
		string line;
		int l =-1;
		vector<int> category;
		while (!test.eof()){
			getline(test, line, '\n');
			istringstream linestr(line);

			vector<string> words;
			string cate;
			linestr >> cate;

			string temp;
			while(linestr >> temp){
				int pos = temp.find(":");
				string word =  temp.substr(0, pos);
				int repeat_time = atoi(temp.substr(pos + 1, temp.length()).c_str());
				while(repeat_time--){
					words.push_back(word);
				}
			}

			nbc.ltest.push_back(atoi(cate.c_str()));
			nbc.testset.push_back(words);
		}
	}
	test.close();

	// Start Training Naive Baysian Classifier
	nbc.Train(nbc.trainset, nbc.ltrain);
	nbc.Test(nbc.testset);

	list<dict> dictList;
	dict tmp;
	for (map<string,vector<double> >::iterator it = nbc.pTable.begin(); it!=nbc.pTable.end(); ++it){
        tmp.word = it->first;
		for(int i = 0; i < NUM_CLASS; i++){
			tmp.likelihood[i] = (it->second).at(i);
		}
		dictList.push_back(tmp);
    }

	for(int j = 0; j < 8; j++){
		sortAccrodingto = j;
		cout<<"The top 20 words with the highest likelihood in class "<<j<<" are:"<<endl;
		dictList.sort(cmp);
		int ct = 0;
		for (list<dict>::iterator it=dictList.begin(); it!=dictList.end(); ++it){
			cout<<it->word<<":"<<it->likelihood[sortAccrodingto]<<endl;
			if(ct < 19) {ct++; continue;}
			break;
		}
	}

//special code follows. very bad code . just for deadline
{
	list<dict_h> dicthList;
	dict_h tmph;
	int ct = 0;
	for (map<string,double >::iterator it = nbc.logodds1.begin(); it!=nbc.logodds1.end(); ++it){
        tmph.word = it->first;
		for(int i = 0; i < NUM_CLASS; i++){
			tmph.logodd = it->second;
		}
		dicthList.push_back(tmph);
    }
	cout<<"The top 20 words with the highest log-odd ratio for <5, 1> is:"<<endl;
	dicthList.sort(cmp_h);
	for (list<dict_h>::iterator it=dicthList.begin(); it!=dicthList.end(); ++it){
		cout<<it->word<<":"<<it->logodd<<endl;
		if(ct < 19) {ct++; continue;}
		break;
	}
}
{
	list<dict_h> dicthList;
	dict_h tmph;
	int ct = 0;
	for (map<string,double >::iterator it = nbc.logodds2.begin(); it!=nbc.logodds2.end(); ++it){
        tmph.word = it->first;
		for(int i = 0; i < NUM_CLASS; i++){
			tmph.logodd = it->second;
		}
		dicthList.push_back(tmph);
    }
	cout<<"The top 20 words with the highest log-odd ratio for <5, 4> is:"<<endl;
	dicthList.sort(cmp_h);
	for (list<dict_h>::iterator it=dicthList.begin(); it!=dicthList.end(); ++it){
		cout<<it->word<<":"<<it->logodd<<endl;
		if(ct < 19) {ct++; continue;}
		break;
	}
}
{
	list<dict_h> dicthList;
	dict_h tmph;
	int ct = 0;
	for (map<string,double >::iterator it = nbc.logodds3.begin(); it!=nbc.logodds3.end(); ++it){
        tmph.word = it->first;
		for(int i = 0; i < NUM_CLASS; i++){
			tmph.logodd = it->second;
		}
		dicthList.push_back(tmph);
    }
	cout<<"The top 20 words with the highest log-odd ratio for <0, 4> is:"<<endl;
	dicthList.sort(cmp_h);
	for (list<dict_h>::iterator it=dicthList.begin(); it!=dicthList.end(); ++it){
		cout<<it->word<<":"<<it->logodd<<endl;
		if(ct < 19) {ct++; continue;}
		break;
	}
}
{
	list<dict_h> dicthList;
	dict_h tmph;
	int ct = 0;
	for (map<string,double >::iterator it = nbc.logodds4.begin(); it!=nbc.logodds4.end(); ++it){
        tmph.word = it->first;
		for(int i = 0; i < NUM_CLASS; i++){
			tmph.logodd = it->second;
		}
		dicthList.push_back(tmph);
    }
	cout<<"The top 20 words with the highest log-odd ratio for <1, 7> is:"<<endl;
	dicthList.sort(cmp_h);
	for (list<dict_h>::iterator it=dicthList.begin(); it!=dicthList.end(); ++it){
		cout<<it->word<<":"<<it->logodd<<endl;
		if(ct < 19) {ct++; continue;}
		break;
	}
}

//special ends

/*
	for(int j = 0; j < 8; j++){
		sortAccrodingto = j;
		classout<<"The top words with the highest likelihood in class "<<j<<" are:"<<endl;
		dictList.sort(cmp);
		for (list<dict>::iterator it=dictList.begin(); it!=dictList.end(); ++it){
			classout<<it->word<<":"<<it->likelihood[sortAccrodingto]<<endl;
		}
	}
*/

	cout<<"The size of test data is "<<nbc.testset.size()<<endl;
}


/*
 * Bayes++ the Bayesian Filtering Library
 * Copyright (c) 2004 Michael Stevens
 * See accompanying Bayes++.html for terms and conditions of use.
 */

/*
 * matlabBfilter.h - Matlab MEX support for Bayes++
 * Defines Bayes++ filter functions for the mex enviroment of Matlab
 * Povides:
 *   Dymanic contructions with RTTI
 *	 Handle managment of dymanically contructed filter filters
 *   Implicitly definied Random number generators
 *	 Function object conversion
 * NOTE: Header file contains definitions!
 *  This makes sense as each MEX function is seperately compiled
 */

// Shut up VC++, it has its own problems!
#if defined(_MSC_VER) && _MSC_VER <= 1200
#pragma warning(disable:4786)	// indentifier more 255 chars
#endif

#include <typeinfo.h>
#include <string>
#include <map>

#include <mex.h>				// Matlab MEX suport
								// Need bounds check so we get exceptions
#define BOOST_UBLAS_BOUNDS_CHECK
#include "BayesFilter/allFilters.hpp" // Include all of Bayesian Filtering library
#include <boost/random.hpp>		// Fast and good random numbers

namespace FM = Bayesian_filter_matrix;
#include "matlabConvert.h"

class Run_Filter
/*
 * Runtime abstract filter : RTTI encapsulation for a filter
 *  By encapsulating all filter classes used at runtime we avoid the need for RTTI
 *  support in filtering library
 */
{
private:
	Run_Filter* signature_data;		// Use 'this' as Unique signature to check memory really is a Run_filter
public:
	Run_Filter()
	{	// Fill out signature
		signature_data = this;
	}
	virtual ~Run_Filter() = 0
	{	// Destory signature
		signature_data = 0;
	};

		// Matlab handle interface
	static Run_Filter* from_handle( const mxArray* ma );
	mxArray* make_handle();
	static void check_def(const Bayesian_filter::Bayes_filter_base* derived);
};

Run_Filter* Run_Filter::from_handle( const mxArray* fap )
/*
 * Get and check handle in mxArray of UINT32 realy is a pointer to a Run_filter
 */
{
	if (mxGetClassID(fap) != mxUINT32_CLASS || mxIsComplex(fap) || mxGetM(fap)!=1 || mxGetN(fap)!=1)
		mexErrMsgTxt("Filter <handle> is not of the type created by bfilter");

						// ASSUME we can store filter pointer in the mxUINT32 of fap
	Run_Filter* filter = *reinterpret_cast<Run_Filter**>(mxGetPr(fap));
						// Gross check to see we don't have an invalid pointer
	if (!filter)
		mexErrMsgTxt("Filter <handle> value does not represent a filter object");
						// Check memory has correct signature
	if (filter->signature_data != filter)
		mexErrMsgTxt("Filter <handle> value does not represent a filter object");
	return filter;
}

mxArray* Run_Filter::make_handle()
/*
 * Create a numeric array as handle for Run_filter
 * ASSUME  can store filter pointer in the mxUINT32 element of mxArray
 */
{
	mxArray* afp  = mxCreateNumericMatrix(1, 1, mxUINT32_CLASS, mxREAL);
	*reinterpret_cast<Run_Filter**>(mxGetPr(afp)) = this;
	return afp;
}

void Run_Filter::check_def(const Bayesian_filter::Bayes_filter_base* derived)
/*
 * Check that derived is defined. If it isn't generate error
 */
{
	std::string error;
	if (!derived) {
		error = std::string("Operation undefine on this filter type");
		mexErrMsgTxt( error.c_str() );
	}
}

/*
 * Runtime filter : Encapsulation for a filter schemse
 */
struct Run_SIR : public Run_Filter, public Bayesian_filter::SIR_filter
{
	Run_SIR(unsigned x, unsigned s, Bayesian_filter::SIR_random& r) :
		SIR_filter(x,s,r) {}
};

struct Run_SIR_kalman : public Run_Filter, public Bayesian_filter::SIR_kalman_filter
{
	Run_SIR_kalman(unsigned x, unsigned s, Bayesian_filter::SIR_random& r) :
		SIR_kalman_filter(x,s,r) {}
};

struct Run_Unscented : public Run_Filter, public Bayesian_filter::Unscented_filter
{
	Run_Unscented(unsigned x) : Unscented_filter(x) {}
};

struct Run_Cov : public Run_Filter, public Bayesian_filter::Covariance_filter
{
	Run_Cov(unsigned x) : Covariance_filter(x) {}
};

struct Run_Inf : public Run_Filter, public Bayesian_filter::Information_filter
{
	Run_Inf(unsigned x) : Information_filter(x) {}
};

struct Run_InfJo : public Run_Filter, public Bayesian_filter::Information_joseph_filter
{
	Run_InfJo(unsigned x) : Information_joseph_filter(x) {}
};

struct Run_SRIF : public Run_Filter, public Bayesian_filter::Information_root_filter
{
	Run_SRIF(unsigned x) : Information_root_filter(x) {}
};

struct Run_UD : public Run_Filter, public Bayesian_filter::UD_filter
{
	Run_UD(unsigned x) : UD_filter(x, x) {}
};

class Boost_random
/*
 * Random number distributions
 */
{
public:
	Boost_random() : gen_normal(rng), gen_uniform(rng)
	{
	}
	double normal(const double mean, const double sigma)
	{
		boost::normal_distribution<boost::mt19937> gen(rng, mean, sigma);
		return gen();
	}
	void normal(FM::Vec& v)
	{
		std::generate (v.begin(), v.end(), gen_normal);
	}
	void uniform_01(FM::Vec& v)
	{
		std::generate (v.begin(), v.end(), gen_uniform);
	}
private:
	boost::mt19937 rng;
	boost::normal_distribution<boost::mt19937> gen_normal;
	boost::uniform_01<boost::mt19937> gen_uniform;
};

// Maintain state of random numbers between function innovations
static Boost_random Random;

class Boost_SIR_random_helper : public Bayesian_filter::SIR_random
/*
 * Random number generator for SIR_filter
 * Uses global Random
 */
{
public:
	Boost_SIR_random_helper()
	{}
	void normal(FM::Vec& v)
	{
		::Random.normal(v);
	}
	void uniform_01(FM::Vec& v)
	{
		::Random.uniform_01(v);
	}
};

class Boost_Predict_random_helper : public Bayesian_filter::General_LiAd_predict_model::Random
/*
 * Random number generator for General_LiAd_predict_model
 * Uses global Random
 */
{
public:
	void normal(FM::Vec& v)
	{
		::Random.normal(v);
	}
};

class Matlab_function_model : public Bayesian_filter::Function_model
/*
 * Function model wrapper for a MuPad function
 */
{
public:
	Matlab_function_model(const mxArray* fname);
	virtual const FM::Vec& fx(const FM::Vec& x) const;
	// Note: Reference return value as a speed optimisation, MUST be copied by caller.
private:
	char* function_name;
	mutable FM::Vec rfx;
	mutable mxArray *plhs[1], *prhs[1];
};

Matlab_function_model::Matlab_function_model(const mxArray* fname) :
	rfx(FM::Empty)
{
	function_name = Matlab_convert::cstring(fname);
    /* must be a string. */
	if (!function_name)
		mexErrMsgTxt("Function name must be a string");
	prhs[0] = 0;
}

const FM::Vec& Matlab_function_model::fx(const FM::Vec& x) const
{
					// Create x argument, assume size is constant
	if (!prhs[0])
		prhs[0] = mxCreateDoubleMatrix(x.size(),1, mxREAL);
	Matlab_convert::Array(prhs[0], x);

					// Call Matlab function by name
	int bad = mexCallMATLAB(1, plhs, 1, prhs, function_name);
	if (bad)
		mexErrMsgTxt( "Bad call to Matlab function");
	else {
		FM::Vec& fx = Matlab_convert::Vector(plhs[0]);
		mxDestroyArray(plhs[0]);

		rfx.resize(fx.size());
		rfx.assign(fx);
	}
	return rfx;
};


/*
    Copyright 2005-2007 Adobe Systems Incorporated
    Distributed under the MIT License (see accompanying file LICENSE_1_0_0.txt
    or a copy at http://stlab.adobe.com/licenses.html)
*/

/*************************************************************************************************/

#ifndef ADOBE_NUMERIC_HPP
#define ADOBE_NUMERIC_HPP

#include <adobe/config.hpp>

#include <iterator>
#include <numeric>

#include <boost/bind.hpp>

// REVISIT (sparent): dissable warnings for unused arguments in boost 1.32.0
#if defined(__MWERKS__)
#pragma warn_unusedarg off
#endif

#include <boost/range.hpp>

#if defined(__MWERKS__)
#pragma warn_unusedarg reset
#endif

namespace adobe {
/*!
\defgroup numeric Numeric Algorithms
\ingroup algorithm

\brief
Extends the C++ standard numeric algorithms with \ref concept_convertible_to_range and \ref concept_convertible_to_function concepts.

\note
These extensions reside in the adobe:: namespace, not the std:: namespace.
*/

/*!
\defgroup max_adjacent_difference max_adjacent_difference
\ingroup numeric
 */

/*************************************************************************************************/

/*!
\ingroup max_adjacent_difference
*/
template <typename ForwardIterator>
ForwardIterator max_adjacent_difference(ForwardIterator first, ForwardIterator last)
{
    typedef typename std::iterator_traits<ForwardIterator>::value_type value_type;

    ForwardIterator result(first);

    if (first == last) return result;

    ForwardIterator previous(first);
    ++first;

    if (first == last) return result;

    value_type result_difference(*first - *previous);
    previous = first; ++first;

    while (first != last)
    {
        value_type difference(*first - *previous);

        if (result_difference < difference)
        {
            result_difference = difference;
            result = previous;
        }
        previous = first;
        ++first;
    }

    return result;
}

/*!
\ingroup max_adjacent_difference
*/
template <typename ForwardRange>
inline typename boost::range_iterator<ForwardRange>::type
        max_adjacent_difference(ForwardRange& range)
{
    return adobe::max_adjacent_difference(boost::begin(range), boost::end(range));
}

/*!
\ingroup max_adjacent_difference
*/
template <typename ForwardRange>
inline typename boost::range_const_iterator<ForwardRange>::type
        max_adjacent_difference(const ForwardRange& range)
{
    return adobe::max_adjacent_difference(boost::begin(range), boost::end(range));
}

/*************************************************************************************************/

// standard calls using bind and range

/*************************************************************************************************/

/*!
\defgroup accumulate accumulate
\ingroup numeric
 */

/*!
\ingroup accumulate
\see
	STL documentation for \ref stldoc_accumulate
*/
template <typename InputRange, typename T>
inline T accumulate(const InputRange& range, T init)
{
    return std::accumulate(boost::begin(range), boost::end(range), init);
}

/*!
\ingroup accumulate
\see
	STL documentation for \ref stldoc_accumulate
*/
template <typename InputIterator, typename T, typename BinaryOperation>
inline T accumulate(InputIterator first, InputIterator last, T init, BinaryOperation binary_op)
{
    return std::accumulate(first, last, init, boost::bind(binary_op, _1, _2));
}

/*!
\ingroup accumulate
\see
	STL documentation for \ref stldoc_accumulate
*/
template <typename InputRange, typename T, typename BinaryOperation>
inline T accumulate(const InputRange& range, T init, BinaryOperation binary_op)
{
    return adobe::accumulate(boost::begin(range), boost::end(range), init, binary_op);
}

/*!
\defgroup inner_product inner_product
\ingroup numeric
 */

/*!
\ingroup inner_product
\see
	STL documentation for \ref inner_product
*/
template <typename InputRange, typename InputIterator, typename T>
inline T inner_product(const InputRange& range, InputIterator first, T init)
{
    return std::inner_product(boost::begin(range), boost::end(range), first, init);
}

/*!
\ingroup inner_product
\see
	STL documentation for \ref inner_product
*/
template <typename InputIterator1, typename InputIterator2, typename T,
          typename BinaryOperation1, typename BinaryOperation2>
inline T inner_product(InputIterator1 first1, InputIterator1 last1, InputIterator2 first2, T init,
        BinaryOperation1 binary_op1, BinaryOperation2 binary_op2)
{
    return std::inner_product(first1, last1, first2, init, boost::bind(binary_op1, _1, _2),
            boost::bind(binary_op2, _1, _2));
}

/*!
\ingroup inner_product
\see
	STL documentation for \ref inner_product
*/
template <typename InputRange, typename InputIterator, typename T,
          typename BinaryOperation1, typename BinaryOperation2>
inline T inner_product(const InputRange& range, InputIterator first, T init,
        BinaryOperation1 binary_op1, BinaryOperation2 binary_op2)
{
    return adobe::inner_product(boost::begin(range), boost::end(range), first, init, binary_op1,
        binary_op2);
}

/*!
\defgroup partial_sum partial_sum
\ingroup numeric
 */

/*!
\ingroup partial_sum
\see
	STL documentation for \ref stldoc_partial_sum
*/
template <typename InputRange, typename OutputIterator>
inline OutputIterator partial_sum(const InputRange& range, OutputIterator result)
{
    return std::partial_sum(boost::begin(range), boost::end(range), result);
}

/*!
\ingroup partial_sum
\see
	STL documentation for \ref stldoc_partial_sum
*/
template <typename InputIterator, typename OutputIterator, typename BinaryOperation>
inline OutputIterator partial_sum(InputIterator first, InputIterator last, OutputIterator result,
        BinaryOperation binary_op)
{
    return std::partial_sum(first, last, result, boost::bind(binary_op, _1, _2));
}

/*!
\ingroup partial_sum
\see
	STL documentation for \ref stldoc_partial_sum
*/
template <typename InputRange, typename OutputIterator, typename BinaryOperation>
inline OutputIterator partial_sum(const InputRange& range, OutputIterator result,
        BinaryOperation binary_op)
{
    return adobe::partial_sum(boost::begin(range), boost::end(range), result, binary_op);
}

/*!
\defgroup adjacent_difference adjacent_difference
\ingroup numeric
 */
/*!
\ingroup adjacent_difference
\see
	STL documentation for \ref stldoc_adjacent_difference
*/
template <typename InputRange, typename OutputIterator>
inline OutputIterator adjacent_difference(const InputRange& range, OutputIterator result)
{
    return std::adjacent_difference(boost::begin(range), boost::end(range), result);
}

/*!
\ingroup adjacent_difference
\see
	STL documentation for \ref stldoc_adjacent_difference
*/
template <typename InputIterator, typename OutputIterator, typename BinaryOperation>
inline OutputIterator adjacent_difference(InputIterator first, InputIterator last,
        OutputIterator result, BinaryOperation binary_op)
{
    return std::adjacent_difference(first, last, result, boost::bind(binary_op, _1, _2));
}

/*!
\ingroup adjacent_difference
\see
	STL documentation for \ref stldoc_adjacent_difference
*/
template <typename InputRange, typename OutputIterator, typename BinaryOperation>
inline OutputIterator adjacent_difference(const InputRange& range, OutputIterator result,
        BinaryOperation binary_op)
{
    return adobe::adjacent_difference(boost::begin(range), boost::end(range), result, binary_op);
}

/*************************************************************************************************/

} // namespace adobe

/*************************************************************************************************/

#endif

/*************************************************************************************************/


//  Copyright (c) 2006-2009, Bernhard Reiter
//
//  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 <boost/tree/binary_tree.hpp>
#include <boost/tree/balancers/unbalanced.hpp>

#include <boost/tree/searcher.hpp>
#include <boost/tree/algorithm.hpp>

#define BOOST_TEST_MODULE key_search test
//#define BOOST_TEST_DYN_LINK
#include <boost/test/included/unit_test.hpp>

//TODO: Make this a test suite.

BOOST_AUTO_TEST_CASE( key_search_binary_tree_test )
{
    using namespace boost::tree;

    typedef test_searcher<false, balance<binary_tree<int>, balancers::unbalanced> > searcher_t;
    searcher_t my_tree;

    searcher_t::iterator c, c1, c2, c3, c4, c5;
    //std::pair<searcher_t::iterator, std::pair<bool, bool> > ret;

    c = my_tree.end();
    BOOST_CHECK_EQUAL(c, my_tree.end());
    BOOST_CHECK_EQUAL(c, my_tree.begin());

//    searcher_t::cursor cur = searcher_t::cursor(c);
//    BOOST_CHECK(cur.is_leaf());
//    BOOST_CHECK_EQUAL(cur, searcher_t::cursor(my_tree.end()));

    c1 = my_tree.insert(c, 8);

    BOOST_CHECK_EQUAL(*c1, 8);
//    BOOST_CHECK_EQUAL(searcher_t::cursor(c1).index(), 0);
    BOOST_CHECK(c != my_tree.end());
    BOOST_CHECK(c1 != my_tree.end());

//    cur = searcher_t::cursor(c1);
//    BOOST_CHECK((++cur).is_leaf());
//    BOOST_CHECK(cur.index());
//
//    cur = cur.parent(); //header-cursor(,1) (root)
//    BOOST_CHECK(!cur.index());
//    BOOST_CHECK_EQUAL(searcher_t::iterator(cur), my_tree.end());
    BOOST_CHECK(*c1 = 8);

    BOOST_CHECK_EQUAL(++c1, my_tree.end());

    --c1;
    BOOST_CHECK_EQUAL(*c1, 8);

//    BOOST_CHECK_EQUAL(searcher_t::cursor(my_tree.end()).index(), 1);
//
//    BOOST_CHECK_EQUAL(cur.end().index(), 1);
//
//    cur = searcher_t::cursor(c1);
//
//    BOOST_CHECK_EQUAL(*cur, 8);
//
//    BOOST_CHECK((++cur).is_leaf());
//    BOOST_CHECK(!(--cur).parent().index()); // root's index...
//
//    BOOST_CHECK_EQUAL(*(searcher_t::cursor(c).begin()), 8);

    BOOST_CHECK_EQUAL(*c1, 8);
    BOOST_CHECK_EQUAL(++c1, my_tree.end());

    // root (e.g. c) instead of c1 would crash this. but should that be really
    // illegal?
    c2 = my_tree.insert(c1, 18);

    BOOST_CHECK_EQUAL(*c2, 18);

    ++c2;
    BOOST_CHECK_EQUAL(c2, my_tree.end());

    c = my_tree.end();
    --c;
    BOOST_CHECK_EQUAL(*c, 18);

    c2 = my_tree.insert(c, 31);

    c2 = my_tree.insert(c, 412);
    c3 = my_tree.insert(c, 39);

    c4 = my_tree.insert(c, 7);

    BOOST_CHECK_EQUAL(*c4, 7);

    BOOST_CHECK_EQUAL(++c2, my_tree.end());
    c = my_tree.end();
    --c;

    BOOST_CHECK(*c != 39);
    BOOST_CHECK_EQUAL(*c, 412);
    --c;
    BOOST_CHECK_EQUAL(*c, 39);

    c = my_tree.begin();
//    BOOST_CHECK_EQUAL(searcher_t::cursor(c).index(), 0);
//    BOOST_CHECK(*(searcher_t::cursor(c).parent()) != 412);
    BOOST_CHECK(*c < 413);

//    searcher_t::container_type& the_tree = my_tree.container();
//    searcher_t::cursor tree_cur = boost::tree::lower_bound(the_tree.root(),
//        39, std::less<int>());
//
//    BOOST_CHECK(tree_cur.is_leaf());
//    BOOST_CHECK((++tree_cur).is_leaf());
//    --tree_cur;
//    BOOST_CHECK_EQUAL(*tree_cur, 39);
//
//    tree_cur = boost::tree::lower_bound(the_tree.root(), 18);
//    BOOST_CHECK_EQUAL(*tree_cur, 18);
//
//    tree_cur = boost::tree::lower_bound(the_tree.root(), 30);
//    BOOST_CHECK(tree_cur.is_leaf());
//    BOOST_CHECK(!(++tree_cur).is_leaf());
//    --tree_cur;
//    BOOST_CHECK_EQUAL(*tree_cur, 31);
//
//    tree_cur = boost::tree::lower_bound(the_tree.root(), 3);
//    BOOST_CHECK_EQUAL(*tree_cur, 7);

    c = my_tree.begin();
    BOOST_CHECK_EQUAL(*c++, 7);
    BOOST_CHECK_EQUAL(*c++, 8);
    BOOST_CHECK_EQUAL(*c++, 18);
    BOOST_CHECK_EQUAL(*c++, 31);
    BOOST_CHECK_EQUAL(*c++, 39);
    BOOST_CHECK_EQUAL(*c++, 412);
    BOOST_CHECK_EQUAL(c, my_tree.end());
    BOOST_CHECK_EQUAL(*--c, 412);
    BOOST_CHECK_EQUAL(*--c, 39);
    BOOST_CHECK_EQUAL(*--c, 31);
    BOOST_CHECK_EQUAL(*--c, 18);
    BOOST_CHECK_EQUAL(*--c, 8);
    BOOST_CHECK_EQUAL(*--c, 7);
    BOOST_CHECK_EQUAL(c, my_tree.begin());

    while (c != my_tree.end())
        ++c;

    ------c;
    BOOST_CHECK_EQUAL(*c, 31);

    //my_tree.erase(c);

    //BOOST_CHECK_EQUAL(*c, 39);

//    tree_cur = boost::tree::lower_bound(the_tree.root(), 412);
//    BOOST_CHECK_EQUAL(*tree_cur, 412);
//    BOOST_CHECK_EQUAL(*tree_cur.parent(), 18);
//
//    c = my_tree.begin();
//    BOOST_CHECK_EQUAL(*c++, 7);
//    BOOST_CHECK_EQUAL(*c++, 8);
//    BOOST_CHECK_EQUAL(*c++, 18);
//    BOOST_CHECK_EQUAL(*c++, 39);
//    BOOST_CHECK_EQUAL(*c++, 412);
//    BOOST_CHECK_EQUAL(c, my_tree.end());

}

//boost::unit_test::test_suite*
//init_unit_test_suite( int argc, char* argv[] )
//{
//    boost::unit_test::test_suite* key_search_test =
//            BOOST_TEST_SUITE( "Key search binary vector test" );
//
//    key_search_test->add( BOOST_TEST_CASE( &key_search_binary_tree_test ) );
//
//    return key_search_test;
//}


#include <examples/wordfreq/partition.h>

#define BOOST_TEST_MAIN
#define BOOST_TEST_DYN_LINK
#include <boost/test/unit_test.hpp>

#include <boost/assign/std/vector.hpp>

#include <math.h>
#include <stdio.h>

using namespace boost::assign;
using std::placeholders::_1;

/*
struct KeyCount
{
  int64_t key;
  int64_t count;
};
std::vector<KeyCount> g_histogram;
*/

// REQUIRE: keys and pivots are sorted
std::vector<int64_t> getHistogramFromKey(const std::vector<int64_t>& keys,
                                         const std::vector<int64_t>& pivots)
{
  std::vector<int64_t> result;
  result.resize(pivots.size());
  size_t j = 0;
  for (size_t i = 0; i < keys.size(); ++i)
  {
    while (keys[i] > pivots[j])
    {
      ++j;
      if (j >= pivots.size())
        goto end;
    }
    ++result[j];
  }
end:
  return result;
}

BOOST_AUTO_TEST_CASE(testGetHistogramFromKey)
{
  std::vector<int64_t> keys;
  std::vector<int64_t> pivots;
  std::vector<int64_t> expected;

  keys += 1, 2, 3, 4, 5;
  pivots += 1, 2, 3, 4, 5;
  expected += 1, 1, 1, 1, 1;
  std::vector<int64_t> histogram = getHistogramFromKey(keys, pivots);
  BOOST_CHECK(histogram == expected);

  pivots.clear(); expected.clear();
  pivots += 3;
  expected += 3;
  histogram = getHistogramFromKey(keys, pivots);
  BOOST_CHECK(histogram == expected);

  pivots.clear(); expected.clear();
  pivots += 6;
  expected += 5;
  histogram = getHistogramFromKey(keys, pivots);
  BOOST_CHECK(histogram == expected);

  pivots.clear(); expected.clear();
  pivots += 1, 3, 5;
  expected += 1, 2, 2;
  histogram = getHistogramFromKey(keys, pivots);
  BOOST_CHECK(histogram == expected);

  keys.clear();
  keys += 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 7;

  pivots.clear(); expected.clear();
  pivots += 1, 3, 5;
  expected += 2, 4, 4;
  histogram = getHistogramFromKey(keys, pivots);
  BOOST_CHECK(histogram == expected);

  pivots.clear(); expected.clear();
  pivots += 1, 3, 5, 6;
  expected += 2, 4, 4, 0;
  histogram = getHistogramFromKey(keys, pivots);
  BOOST_CHECK(histogram == expected);
}

BOOST_AUTO_TEST_CASE(test1)
{
  std::vector<int64_t> keys;
  keys += 1, 2, 3, 4, 5;

  std::vector<int64_t> pivots = wordfreq::partition(
      keys.back(), keys.size(), 1,
      std::bind(getHistogramFromKey, std::ref(keys), _1));

  BOOST_CHECK_EQUAL(pivots.size(), 1);
  BOOST_CHECK_EQUAL(pivots[0], 5);

  pivots = wordfreq::partition(
      keys.back(), keys.size(), 2,
      std::bind(getHistogramFromKey, std::ref(keys), _1));

  BOOST_CHECK_EQUAL(pivots.size(), 2);
  BOOST_CHECK_EQUAL(pivots[0], 2);
  BOOST_CHECK_EQUAL(pivots[1], 5);

  keys += 6;
  pivots = wordfreq::partition(
      keys.back(), keys.size(), 2,
      std::bind(getHistogramFromKey, std::ref(keys), _1));

  BOOST_CHECK_EQUAL(pivots.size(), 2);
  BOOST_CHECK_EQUAL(pivots[0], 3);
  BOOST_CHECK_EQUAL(pivots[1], 6);
}

BOOST_AUTO_TEST_CASE(testUniform)
{
  int maxKey = 1000 * 1000;
  int count = 10000;
  std::vector<int64_t> keys;
  keys.resize(count);
  for (int i = 0; i < count; ++i)
  {
    keys[i] = rand() % maxKey;
  }
  std::sort(keys.begin(), keys.end());

  std::vector<int64_t> pivots = wordfreq::partition(
      keys.back(), keys.size(), 1,
      std::bind(getHistogramFromKey, std::ref(keys), _1));

  BOOST_CHECK_EQUAL(pivots.size(), 1);
  BOOST_CHECK_EQUAL(pivots[0], keys.back());

  {
  pivots = wordfreq::partition(
      keys.back(), keys.size(), 2,
      std::bind(getHistogramFromKey, std::ref(keys), _1));

  BOOST_CHECK_EQUAL(pivots.size(), 2);
  size_t p0 = std::count_if(keys.begin(), keys.end(),
                            std::bind(std::less_equal<int64_t>(), _1, pivots[0]));
  double perWorker = static_cast<double>((keys.size() / pivots.size()));
  double skew = fabs(static_cast<double>(p0) / perWorker - 1.0);
  BOOST_CHECK(skew < 0.05);
  BOOST_CHECK_EQUAL(pivots[1], keys.back());
  }

  for (int nWorkers = 1; nWorkers <= 10; ++nWorkers)
  {
    pivots = wordfreq::partition(
        keys.back(), keys.size(), nWorkers,
        std::bind(getHistogramFromKey, std::ref(keys), _1));
    BOOST_CHECK_EQUAL(pivots.size(), nWorkers);

    double perWorker = static_cast<double>((keys.size() / pivots.size()));

    int64_t last = 0;
    // printf("workers: %d\n", nWorkers);
    for (int i = 0; i < nWorkers; ++i)
    {
      int64_t less_equal = upper_bound(keys.begin(), keys.end(), pivots[i]) - keys.begin();
      int64_t thiscount = less_equal - last;
      last = less_equal;
      double skew = (static_cast<double>(thiscount) / perWorker - 1.0);
      BOOST_CHECK(fabs(skew) < 0.05);
      // printf(" %3d: %f\n", i, skew);
    }
  }
}

BOOST_AUTO_TEST_CASE(testNormal)
{
  // Normal distribution
  // FIXME
}


#ifndef INC_MUSPELHEIM_IMAGE_HPP
#define INC_MUSPELHEIM_IMAGE_HPP

#include <boost/gil/image.hpp>

namespace images {

  template<typename Pixel>
  Pixel blend(const Pixel &a, const Pixel &b, double ratio) {
    Pixel p;
    for(size_t i = 0; i != boost::gil::size<Pixel>::value; i++)
      p[i] = a[i] * ratio + b[i] * (1 - ratio);
    return p;
  }

  template<typename ColorPixel, typename AlphaPixel>
  struct image_data {
    using color_pixel = ColorPixel;
    using color_image = boost::gil::image<color_pixel, false>;

    using alpha_pixel = AlphaPixel;
    using alpha_image = boost::gil::image<alpha_pixel, false>;

    template<typename Color, typename Alpha>
    image_data(Color &&color, Alpha &&alpha) :
      color(std::forward<Color>(color)),
      alpha(std::forward<Alpha>(alpha)) {}

    explicit image_data(const boost::gil::point2<ptrdiff_t> &dimensions) :
      color(dimensions, color_pixel(0), 0),
      alpha(dimensions, alpha_pixel(0), 0) {}

    auto dimensions() const {
      return color.dimensions();
    }

    color_image color;
    alpha_image alpha;
  };

  template<typename ColorPixel>
  using raw_image_data = image_data<ColorPixel, uint32_t>;
  template<typename ColorPixel>
  using cooked_image_data = image_data<ColorPixel, double>;

  namespace detail {
    inline auto do_linear_alpha(const boost::gil::image<uint32_t, false> &src) {
      using namespace boost::gil;

      image<double, false> dst(src.dimensions(), 0, 0);

      auto src_view = const_view(src);
      auto dst_view = view(dst);

      auto max_alpha = *std::max_element(src_view.begin(), src_view.end());
      for(size_t i = 0; i != dst_view.size(); i++)
        dst_view[i] = static_cast<double>(src_view[i]) / max_alpha;
      return dst;
    }

    inline auto do_log_alpha(const boost::gil::image<uint32_t, false> &src) {
      using namespace boost::gil;

      image<double, false> dst(src.dimensions(), 0, 0);

      auto src_view = const_view(src);
      auto dst_view = view(dst);

      auto max_alpha = *std::max_element(src_view.begin(), src_view.end());
      auto logmax = std::log(static_cast<double>(max_alpha));
      for(size_t i = 0; i != dst_view.size(); i++)
        dst_view[i] = std::log(static_cast<double>(src_view[i])) / logmax;
      return dst;
    }
  }

  template<typename ColorPixel>
  cooked_image_data<ColorPixel>
  linear_alpha(const raw_image_data<ColorPixel> &src) {
    return {src.color, detail::do_linear_alpha(src.alpha)};
  }

  template<typename ColorPixel>
  cooked_image_data<ColorPixel>
  linear_alpha(raw_image_data<ColorPixel> &&src) {
    return {std::move(src.color), detail::do_linear_alpha(src.alpha)};
  }

  template<typename ColorPixel>
  cooked_image_data<ColorPixel>
  log_alpha(const raw_image_data<ColorPixel> &src) {
    return {src.color, detail::do_log_alpha(src.alpha)};
  }

  template<typename ColorPixel>
  cooked_image_data<ColorPixel>
  log_alpha(raw_image_data<ColorPixel> &&src) {
    return {std::move(src.color), detail::do_log_alpha(src.alpha)};
  }

  template<typename View, typename ColorPixel>
  void render(View &dst, const cooked_image_data<ColorPixel> &src,
              double gamma = 1.0) {
    using namespace boost::gil;

    auto color = const_view(src.color);
    auto alpha = const_view(src.alpha);

    double inv_gamma = 1/gamma;
    for(size_t i = 0; i != dst.size(); i++) {
      assert(alpha[i] <= 1);
      auto a = std::pow(alpha[i], inv_gamma);
      auto &c = color[i];
      dst[i] = typename View::value_type(c[0] * a, c[1] * a, c[2] * a);
    }
  }

  template<typename View, typename ColorPixel>
  void render_monochrome(View &dst, const cooked_image_data<ColorPixel> &src,
                         const ColorPixel &color, double gamma = 1.0) {
    using namespace boost::gil;
    auto alpha = const_view(src.alpha);

    double inv_gamma = 1/gamma;
    for(size_t i = 0; i != dst.size(); i++) {
      assert(alpha[i] <= 1);
      auto a = std::pow(alpha[i], inv_gamma);
      for(size_t chan = 0; chan != boost::gil::size<ColorPixel>::value; chan++)
        dst[i][chan] = a * color[chan];
    }
  }

  template<typename Pixel>
  raw_image_data<Pixel>
  combine(const std::vector<raw_image_data<Pixel>> &srcs) {
    using namespace boost::gil;
    using image_data = raw_image_data<Pixel>;

    assert(!srcs.empty());
    image_data dst(srcs[0].dimensions());
    auto dst_color_view = view(dst.color);
    auto dst_alpha_view = view(dst.alpha);

    std::vector<typename image_data::color_image::const_view_t> src_color_views;
    std::vector<typename image_data::alpha_image::const_view_t> src_alpha_views;
    for(const auto &src : srcs) {
      src_color_views.push_back(const_view(src.color));
      src_alpha_views.push_back(const_view(src.alpha));
    }

    for(size_t px = 0; px != dst_color_view.size(); px++) {
      for(const auto &alpha : src_alpha_views)
        dst_alpha_view[px] += alpha[px];

      for(size_t s = 0; s != srcs.size(); s++) {
        double weight = static_cast<double>(src_alpha_views[s][px]) /
          dst_alpha_view[px];
        for(size_t chan = 0; chan != boost::gil::size<Pixel>::value; chan++)
          dst_color_view[px][chan] += src_color_views[s][px][chan] * weight;
      }
    }

    return dst;
  }

  template<typename View, typename ConstView>
  void lighten(View &a, const ConstView &b) {
    using namespace boost::gil;
    using dst_pixel = typename View::value_type;
    using channel_t = typename channel_type<dst_pixel>::type;
    auto max = channel_traits<channel_t>::max_value();

    for(size_t i = 0; i != a.size(); i++) {
      for(size_t chan = 0; chan != boost::gil::size<dst_pixel>::value; chan++)
        a[i][chan] = max - (max - a[i][chan]) * (max - b[i][chan]) / max;
    }
  }

} // namespace images

#endif


#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>

#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#include <map>
#include <algorithm>
#include <stdint.h>

#include "gflags/gflags.h"
#include "glog/logging.h"
#include <boost/shared_ptr.hpp>

#include "caffe/proto/caffe.pb.h"
#include "caffe/blob.hpp"
#include "caffe/util/benchmark.hpp"
#include "caffe/util/io.hpp"
#include "caffe/util/math_functions.hpp"
#include "caffe/util/rng.hpp"
#include "caffe/layer.hpp"
#include "caffe/layers/roi_pooling_layer.hpp"
#include "caffe/filler.hpp"

#include "gtest/gtest.h"
#include "caffe/test/test_caffe_main.hpp"
#include "caffe/test/test_gradient_check_util.hpp"

using namespace std;
using namespace caffe;

int main(int nargc, char** args) {

  // set caffe::GPU
  caffe::Caffe::SetDevice(0);
  caffe::Caffe::set_mode(caffe::Caffe::GPU);

  // define input & output blobs
  Blob<float>* input_0 = new Blob<float>(1, 2, 12, 12);
  Blob<float>* input_1 = new Blob<float>(1, 1, 2, 5);
  Blob<float>* output = new Blob<float>(2, 2, 6, 6);
  vector<Blob<float>*> bottom_vec;
  vector<Blob<float>*> top_vec;

  LOG(INFO) << "Step1...";

  // set the bottom blob data
  FillerParameter filler_param;
  filler_param.set_std(0.5);
  GaussianFiller<float> filler(filler_param);
  filler.Fill(input_0);

  LOG(INFO) << "Step2...";
  // set the ROI data
  float* roi_data = input_1->mutable_cpu_data();
  caffe_set(input_1->count(), (float)0, roi_data);
  int size = input_0->width() * 16;
  roi_data[0] = 0;
  roi_data[3] = 0.1 * size;
  roi_data[4] = 0.17 * size;
  roi_data[5] = 0.4 * size;
  roi_data[6] = 0.85 * size;
  roi_data[7+0] = 0;
  roi_data[7+3] = 0.38 * size;
  roi_data[7+4] = 0.02 * size;
  roi_data[7+5] = 0.79 * size;
  roi_data[7+6] = 0.9 * size;
  LOG(INFO) << "Step3...";
  // set the bottom and top data-pointer
  bottom_vec.push_back(input_0);
  bottom_vec.push_back(input_1);
  top_vec.push_back(output);

  // set the param of Align layer
  int roiResizedWidth = output->width();
  int roiResizedHeight = output->height();
  float spatial_scale = (float)1. / 16.;

  LOG(INFO) << "Step4...";
  // create reorg layer
  LayerParameter layer_param;
  layer_param.set_name("test_Roipool");
  layer_param.set_type("ROIPooling");
  ROIPoolingParameter* roi_pooling_param = layer_param.mutable_roi_pooling_param();
  roi_pooling_param->set_pooled_h(roiResizedHeight);
  roi_pooling_param->set_pooled_w(roiResizedWidth);
  roi_pooling_param->set_spatial_scale(spatial_scale);
  LOG(INFO) << "Step5...";

  // RoiAlignLayer<float> roi_align_layer(layer_param);
  boost::shared_ptr<Layer<float> > roi_pooling_layer = LayerRegistry<float>::CreateLayer(layer_param);
  LOG(INFO) << "Step6...";
  roi_pooling_layer->SetUp(bottom_vec, top_vec);

  LOG(INFO) << "Start Running...";

  // Runing the layer
  roi_pooling_layer->Reshape(bottom_vec, top_vec);
  roi_pooling_layer->Forward(bottom_vec, top_vec);

  GradientChecker<float> checker(1e-2, 1e-2);
  checker.CheckGradientExhaustive(roi_pooling_layer.get(), bottom_vec, top_vec);

  return 0;
}


#ifndef EIGEN_INC_HPP
#define EIGEN_INC_HPP

#ifndef STL_INC_HPP
#define STL_INC_HPP
#include <STL_INC.hpp>
#endif

#define EIGEN_VECTORIZE
#define EIGEN_DEFAULT_TO_ROW_MAJOR
#define EIGEN_HAVE_RVALUE_REFERENCES

#include <Eigen/Core>
#include <Eigen/Dense>
#include<Eigen/StdVector>

// DEFINE STORAGE ORDERS
#define C_Contiguous Eigen::RowMajor
#define F_Contiguous Eigen::ColMajor
// GENERIC ALIGNED
#define POSTMESH_ALIGNED C_Contiguous

// DYNAMIC MATRICES
#define DYNAMIC Eigen::Dynamic

// FOR EIGEN WRAPPER
//#define MAP_DATA // DEFAULT, NOT USED
//#define WRAP_DATA // UNCOMMENT THIS IF YOU WANT TO WRAP THE DATA

namespace Eigen {
// DEFINE Real, Integer AND UInteger BASED MATRICES
typedef Eigen::Matrix<Real,DYNAMIC,1,POSTMESH_ALIGNED> VectorR;
typedef Eigen::Matrix<Integer,DYNAMIC,1,POSTMESH_ALIGNED> VectorI;
typedef Eigen::Matrix<Real,1,DYNAMIC,POSTMESH_ALIGNED> RowVectorR;
typedef Eigen::Matrix<Integer,1,DYNAMIC,POSTMESH_ALIGNED> RowVectorI;
typedef Eigen::Matrix<Real,DYNAMIC,DYNAMIC,POSTMESH_ALIGNED> MatrixR;
typedef Eigen::Matrix<Integer,DYNAMIC,DYNAMIC,POSTMESH_ALIGNED> MatrixI;
typedef Eigen::Matrix<UInteger,DYNAMIC,DYNAMIC,POSTMESH_ALIGNED> MatrixUI;

#ifdef WRAP_DATA
// AN EIGEN MATRIX WRAPPER OVER RAW BUFFERS (WHILE USING THIS WRAPPER
// TO WRAP C-STYLE POINTERS DO NOT FREE THE MEMORY OWNED BY THE POINTER
// EXPLICITLY, AS THE OWNERSHIP IS TRANSFERRED TO EIGEN MATRIX AND HENCE
// EIGEN TAKES CARE OF DESTRUCTION, OTHERWISE YOU WILL END UP WITH DOUBLE
// FREE CORRUPTION ISSUE)
template<typename T> struct WrapRawBuffer {
    // NOTE THAT THIS WRAPPER TRANSFERS OWNRSHIP OF THE DATA SO
    // ANY RESOURCE MANAGING CLASS LIKE SHARED_PTR, UNIQUE_PTR,
    // STD::VECTOR OR EIGEN MATRIX WILL DELETE THE DATA

    // THIS IS JUST TO USE AN EXISTING PIECE OF MEMORY AND TRANSFER
    // IT TO EIGEN MATRIX WITHOUT MAKING COPIES. EIGEN DOES NOT PROVIDE
    // A WRAPPER OVER RAW BUFFERS AND PROBABLY NEVER WILL BECAUSE OF ABI
    // COMPATIBILITIES AND API CONSISTENCY.

    // DO NOT USE THIS WRAPPER IF THE DATA IS USED (OR BELONGS TO) THE
    // OUTSIDE WORLD (BEYOND THE TRANSLATION UNITS THAT IMPORT THIS),
    // FOR INSTANCE IF THE DATA IS COMMING FROM PYTHON NUMPY ARRAYS

    // WITH POSTMESH MEMBER DATA THIS WRAPPER HAS TO BE USED IN
    // CONJUNCTION WITH MOVE SEMANTICS

    T* data;
    Eigen::DenseIndex rows, cols;
    Eigen::Matrix<T, DYNAMIC, DYNAMIC, POSTMESH_ALIGNED>& asPostMeshMatrix(){
        return reinterpret_cast<Eigen::Matrix<T, DYNAMIC,
                DYNAMIC, POSTMESH_ALIGNED>&>(*this);
  }
  // NO DESTRUCTOR NEEDED, AS THEN WE WILL FREE THE MEMORY MORE THAN ONCE
//  ~WrapRawBuffer(){delete data;}
};
#endif

}

#endif // EIGEN_INC_HPP