#ifndef SHASTA_APPROXIMATE_TOPOLOGICAL_SORT_HPP
#define SHASTA_APPROXIMATE_TOPOLOGICAL_SORT_HPP

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

Approximate topological sort of a directed graph,
non necessarily acyclic.

The computed approximate topological sort of the given graph
is an exact topological sort for an acyclic graph
that uses a subset of the edges of the input graph.
Edges are added to this subset in the order specified
in the call, but edges that would cause a cycle are not added.

The topological sort of the acyclic graph is maintained dynamically
using the PK algorithm described in:
David. J. Pearce and Paul H. J. Kelly,
A Dynamic Topological Sort Algorithm for Directed Acyclic graphs,
ACM Journal of Experimental Algorithmics 11, 1-24 (2006).

Note that a C++ implementation of this algorithm
is available on GitHub:
https://github.com/DavePearce/DynamicTopologicalSort
However, this code was written from scratch using the algorithm
description in the paper.

This function is intrusive and requires the following
fields to exist in the vertices and edges of the input graph:
Vertices:
- color (an integer type), used for DFSs in the affected region.
- rank (an integer type), the computed topological sort order,
  beginning at 0.
Edge:
- isDagEdge (bool), set to true by this function for edges that contributed
  to the topological sort computations. The remaining
  edges have isDagEdge set to false. They were excluded because they caused cycles.

Note that the order in which the edges are processed determines which
edges are classified as causing cycles.
Therefore, the edges should be processed in decreasing order of "importance".
Edges processed first have lower chance of being flagged as causing cycles
and therefore excluded from the topological sort.

As an example, if the input graph consists of a single cycle,
Only the last edge processed will be classified as causing a cycle.

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

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/filtered_graph.hpp>
#include <boost/graph/iteration_macros.hpp>

#include <stack>
#include "vector.hpp"

namespace shasta {
    template<class Graph> class ApproximateTopologicalSortEdgePredicate;
    template<class Graph> void approximateTopologicalSort(
        Graph&,
        const vector<typename Graph::edge_descriptor>&);
}

// Predicate class used by function approximateTopologicalSort.
template<class Graph> class shasta::ApproximateTopologicalSortEdgePredicate {
public:
    ApproximateTopologicalSortEdgePredicate(const Graph& graph) : graph(&graph) {}
    const Graph* graph;
    bool operator()(const typename Graph::edge_descriptor e) const
    {
        return (*graph)[e].isDagEdge;
    }
};

template<class Graph> void shasta::approximateTopologicalSort(
    Graph& graph,
    const vector<typename Graph::edge_descriptor>& edgesToProcess)
{
    // Type names for readability.
    using vertex_descriptor = typename Graph::vertex_descriptor;
    using edge_descriptor = typename Graph::edge_descriptor;

    // Initialize.
    size_t rank = 0;
    BGL_FORALL_VERTICES_T(v, graph, Graph) {
        auto& vertex = graph[v];
        vertex.color = 0;
        vertex.rank = rank++;
    }
    BGL_FORALL_EDGES_T(e, graph, Graph) {
        graph[e].isDagEdge = false;
    }

    // We will work with a filtered graph that includes only the edges
    // that we have already processed and that did not introduce cycles.
    using EdgePredicate = ApproximateTopologicalSortEdgePredicate<Graph>;
    const EdgePredicate edgePredicate(graph);
    using FilteredGraph = boost::filtered_graph<Graph, EdgePredicate>;
    FilteredGraph filteredGraph(graph, edgePredicate);

    // Vectors to hold deltaF and deltaB
    // (see definition 2.6 in the paper).
    // Stored as pairs (rank, vertex descriptor).
    // Defined here outside the loop to reduce memory allocation activity.
    vector< pair<size_t, vertex_descriptor> > deltaF;
    vector< pair<size_t, vertex_descriptor> > deltaB;

    // Vector to hold the ranks of vertices in deltaF and deltaB.
    vector<size_t> deltaRanks;

    // Stack used for DFS's.
    std::stack<vertex_descriptor> vertexStack;

    // Loop over the given edges in the specified order.
    for(const edge_descriptor e: edgesToProcess) {
        const vertex_descriptor vX = source(e, graph);
        const vertex_descriptor vY = target(e, graph);
        auto& vertexX = graph[vX];
        auto& vertexY = graph[vY];

        // If this edge is consistent with the existing rank,
        // just mark the edge as belonging to the DAG.
        // The topological sort is not affected.
        if(vertexX.rank < vertexY.rank) {
            graph[e].isDagEdge = true;
            continue;
        }

        deltaRanks.clear();

        // Use a forward DFS starting at vY, and limited to the affected region,
        // to compute deltaF (see definition 2.6 in the paper).
        deltaF.clear();
        if(!vertexStack.empty()) {
            throw runtime_error("Vertex stack is not empty.");
        }
        vertexStack.push(vY);
        deltaF.push_back(make_pair(vertexY.rank, vY));
        deltaRanks.push_back(vertexY.rank);
        graph[vY].color = 1;
        while(!vertexStack.empty()) {
            const vertex_descriptor v0 = vertexStack.top();
            vertexStack.pop();
            BGL_FORALL_OUTEDGES_T(v0, e01, filteredGraph, FilteredGraph) {
                const vertex_descriptor v1 = target(e01, graph);
                auto& vertex1 = graph[v1];
                if(vertex1.rank > vertexX.rank) {
                    // Outside the affected region.
                    continue;
                }
                if(vertex1.color == 1) {
                    // We already have this vertex.
                    continue;
                }
                vertex1.color = 1;
                vertexStack.push(v1);
                deltaF.push_back(make_pair(vertex1.rank, v1));
                deltaRanks.push_back(vertex1.rank);
            }
        }
        sort(deltaF.begin(), deltaF.end());
        for(const auto& p: deltaF) {
            graph[p.second].color = 0;
        }

        // Use a backward DFS starting at vX, and limited to the affected region,
        // to compute deltaB (see definition 2.6 in the paper).
        deltaB.clear();
        if(!vertexStack.empty()) {
            throw runtime_error("Vertex stack is not empty.");
        }
        vertexStack.push(vX);
        deltaB.push_back(make_pair(vertexX.rank, vX));
        deltaRanks.push_back(vertexX.rank);
        graph[vX].color = 1;
        while(!vertexStack.empty()) {
            const vertex_descriptor v0 = vertexStack.top();
            vertexStack.pop();
            BGL_FORALL_INEDGES_T(v0, e01, filteredGraph, FilteredGraph) {
                const vertex_descriptor v1 = source(e01, graph);
                auto& vertex1 = graph[v1];
                if(vertex1.rank < vertexY.rank) {
                    // Outside the affected region.
                    continue;
                }
                if(vertex1.color == 1) {
                    // We already have this vertex.
                    continue;
                }
                vertex1.color = 1;
                vertexStack.push(v1);
                deltaB.push_back(make_pair(vertex1.rank, v1));
                deltaRanks.push_back(vertex1.rank);
            }
        }
        sort(deltaB.begin(), deltaB.end());
        for(const auto& p: deltaB) {
            graph[p.second].color = 0;
        }

        // Sort the ranks. If we find any duplicates, deltaF and deltaB
        // intersect, which means this edge introduces a cycle.
        // So we ignore it.
        sort(deltaRanks.begin(), deltaRanks.end());
        bool createsCycle = false;
        for(size_t i=1; i<deltaRanks.size(); i++) {
            if(deltaRanks[i-1] == deltaRanks[i]) {
                createsCycle = true;
                break;
            }
        }
        if(!createsCycle) {

            // This edge does not create a cycle.
            // redistribute the deltaRanks to vertices in deltaB and deltaF.
            size_t i = 0;
            for(const auto& p: deltaB) {
               graph[p.second].rank = deltaRanks[i++];
            }
            for(const auto& p: deltaF) {
               graph[p.second].rank = deltaRanks[i++];
            }
            graph[e].isDagEdge = true;
        }

    }
}

#endif


#pragma once

#include <types.hpp>
#include <Discregrid/mesh/triangle_mesh.hpp>

namespace std {
	template <> struct hash<Discregrid::Vector3r>
	{
		std::size_t operator()(Discregrid::Vector3r const& x) const
		{
			std::size_t seed = 0;
			std::hash<Discregrid::real> hasher;
			seed ^= hasher(x[0]) + 0x9e3779b9 + (seed<<6) + (seed>>2);
			seed ^= hasher(x[1]) + 0x9e3779b9 + (seed<<6) + (seed>>2);
			seed ^= hasher(x[2]) + 0x9e3779b9 + (seed<<6) + (seed>>2);
			return seed;
		}
	};

	template <> struct less<Discregrid::Vector3r>
	{
		bool operator()(Discregrid::Vector3r const& left, Discregrid::Vector3r const& right) const
		{
			for (auto i = 0u; i < 3u; ++i)
			{
				if (left(i) < right(i))
					return true;
				else if (left(i) > right(i))
					return false;
			}
			return false;
		}
	};
}
#include <Discregrid/utility/lru_cache.hpp>

#include <Discregrid/acceleration/bounding_sphere_hierarchy.hpp>

#include <array>
#include <vector>
#include <set>
#include <map>
#include <unordered_map>

#include <Eigen/Dense>

namespace Discregrid
{

enum class NearestEntity;
class TriangleMesh;
class Halfedge;
class MeshDistance
{

	struct Candidate
	{
		bool operator<(Candidate const& other) const { return b < other.b; }
		int node_index;
		real b, w;
	};

public:
	MeshDistance(const TriangleMesh* mesh, bool precompute_normals = true);

	// Returns the shortest unsigned distance from a given point x to
	// the stored mesh.
	// Thread-safe function.
	real distance(Vector3r const& x, Vector3r* nearest_point = nullptr,
		int* nearest_face = nullptr, NearestEntity* ne = nullptr) const;

	// Requires a closed two-manifold mesh as input data.
	// Thread-safe function.
	real signedDistance(Vector3r const& x,
                          Vector3r* nearest_point = nullptr, Vector3r* normal = nullptr) const;

	real unsignedDistance(Vector3r const& x) const;

private:

	Vector3r vertex_normal(int v) const;
	Vector3r edge_normal(Halfedge const& h) const;
	Vector3r face_normal(int f) const;

	void callback(int node_index, TriangleMeshBSH const& bsh,
		Vector3r const& x,
		real& dist, int& nearest_face) const;

	bool predicate(int node_index, TriangleMeshBSH const& bsh,
		Vector3r const& x, real& dist) const;

private:

	const TriangleMesh* m_mesh;
	TriangleMeshBSH m_bsh;

	using FunctionValueCache = LRUCache<Vector3r, real>;

	std::vector<Vector3r> m_face_normals;
	std::vector<Vector3r> m_vertex_normals;
	bool m_precomputed_normals;
};

}


///////////////////////////////////////////////////////////////////////////////
//  Copyright Christopher Kormanyos 2021.
//  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 <cstdint>
#include <ctime>
#include <random>
#include <sstream>
#include <string>

#if defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wconversion"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wsign-conversion"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif

#if (defined(__clang__) && (__clang_major__ > 9)) && !defined(__APPLE__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-copy"
#endif

#include <boost/multiprecision/cpp_int.hpp>

#include <math/wide_integer/uintwide_t.h>
#include <test/test_uintwide_t.h>

namespace local_float_convert
{
  auto engine_man() -> std::mt19937&                                                         { static std::mt19937                                                         my_engine_man; return my_engine_man; } // NOLINT(cert-msc32-c,cert-msc51-cpp)
  auto engine_sgn() -> std::ranlux24_base&                                                   { static std::ranlux24_base                                                   my_engine_sgn; return my_engine_sgn; } // NOLINT(cert-msc32-c,cert-msc51-cpp)
  auto engine_e10() -> std::linear_congruential_engine<std::uint32_t, 48271, 0, 2147483647>& { static std::linear_congruential_engine<std::uint32_t, 48271, 0, 2147483647> my_engine_e10; return my_engine_e10; } // NOLINT(cert-msc32-c,cert-msc51-cpp)

  template<typename FloatingPointType,
           const std::int32_t LoExp10,
           const std::int32_t HiExp10>
  auto get_random_float() -> FloatingPointType
  {
    using local_builtin_float_type = FloatingPointType;

    static std::uniform_real_distribution<local_builtin_float_type>
    dist_man
    (
      local_builtin_float_type(0.0F),
      local_builtin_float_type(1.0F)
    );

    static std::uniform_int_distribution<unsigned>
    dist_sgn
    (
      0,
      1
    );

    static std::uniform_int_distribution<std::int32_t>
    dist_e10
    (
      LoExp10,
      HiExp10
    );

    using std::pow;

    const std::int32_t             p10 = dist_e10(engine_e10());
    const local_builtin_float_type e10 = pow(local_builtin_float_type(10.0F),
                                             local_builtin_float_type(p10));

    const local_builtin_float_type a = dist_man(engine_man()) * e10;

    const bool is_neg = (dist_sgn(engine_sgn()) != 0);

    const local_builtin_float_type f = ((!is_neg) ? a : -a);

    return f;
  }

  template<const std::size_t MaxDigitsToGet,
           const std::size_t MinDigitsToGet = 2U>
  void get_random_digit_string(std::string& str)
  {
    static_assert(MinDigitsToGet >=  2U, "Error: The minimum number of digits to get must be  2 or more");

    static std::uniform_int_distribution<unsigned>
    dist_sgn
    (
      0,
      1
    );

    static std::uniform_int_distribution<unsigned>
    dist_len
    (
      MinDigitsToGet,
      MaxDigitsToGet
    );

    static std::uniform_int_distribution<unsigned>
    dist_first
    (
      1,
      9
    );

    static std::uniform_int_distribution<unsigned>
    dist_following
    (
      0,
      9
    );

    const bool is_neg = (dist_sgn(engine_sgn()) != 0);

    const auto len = static_cast<std::string::size_type>(dist_len(engine_e10()));

    std::string::size_type pos = 0U;

    if(is_neg)
    {
      str.resize(len + 1U);

      str.at(pos) = char('-');

      ++pos;
    }
    else
    {
      str.resize(len);
    }

    str.at(pos) = static_cast<char>(dist_first(engine_man()) + 0x30U);

    ++pos;

    while(pos < str.length())
    {
      str.at(pos) = static_cast<char>(dist_following(engine_man()) + 0x30U);

      ++pos;
    }
  }

  template<typename UnsignedIntegralType>
  static auto hexlexical_cast(const UnsignedIntegralType& u) -> std::string
  {
    std::stringstream ss;

    ss << std::hex << u;

    return ss.str();
  }
} // namespace local_float_convert

auto math::wide_integer::test_uintwide_t_float_convert() -> bool
{
  constexpr unsigned digits2 = 256U;

  using boost_uint_backend_type =
    boost::multiprecision::cpp_int_backend<digits2,
                                           digits2,
                                           boost::multiprecision::unsigned_magnitude>;
  using boost_sint_backend_type =
    boost::multiprecision::cpp_int_backend<digits2,
                                           digits2,
                                           boost::multiprecision::signed_magnitude>;

  using boost_uint_type = boost::multiprecision::number<boost_uint_backend_type, boost::multiprecision::et_on>;
  using boost_sint_type = boost::multiprecision::number<boost_sint_backend_type, boost::multiprecision::et_on>;

  #if defined(WIDE_INTEGER_HAS_LIMB_TYPE_UINT64)
  using local_limb_type = std::uint64_t;
  #else
  using local_limb_type = std::uint32_t;
  #endif

  using local_uint_type = math::wide_integer::uintwide_t<digits2, local_limb_type, void>;
  using local_sint_type = math::wide_integer::uintwide_t<digits2, local_limb_type, void, true>;

  local_float_convert::engine_man().seed(static_cast<typename std::mt19937::result_type>                                                        (std::clock()));
  local_float_convert::engine_sgn().seed(static_cast<typename std::ranlux24_base::result_type>                                                  (std::clock()));
  local_float_convert::engine_e10().seed(static_cast<typename std::linear_congruential_engine<std::uint32_t, 48271, 0, 2147483647>::result_type>(std::clock()));

  bool result_is_ok = true;

  for(auto i = std::size_t(0U); i < std::size_t(0x80000U); ++i)
  {
    const auto f = local_float_convert::get_random_float<float, -1, 27>();

    boost_sint_type n_boost = boost_sint_type(f);
    local_sint_type n_local = local_sint_type(f);

    const std::string str_boost_signed = local_float_convert::hexlexical_cast(static_cast<boost_uint_type>(n_boost));
    const std::string str_local_signed = local_float_convert::hexlexical_cast(static_cast<local_uint_type>(n_local));

    result_is_ok &= (str_boost_signed == str_local_signed);
  }

  for(auto i = std::size_t(0U); i < std::size_t(0x80000U); ++i)
  {
    const auto d = local_float_convert::get_random_float<double, -1, 75>();

    boost_sint_type n_boost = boost_sint_type(d);
    local_sint_type n_local = local_sint_type(d);

    const std::string str_boost_signed = local_float_convert::hexlexical_cast(static_cast<boost_uint_type>(n_boost));
    const std::string str_local_signed = local_float_convert::hexlexical_cast(static_cast<local_uint_type>(n_local));

    result_is_ok &= (str_boost_signed == str_local_signed);
  }

  local_float_convert::engine_man().seed(static_cast<typename std::mt19937::result_type>                                                        (std::clock()));
  local_float_convert::engine_sgn().seed(static_cast<typename std::ranlux24_base::result_type>                                                  (std::clock()));
  local_float_convert::engine_e10().seed(static_cast<typename std::linear_congruential_engine<std::uint32_t, 48271, 0, 2147483647>::result_type>(std::clock()));

  for(auto i = std::size_t(0U); i < std::size_t(0x100000U); ++i)
  {
    std::string str_digits;

    local_float_convert::get_random_digit_string<31U>(str_digits);

    const boost_sint_type n_boost = boost_sint_type(str_digits.c_str());
    const local_sint_type n_local = local_sint_type(str_digits.c_str());

    const auto f_boost = static_cast<float>(n_boost);
    const auto f_local = static_cast<float>(n_local);

    using std::fabs;

    constexpr auto cast_tol_float = float(std::numeric_limits<float>::epsilon() * 2.0F);

    const float closeness      = fabs(1.0F - fabs(f_boost / f_local));
    const bool  result_f_is_ok = (closeness < cast_tol_float);

    result_is_ok &= result_f_is_ok;
  }

  for(auto i = std::size_t(0U); i < std::size_t(0x40000U); ++i)
  {
    std::string str_digits;

    local_float_convert::get_random_digit_string<71U>(str_digits);

    const boost_sint_type n_boost = boost_sint_type(str_digits.c_str());
    const local_sint_type n_local = local_sint_type(str_digits.c_str());

    const auto d_boost = static_cast<double>(n_boost);
    const auto d_local = static_cast<double>(n_local);

    using std::fabs;

    constexpr auto cast_tol_double = double(std::numeric_limits<double>::epsilon() * 2.0);

    const double closeness      = fabs(1.0 - fabs(d_boost / d_local));
    const bool   result_f_is_ok = (closeness < cast_tol_double);

    result_is_ok &= result_f_is_ok;
  }

  return result_is_ok;
}

#if (defined(__clang__) && (__clang_major__ > 9)) && !defined(__APPLE__)
#pragma GCC diagnostic pop
#endif

#if defined(__GNUC__)
#pragma GCC diagnostic pop
#pragma GCC diagnostic pop
#pragma GCC diagnostic pop
#endif


/*

Reads in nuclide grid data from the nuclide grid files

*/
#include "vexs_header.h"
#include <boost/algorithm/string.hpp>

//using namespace boost;

void read_nuclide(problem_data &problem, int nuclide){

	//Check if nuclide file exists
	string path = problem.library + "/ng" + to_string(nuclide) + ".txt";
	struct stat filecheck;
	if( stat(path.c_str(), &filecheck) != 0 || !(S_ISREG(filecheck.st_mode)) ){
		cout << "[ERROR]: Nuclide grid file " << path << " does not exist.\n";
		exit(-1);
	}

	/*
	We fetch the first line and read in the value as an integer, telling us how far down we have to read to get all
	energy point information for the nuclide; we assume the user has been diligent enough to make sure this number is
	accurate, as the number of energy points in a nuclide grid is very broadly important information for the simulation
	*/
	ifstream file(path);
	string  token;
	string line;

	getline(file,line);
	token = line.substr( 0, line.find(" ") );
	//split(token, line, is_any_of(" "), token_compress_on);
	int n_points = stoi(token);
	token.clear();

	//If we are reading from a binary file, push back an empty vector because we'll be loading it in later
	if(problem.binary){
		nuclide_gridpoint point;
		for (int i = 0; i < n_points; i++){
			(*problem.nuclides.back()).push_back(point);
		}
		return;
	}

	for (int i = 0; i < n_points; i++){
		getline(file, line);
		token = line.substr( 0, line.find(" ") );
		nuclide_gridpoint point;
		point.energy = stold(token);
		(*problem.nuclides.back()).push_back(point);
		token.clear();
	}
}


#include <csignal>
#include <fstream>
#include <functional>
#include <iostream>
#include <string>
#include <vector>

#include <boost/format.hpp>
#include <boost/gil/gil_all.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/optional.hpp>
#include <boost/program_options.hpp>

#include <QApplication>

#include <vi_ea_nsga2.hpp>
#include <vi_image_segmentation.hpp>

#include <termcolor.hpp>

volatile bool run_program = true;

template <typename solver_type>
void
print_evolution_info(
    const solver_type&                                                                                solver,
    const unsigned                                                                                generation,
    const unsigned                                                                                objective_count,
    const std::vector<std::string>                                                                          objective_names,
    const std::vector<unsigned>&                                                                            segmentation_count_hints,
    const std::vector<std::vector<vi::ea::nsga2::individual<vi::image_segmentation::moea::genotype_type>>>& cherrypick_solutions,
    const int                                                                                cherrypick_boundary_best)
{
    std::cout << boost::format(
        "Generation %u: ")
        % generation;

    for (unsigned objective_value_index = 0U;
         objective_value_index < objective_count;
         ++objective_value_index)
    {
        const auto individual = solver.extreme_min[objective_value_index];
        std::cout << boost::format(
            "Best %s: %.2f")
            % objective_names[objective_value_index].c_str()
            % individual->objective_values[objective_value_index];

        if (objective_count > 1)
        {
            std::cout << " (";
            bool first_inside = true;

            for (unsigned other_objective_value_index = 0U;
                 other_objective_value_index < objective_count;
                 ++other_objective_value_index)
            {
                if (other_objective_value_index != objective_value_index)
                {
                    std::cout << boost::format(
                        "%s%s=%.2f")
                        % (first_inside ? "" : " ")
                        % objective_names[other_objective_value_index].c_str()
                        % individual->objective_values[other_objective_value_index];

                    first_inside = false;
                }
            }

            std::cout << ") ";
        }
    }

    if (!cherrypick_solutions.empty())
    {
        for (unsigned hint_index = 0; hint_index < segmentation_count_hints.size(); ++hint_index)
        {
            const auto& cherrypick_solutions_hint = cherrypick_solutions[hint_index];
            if (!cherrypick_solutions_hint.empty())
            {
                const auto  hint     = static_cast<int>(segmentation_count_hints[hint_index]);
                const auto  distance = std::abs(static_cast<int>(cherrypick_solutions_hint[0].genotype.second) - hint);

                if (distance <= cherrypick_boundary_best)
                {
                    std::cout << termcolor::green << "\u2764" << termcolor::reset;
                }
                else
                {
                    std::cout << termcolor::blue << "\u2764" << termcolor::reset;
                }
            }
            else
            {
                std::cout << termcolor::red << "\u2764" << termcolor::reset;
            }
        }
    }

    std::cout << std::endl;
}

template <typename solver_type>
void
save_population(const std::string&                  filename,
                const solver_type&                  solver,
                const vi::image_segmentation::moea& problem,
                const unsigned                      objective_count,
                const std::vector<std::string>      objective_names)
{
    std::ofstream output_file{filename.c_str()};

    output_file << "#individual_index individual_rank segment_count";

    for (const auto& objective_name : objective_names)
    {
        output_file << ' ' << objective_name;
    }

    output_file << std::endl;

    auto individual_index = 0U;

    for (const auto& individual : solver.current_population)
    {
        output_file << boost::format("%u %u %u")
            % individual_index
            % individual.rank
            % individual.genotype.second;

        for (unsigned objective_value_index = 0U;
             objective_value_index < objective_count;
             ++objective_value_index)
        {
            output_file << boost::format(" %.5f") % individual.objective_values[objective_value_index];
        }

        output_file << std::endl;

        ++individual_index;
    }
}

template <typename solver_type>
void
update_cherrypicked_solutions(
    const solver_type&                                                                                solver,
    const std::vector<unsigned>&                                                                      segmentation_count_hints,
    std::vector<std::vector<vi::ea::nsga2::individual<vi::image_segmentation::moea::genotype_type>>>& cherrypick_solutions,
    const int                                                                                cherrypick_boundary_worst,
    const unsigned                                                                                cherrypick_count)
{
    if (!cherrypick_solutions.empty())
    {
        for (const auto& individual : solver.current_population)
        {
            const auto individual_segmentation_count = individual.genotype.second;

            for (unsigned hint_index = 0; hint_index < segmentation_count_hints.size(); ++hint_index)
            {
                const auto  hint     = static_cast<int>(segmentation_count_hints[hint_index]);
                const auto  distance = std::abs(static_cast<int>(individual_segmentation_count) - hint);

                auto& cherrypick_solutions_hint = cherrypick_solutions[hint_index];

                if (distance <= cherrypick_boundary_worst)
                {
                    if (cherrypick_solutions_hint.size() >= cherrypick_count)
                    {
                        const auto& other          = cherrypick_solutions_hint[cherrypick_count - 1];
                        const auto  other_distance = std::abs(static_cast<int>(other.genotype.second) - hint);

                        if (distance < other_distance or (distance == other_distance and vi::ea::nsga2::dominates(individual.objective_values, other.objective_values)))
                        {
                            cherrypick_solutions_hint.pop_back();
                            cherrypick_solutions_hint.push_back(individual);

                            std::sort(cherrypick_solutions_hint.begin(), cherrypick_solutions_hint.end(),
                                [hint](const auto& a, const auto& b)
                                {
                                    const auto distance_a = std::abs(static_cast<int>(a.genotype.second) - hint);
                                    const auto distance_b = std::abs(static_cast<int>(b.genotype.second) - hint);

                                    return ((distance_a < distance_b) or
                                        ((distance_a == distance_b) and
                                         (vi::ea::nsga2::dominates(a.objective_values, b.objective_values))));
                                });
                        }
                    }
                    else
                    {
                        cherrypick_solutions_hint.push_back(individual);

                        std::sort(cherrypick_solutions_hint.begin(), cherrypick_solutions_hint.end(),
                            [hint](const auto& a, const auto& b)
                            {
                                const auto distance_a = std::abs(static_cast<int>(a.genotype.second) - hint);
                                const auto distance_b = std::abs(static_cast<int>(b.genotype.second) - hint);

                                return ((distance_a < distance_b) or
                                    ((distance_a == distance_b) and
                                     (vi::ea::nsga2::dominates(a.objective_values, b.objective_values))));
                            });
                    }
                }
            }
        }
    }
}

int
main(int argc, char** argv)
{
    namespace po = boost::program_options;

    std::signal(SIGINT, [](int /*signal*/) { run_program = false; });

    int arg_count = 3;
    auto arg0     = "application";
    auto arg1     = "-platform";
    auto arg2     = "offscreen";
    char* args[]  = {const_cast<char*>(arg0), const_cast<char*>(arg1), const_cast<char*>(arg2)};

    QApplication app{arg_count, args};

    try
    {
        po::options_description options{"Options"};
        options.add_options()
            ("cherrypick_boundary_best",      po::value<int>()->default_value(2),                        "Best required segmentation count distance between hint and solution")
            ("cherrypick_boundary_worst",     po::value<int>()->default_value(7),                        "Worst allowed segmentation count distance between hint and solution")
            ("cherrypick_count",              po::value<unsigned>()->default_value(5),                   "Cherrypick solution count")
            ("crossover_rate",                po::value<double>()->default_value(1.0),                   "Crossover rate")
            ("evaluate_connectivity_measure", po::value<bool>()->default_value(true),                    "Evaluate connectivity measure")
            ("evaluate_edge_value",           po::value<bool>()->default_value(true),                    "Evaluate edge value")
            ("evaluate_overall_deviation",    po::value<bool>()->default_value(true),                    "Evaluate overall deviation")
            ("generations",                   po::value<unsigned>()->default_value(1000),                "Generation count")
            ("hint",                          po::value<std::vector<unsigned>>()->multitoken(),          "Segment count hints")
            ("input_image_filename",          po::value<std::string>()->required(),                      "Input image filename")
            ("mutation_rate",                 po::value<double>()->default_value(0.05),                  "Mutation rate")
            ("population_size",               po::value<unsigned>()->default_value(100),                 "Population size")
            ("render",                        po::value<bool>()->default_value(true),                    "Render solutions")
            ("render_base_path",              po::value<std::string>()->default_value(""),               "Render output base path")
            ("save",                          po::value<bool>()->default_value(true),                    "Save population information to file")
            ("save_filename",                 po::value<std::string>()->default_value("population.txt"), "Population filename")
            ("tournament_group_size",         po::value<unsigned>()->default_value(5),                   "Tournament group size")
            ("tournament_randomness",         po::value<double>()->default_value(0.1),                   "Tournament probability of selecting random winner");

        po::positional_options_description positional_options{};
        positional_options.add("input_image_filename", 1);

        po::variables_map variables{};
        po::store(po::command_line_parser(argc, argv).options(options).positional(positional_options).run(), variables);
        po::notify(variables);

        const auto segmentation_count_hints =
            variables["hint"].empty() ? std::vector<unsigned>{} : variables["hint"].as<std::vector<unsigned>>();

        std::vector<std::vector<vi::ea::nsga2::individual<vi::image_segmentation::moea::genotype_type>>>
            cherrypick_solutions(segmentation_count_hints.size());

        const auto cherrypick_boundary_best  = variables["cherrypick_boundary_best"].as<int>();
        const auto cherrypick_boundary_worst = variables["cherrypick_boundary_worst"].as<int>();
        const auto cherrypick_count          = variables["cherrypick_count"].as<unsigned>();

        const auto objective_count =
            (variables["evaluate_overall_deviation"].as<bool>()    ? 1U : 0U) +
            (variables["evaluate_edge_value"].as<bool>()           ? 1U : 0U) +
            (variables["evaluate_connectivity_measure"].as<bool>() ? 1U : 0U);

        vi::ea::nsga2::options solver_options{
            variables["crossover_rate"].as<double>(),
            variables["mutation_rate"].as<double>(),
            objective_count,
            variables["population_size"].as<unsigned>(),
            variables["tournament_group_size"].as<unsigned>(),
            variables["tournament_randomness"].as<double>()
        };

        auto rng{std::default_random_engine{std::random_device{}()}};

        vi::image_segmentation::moea problem{
            variables["input_image_filename"].as<std::string>(),
            variables["evaluate_overall_deviation"].as<bool>(),
            variables["evaluate_edge_value"].as<bool>(),
            variables["evaluate_connectivity_measure"].as<bool>()};

        using namespace std::placeholders;

        auto solver = vi::ea::nsga2::build_system<vi::image_segmentation::moea::genotype_type>(
            rng,
            solver_options,
            std::bind(&vi::image_segmentation::moea::generate<decltype(rng)>, &problem, _1),
            std::bind(&vi::image_segmentation::moea::evaluate, &problem, _1),
            std::bind(&vi::image_segmentation::moea::crossover_operator<decltype(rng)>, _1, _2, _3),
            std::bind(&vi::image_segmentation::moea::mutate_operator<decltype(rng)>, _1, _2));

        const auto generations = variables["generations"].as<unsigned>();

        std::vector<std::string> objective_names{};
        if (variables["evaluate_overall_deviation"].as<bool>())
        {
            objective_names.push_back("deviation");
        }
        if (variables["evaluate_edge_value"].as<bool>())
        {
            objective_names.push_back("edge");
        }
        if (variables["evaluate_connectivity_measure"].as<bool>())
        {
            objective_names.push_back("connectivity");
        }

        for (auto generation = 1U; run_program && generation <= generations; ++generation)
        {
            solver.evolve(rng);
            update_cherrypicked_solutions(solver, segmentation_count_hints, cherrypick_solutions, cherrypick_boundary_worst, cherrypick_count);
            print_evolution_info(solver, generation, objective_count, objective_names, segmentation_count_hints, cherrypick_solutions, cherrypick_boundary_best);
        }

        solver.fast_non_dominated_sort();

        if (variables["save"].as<bool>())
        {
            save_population(variables["save_filename"].as<std::string>(),
                            solver,
                            problem,
                            objective_count,
                            objective_names);
        }

        if (variables["render"].as<bool>())
        {
            auto solution_index = 0;
            auto hint_index = 0;

            while (solution_index < cherrypick_count and hint_index < cherrypick_count * segmentation_count_hints.size())
            {
                const auto& cherrypick_solutions_hint  = cherrypick_solutions[hint_index % cherrypick_solutions.size()];
                const auto  cherrypick_solutions_index = hint_index / cherrypick_solutions.size();

                if (cherrypick_solutions_index < cherrypick_solutions_hint.size())
                {
                    const auto& cherrypicked_solution = cherrypick_solutions_hint[cherrypick_solutions_index];

                    std::ostringstream description{};

                    description << boost::format("_solution_%u_segments_%u")
                        % (solution_index + 1) % cherrypicked_solution.genotype.second;

                    for (unsigned objective_value_index = 0U;
                         objective_value_index < objective_count;
                         ++objective_value_index)
                    {
                        description << boost::format("_%s_%.5f")
                            % objective_names[objective_value_index].c_str()
                            % cherrypicked_solution.objective_values[objective_value_index];
                    }

                    std::vector<vi::image_segmentation::segment_index> segmentation{};
                    vi::image_segmentation::segment_index              segment_count{};

                    std::tie(segmentation, segment_count) =
                        vi::image_segmentation::compile_segmentation_graph(
                            cherrypicked_solution.genotype.first,
                            problem.input_image_width,
                            problem.input_image_height);

                    const auto description_str = description.str();

                    vi::image_segmentation::render(
                        boost::gil::view(problem.input_image),
                        segmentation,
                        boost::str(boost::format("%srender_type_1%s.pdf")
                            % variables["render_base_path"].as<std::string>().c_str()
                            % description_str.c_str()),
                        true,
                        true,
                        false);

                    vi::image_segmentation::render(
                        boost::gil::view(problem.input_image),
                        segmentation,
                        boost::str(boost::format("%srender_type_2%s.pdf")
                            % variables["render_base_path"].as<std::string>().c_str()
                            % description_str.c_str()),
                        false,
                        true,
                        false);

                    vi::image_segmentation::render(
                        boost::gil::view(problem.input_image),
                        segmentation,
                        boost::str(boost::format("%srender_type_3%s.pdf")
                            % variables["render_base_path"].as<std::string>().c_str()
                            % description_str.c_str()),
                        false,
                        false,
                        true);

                    ++solution_index;
                }

                ++hint_index;
            }
        }
    }
    catch (const po::error& error)
    {
        std::cerr << "error: " << error.what() << std::endl;
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}


#include <iostream>
#include "node.hpp"
#include "expression.hpp"
#include "statement.hpp"
#include "type.hpp"
#include "monotype.hpp"
#include "repr_printer.hpp"
#include "type_printer.hpp"
#include "py_printer.hpp"
#include "compiler.hpp"
#include "cuda_printer.hpp"
#include <boost/variant.hpp>

using namespace backend;
using std::shared_ptr;
using std::make_shared;
using std::vector;

int main(void)
{
  // types
  shared_ptr<monotype_t> Int32_t = int32_mt;
  shared_ptr<monotype_t> vecInt32_t(new sequence_t(Int32_t));

  // make op_add
  shared_ptr<tuple_t> int_tuple = make_shared<tuple_t>(vector<shared_ptr<type_t> >{Int32_t, Int32_t});
  shared_ptr<monotype_t> binop_t = make_shared<fn_t>(int_tuple, Int32_t);
  shared_ptr<name> op_add_scalar(new name("op_add", binop_t));

  // make map2
  shared_ptr<tuple_t> intvec_tuple = make_shared<tuple_t>(vector<shared_ptr<type_t> >{binop_t, vecInt32_t, vecInt32_t});
  shared_ptr<monotype_t> vecop_t = make_shared<fn_t>(intvec_tuple, vecInt32_t);
  shared_ptr<name> op_add_vector(new name("map2", vecop_t));

  // a, b
  shared_ptr<name> a(new name("a", vecInt32_t));
  shared_ptr<name> b(new name("b", vecInt32_t));

  // addproc(a,b)
  //   result = map2(op_add, a, b)
  //   return result
  shared_ptr<tuple> args(new tuple(vector<shared_ptr<expression> >{op_add_scalar, a, b}));
  shared_ptr<name> result(new name("result", vecInt32_t));
  shared_ptr<bind> res =
    make_shared<bind>(result,
                      make_shared<apply>(op_add_vector, args));
  shared_ptr<ret> addret(new ret(result));
  shared_ptr<name> addprocname(new name("addproc", vecop_t));
  shared_ptr<suite> funcbody(new suite(vector<shared_ptr<statement> >{res, addret}));
  shared_ptr<tuple> funcargs(new tuple(vector<shared_ptr<expression> >{a, b}));
  shared_ptr<procedure> addproc(new procedure(addprocname, funcargs, funcbody, vecop_t));

  // the whole program
  shared_ptr<suite> program(new suite(vector<shared_ptr<statement> >{addproc}));

  // print the representation
  std::cout << *program << std::endl;

  // compile
  std::string entry("addproc");
  compiler comp(entry);
  shared_ptr<suite> functorized = comp(*program);

  // print the representation
  py_printer pp(std::cout);
  pp(*program); std::cout << "\n";

  // cuda printer
  std::ostringstream os;
  backend::cuda_printer p(entry, comp.reg(), os);
  p(*functorized);
  std::cout << os.str() << "\n";

  return 0;
}


/****************************************************************************
 * Copyright (c) 2012-2017 by the DataTransferKit authors                   *
 * All rights reserved.                                                     *
 *                                                                          *
 * This file is part of the DataTransferKit library. DataTransferKit is     *
 * distributed under a BSD 3-clause license. For the licensing terms see    *
 * the LICENSE file in the top-level directory.                             *
 ****************************************************************************/

#include <DTK_DistributedSearchTree.hpp>

#include <Teuchos_DefaultComm.hpp>
#include <Teuchos_UnitTestHarness.hpp>

#include <boost/geometry.hpp>

#include <algorithm>
#include <bitset>
#include <iostream>
#include <random>
#include <tuple>

namespace details = DataTransferKit::Details;

TEUCHOS_UNIT_TEST_TEMPLATE_1_DECL( DistributedSearchTree, hello_world,
                                   DeviceType )
{
    Teuchos::RCP<const Teuchos::Comm<int>> comm =
        Teuchos::DefaultComm<int>::getComm();
    int const comm_rank = Teuchos::rank( *comm );
    int const comm_size = Teuchos::size( *comm );

    DataTransferKit::DistributedSearchTreeImpl<DeviceType>::epsilon = 0.5;
    int const n = 4;
    Kokkos::View<DataTransferKit::Box *, DeviceType> boxes( "boxes", n );
    auto boxes_host = Kokkos::create_mirror_view( boxes );
    // [  rank 0       [  rank 1       [  rank 2       [  rank 3       [
    // x---x---x---x---x---x---x---x---x---x---x---x---x---x---x---x---
    // ^   ^   ^   ^
    // 0   1   2   3   ^   ^   ^   ^
    //                 0   1   2   3   ^   ^   ^   ^
    //                                 0   1   2   3   ^   ^   ^   ^
    //                                                 0   1   2   3
    for ( int i = 0; i < n; ++i )
    {
        DataTransferKit::Box box;
        DataTransferKit::Point point = {{(double)i / n + comm_rank, 0., 0.}};
        DataTransferKit::Details::expand( box, point );
        boxes_host( i ) = box;
    }
    Kokkos::deep_copy( boxes, boxes_host );

    DataTransferKit::DistributedSearchTree<DeviceType> tree( comm, boxes );

    // 0---0---0---0---1---1---1---1---2---2---2---2---3---3---3---3---
    // |               |               |               |               |
    // |               |               |               x   x   x   x   |
    // |               |               |               |<------0------>|
    // |               |               x   x   x   x   x               |
    // |               |               |<------1------>|               |
    // |               x   x   x   x   x               |               |
    // |               |<------2------>|               |               |
    // x   x   x   x   x               |               |               |
    // |<------3------>|               |               |               |
    // |               |               |               |               |
    Kokkos::View<DataTransferKit::Details::Within *, DeviceType> queries(
        "queries", 1 );
    auto queries_host = Kokkos::create_mirror_view( queries );
    queries_host( 0 ) = DataTransferKit::Details::within(
        {{0.5 + comm_size - 1 - comm_rank, 0., 0.}}, 0.5 );
    deep_copy( queries, queries_host );

    // 0---0---0---0---1---1---1---1---2---2---2---2---3---3---3---3---
    // |               |               |               |               |
    // |               |               |           x   x   x           |
    // |               |           x   x   x        <--0-->            |
    // |           x   x   x        <--1-->            |               |
    // x   x        <--2-->            |               |               |
    // 3-->            |               |               |               |
    // |               |               |               |               |
    Kokkos::View<DataTransferKit::Details::Nearest *, DeviceType>
        nearest_queries( "nearest_queries", 1 );
    auto nearest_queries_host = Kokkos::create_mirror_view( nearest_queries );
    nearest_queries_host( 0 ) = DataTransferKit::Details::nearest(
        {{0.0 + comm_size - 1 - comm_rank, 0., 0.}},
        comm_rank < comm_size - 1 ? 3 : 2 );
    deep_copy( nearest_queries, nearest_queries_host );

    Kokkos::View<int *, DeviceType> offset( "offset" );
    Kokkos::View<int *, DeviceType> indices( "indices" );
    Kokkos::View<int *, DeviceType> ranks( "ranks" );
    tree.query( queries, indices, offset, ranks );

    auto indices_host = Kokkos::create_mirror_view( indices );
    Kokkos::deep_copy( indices_host, indices );
    auto ranks_host = Kokkos::create_mirror_view( ranks );
    Kokkos::deep_copy( ranks_host, ranks );
    auto offset_host = Kokkos::create_mirror_view( offset );
    Kokkos::deep_copy( offset_host, offset );

    TEST_EQUALITY( offset_host.extent( 0 ), 2 );
    TEST_EQUALITY( offset_host( 0 ), 0 );
    TEST_EQUALITY( offset_host( 1 ), indices_host.extent_int( 0 ) );
    TEST_EQUALITY( indices_host.extent( 0 ), ranks_host.extent( 0 ) );
    TEST_EQUALITY( indices_host.extent( 0 ), comm_rank > 0 ? n + 1 : n );
    for ( int i = 0; i < n; ++i )
    {
        TEST_EQUALITY( n - 1 - i, indices_host( i ) );
        TEST_EQUALITY( comm_size - 1 - comm_rank, ranks_host( i ) );
    }
    if ( comm_rank > 0 )
    {
        TEST_EQUALITY( indices_host( n ), 0 );
        TEST_EQUALITY( ranks_host( n ), comm_size - comm_rank );
    }

    tree.query( nearest_queries, indices, offset, ranks );

    indices_host = Kokkos::create_mirror_view( indices );
    ranks_host = Kokkos::create_mirror_view( ranks );
    offset_host = Kokkos::create_mirror_view( offset );
    Kokkos::deep_copy( indices_host, indices );
    Kokkos::deep_copy( ranks_host, ranks );
    Kokkos::deep_copy( offset_host, offset );

    TEST_COMPARE( n, >, 2 );
    TEST_EQUALITY( offset_host.extent( 0 ), 2 );
    TEST_EQUALITY( offset_host( 0 ), 0 );
    TEST_EQUALITY( offset_host( 1 ), indices_host.extent_int( 0 ) );
    TEST_EQUALITY( indices_host.extent( 0 ),
                   comm_rank < comm_size - 1 ? 3 : 2 );

    TEST_EQUALITY( indices_host( 0 ), 0 );
    TEST_EQUALITY( ranks_host( 0 ), comm_size - 1 - comm_rank );
    if ( comm_rank < comm_size - 1 )
    {
        TEST_EQUALITY( indices_host( 1 ), n - 1 );
        TEST_EQUALITY( ranks_host( 1 ), comm_size - 2 - comm_rank );
        TEST_EQUALITY( indices_host( 2 ), 1 );
        TEST_EQUALITY( ranks_host( 2 ), comm_size - 1 - comm_rank );
    }
    else
    {
        TEST_EQUALITY( indices_host( 1 ), 1 );
        TEST_EQUALITY( ranks_host( 1 ), comm_size - 1 - comm_rank );
    }
}

// The `out` and `success` parameters come from the Teuchos unit testing macros
// expansion.
template <typename Query, typename DeviceType>
void check_results( DataTransferKit::DistributedSearchTree<DeviceType> &tree,
                    Kokkos::View<Query *, DeviceType> const &queries,
                    std::vector<int> const &indices_ref,
                    std::vector<int> const &offset_ref,
                    std::vector<int> const &ranks_ref, bool &success,
                    Teuchos::FancyOStream &out )
{
    Kokkos::View<int *, DeviceType> indices( "indices" );
    Kokkos::View<int *, DeviceType> offset( "offset" );
    Kokkos::View<int *, DeviceType> ranks( "ranks" );
    tree.query( queries, indices, offset, ranks );

    auto indices_host = Kokkos::create_mirror_view( indices );
    deep_copy( indices_host, indices );
    auto offset_host = Kokkos::create_mirror_view( offset );
    deep_copy( offset_host, offset );
    auto ranks_host = Kokkos::create_mirror_view( ranks );
    deep_copy( ranks_host, ranks );

    TEST_COMPARE_ARRAYS( indices_host, indices_ref );
    TEST_COMPARE_ARRAYS( offset_host, offset_ref );
    TEST_COMPARE_ARRAYS( ranks_host, ranks_ref );
}

TEUCHOS_UNIT_TEST_TEMPLATE_1_DECL( DistributedSearchTree, empty_tree_no_queries,
                                   DeviceType )
{
    Teuchos::RCP<const Teuchos::Comm<int>> comm =
        Teuchos::DefaultComm<int>::getComm();
    int const comm_rank = Teuchos::rank( *comm );
    int const comm_size = Teuchos::size( *comm );

    Kokkos::View<DataTransferKit::Box *, DeviceType> boxes( "boxes", 1 );
    auto boxes_host = Kokkos::create_mirror_view( boxes );
    boxes_host( 0 ) = {
        (double)comm_rank, (double)comm_rank + 1., 0., 1., 0., 1.};
    Kokkos::deep_copy( boxes, boxes_host );
    DataTransferKit::DistributedSearchTree<DeviceType> tree( comm, boxes );

    Kokkos::View<DataTransferKit::Box *, DeviceType> no_boxes( "no_boxes", 0 );
    DataTransferKit::DistributedSearchTree<DeviceType> empty_tree( comm,
                                                                   no_boxes );

    Kokkos::View<DataTransferKit::Details::Overlap *, DeviceType> queries(
        "queries", 2 );
    auto queries_host = Kokkos::create_mirror_view( queries );
    queries_host( 0 ) = DataTransferKit::Details::overlap( DataTransferKit::Box(
        {(double)comm_size - (double)comm_rank - 0.5,
         (double)comm_size - (double)comm_rank - 0.5, 0.5, 0.5, 0.5, 0.5} ) );
    queries_host( 1 ) = DataTransferKit::Details::overlap(
        DataTransferKit::Box( {(double)comm_rank + 0.5, (double)comm_rank + 0.5,
                               0.5, 0.5, 0.5, 0.5} ) );
    Kokkos::deep_copy( queries, queries_host );

    check_results( empty_tree, queries, {}, {0, 0, 0}, {}, success, out );
    check_results( tree, queries, {0, 0}, {0, 1, 2},
                   {comm_size - 1 - comm_rank, comm_rank}, success, out );

    check_results(
        empty_tree,
        Kokkos::View<DataTransferKit::Details::Nearest *, DeviceType>(
            "nothing", 0 ),
        {}, {0}, {}, success, out );
    check_results(
        empty_tree,
        Kokkos::View<DataTransferKit::Details::Overlap *, DeviceType>(
            "nothing", 0 ),
        {}, {0}, {}, success, out );

    check_results(
        tree,
        Kokkos::View<DataTransferKit::Details::Nearest *, DeviceType>(
            "nothing", 0 ),
        {}, {0}, {}, success, out );
    check_results(
        tree,
        Kokkos::View<DataTransferKit::Details::Overlap *, DeviceType>(
            "nothing", 0 ),
        {}, {0}, {}, success, out );

    TEST_ASSERT( empty_tree.empty() );
    TEST_ASSERT( !tree.empty() );
    TEST_EQUALITY( (int)empty_tree.size(), 0 );
    TEST_EQUALITY( (int)tree.size(), comm_size );
    // NOTE: we need that box comparison function or operator== so badly...
    auto checkBoxesAreEqual = [&out,
                               &success]( DataTransferKit::Box const &l,
                                          DataTransferKit::Box const &r ) {
        for ( int i = 0; i < 6; ++i )
            TEST_EQUALITY( l[i], r[i] );
    };
    checkBoxesAreEqual( empty_tree.bounds(), DataTransferKit::Box() );
    checkBoxesAreEqual( tree.bounds(), DataTransferKit::Box( {
                                           0.,
                                           (double)comm_size,
                                           0.,
                                           1.,
                                           0.,
                                           1.,
                                       } ) );
}

std::vector<std::array<double, 3>>
make_random_cloud( double const Lx, double const Ly, double const Lz,
                   int const n, double const seed )
{
    std::vector<std::array<double, 3>> cloud( n );
    std::default_random_engine generator( seed );
    std::uniform_real_distribution<double> distribution_x( 0.0, Lz );
    std::uniform_real_distribution<double> distribution_y( 0.0, Ly );
    std::uniform_real_distribution<double> distribution_z( 0.0, Lz );
    for ( int i = 0; i < n; ++i )
    {
        double x = distribution_x( generator );
        double y = distribution_y( generator );
        double z = distribution_z( generator );
        cloud[i] = {{x, y, z}};
    }
    return cloud;
}

TEUCHOS_UNIT_TEST_TEMPLATE_1_DECL( DistributedSearchTree, boost_comparison,
                                   DeviceType )
{
    namespace bg = boost::geometry;
    namespace bgi = boost::geometry::index;
    using BPoint = bg::model::point<double, 3, bg::cs::cartesian>;
    using RTree = bgi::rtree<std::pair<BPoint, int>, bgi::linear<16>>;

    Teuchos::RCP<const Teuchos::Comm<int>> comm =
        Teuchos::DefaultComm<int>::getComm();
    unsigned int const comm_rank = Teuchos::rank( *comm );
    unsigned int const comm_size = Teuchos::size( *comm );

    // Construct a random cloud of point. We use the same seed on all the
    // processors.
    double const Lx = 10.0;
    double const Ly = 10.0;
    double const Lz = 10.0;
    int const n = 100;
    auto cloud = make_random_cloud( Lx, Ly, Lz, n, 0 );
    auto queries = make_random_cloud( Lx, Ly, Lz, n, 1234 );

    // Create a R-tree to compare radius search results against
    RTree rtree;
    for ( int i = 0; i < n; ++i )
    {
        auto const &point = cloud[i];
        double const x = std::get<0>( point );
        double const y = std::get<1>( point );
        double const z = std::get<2>( point );
        rtree.insert( std::make_pair( BPoint( x, y, z ), i ) );
    }

    unsigned int const local_n = n / comm_size;
    Kokkos::View<DataTransferKit::Box *, DeviceType> bounding_boxes(
        "bounding_boxes", local_n );
    auto bounding_boxes_host = Kokkos::create_mirror_view( bounding_boxes );
    for ( unsigned int i = 0; i < n; ++i )
    {
        if ( i % comm_size == comm_rank )
        {
            auto const &point = cloud[i];
            double const x = std::get<0>( point );
            double const y = std::get<1>( point );
            double const z = std::get<2>( point );
            bounding_boxes_host[i / comm_size] = {x, x, y, y, z, z};
        }
    }

    std::map<std::pair<unsigned int, unsigned int>, unsigned int> indices_map;
    for ( unsigned int i = 0; i < n; ++i )
        for ( unsigned int j = 0; j < comm_size; ++j )
            if ( i % comm_size == j )
                indices_map[std::make_pair( i / comm_size, j )] = i;

    Kokkos::deep_copy( bounding_boxes, bounding_boxes_host );

    // Initialize the distributed search tree
    DataTransferKit::DistributedSearchTree<DeviceType> distributed_tree(
        comm, bounding_boxes );
    using ExecutionSpace = typename DeviceType::execution_space;
    Kokkos::View<double * [3], ExecutionSpace> point_coords( "point_coords",
                                                             local_n );
    auto point_coords_host = Kokkos::create_mirror_view( point_coords );
    Kokkos::View<double *, ExecutionSpace> radii( "radii", local_n );
    auto radii_host = Kokkos::create_mirror_view( radii );
    Kokkos::View<int * [2], ExecutionSpace> within_n_pts( "within_n_pts",
                                                          local_n );
    Kokkos::View<int *, ExecutionSpace> k( "distribution_k", local_n );
    auto k_host = Kokkos::create_mirror_view( k );
    std::vector<std::vector<std::pair<BPoint, int>>> returned_values_within(
        local_n );
    std::default_random_engine generator( 0 );
    std::uniform_real_distribution<double> distribution_radius(
        0.0, std::sqrt( Lx * Lx + Ly * Ly + Lz * Lz ) );
    std::uniform_int_distribution<int> distribution_k(
        1, std::floor( sqrt( n * n ) ) );
    for ( unsigned int i = 0; i < n; ++i )
    {
        if ( i % comm_size == comm_rank )
        {
            auto const &point = queries[i];
            unsigned int const j = i / comm_size;
            double const x = std::get<0>( point );
            double const y = std::get<1>( point );
            double const z = std::get<2>( point );
            BPoint centroid( x, y, z );
            radii_host[j] = distribution_radius( generator );
            double radius = radii_host[j];

            point_coords_host( j, 0 ) = x;
            point_coords_host( j, 1 ) = y;
            point_coords_host( j, 2 ) = z;

            // use the R-tree to obtain a reference solution
            rtree.query(
                bgi::satisfies(
                    [centroid, radius]( std::pair<BPoint, int> const &val ) {
                        return bg::distance( centroid, val.first ) <= radius;
                    } ),
                std::back_inserter( returned_values_within[j] ) );
        }
    }

    Kokkos::deep_copy( point_coords, point_coords_host );
    Kokkos::deep_copy( radii, radii_host );

    Kokkos::View<details::Within *, DeviceType> within_queries(
        "within_queries", local_n );
    Kokkos::parallel_for( "register_within_queries",
                          Kokkos::RangePolicy<ExecutionSpace>( 0, local_n ),
                          KOKKOS_LAMBDA( int i ) {
                              within_queries( i ) = details::within(
                                  {{point_coords( i, 0 ), point_coords( i, 1 ),
                                    point_coords( i, 2 )}},
                                  radii( i ) );
                          } );
    Kokkos::fence();

    // Perform the search
    Kokkos::View<int *, DeviceType> indices( "indices" );
    Kokkos::View<int *, DeviceType> offset( "offset" );
    Kokkos::View<int *, DeviceType> ranks( "ranks" );
    distributed_tree.query( within_queries, indices, offset, ranks );

    auto indices_host = Kokkos::create_mirror_view( indices );
    auto offset_host = Kokkos::create_mirror_view( offset );
    auto ranks_host = Kokkos::create_mirror_view( ranks );

    for ( unsigned int i = 0; i < n; ++i )
    {
        if ( i % comm_size == comm_rank )
        {
            unsigned int k = i / comm_size;
            auto const &ref = returned_values_within[k];
            std::set<int> ref_ids;
            for ( auto const &id : ref )
                ref_ids.emplace( id.second );
            for ( int j = offset_host( k ); j < offset_host( k + 1 ); ++j )
            {
                TEST_ASSERT( ref_ids.count( indices_map[std::make_pair(
                                 indices_host( j ), ranks( j ) )] ) != 0 );
            }
        }
    }
}

// Include the test macros.
#include "DataTransferKitSearch_ETIHelperMacros.h"

// Create the test group
#define UNIT_TEST_GROUP( NODE )                                                \
    using DeviceType##NODE = typename NODE::device_type;                       \
    TEUCHOS_UNIT_TEST_TEMPLATE_1_INSTANT( DistributedSearchTree, hello_world,  \
                                          DeviceType##NODE )                   \
    TEUCHOS_UNIT_TEST_TEMPLATE_1_INSTANT(                                      \
        DistributedSearchTree, empty_tree_no_queries, DeviceType##NODE )       \
    TEUCHOS_UNIT_TEST_TEMPLATE_1_INSTANT( DistributedSearchTree,               \
                                          boost_comparison, DeviceType##NODE )

// Demangle the types
DTK_ETI_MANGLING_TYPEDEFS()

// Instantiate the tests
DTK_INSTANTIATE_N( UNIT_TEST_GROUP )


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

#ifndef NETKET_EXACT_SAMPLER_HPP
#define NETKET_EXACT_SAMPLER_HPP

#include <mpi.h>
#include <Eigen/Dense>
#include <iostream>
#include <limits>
#include "Utils/parallel_utils.hpp"
#include "Utils/random_utils.hpp"
#include "abstract_sampler.hpp"

namespace netket {

// Exact sampling using heat bath, mostly for testing purposes on small systems
template <class WfType>
class ExactSampler : public AbstractSampler<WfType> {
  WfType &psi_;

  const Hilbert &hilbert_;

  // number of visible units
  const int nv_;

  netket::default_random_engine rgen_;

  // states of visible units
  Eigen::VectorXd v_;

  Eigen::VectorXd accept_;
  Eigen::VectorXd moves_;

  int mynode_;
  int totalnodes_;

  const HilbertIndex hilbert_index_;

  const int dim_;

  std::discrete_distribution<int> dist_;

  std::vector<std::complex<double>> logpsivals_;
  std::vector<double> psivals_;

 public:
  explicit ExactSampler(WfType &psi)
      : psi_(psi),
        hilbert_(psi.GetHilbert()),
        nv_(hilbert_.Size()),
        hilbert_index_(hilbert_),
        dim_(hilbert_index_.NStates()) {
    Init();
  }

  void Init() {
    v_.resize(nv_);

    MPI_Comm_size(MPI_COMM_WORLD, &totalnodes_);
    MPI_Comm_rank(MPI_COMM_WORLD, &mynode_);

    if (!hilbert_.IsDiscrete()) {
      throw InvalidInputError(
          "Exact sampler works only for discrete "
          "Hilbert spaces");
    }

    accept_.resize(1);
    moves_.resize(1);

    Seed();

    Reset(true);

    InfoMessage() << "Exact sampler is ready " << std::endl;
  }

  void Seed(int baseseed = 0) {
    std::random_device rd;
    std::vector<int> seeds(totalnodes_);

    if (mynode_ == 0) {
      for (int i = 0; i < totalnodes_; i++) {
        seeds[i] = rd() + baseseed;
      }
    }

    SendToAll(seeds);

    rgen_.seed(seeds[mynode_]);
  }

  void Reset(bool initrandom) override {
    if (initrandom) {
      hilbert_.RandomVals(v_, rgen_);
    }

    double logmax = -std::numeric_limits<double>::infinity();

    logpsivals_.resize(dim_);
    psivals_.resize(dim_);

    for (int i = 0; i < dim_; ++i) {
      auto v = hilbert_index_.NumberToState(i);
      logpsivals_[i] = psi_.LogVal(v);
      logmax = std::max(logmax, std::real(logpsivals_[i]));
    }

    for (int i = 0; i < dim_; ++i) {
      psivals_[i] = std::norm(std::exp(logpsivals_[i] - logmax));
    }

    dist_ = std::discrete_distribution<int>(psivals_.begin(), psivals_.end());

    accept_ = Eigen::VectorXd::Zero(1);
    moves_ = Eigen::VectorXd::Zero(1);
  }

  void Sweep() override {
    int newstate = dist_(rgen_);
    v_ = hilbert_index_.NumberToState(newstate);

    accept_(0) += 1;
    moves_(0) += 1;
  }

  Eigen::VectorXd Visible() override { return v_; }

  void SetVisible(const Eigen::VectorXd &v) override { v_ = v; }

  WfType &Psi() override { return psi_; }

  Eigen::VectorXd Acceptance() const override {
    Eigen::VectorXd acc = accept_;
    for (int i = 0; i < 1; i++) {
      acc(i) /= moves_(i);
    }
    return acc;
  }
};

}  // namespace netket

#endif


#include <ShivLib/algorithm.hpp>
#include <ShivLib/dataStructures/array.hpp>
#include <boost/test/unit_test.hpp>

BOOST_AUTO_TEST_SUITE(algorithm_test)
BOOST_AUTO_TEST_CASE(comparitor_test){
    auto test = shiv::min(3, 8);
    BOOST_TEST(shiv::max(5, 8) == 8);
    BOOST_TEST(test == 3);
    shiv::array<int, 3> test_array{0, 1, 2};
    shiv::array<int, 3> test_array_2{1, 2, 3};
    BOOST_TEST(shiv::equal(test_array.begin(), test_array.end(), test_array_2.begin()) == false);
    shiv::array<int, 3> test_array_3{0, 1, 2};
    shiv::array<int, 3> test_array_4{0, 1, 2};
    BOOST_TEST(shiv::equal(test_array_3.begin(), test_array_3.end(), test_array_4.begin()) == true);
}
BOOST_AUTO_TEST_SUITE_END()


#include <ros/ros.h>
#include <sensor_msgs/PointCloud2.h>
#include <pcl_ros/point_cloud.h>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <boost/thread/thread.hpp>
#include "mongodb_store/SetParam.h"
#include <sstream>

#include <actionlib/server/simple_action_server.h>
#include <calibrate_chest/CalibrateCameraAction.h>

#include <sensor_msgs/JointState.h>

class CalibrateCameraServer {
private:

    ros::NodeHandle n;
    actionlib::SimpleActionServer<calibrate_chest::CalibrateCameraAction> server;
    std::string action_name;
    ros::ServiceClient client;
    std::string camera_name;
    std::string camera_topic;
    double desired_angle;
    calibrate_chest::CalibrateCameraFeedback feedback;
    calibrate_chest::CalibrateCameraResult result;

public:

    CalibrateCameraServer(const std::string& name, const std::string& camera_name, double angle) :
        server(n, name, boost::bind(&CalibrateCameraServer::execute_cb, this, _1), false),
        action_name(name),
        client(n.serviceClient<mongodb_store::SetParam>("/config_manager/set_param")),
        camera_name(camera_name),
        camera_topic(camera_name + "/depth/points"),
        desired_angle(angle)
    {
        server.start();
    }

private:

    bool is_inlier(const Eigen::Vector3f& point, const Eigen::Vector4f plane, double threshold) const
    {
        return fabs(point.dot(plane.segment<3>(0)) + plane(3)) < threshold;
    }

    void plot_best_plane(const pcl::PointCloud<pcl::PointXYZ>& points, const Eigen::Vector4f plane, double threshold) const
    {
        pcl::PointCloud<pcl::PointXYZ>::Ptr inlier_cloud(new pcl::PointCloud<pcl::PointXYZ>());
        pcl::PointCloud<pcl::PointXYZ>::Ptr outlier_cloud(new pcl::PointCloud<pcl::PointXYZ>());

        for (int i = 0; i < points.size(); ++i) {
            if (is_inlier(points[i].getVector3fMap(), plane, threshold)) {
                inlier_cloud->push_back(points[i]);
            }
            else {
                outlier_cloud->push_back(points[i]);
            }
        }

        pcl::visualization::PCLVisualizer viewer("3D Viewer");
        viewer.setBackgroundColor(0, 0, 0);
        viewer.addCoordinateSystem(1.0);
        viewer.initCameraParameters();

        pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> inlier_color_handler(inlier_cloud, 255, 0, 0);
        viewer.addPointCloud(inlier_cloud, inlier_color_handler, "inliers");

        pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> outlier_color_handler(outlier_cloud, 0, 0, 255);
        viewer.addPointCloud(outlier_cloud, outlier_color_handler, "outliers");

        while (!viewer.wasStopped())
        {
            viewer.spinOnce(100);
            boost::this_thread::sleep(boost::posix_time::microseconds(100000));
        }

    }

    void compute_plane(Eigen::Vector4f& plane, const pcl::PointCloud<pcl::PointXYZ>& points, int* inds) const
    {
        Eigen::Vector3f first = points[inds[1]].getVector3fMap() - points[inds[0]].getVector3fMap();
        Eigen::Vector3f second = points[inds[2]].getVector3fMap() - points[inds[0]].getVector3fMap();
        Eigen::Vector3f normal = first.cross(second);
        normal.normalize();
        plane.segment<3>(0) = normal;
        plane(3) = -normal.dot(points[inds[0]].getVector3fMap());
    }

    void extract_height_and_angle(const Eigen::Vector4f& plane)
    {
        feedback.status = "Checking and saving calibration...";
        server.publishFeedback(feedback);

        ROS_INFO("Ground plane: %f, %f, %f, %f", plane(0), plane(1), plane(2), plane(3));
        double dist = fabs(plane(3)/plane(2)); // distance along z axis
        double height = fabs(plane(3)/plane.segment<3>(0).squaredNorm()); // height
        ROS_INFO("Distance to plane along camera axis: %f", dist);
        ROS_INFO("Height above ground: %f", height);
        double angle = asin(height/dist);
        double angle_deg = 180.0f*angle/M_PI;
        ROS_INFO("Angle radians: %f", angle);
        ROS_INFO("Angle degrees: %f", angle_deg);

        result.angle = angle_deg;
        result.height = height;

        if (fabs(desired_angle - angle_deg) > 3.0f) {
            std::stringstream ss;
            ss << desired_angle;
            result.status = std::string("Angle not close enough to ") + ss.str() + " degrees.";
            server.setAborted(result);
            return;
        }

        mongodb_store::SetParam srv;
        char buffer[250];

        // store height above ground in datacentre
        ros::param::set(std::string("/") + camera_name + "_height", height);
        sprintf(buffer, "{\"path\":\"/%s_height\",\"value\":%f}", camera_name.c_str(), height);
        srv.request.param = buffer;
        if (!client.call(srv)) {
            ROS_ERROR("Failed to call set height, is config manager running?");
        }

        // store angle between camera and horizontal plane
        ros::param::set(std::string("/") + camera_name + "_angle", angle);
        sprintf(buffer, "{\"path\":\"/%s_angle\",\"value\":%f}", camera_name.c_str(), angle);
        srv.request.param = buffer;
        if (!client.call(srv)) {
            ROS_ERROR("Failed to call set angle, is config manager running?");
        }

        result.status = "Successfully computed and saved calibration.";
        server.setSucceeded(result);
    }

    void publish_calibration()
    {
        feedback.status = "Publishing calibration...";
        feedback.progress = 0.0f;

        // get calibration with respect to ground plane from the calibrate_chest node
        double height, angle;
        n.param<double>(std::string("/") + camera_name + "_height", height, 1.10f); // get the height calibration
        n.param<double>(std::string("/") + camera_name + "_angle", angle, 0.72f); // get the angle calibration

        ros::Rate rate(1.0f);
        ros::Publisher pub = n.advertise<sensor_msgs::JointState>("/chest_calibration_publisher/state", 1);

        int counter = 0; // only publish for the first 10 secs, transforms will stay in tf
        while (n.ok() && counter < 5) {
            sensor_msgs::JointState joint_state;
            joint_state.header.stamp = ros::Time::now();
            joint_state.name.resize(2);
            joint_state.position.resize(2);
            joint_state.velocity.resize(2);
            joint_state.name[0] = camera_name + "_height_joint";
            joint_state.name[1] = camera_name + "_tilt_joint";
            joint_state.position[0] = height;
            joint_state.position[1] = angle;
            joint_state.velocity[0] = 0;
            joint_state.velocity[1] = 0;
            pub.publish(joint_state);

            feedback.progress = float(counter+1)/5.0f;
            server.publishFeedback(feedback);

            rate.sleep();
            ++counter;
        }

        ROS_INFO("Stopping to publish chest transform after 5 seconds, quitting...");

        result.status = "Published calibration.";
        result.angle = 180.0f/M_PI*angle;
        result.height = height;
        server.setSucceeded(result);
    }

public:

    void msg_callback(const sensor_msgs::PointCloud2::ConstPtr& msg)
    {
        //if (!do_calibrate) {
        //    return;
        //}
        //do_calibrate = false;

        feedback.status = "Calibrating...";
        feedback.progress = 0.0f;
        server.publishFeedback(feedback);

        ROS_INFO("Got a pointcloud, calibrating...");
        pcl::PointCloud<pcl::PointXYZ> cloud;
        pcl::fromROSMsg(*msg, cloud);

        int nbr = cloud.size();

        int max = 1000; // ransac iterations
        double threshold = 0.02; // threshold for plane inliers

        Eigen::Vector4f best_plane; // best plane parameters found
        int best_inliers = -1; // best number of inliers

        int inds[3];

        Eigen::Vector4f plane;
        int inliers;
        for (int i = 0; i < max; ++i) {

            for (int j = 0; j < 3; ++j) {
                inds[j] = rand() % nbr; // get a random point
            }

            // check that the points aren't the same
            if (inds[0] == inds[1] || inds[0] == inds[2] || inds[1] == inds[2]) {
                continue;
            }

            compute_plane(plane, cloud, inds);
            inliers = 0;
            for (int j = 0; j < nbr; j += 30) { // count number of inliers
                if (is_inlier(cloud[j].getVector3fMap(), plane, threshold)) {
                    ++inliers;
                }
            }

            if (inliers > best_inliers) {
                best_plane = plane;
                best_inliers = inliers;
            }

            if (i % 10 == 0 || i == max - 1) {
                feedback.progress = float(i+1)/float(max);
                server.publishFeedback(feedback);
            }
        }

        extract_height_and_angle(best_plane); // find parameters and feed them to datacentre
        //plot_best_plane(cloud, best_plane, threshold); // visually evaluate plane fit
    }

    void execute_cb(const calibrate_chest::CalibrateCameraGoalConstPtr& goal)
    {
        if (goal->command == "calibrate") {
            sensor_msgs::PointCloud2::ConstPtr msg = ros::topic::waitForMessage<sensor_msgs::PointCloud2>(camera_topic, n, ros::Duration(5));
            if (msg) {
                msg_callback(msg);
            }
            else {
                result.status = "Did not receive any point cloud.";
                server.setAborted(result);
            }
        }
        else if (goal->command == "publish") {
            publish_calibration();
        }
        else {
            result.status = "Enter command \"calibrate\" or \"publish\".";
            server.setAborted(result);
        }
    }
};

int main(int argc, char** argv)
{
    ros::init(argc, argv, "calibrate_chest");
    CalibrateCameraServer calibrate(ros::this_node::getName(), "chest_xtion", 46.0);
    ros::spin();

	return 0;
}


/*
 * test_LOGIC.cpp
 *
 *  Created on: 2017年7月13日
 *      Author: fasiondog
 */

#ifdef TEST_ALL_IN_ONE
    #include <boost/test/unit_test.hpp>
#else
    #define BOOST_TEST_MODULE test_hikyuu_indicator_suite
    #include <boost/test/unit_test.hpp>
#endif

#include <hikyuu/StockManager.h>
#include <hikyuu/indicator/crt/IND_LOGIC.h>
#include <hikyuu/indicator/crt/PRICELIST.h>
#include <hikyuu/indicator/crt/KDATA.h>

using namespace hku;

/**
 * @defgroup test_indicator_LOGIC test_indicator_LOGIC
 * @ingroup test_hikyuu_indicator_suite
 * @{
 */

/** @par 检测点 */
BOOST_AUTO_TEST_CASE( test_LOGIC_AND ) {
    PriceList d1, d2, d3;
    for (size_t i = 0; i < 10; ++i) {
        d1.push_back(0);
        d2.push_back(1);
        d3.push_back(i);
    }

    Indicator data1 = PRICELIST(d1);
    Indicator data2 = PRICELIST(d2);
    Indicator data3 = PRICELIST(d3);

    /** @arg ind1为全0， ind2为全1 */
    Indicator result = IND_AND(data1, data2);
    BOOST_CHECK(result.size() == 10);
    BOOST_CHECK(result.getResultNumber() == 1);
    BOOST_CHECK(result.discard() == 0);
    for (size_t i = 0; i < 10; ++i) {
        BOOST_CHECK(result[i] == 0.0);
    }

    /** @arg ind为全0， val为1 */
    result = IND_AND(data1, 1.0);
    BOOST_CHECK(result.size() == 10);
    BOOST_CHECK(result.getResultNumber() == 1);
    BOOST_CHECK(result.discard() == 0);
    for (size_t i = 0; i < 10; ++i) {
        BOOST_CHECK(result[i] == 0.0);
    }

    /** @arg ind1为全0， ind2为从0开始的整数 */
    result = IND_AND(data1, data3);
    BOOST_CHECK(result.size() == 10);
    BOOST_CHECK(result.getResultNumber() == 1);
    BOOST_CHECK(result.discard() == 0);
    for (size_t i = 0; i < 10; ++i) {
        BOOST_CHECK(result[i] == 0.0);
    }

    /** @arg ind1为全1， ind2为从0开始的整数 */
    result = IND_AND(data2, data3);
    BOOST_CHECK(result.size() == 10);
    BOOST_CHECK(result.getResultNumber() == 1);
    BOOST_CHECK(result.discard() == 0);
    BOOST_CHECK(result[0] == 0.0);
    for (size_t i = 1; i < 10; ++i) {
        BOOST_CHECK(result[i] == 1.0);
    }

    /** @arg 两个ind的size不同 */
    Indicator data4;
    result = data1 < data4;
    BOOST_CHECK(result.empty());
    BOOST_CHECK(result.size() == 0);

    /** @arg 两个ind的size相同，但result_number不同 */
    StockManager& sm = StockManager::instance();
    Stock stock = sm.getStock("sh600000");
    KQuery query(0, 10);
    KData kdata = stock.getKData(query);
    Indicator k = KDATA(kdata);
    BOOST_CHECK(k.size() == data1.size());
    result = (k < data1);
    BOOST_CHECK(result.size() == k.size());
    for (size_t i = 0; i < result.size(); ++i) {
        BOOST_CHECK(result[i] == 0.0);
    }
}

/** @par 检测点 */
BOOST_AUTO_TEST_CASE( test_LOGIC_OR ) {
    PriceList d1, d2, d3;
    for (size_t i = 0; i < 10; ++i) {
        d1.push_back(0);
        d2.push_back(1);
        d3.push_back(i);
    }

    Indicator data1 = PRICELIST(d1);
    Indicator data2 = PRICELIST(d2);
    Indicator data3 = PRICELIST(d3);

    /** @arg ind1为全0， ind2为全1 */
    Indicator result = IND_OR(data1, data2);
    BOOST_CHECK(result.size() == 10);
    BOOST_CHECK(result.getResultNumber() == 1);
    BOOST_CHECK(result.discard() == 0);
    for (size_t i = 0; i < 10; ++i) {
        BOOST_CHECK(result[i] == 1.0);
    }

    /** @arg ind为全0， val为1 */
    result = IND_OR(data1, 1.0);
    BOOST_CHECK(result.size() == 10);
    BOOST_CHECK(result.getResultNumber() == 1);
    BOOST_CHECK(result.discard() == 0);
    for (size_t i = 0; i < 10; ++i) {
        BOOST_CHECK(result[i] == 1.0);
    }

    /** @arg ind1为全0， ind2为从0开始的整数 */
    result = IND_OR(data1, data3);
    BOOST_CHECK(result.size() == 10);
    BOOST_CHECK(result.getResultNumber() == 1);
    BOOST_CHECK(result.discard() == 0);
    BOOST_CHECK(result[0] == 0.0);
    for (size_t i = 1; i < 10; ++i) {
        BOOST_CHECK(result[i] == 1.0);
    }

    /** @arg ind1为全1， ind2为从0开始的整数 */
    result = IND_OR(data2, data3);
    BOOST_CHECK(result.size() == 10);
    BOOST_CHECK(result.getResultNumber() == 1);
    BOOST_CHECK(result.discard() == 0);
    for (size_t i = 0; i < 10; ++i) {
        BOOST_CHECK(result[i] == 1.0);
    }

    /** @arg 两个ind的size不同 */
    Indicator data4;
    result = data1 < data4;
    BOOST_CHECK(result.empty());
    BOOST_CHECK(result.size() == 0);

    /** @arg 两个ind的size相同，但result_number不同 */
    StockManager& sm = StockManager::instance();
    Stock stock = sm.getStock("sh600000");
    KQuery query(0, 10);
    KData kdata = stock.getKData(query);
    Indicator k = KDATA(kdata);
    BOOST_CHECK(k.size() == data1.size());
    result = (k < data1);
    BOOST_CHECK(result.size() == k.size());
    for (size_t i = 0; i < result.size(); ++i) {
        BOOST_CHECK(result[i] == 0.0);
    }
}

/** @} */


#include <boost/test/unit_test.hpp>
#include <limits>
#include <algorithm>
#include <iostream>
#include "algorithms/data_structures/array/find_kth_largest_element.hpp"

BOOST_AUTO_TEST_SUITE(TestFindKthLargestElement)

BOOST_AUTO_TEST_CASE(invalid_args)
{
    {
        std::vector<int> numbers = {};
        size_t k = 10;
        int expected = 0;
        BOOST_CHECK(expected ==
                    Algo::DS::Array::KthLargestElement::Find(numbers, k));
    }

    {
        std::vector<int> numbers = {1};
        size_t k = 10;
        int expected = 0;
        BOOST_CHECK(expected ==
                    Algo::DS::Array::KthLargestElement::Find(numbers, k));
    }
}

BOOST_AUTO_TEST_CASE(valid_args)
{
    {
        std::vector<int> numbers = {1};
        size_t k = 1;
        int expected = 1;
        BOOST_CHECK(expected ==
                    Algo::DS::Array::KthLargestElement::Find(numbers, k));
    }

    {
        std::vector<int> numbers = {1, 2};
        size_t k = 1;
        int expected = 2;
        BOOST_CHECK(expected ==
                    Algo::DS::Array::KthLargestElement::Find(numbers, k));
    }

    {
        std::vector<int> numbers = {2, 1};
        size_t k = 1;
        int expected = 2;
        BOOST_CHECK(expected ==
                    Algo::DS::Array::KthLargestElement::Find(numbers, k));
    }

    {
        std::vector<int> numbers = {2, 1};
        size_t k = 2;
        int expected = 1;
        BOOST_CHECK(expected ==
                    Algo::DS::Array::KthLargestElement::Find(numbers, k));
    }

    {
        std::vector<int> numbers = {2, 1, 7, 3, 6, 5};
        size_t k = 4;
        int expected = 3;
        BOOST_CHECK(expected ==
                    Algo::DS::Array::KthLargestElement::Find(numbers, k));
    }

    {
        std::vector<int> numbers = {3,2,3,1,2,4,5,5,6};
        size_t k = 4;
        int expected = 4;
        BOOST_CHECK(expected ==
                    Algo::DS::Array::KthLargestElement::Find(numbers, k));
    }
}

BOOST_AUTO_TEST_CASE(valid_args_rand) {
    std::mt19937 mt(42);
    std::uniform_int_distribution<size_t> distSize(0, 1000);
    std::uniform_int_distribution<int> distValues(
              std::numeric_limits<int>::min(), std::numeric_limits<int>::max());
    for (size_t i = 0; i < 100; ++i) {
        size_t size = distSize(mt);
        std::vector<int> nums(size, 0);
        for (int& value : nums) {
            value = distValues(mt);
        }

        std::uniform_int_distribution<size_t> distK(0, size);
        size_t k = distK(mt);

        std::vector<int> sortedNums = nums;
        std::sort(sortedNums.begin(), sortedNums.end());
        const int expected = sortedNums[sortedNums.size() - k];
        const int result = Algo::DS::Array::KthLargestElement::Find(nums, k);
        if (expected != result) {
            std::cout << "WRONG" << std::endl;
        }
    }
}

BOOST_AUTO_TEST_SUITE_END()


/*******************************************************************************
 * Copyright 2013-2014 Sebastian Niemann <niemann@sra.uni-hannover.de>.
 *
 * Licensed under the MIT License (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://opensource.org/licenses/MIT
 *
 * Developers:
 *   Sebastian Niemann - Lead developer
 *   Daniel Kiechle - Unit testing
 ******************************************************************************/
#include <Expected.hpp>
using armadilloJava::Expected;

#include <iostream>
using std::cout;
using std::endl;

#include <utility>
using std::pair;

#include <armadillo>
using arma::Mat;
using arma::Col;
using arma::uword;

#include <InputClass.hpp>
using armadilloJava::InputClass;

#include <Input.hpp>
using armadilloJava::Input;

namespace armadilloJava {
  class ExpectedInPlaceGenMatRowIndsGenMat : public Expected {
    public:
      ExpectedInPlaceGenMatRowIndsGenMat() {
        cout << "Compute ExpectedInPlaceGenMatRowIndsGenMat(): " << endl;

        vector<vector<pair<string, void*>>> inputs = Input::getTestParameters({
          InputClass::GenMat,
          InputClass::RowInds,
          InputClass::GenMat
        });

        for (vector<pair<string, void*>> input : inputs) {
          _fileSuffix = "";

          int n = 0;
          for (pair<string, void*> value : input) {
            switch (n) {
              case 0:
                _fileSuffix += value.first;
                _genMatA = *static_cast<Mat<double>*>(value.second);
                break;
              case 1:
                _fileSuffix += "," + value.first;
                _rowInds = *static_cast<Col<uword>*>(value.second);
                break;
              case 2:
                _fileSuffix += "," + value.first;
                _genMatB = *static_cast<Mat<double>*>(value.second);
                break;
            }
            ++n;
          }

          cout << "Using input: " << _fileSuffix << endl;

          _copyOfGenMatA = _genMatA;
          _copyOfRowInds = _rowInds;
          _copyOfGenMatB = _genMatB;

          expectedRowsEqual();

          _genMatA = _copyOfGenMatA;
          _rowInds = _copyOfRowInds;
          _genMatB = _copyOfGenMatB;
          expectedRowsPlus();

          _genMatA = _copyOfGenMatA;
          _rowInds = _copyOfRowInds;
          _genMatB = _copyOfGenMatB;
          expectedRowsMinus();

          _genMatA = _copyOfGenMatA;
          _rowInds = _copyOfRowInds;
          _genMatB = _copyOfGenMatB;
          expectedRowsElemTimes();

          _genMatA = _copyOfGenMatA;
          _rowInds = _copyOfRowInds;
          _genMatB = _copyOfGenMatB;
          expectedRowsElemDivide();

          _genMatA = _copyOfGenMatA;
          _rowInds = _copyOfRowInds;
          _genMatB = _copyOfGenMatB;
          expectedEach_rowEqual();

          _genMatA = _copyOfGenMatA;
          _rowInds = _copyOfRowInds;
          _genMatB = _copyOfGenMatB;
          expectedEach_rowPlus();

          _genMatA = _copyOfGenMatA;
          _rowInds = _copyOfRowInds;
          _genMatB = _copyOfGenMatB;
          expectedEach_rowMinus();

          _genMatA = _copyOfGenMatA;
          _rowInds = _copyOfRowInds;
          _genMatB = _copyOfGenMatB;
          expectedEach_rowElemTimes();

          _genMatA = _copyOfGenMatA;
          _rowInds = _copyOfRowInds;
          _genMatB = _copyOfGenMatB;
          expectedEach_rowElemDivide();
        }

        cout << "done." << endl;
      }

    protected:
      Mat<double> _genMatA;
      Mat<double> _copyOfGenMatA;

      Col<uword> _rowInds;
      Col<uword> _copyOfRowInds;

      Mat<double> _genMatB;
      Mat<double> _copyOfGenMatB;

      void expectedRowsEqual() {
        for(int i = 0; i < _rowInds.n_elem; i++) {
          if(_rowInds.at(i) >= _genMatA.n_rows) {
            return;
          }
        }

        if(_genMatB.n_rows != _rowInds.n_elem) {
          return;
        }

        if(_genMatB.n_cols != _genMatA.n_cols) {
          return;
        }

        cout << "- Compute expectedRowsEqual() ... ";

        _genMatA.rows(_rowInds) = _genMatB;
        save<double>("Mat.rowsEqual", _genMatA);

        cout << "done." << endl;
      }

      void expectedRowsPlus() {
        for(int i = 0; i < _rowInds.n_elem; i++) {
          if(_rowInds.at(i) >= _genMatA.n_rows) {
            return;
          }
        }

        if(_genMatB.n_rows != _rowInds.n_elem) {
          return;
        }

        if(_genMatB.n_cols != _genMatA.n_cols) {
          return;
        }

        cout << "- Compute expectedRowsPlus() ... ";

        _genMatA.rows(_rowInds) += _genMatB;
        save<double>("Mat.rowsPlus", _genMatA);

        cout << "done." << endl;
      }

      void expectedRowsMinus() {
        for(int i = 0; i < _rowInds.n_elem; i++) {
          if(_rowInds.at(i) >= _genMatA.n_rows) {
            return;
          }
        }

        if(_genMatB.n_rows != _rowInds.n_elem) {
          return;
        }

        if(_genMatB.n_cols != _genMatA.n_cols) {
          return;
        }

        cout << "- Compute expectedRowsMinus() ... ";

        _genMatA.rows(_rowInds) -= _genMatB;
        save<double>("Mat.rowsMinus", _genMatA);

        cout << "done." << endl;
      }

      void expectedRowsElemTimes() {
        for(int i = 0; i < _rowInds.n_elem; i++) {
          if(_rowInds.at(i) >= _genMatA.n_rows) {
            return;
          }
        }

        if(_genMatB.n_rows != _rowInds.n_elem) {
          return;
        }

        if(_genMatB.n_cols != _genMatA.n_cols) {
          return;
        }

        cout << "- Compute expectedRowsElemTimes() ... ";

        _genMatA.rows(_rowInds) %= _genMatB;
        save<double>("Mat.rowsElemTimes", _genMatA);

        cout << "done." << endl;
      }

      void expectedRowsElemDivide() {
        for(int i = 0; i < _rowInds.n_elem; i++) {
          if(_rowInds.at(i) >= _genMatA.n_rows) {
            return;
          }
        }

        if(_genMatB.n_rows != _rowInds.n_elem) {
          return;
        }

        if(_genMatB.n_cols != _genMatA.n_cols) {
          return;
        }

        cout << "- Compute expectedRowsElemDivide() ... ";

        _genMatA.rows(_rowInds) /= _genMatB;
        save<double>("Mat.rowsElemDivide", _genMatA);

        cout << "done." << endl;
      }

      void expectedEach_rowEqual() {
        for(int i = 0; i < _rowInds.n_elem; i++) {
          if(_rowInds.at(i) >= _genMatA.n_rows) {
            return;
          }
        }

        if(!_genMatB.is_rowvec()) {
          return;
        }

        if(_genMatB.n_cols != _genMatA.n_cols) {
          return;
        }

        cout << "- Compute expectedEach_rowEqual() ... ";

        _genMatA.each_row(_rowInds) = _genMatB;
        save<double>("Mat.each_rowEqual", _genMatA);

        cout << "done." << endl;
      }

      void expectedEach_rowPlus() {
        for(int i = 0; i < _rowInds.n_elem; i++) {
          if(_rowInds.at(i) >= _genMatA.n_rows) {
            return;
          }
        }

        if(!_genMatB.is_rowvec()) {
          return;
        }

        if(_genMatB.n_cols != _genMatA.n_cols) {
          return;
        }

        cout << "- Compute expectedEach_rowPlus() ... ";

        _genMatA.each_row(_rowInds) += _genMatB;
        save<double>("Mat.each_rowPlus", _genMatA);

        cout << "done." << endl;
      }

      void expectedEach_rowMinus() {
        for(int i = 0; i < _rowInds.n_elem; i++) {
          if(_rowInds.at(i) >= _genMatA.n_rows) {
            return;
          }
        }

        if(!_genMatB.is_rowvec()) {
          return;
        }

        if(_genMatB.n_cols != _genMatA.n_cols) {
          return;
        }

        cout << "- Compute expectedEach_rowMinus() ... ";

        _genMatA.each_row(_rowInds) -= _genMatB;
        save<double>("Mat.each_rowMinus", _genMatA);

        cout << "done." << endl;
      }

      void expectedEach_rowElemTimes() {
        for(int i = 0; i < _rowInds.n_elem; i++) {
          if(_rowInds.at(i) >= _genMatA.n_rows) {
            return;
          }
        }

        if(!_genMatB.is_rowvec()) {
          return;
        }

        if(_genMatB.n_cols != _genMatA.n_cols) {
          return;
        }

        cout << "- Compute expectedEach_rowElemTimes() ... ";

        _genMatA.each_row(_rowInds) %= _genMatB;
        save<double>("Mat.each_rowElemTimes", _genMatA);

        cout << "done." << endl;
      }

      void expectedEach_rowElemDivide() {
        for(int i = 0; i < _rowInds.n_elem; i++) {
          if(_rowInds.at(i) >= _genMatA.n_rows) {
            return;
          }
        }

        if(!_genMatB.is_rowvec()) {
          return;
        }

        if(_genMatB.n_cols != _genMatA.n_cols) {
          return;
        }

        cout << "- Compute expectedEach_rowElemDivide() ... ";

        _genMatA.each_row(_rowInds) /= _genMatB;
        save<double>("Mat.each_rowElemDivide", _genMatA);

        cout << "done." << endl;
      }

  };
}